diff --git a/app/controllers/concerns/filter_parameters.rb b/app/controllers/concerns/filter_parameters.rb index 6e3a476157..1885ee8746 100644 --- a/app/controllers/concerns/filter_parameters.rb +++ b/app/controllers/concerns/filter_parameters.rb @@ -74,6 +74,7 @@ def form_params :engineers_teach_physics, :funding, :has_vacancies, + :university_degree_status, :applications_open, :l, :latitude, diff --git a/app/controllers/find/application_controller.rb b/app/controllers/find/application_controller.rb index f99355c221..e5ae70d894 100644 --- a/app/controllers/find/application_controller.rb +++ b/app/controllers/find/application_controller.rb @@ -16,11 +16,23 @@ def render_feedback_component @render_feedback_component = true end + def backlink_query_parameters + ResultsView.new( + query_parameters: request.query_parameters[form_name].presence || request.query_parameters + ) + .query_parameters_with_defaults + .except(form_name) + end + # DFE Analytics namespace def current_namespace 'find' end + def teacher_degree_apprenticeship_active? + Settings.current_recruitment_cycle_year.to_i > 2024 && FeatureService.enabled?(:teacher_degree_apprenticeship) + end + private def provider diff --git a/app/controllers/find/search/no_degree_and_requires_visa_sponsorship_controller.rb b/app/controllers/find/search/no_degree_and_requires_visa_sponsorship_controller.rb new file mode 100644 index 0000000000..cdd510c4d3 --- /dev/null +++ b/app/controllers/find/search/no_degree_and_requires_visa_sponsorship_controller.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Find + module Search + class NoDegreeAndRequiresVisaSponsorshipController < Find::ApplicationController + def back_path + find_visa_status_path(backlink_query_parameters) + end + helper_method :back_path + + # this controller is an exit page, so returning the latest form + def form_name = :find_visa_status_form + end + end +end diff --git a/app/controllers/find/search/subjects_controller.rb b/app/controllers/find/search/subjects_controller.rb index a5ffe85cec..b34f90dd2c 100644 --- a/app/controllers/find/search/subjects_controller.rb +++ b/app/controllers/find/search/subjects_controller.rb @@ -16,7 +16,7 @@ def create @subjects_form = SubjectsForm.new(subjects: sanitised_subject_codes, age_group: form_params[:age_group]) if @subjects_form.valid? - redirect_to find_visa_status_path(filter_params[:find_subjects_form]) + redirect_to next_page else render :new end @@ -24,6 +24,14 @@ def create private + def next_page + if teacher_degree_apprenticeship_active? + find_university_degree_status_path(filter_params[:find_subjects_form]) + else + find_visa_status_path(filter_params[:find_subjects_form]) + end + end + def sanitised_subject_codes form_params['subjects'].compact_blank! end diff --git a/app/controllers/find/search/university_degree_status_controller.rb b/app/controllers/find/search/university_degree_status_controller.rb new file mode 100644 index 0000000000..b4c2ebd50e --- /dev/null +++ b/app/controllers/find/search/university_degree_status_controller.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +module Find + module Search + class UniversityDegreeStatusController < Find::ApplicationController + include FilterParameters + + def new + @university_degree_status_form = UniversityDegreeStatusForm.new( + university_degree_status: params[:university_degree_status] + ) + end + + def create + @university_degree_status_form = UniversityDegreeStatusForm.new( + university_degree_status: form_params[:university_degree_status] + ) + + if @university_degree_status_form.valid? + redirect_to find_visa_status_path(filter_params[:find_university_degree_status_form]) + else + render :new + end + end + + def form_name = :find_university_degree_status_form + + def back_path + find_subjects_path(backlink_query_parameters) + end + helper_method :back_path + end + end +end diff --git a/app/controllers/find/search/visa_status_controller.rb b/app/controllers/find/search/visa_status_controller.rb index 8fecc86a9d..a258208044 100644 --- a/app/controllers/find/search/visa_status_controller.rb +++ b/app/controllers/find/search/visa_status_controller.rb @@ -7,23 +7,17 @@ class VisaStatusController < Find::ApplicationController include DefaultVacancies include DefaultApplicationsOpen - before_action :build_backlink_query_parameters - helper_method :back_path - def new @visa_status_form = VisaStatusForm.new(visa_status: params[:visa_status]) end def create - @visa_status_form = VisaStatusForm.new(visa_status: form_params[:visa_status]) + @visa_status_form = VisaStatusForm.new( + visa_status: form_params[:visa_status] + ) if @visa_status_form.valid? - redirect_to find_results_path(form_params.merge( - subjects: sanitised_subject_codes, - has_vacancies: default_vacancies, - applications_open: default_applications_open, - can_sponsor_visa: form_params[:visa_status] - )) + redirect_to next_step_path else render :new end @@ -31,25 +25,45 @@ def create private + def next_step_path + if course_type_answer_determiner.show_exit_page? + find_no_degree_and_requires_visa_sponsorship_path(filter_params[:find_visa_status_form]) + else + find_results_path( + form_params.merge( + subjects: sanitised_subject_codes, + has_vacancies: default_vacancies, + applications_open: default_applications_open, + can_sponsor_visa: form_params[:visa_status] + ) + ) + end + end + + def course_type_answer_determiner + @course_type_answer_determiner ||= CourseTypeAnswerDeterminer.new( + university_degree_status: form_params[:university_degree_status], + age_group: form_params[:age_group], + visa_status: form_params[:visa_status] + ) + end + def sanitised_subject_codes form_params['subjects'].compact_blank! end def form_name = :find_visa_status_form - def back_path(backlink_params) + def back_path + return find_university_degree_status_path(backlink_query_parameters) if teacher_degree_apprenticeship_active? + if params[:age_group] == 'further_education' || (params[:find_visa_status_form] && params[:find_visa_status_form][:age_group] == 'further_education') - find_age_groups_path(backlink_params) + find_age_groups_path(backlink_query_parameters) else - find_subjects_path(backlink_params) + find_subjects_path(backlink_query_parameters) end end - - def build_backlink_query_parameters - @backlink_query_parameters = ResultsView.new(query_parameters: request.query_parameters[:find_visa_status_form].presence || request.query_parameters) - .query_parameters_with_defaults - .except(:find_visa_status_form) - end + helper_method :back_path end end end diff --git a/app/forms/find/university_degree_status_form.rb b/app/forms/find/university_degree_status_form.rb new file mode 100644 index 0000000000..75033a8141 --- /dev/null +++ b/app/forms/find/university_degree_status_form.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +module Find + class UniversityDegreeStatusForm + include ActiveModel::Model + attr_accessor :university_degree_status + + validates :university_degree_status, presence: true + end +end diff --git a/app/models/course.rb b/app/models/course.rb index de78713aaf..5c7963bba9 100644 --- a/app/models/course.rb +++ b/app/models/course.rb @@ -323,6 +323,14 @@ def new_draft_attributes ) } + scope :with_course_type, lambda { |course_type| + if course_type == :undergraduate + where(program_type: Course.program_types[:teacher_degree_apprenticeship]) + else + where.not(program_type: Course.program_types[:teacher_degree_apprenticeship]) + end + } + def self.entry_requirement_options_without_nil_choice ENTRY_REQUIREMENT_OPTIONS.reject { |option| option == :not_set }.keys.map(&:to_s) end diff --git a/app/services/course_search_service.rb b/app/services/course_search_service.rb index 28b9251c63..fc282507d9 100644 --- a/app/services/course_search_service.rb +++ b/app/services/course_search_service.rb @@ -6,15 +6,22 @@ class CourseSearchService def initialize( filter:, sort: nil, - course_scope: Course + course_scope: Course, + course_type_answer_determiner: Find::CourseTypeAnswerDeterminer ) @filter = filter || {} @course_scope = course_scope @sort = sort + @course_type_answer_determiner = course_type_answer_determiner.new( + university_degree_status: @filter['university_degree_status'], + age_group: @filter['age_group'], + visa_status: @filter['visa_status'] + ) end def call scope = course_scope + scope = scope.with_course_type(course_type) scope = scope.with_salary if funding_filter_salary? scope = scope.with_qualifications(qualifications) if qualifications.any? scope = scope.application_status_open if applications_open? @@ -76,6 +83,12 @@ def call private + def course_type + return :undergraduate if @course_type_answer_determiner.show_undergraduate_courses? + + :postgraduate + end + def expand_university? filter[:expand_university].to_s.downcase == 'true' end @@ -197,7 +210,7 @@ def funding_filter_salary? end def qualifications - return [] if filter[:qualification].blank? + return [] if filter[:qualification].blank? || course_type == :undergraduate filter[:qualification] = filter[:qualification].values if filter[:qualification].is_a?(Hash) filter[:qualification] = filter[:qualification].split(',') if filter[:qualification].is_a?(String) diff --git a/app/services/find/course_type_answer_determiner.rb b/app/services/find/course_type_answer_determiner.rb new file mode 100644 index 0000000000..d22379c2c3 --- /dev/null +++ b/app/services/find/course_type_answer_determiner.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +module Find + class CourseTypeAnswerDeterminer + attr_reader :age_group, :visa_status, :university_degree_status + + def initialize(age_group:, visa_status:, university_degree_status:) + @age_group = age_group + @visa_status = visa_status + @university_degree_status = university_degree_status + end + + def show_undergraduate_courses? + !further_education? && university_degree_question_answered? && no_degrees? && does_not_require_visa_sponsorship? + end + + def show_exit_page? + !further_education? && university_degree_question_answered? && no_degrees? && require_visa_sponsorship? + end + + private + + def university_degree_question_answered? + university_degree_status.present? + end + + def further_education? + age_group == 'further_education' + end + + def require_visa_sponsorship? + ActiveModel::Type::Boolean.new.cast(visa_status) + end + + def does_not_require_visa_sponsorship? + !require_visa_sponsorship? + end + + def no_degrees? + !degrees? + end + + def degrees? + ActiveModel::Type::Boolean.new.cast(university_degree_status) + end + end +end diff --git a/app/view_objects/find/result_filters/filters_view.rb b/app/view_objects/find/result_filters/filters_view.rb index a2c01b9538..057fe89ed4 100644 --- a/app/view_objects/find/result_filters/filters_view.rb +++ b/app/view_objects/find/result_filters/filters_view.rb @@ -39,6 +39,14 @@ def qualification_params_nil? params[:qualification].nil? end + def show_undergraduate_courses? + CourseTypeAnswerDeterminer.new( + age_group: params[:age_group], + university_degree_status: params[:university_degree_status], + visa_status: params[:visa_status] + ).show_undergraduate_courses? + end + def location_query? params[:l] == '1' end diff --git a/app/views/find/result_filters/_all.html.erb b/app/views/find/result_filters/_all.html.erb index 2730a36c59..bb36d6556c 100644 --- a/app/views/find/result_filters/_all.html.erb +++ b/app/views/find/result_filters/_all.html.erb @@ -5,15 +5,21 @@
<%= form.submit "Apply filters", name: nil, class: "govuk-button", data: { qa: "apply-filters" } %> - <%= render "find/result_filters/radius", form: %> - <%= render "find/result_filters/visa_filter", form: %> - <%= render "find/result_filters/engineers_teach_physics_filter", form: %> - <%= render "find/result_filters/study_type_filter", form: %> - <%= render "find/result_filters/qualifications_filter", form: %> - <%= render "find/result_filters/degree_required_filter", form: %> - <%= render "find/result_filters/send_filter", form: %> - <%= render "find/result_filters/salary_filter", form: %> - <%= render "find/result_filters/applications_open_filter", form: %> - <%= render "find/result_filters/hidden_fields", form: %> + <% if @filters_view.show_undergraduate_courses? %> + <%= render "find/result_filters/send_filter", form: %> + <%= render "find/result_filters/applications_open_filter", form: %> + <%= render "find/result_filters/hidden_fields", form: %> + <% else %> + <%= render "find/result_filters/radius", form: %> + <%= render "find/result_filters/visa_filter", form: %> + <%= render "find/result_filters/engineers_teach_physics_filter", form: %> + <%= render "find/result_filters/study_type_filter", form: %> + <%= render "find/result_filters/qualifications_filter", form: %> + <%= render "find/result_filters/degree_required_filter", form: %> + <%= render "find/result_filters/send_filter", form: %> + <%= render "find/result_filters/salary_filter", form: %> + <%= render "find/result_filters/applications_open_filter", form: %> + <%= render "find/result_filters/hidden_fields", form: %> + <% end %>
<% end %> diff --git a/app/views/find/result_filters/_hidden_fields.html.erb b/app/views/find/result_filters/_hidden_fields.html.erb index fdcde44550..fbfea7d9df 100644 --- a/app/views/find/result_filters/_hidden_fields.html.erb +++ b/app/views/find/result_filters/_hidden_fields.html.erb @@ -15,3 +15,7 @@ <%= form.hidden_field(:subject_codes, multiple: true, value: subject_code) %> <% end %> <% end %> + +<% if params[:university_degree_status] %> + <%= form.hidden_field(:university_degree_status, value: params[:university_degree_status]) %> +<% end %> diff --git a/app/views/find/search/no_degree_and_requires_visa_sponsorship/new.html.erb b/app/views/find/search/no_degree_and_requires_visa_sponsorship/new.html.erb new file mode 100644 index 0000000000..76f16917e9 --- /dev/null +++ b/app/views/find/search/no_degree_and_requires_visa_sponsorship/new.html.erb @@ -0,0 +1,33 @@ +<%= content_for :page_title, t("find.no_degree_and_requires_visa_sponsorship_page.title") %> + +<% content_for :before_content do %> + <%= govuk_back_link( + text: "Back", + href: back_path, + html_attributes: { + data: { qa: "page-back" } + } + ) %> +<% end %> + +
+
+

+ <%= t("find.no_degree_and_requires_visa_sponsorship_page.title") %>. +

+ +

+ <%= t("find.no_degree_and_requires_visa_sponsorship_page.content") %> +

+ +

+ <%= t( + "find.no_degree_and_requires_visa_sponsorship_page.explanation", + link: govuk_link_to( + t("find.get_into_teaching.undergraduate_degree_courses"), + t("find.get_into_teaching.url_ways_to_train_no_degree") + ) + ).html_safe %> +

+
+
diff --git a/app/views/find/search/university_degree_status/new.html.erb b/app/views/find/search/university_degree_status/new.html.erb new file mode 100644 index 0000000000..de142d5dff --- /dev/null +++ b/app/views/find/search/university_degree_status/new.html.erb @@ -0,0 +1,43 @@ +<%= content_for :page_title, title_with_error_prefix(t("find.university_degree_status_page.title"), @university_degree_status_form.errors.any?) %> +<% content_for :before_content do %> + <%= govuk_back_link( + text: "Back", + href: back_path, + html_attributes: { + data: { qa: "page-back" } + } + ) %> +<% end %> + +
+
+ <%= form_with(model: @university_degree_status_form, url: find_university_degree_status_create_path, method: :get) do |f| %> + <%= f.govuk_error_summary %> + + <%= render Find::HiddenFieldsComponent.new( + query_params: request.query_parameters, + form: f, + form_name: :find_university_degree_status_form, + exclude_keys: ["university_degree_status"] + ) %> + + <%= f.govuk_radio_buttons_fieldset :university_degree_status, legend: { size: "l", tag: "h1" } do %> +

+ <%= t("find.university_degree_status_page.content") %> +

+

+ <%= govuk_link_to( + t("find.get_into_teaching.train_to_be_a_teacher"), + t("find.get_into_teaching.url_train_to_be_a_teacher"), + target: "_blank", + rel: "noopener" + ) %>. +

+ <%= f.govuk_radio_button :university_degree_status, "true", link_errors: true %> + <%= f.govuk_radio_button :university_degree_status, "false" %> + <% end %> + + <%= f.govuk_submit t("continue") %> + <% end %> +
+
diff --git a/app/views/find/search/visa_status/new.html.erb b/app/views/find/search/visa_status/new.html.erb index f32fa4fe24..899b8cab41 100644 --- a/app/views/find/search/visa_status/new.html.erb +++ b/app/views/find/search/visa_status/new.html.erb @@ -2,7 +2,7 @@ <% content_for :before_content do %> <%= govuk_back_link( text: "Back", - href: back_path(@backlink_query_parameters), + href: back_path, html_attributes: { data: { qa: "page-back" } } @@ -25,7 +25,7 @@

You’ll need to have the right to work or study in the UK for the duration of your teacher training course.

-

<%= govuk_link_to("Check if you need a UK visa and find out how to apply", t("find.get_into_teaching.url_visas_for_non_uk_trainees"), target: "_blank", rel: "noopener") %>.

+

<%= govuk_link_to("Check if you need a UK visa and find out how to apply (opens in new tab)", t("find.get_into_teaching.url_visas_for_non_uk_trainees"), target: "_blank", rel: "noopener") %>.

If you do not already have the right to work or study, you should only apply to courses that have visa sponsorship available.

diff --git a/config/locales/en.yml b/config/locales/en.yml index 949d80e332..7c867b1d91 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -322,6 +322,8 @@ en: subject: Subject add_a_level_to_a_list: add_another_a_level: Do you want to add another A level? + find_university_degree_status_form: + university_degree_status: Do you have a university degree? label: what_a_level_is_required: other_subject: Subjects @@ -357,6 +359,10 @@ en: accept_a_level_equivalency_options: "yes": "Yes" "no": "No" + find_university_degree_status_form: + university_degree_status_options: + "true": "Yes, I have a degree or am studying for one" + "false": "No, I do not have a degree" course: application_status: open: "Course opened" diff --git a/config/locales/find.yml b/config/locales/find.yml index f9f96389fa..955b38d297 100644 --- a/config/locales/find.yml +++ b/config/locales/find.yml @@ -70,6 +70,13 @@ en: title: Select a subject - primary_title: Primary courses with subject specialisms secondary_title: Which secondary subjects do you want to teach? + university_degree_status_page: + title: Do you have a university degree + content: You need a bachelor’s degree or equivalent to do postgraduate teacher training. You do not need a degree to apply for a teacher degree apprenticeship (TDA). + no_degree_and_requires_visa_sponsorship_page: + title: You are not eligible for teacher training courses on this service + content: You do not have a university degree, and you require visa sponsorship, so you are not eligible for postgraduate teacher training or teacher degree apprenticeships. + explanation: You may be eligible for %{link}, which are not on this service. courses: fees_component: view: @@ -240,6 +247,9 @@ en: learn_more_about_non_uk_qualifications_html: Learn more about how to check your qualifications meet the required standard. tel: 0800 389 2500 opening_times: "Monday to Friday, 8.30am to 5.30pm" + undergraduate_degree_courses: undergraduate degree courses + train_to_be_a_teacher: Find out about the different ways to train to be a teacher (opens in a new tab) + url_train_to_be_a_teacher: https://getintoteaching.education.gov.uk/train-to-be-a-teacher url_tda: https://getintoteaching.education.gov.uk/train-to-be-a-teacher/teacher-degree-apprenticeships url_engineers_teach_physics: https://getintoteaching.education.gov.uk/subjects/engineers-teach-physics url_get_an_advisor: https://getintoteaching.education.gov.uk/teacher-training-adviser/sign_up/identity @@ -267,6 +277,8 @@ en: html: PGCE without QTS pgde: html: PGDE without QTS + undergraduate_degree_with_qts: + html: Teacher degree apprenticeship with QTS links: national_pay_scales: https://www.gov.uk/government/publications/national-pay-scales-for-eligible-teaching-and-education-jobs/national-pay-scales-for-eligible-teaching-and-education-leadership-occupation-codes result_filters_filters_view: @@ -277,6 +289,10 @@ en: activemodel: errors: models: + find/university_degree_status_form: + attributes: + university_degree_status: + blank: Select whether you have a university degree find/age_groups_form: attributes: age_group: diff --git a/config/routes/find.rb b/config/routes/find.rb index 19b111dbb5..0c6578c057 100644 --- a/config/routes/find.rb +++ b/config/routes/find.rb @@ -44,8 +44,11 @@ get '/age-groups-submit' => 'age_groups#create', as: :age_groups_create get '/subjects' => 'subjects#new', as: :subjects get '/subjects-submit' => 'subjects#create', as: :subjects_create + get '/university-degree-status' => 'university_degree_status#new', as: :university_degree_status + get '/university-degree-status-create' => 'university_degree_status#create', as: :university_degree_status_create get '/visa-status' => 'visa_status#new', as: :visa_status get '/visa-status-submit' => 'visa_status#create', as: :visa_status_create + get '/no-degree-and-requires-visa-sponsorship' => 'no_degree_and_requires_visa_sponsorship#new', as: :no_degree_and_requires_visa_sponsorship resources :locations, path: '/' end diff --git a/spec/factories/courses.rb b/spec/factories/courses.rb index 38866367a7..0052b6d0a1 100644 --- a/spec/factories/courses.rb +++ b/spec/factories/courses.rb @@ -224,6 +224,10 @@ sites { build_list(:site, 1, provider:) } end + trait :with_full_time_sites do + site_statuses { [build(:site_status, :findable, vac_status: :full_time_vacancies, site: build(:site, latitude: 51.5079, longitude: 0.0877, address1: '1 Foo Street', postcode: 'BN1 1AA'))] } + end + trait :draft_enrichment do enrichments { [build(:course_enrichment, :initial_draft, course: nil)] } end @@ -255,5 +259,20 @@ trait :closed do application_status { :closed } end + + trait :published_teacher_degree_apprenticeship do + open + published + with_full_time_sites + with_teacher_degree_apprenticeship + resulting_in_undergraduate_degree_with_qts + end + + trait :published_postgraduate do + open + published + with_full_time_sites + resulting_in_pgce_with_qts + end end end diff --git a/spec/features/find/search/undergraduate/course_results_spec.rb b/spec/features/find/search/undergraduate/course_results_spec.rb new file mode 100644 index 0000000000..d95ff265f0 --- /dev/null +++ b/spec/features/find/search/undergraduate/course_results_spec.rb @@ -0,0 +1,389 @@ +# frozen_string_literal: true + +require 'rails_helper' + +feature 'Questions and results for undergraduate courses' do + after do + Timecop.return + end + + scenario 'in the 2025 cycle with the TDA feature active and searching for secondary courses' do + given_i_have_2025_courses + and_i_am_in_the_2025_cycle + and_the_tda_feature_flag_is_active + when_i_visit_the_start_page + and_i_select_the_across_england_radio_button + and_i_click_continue + and_i_choose_secondary + and_i_click_continue + and_i_choose_subjects + and_i_click_continue + then_i_am_on_the_degree_question_page + + and_i_click_continue + then_i_see_an_error_message_on_the_degree_question_page + and_the_back_link_points_to_the_secondary_subjects_page + + when_i_choose_no_i_do_not_have_a_degree + and_i_click_continue + then_i_am_on_the_visa_status_page + and_the_back_link_points_to_the_degree_question + + when_i_choose_yes + and_i_click_find_courses + then_i_am_on_an_exit_page_for_no_degree_and_requires_visa_sponsorship + + when_i_click_back + then_i_am_on_the_visa_status_page + and_the_back_link_points_to_the_degree_question + + when_i_choose_no + and_i_click_find_courses + + then_i_am_on_results_page + and_some_filters_are_hidden_for_undergraduate_courses + and_some_filters_are_visible_for_undergraduate_courses + and_i_only_see_secondary_undergraduate_courses + + when_i_uncheck_all_the_filters + and_i_click_apply_filters + then_i_am_on_results_page + and_some_filters_are_hidden_for_undergraduate_courses + and_some_filters_are_visible_for_undergraduate_courses + end + + scenario 'in the 2025 cycle with the TDA feature active and searching primary courses' do + given_i_have_2025_courses + and_i_am_in_the_2025_cycle + and_the_tda_feature_flag_is_active + when_i_visit_the_start_page + and_i_select_the_across_england_radio_button + and_i_click_continue + and_i_choose_primary + and_i_click_continue + and_i_choose_primary_subjects + and_i_click_continue + then_i_am_on_the_degree_question_page + and_the_back_link_points_to_the_primary_subjects_page + + when_i_choose_no_i_do_not_have_a_degree + and_i_click_continue + then_i_am_on_the_visa_status_page + + when_i_choose_no + and_i_click_find_courses + then_i_am_on_results_page + and_some_filters_are_hidden_for_undergraduate_courses + and_some_filters_are_visible_for_undergraduate_courses + and_i_can_see_only_primary_undergraduate_courses + + when_i_uncheck_all_the_filters + and_i_click_apply_filters + then_i_am_on_results_page + and_some_filters_are_hidden_for_undergraduate_courses + and_some_filters_are_visible_for_undergraduate_courses + end + + scenario 'in the 2024 cycle with the TDA feature active' do + given_i_have_2024_courses + and_i_am_in_the_2024_cycle + and_the_tda_feature_flag_is_active + when_i_visit_the_start_page + and_i_select_the_across_england_radio_button + and_i_click_continue + and_i_choose_secondary + and_i_click_continue + and_i_choose_subjects + and_i_click_continue + then_i_am_on_the_visa_status_page + when_i_choose_no + and_i_click_find_courses + then_i_am_on_results_page + and_all_filters_are_visible + end + + scenario 'in the 2025 cycle with the TDA feature active and searching for further education courses' do + given_i_have_2025_courses + and_i_am_in_the_2025_cycle + and_the_tda_feature_flag_is_active + + when_i_visit_the_start_page + and_i_select_the_across_england_radio_button + and_i_click_continue + and_i_choose_further_education + and_i_click_continue + then_i_am_on_the_visa_status_page + when_i_choose_yes + and_i_click_find_courses + then_i_am_on_results_page + end + + scenario 'in the 2025 cycle, with the TDA feature active and searching for postgraduate courses' do + given_i_have_2025_courses + and_i_am_in_the_2025_cycle + and_the_tda_feature_flag_is_active + when_i_visit_the_start_page + and_i_select_the_across_england_radio_button + and_i_click_continue + and_i_choose_secondary + and_i_click_continue + and_i_choose_subjects + and_i_click_continue + then_i_am_on_the_degree_question_page + + when_i_choose_yes_i_have_a_degree + and_i_click_continue + then_i_am_on_the_visa_status_page + and_the_back_link_points_to_the_degree_question + + when_i_choose_no + and_i_click_find_courses + then_i_am_on_results_page + and_all_filters_are_visible + and_i_can_see_only_postgraduate_courses + end + + def given_i_have_2025_courses + _, provider = setup_recruitment_cycle(year: 2025) + + @biology_course = create(:course, :published_teacher_degree_apprenticeship, :secondary, provider:, name: 'Biology', subjects: [find_or_create(:secondary_subject, :biology)]) + @history_course = create(:course, :published_teacher_degree_apprenticeship, :secondary, provider:, name: 'History', subjects: [find_or_create(:secondary_subject, :history)]) + @primary_with_science_course = create(:course, :published_teacher_degree_apprenticeship, :primary, provider:, name: 'Primary with science', subjects: [find_or_create(:primary_subject, :primary_with_science)]) + + @mathematics_course = create(:course, :published_postgraduate, :secondary, provider:, name: 'Mathematics', subjects: [find_or_create(:secondary_subject, :mathematics)]) + @chemistry_course = create(:course, :published_postgraduate, :secondary, provider:, name: 'Chemistry', subjects: [find_or_create(:secondary_subject, :chemistry)]) + end + + def setup_recruitment_cycle(year:) + recruitment_cycle = create(:recruitment_cycle, year:) + user = create(:user, providers: [build(:provider, recruitment_cycle:, provider_type: 'lead_school', sites: [build(:site), build(:site)], study_sites: [build(:site, :study_site), build(:site, :study_site)])]) + provider = user.providers.first + create(:provider, :accredited_provider, provider_code: '1BJ') + accredited_provider = create(:provider, :accredited_provider, provider_code: '1BJ', recruitment_cycle:) + provider.accrediting_provider_enrichments = [] + provider.accrediting_provider_enrichments << AccreditingProviderEnrichment.new( + { + UcasProviderCode: accredited_provider.provider_code, + Description: 'description' + } + ) + [recruitment_cycle, provider] + end + + def given_i_have_2024_courses + _, provider = setup_recruitment_cycle(year: 2024) + + create(:course, :resulting_in_pgce_with_qts, provider:, name: 'Chemistry') + create(:course, :resulting_in_pgce_with_qts, provider:, name: 'Mathematics') + end + + def and_i_am_in_the_2025_cycle + Timecop.travel(Find::CycleTimetable.find_reopens) + allow(Settings).to receive(:current_recruitment_cycle_year).and_return(2025) + end + + def and_i_am_in_the_2024_cycle + Timecop.travel(Find::CycleTimetable.find_opens) + allow(Settings).to receive(:current_recruitment_cycle_year).and_return(2024) + end + + def and_the_tda_feature_flag_is_active + allow(Settings.features).to receive(:teacher_degree_apprenticeship).and_return(true) + end + + def when_i_visit_the_start_page + visit root_path + end + + def and_i_select_the_across_england_radio_button + choose 'Across England' + end + + def and_i_click_continue + click_link_or_button 'Continue' + end + + def and_i_choose_secondary + choose 'Secondary' + end + + def and_i_choose_primary + choose 'Primary' + end + + def and_i_choose_primary_subjects + check 'Primary with science' + end + + def and_i_choose_subjects + check 'Biology' + check 'Chemistry' + check 'History' + check 'Mathematics' + end + + def then_i_am_on_the_degree_question_page + expect(page).to have_current_path( + find_university_degree_status_path, + ignore_query: true + ) + + expect(page).to have_content('Do you have a university degree?') + end + + def then_i_am_on_the_visa_status_page + expect(page).to have_current_path( + find_visa_status_path, + ignore_query: true + ) + end + + def and_i_choose_further_education + choose 'Further education' + end + + def then_i_see_an_error_message_on_the_degree_question_page + expect(page).to have_content( + 'Select whether you have a university degree' + ) + end + + def when_i_choose_no + choose 'No' + end + + def when_i_choose_no_i_do_not_have_a_degree + choose 'No, I do not have a degree' + end + + def when_i_choose_yes_i_have_a_degree + choose 'Yes, I have a degree or am studying for one' + end + + def when_i_choose_yes + choose 'Yes' + end + + def then_i_am_on_an_exit_page_for_no_degree_and_requires_visa_sponsorship + expect(page).to have_current_path( + find_no_degree_and_requires_visa_sponsorship_path, + ignore_query: true + ) + + expect(page).to have_content('You are not eligible for teacher training courses on this service.') + end + + def then_i_am_on_results_page + expect(page).to have_current_path( + find_results_path, + ignore_query: true + ) + end + + def and_i_click_find_courses + click_link_or_button 'Find courses' + end + + def and_some_filters_are_hidden_for_undergraduate_courses + within 'form.app-filter' do + expect(page).to have_no_content('Study type') + expect(page).to have_no_content('Qualifications') + expect(page).to have_no_content('Degree grade accepted') + expect(page).to have_no_content('Salary') + expect(page).to have_no_content('Qualifications') + end + end + + def and_some_filters_are_visible_for_undergraduate_courses + within 'form.app-filter' do + expect(page).to have_content('Special educational needs') + expect(page).to have_content('Applications open') + end + end + + def and_i_can_see_only_primary_undergraduate_courses + within '.app-search-results' do + expect(page).to have_content('Primary with science') + expect(page).to have_content(@primary_with_science_course.course_code) + expect(page).to have_no_content('Biology') + expect(page).to have_no_content(@biology_course.course_code) + expect(page).to have_no_content('History') + expect(page).to have_no_content(@history_course.course_code) + expect(page).to have_no_content('Chemistry') + expect(page).to have_no_content(@chemistry_course.course_code) + expect(page).to have_no_content('Mathematics') + expect(page).to have_no_content(@mathematics_course.course_code) + end + end + + def and_i_only_see_secondary_undergraduate_courses + within '.app-search-results' do + expect(page).to have_content('Biology') + expect(page).to have_content(@biology_course.course_code) + expect(page).to have_content('History') + expect(page).to have_content(@history_course.course_code) + expect(page).to have_no_content('Primary with science') + expect(page).to have_no_content(@primary_with_science_course.course_code) + expect(page).to have_no_content('Chemistry') + expect(page).to have_no_content(@chemistry_course.course_code) + expect(page).to have_no_content('Mathematics') + expect(page).to have_no_content(@mathematics_course.course_code) + end + end + + def when_i_click_back + click_link_or_button 'Back' + end + + def and_the_back_link_points_to_the_degree_question + expect(back_link[:href]).to include(find_university_degree_status_path) + end + + def and_the_back_link_points_to_the_secondary_subjects_page + expect(back_link[:href]).to include(find_subjects_path(age_group: 'secondary')) + end + + def and_the_back_link_points_to_the_primary_subjects_page + expect(back_link[:href]).to include(find_subjects_path(age_group: 'primary')) + end + + def and_all_filters_are_visible + within 'form.app-filter' do + expect(page).to have_content('Study type') + expect(page).to have_content('Qualifications') + expect(page).to have_content('Degree grade accepted') + expect(page).to have_content('Salary') + expect(page).to have_content('Qualifications') + expect(page).to have_content('Special educational needs') + expect(page).to have_content('Applications open') + end + end + + def and_i_can_see_only_postgraduate_courses + within '.app-search-results' do + expect(page).to have_content('Chemistry') + expect(page).to have_content(@chemistry_course.course_code) + expect(page).to have_content('Mathematics') + expect(page).to have_content(@mathematics_course.course_code) + expect(page).to have_no_content('Biology') + expect(page).to have_no_content(@biology_course.course_code) + expect(page).to have_no_content('History') + expect(page).to have_no_content(@history_course.course_code) + expect(page).to have_no_content('Primary with science') + expect(page).to have_no_content(@primary_with_science_course.course_code) + end + end + + def back_link + page.find_link('Back') + end + + def when_i_uncheck_all_the_filters + uncheck 'Only show courses open for applications' + uncheck 'Only show courses with a SEND specialism' + end + + def and_i_click_apply_filters + click_link_or_button 'Apply filters' + end +end diff --git a/spec/forms/find/university_degree_status_form_spec.rb b/spec/forms/find/university_degree_status_form_spec.rb new file mode 100644 index 0000000000..57a8be3129 --- /dev/null +++ b/spec/forms/find/university_degree_status_form_spec.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe Find::UniversityDegreeStatusForm do + describe 'validations' do + it 'is valid when university_degree_status is present' do + form = described_class.new(university_degree_status: 'true') + expect(form).to be_valid + end + + it 'is not valid when university_degree_status is not present' do + form = described_class.new(university_degree_status: nil) + expect(form).not_to be_valid + expect(form.errors[:university_degree_status]).to include( + 'Select whether you have a university degree' + ) + end + end +end diff --git a/spec/services/course_search_service_spec.rb b/spec/services/course_search_service_spec.rb index 4aedefc7f1..b85502ce39 100644 --- a/spec/services/course_search_service_spec.rb +++ b/spec/services/course_search_service_spec.rb @@ -32,6 +32,7 @@ ).and_return(course_with_includes) allow(course_with_includes).to receive(:where).and_return(outer_query_scope) + allow(scope).to receive(:with_course_type).and_return(scope) end describe 'when no scope is passed' do @@ -40,7 +41,6 @@ let(:filter) { {} } it 'defaults to Course' do - expect(Course).to receive(:select).and_return(inner_query_scope) expect(course_with_includes).to receive(:where).and_return(expected_scope) expect(subject).to eq(expected_scope) end @@ -323,6 +323,24 @@ expect(subject).to eq(expected_scope) end end + + context 'when filter for undergraduate courses' do + let(:filter) do + { + age_group: 'secondary', + visa_status: 'false', + university_degree_status: 'false', + qualification: 'qts' + }.with_indifferent_access + end + + it 'does add the with_qualifications scope' do + expect(scope).not_to receive(:with_qualifications) + expect(scope).to receive(:select).and_return(inner_query_scope) + expect(course_with_includes).to receive(:where).and_return(expected_scope) + expect(subject).to eq(expected_scope) + end + end end describe 'filter[findable]' do diff --git a/spec/services/find/course_type_answer_determiner_spec.rb b/spec/services/find/course_type_answer_determiner_spec.rb new file mode 100644 index 0000000000..b4a71aef39 --- /dev/null +++ b/spec/services/find/course_type_answer_determiner_spec.rb @@ -0,0 +1,159 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe Find::CourseTypeAnswerDeterminer do + describe '#show_undergraduate_courses?' do + context 'when the age group is not further education, no degree, and no visa sponsorship required' do + let(:params) do + { + age_group: 'secondary', + visa_status: 'false', + university_degree_status: 'false' + } + end + + it 'returns true' do + determiner = described_class.new(**params) + expect(determiner.show_undergraduate_courses?).to be true + end + end + + context 'when the age group is further education' do + let(:params) do + { + age_group: 'further_education', + visa_status: 'false', + university_degree_status: nil + } + end + + it 'returns false' do + determiner = described_class.new(**params) + expect(determiner.show_undergraduate_courses?).to be false + end + end + + context 'when visa status requires sponsorship' do + let(:params) do + { + age_group: 'secondary', + visa_status: 'true', + university_degree_status: 'false' + } + end + + it 'returns false' do + determiner = described_class.new(**params) + expect(determiner.show_undergraduate_courses?).to be false + end + end + + context 'when there is a university degree' do + let(:params) do + { + age_group: 'secondary', + visa_status: 'false', + university_degree_status: 'true' + } + end + + it 'returns false' do + determiner = described_class.new(**params) + expect(determiner.show_undergraduate_courses?).to be false + end + end + + context 'when there is not a university degree' do + let(:params) do + { + age_group: 'secondary', + visa_status: 'false', + university_degree_status: nil + } + end + + it 'returns false' do + determiner = described_class.new(**params) + expect(determiner.show_undergraduate_courses?).to be false + end + end + end + + describe '#show_exit_page?' do + context 'when the age group is not further education, requires visa sponsorship, and no degree' do + let(:params) do + { + age_group: 'secondary', + visa_status: 'true', + university_degree_status: 'false' + } + end + + it 'returns true' do + determiner = described_class.new(**params) + expect(determiner.show_exit_page?).to be true + end + end + + context 'when the age group is further education' do + let(:params) do + { + age_group: 'further_education', + visa_status: 'true', + university_degree_status: nil + } + end + + it 'returns false' do + determiner = described_class.new(**params) + expect(determiner.show_exit_page?).to be false + end + end + + context 'when visa sponsorship is not required' do + let(:params) do + { + age_group: 'secondary', + visa_status: 'false', + university_degree_status: 'false' + } + end + + it 'returns false' do + determiner = described_class.new(**params) + expect(determiner.show_exit_page?).to be false + end + end + + context 'when there is a university degree' do + let(:params) do + { + age_group: 'secondary', + visa_status: 'true', + university_degree_status: 'true' + } + end + + it 'returns false' do + determiner = described_class.new(**params) + expect(determiner.show_exit_page?).to be false + end + end + + context 'when there is not a value for university degree' do + let(:params) do + { + age_group: 'secondary', + visa_status: 'true', + university_degree_status: nil + } + end + + it 'returns false' do + determiner = described_class.new(**params) + expect(determiner.show_exit_page?).to be false + end + end + end +end