diff --git a/Changelog.md b/Changelog.md index 7c97492889..10f3d17b60 100644 --- a/Changelog.md +++ b/Changelog.md @@ -20,6 +20,7 @@ - Ensure user params are passed as keyword arguments to database queries (#7040) - Added a progress bar for when a student uploads a file for submission (#7078) - Added validations to the `TextAnnotation` model to ensure `line_start` and `line_end` are >= 1, and `column_start` and `column_end` are >= 0. (#7081) +- Calculate and display the exact future time for the next token generation to help students plan their test runs more effectively. (#7127) ### 🐛 Bug fixes diff --git a/app/controllers/automated_tests_controller.rb b/app/controllers/automated_tests_controller.rb index 58d3513d5e..cf5642e704 100644 --- a/app/controllers/automated_tests_controller.rb +++ b/app/controllers/automated_tests_controller.rb @@ -35,14 +35,23 @@ def student_interface @student = current_role @grouping = @student.accepted_grouping_for(@assignment.id) - unless @grouping.nil? + if @grouping.present? @grouping.refresh_test_tokens @authorized = flash_allowance(:notice, allowance_to(:run_tests?, current_role, context: { assignment: @assignment, grouping: @grouping })).value + + if @assignment.enable_student_tests && !@assignment.non_regenerating_tokens && !@assignment.unlimited_tokens + hours_from_start = [(Time.current - @assignment.token_start_date) / 3600, 0].max + periods_from_start = (hours_from_start / @assignment.token_period).floor + last_period_begin = @assignment.token_start_date + (periods_from_start * @assignment.token_period).hours + @next_token_generation_time = last_period_begin + @assignment.token_period.hours + @next_token_generation_time = I18n.l(@next_token_generation_time) + end end + @next_token_generation_time ||= nil render layout: 'assignment_content' end diff --git a/app/views/automated_tests/student_interface.html.erb b/app/views/automated_tests/student_interface.html.erb index 2b0909faf4..da783f61d6 100644 --- a/app/views/automated_tests/student_interface.html.erb +++ b/app/views/automated_tests/student_interface.html.erb @@ -22,6 +22,13 @@
<% end %> + <% unless @assignment.unlimited_tokens || @assignment.non_regenerating_tokens %> ++ <%= t('automated_tests.next_token_generation') %>: + <%= @next_token_generation_time %> +
+ <% end %> +<%= Grouping.human_attribute_name(:test_tokens) %>: <% if @assignment.unlimited_tokens %> diff --git a/config/locales/views/automated_tests/en.yml b/config/locales/views/automated_tests/en.yml index 3e05e34ae4..f96f6f30e9 100644 --- a/config/locales/views/automated_tests/en.yml +++ b/config/locales/views/automated_tests/en.yml @@ -38,6 +38,7 @@ en: need_group_for_test: You must have a group to run tests. need_one_file: You must submit at least one file in order to run tests. need_submission: Submissions must be collected before tests are run. Not all selected groupings had tests run. + next_token_generation: Next token generation time no_autotest_settings: No automated tester settings exist for this course. Please update the automated tester settings and try again. no_criteria: Unable to find a criterion with name "%{name}". no_instructor_runnable_tests: No tests are available for instructors to run. diff --git a/spec/controllers/automated_tests_controller_spec.rb b/spec/controllers/automated_tests_controller_spec.rb index 52c863bb6c..bc0454b713 100644 --- a/spec/controllers/automated_tests_controller_spec.rb +++ b/spec/controllers/automated_tests_controller_spec.rb @@ -1,4 +1,5 @@ describe AutomatedTestsController do + include ActiveSupport::Testing::TimeHelpers include AutomatedTestsHelper # TODO: add 'role is from a different course' shared tests to each route test below @@ -548,8 +549,83 @@ let(:role) { create(:student) } context 'GET student_interface' do - before { get_as role, :student_interface, params: params } - # TODO: write tests + let(:assignment) do + create(:assignment, + assignment_properties_attributes: { + enable_student_tests: true, + tokens_per_period: 5, + token_start_date: 1.day.ago, + token_period: 24 + }) + end + let(:grouping) { create(:grouping, assignment: assignment) } + let(:student) { create(:student) } + let(:role) { student } + let(:params) { { course_id: assignment.course.id, assignment_id: assignment.id } } + + before do + create(:student_membership, role: student, grouping: grouping, membership_status: 'accepted') + sign_in student + end + + context 'when student tests are enabled and the student has a grouping' do + before do + get_as student, :student_interface, params: params + end + + it 'should calculate the next token generation time' do + travel 14.hours do + get_as student, :student_interface, params: params + assignment.reload + token_start_time = assignment.token_start_date + expected_next_token_generation_time = token_start_time + 2.days + formatted_expected_time = I18n.l(expected_next_token_generation_time) + expect(assigns(:next_token_generation_time)).to eq(formatted_expected_time) + end + end + + it 'should respond with success' do + expect(response).to have_http_status(:ok) + end + + it 'should render the assignment_content layout' do + expect(response).to render_template('layouts/assignment_content') + end + end + + context 'when student tests are disabled' do + before do + assignment.assignment_properties.update!(enable_student_tests: false) + get_as student, :student_interface, params: params + end + + it 'should not calculate the next token generation time' do + expect(assigns(:next_token_generation_time)).to be_nil + end + + it 'should respond with success' do + expect(response).to have_http_status(:ok) + end + + it 'should render the assignment_content layout' do + expect(response).to render_template('layouts/assignment_content') + end + end + + context 'when the student does not have a grouping' do + before do + grouping.update!(inviter: nil) + get_as student, :student_interface, params: params + end + + it 'should respond with success' do + expect(response).to have_http_status(:ok) + end + + it 'should render the assignment_content layout' do + expect(response).to render_template('layouts/assignment_content') + end + end end context 'POST execute_test_run' do