|
| 1 | +# frozen_string_literal: true |
| 2 | +require 'rspec/expectations' |
| 3 | + |
| 4 | +module ReferenceTimelinesTestHelper |
| 5 | + # Checks whether two `Course::ReferenceTimeline`s are similar to each other. |
| 6 | + # |
| 7 | + # Note that this is not a check for equality or equivalency, but similarity. This method is meant to compare |
| 8 | + # two timelines that were duplicated when duplicating a course. As such, it checks for the equivalency of the |
| 9 | + # attributes in both `Course::ReferenceTimeline` records. |
| 10 | + # |
| 11 | + # @param timeline1 [Course::ReferenceTimeline] a timeline |
| 12 | + # @param timeline2 [Course::ReferenceTimeline] the timeline to compare against `timeline1` |
| 13 | + # @param time_shift [ActiveSupport::Duration] number of days expected to be between the times in both timelines |
| 14 | + # @return [Boolean] `true` if both timelines are similar to each other |
| 15 | + # |
| 16 | + # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity |
| 17 | + def similar_timelines?(timeline1, timeline2, time_shift = 0.day) |
| 18 | + return false unless timeline1.title == timeline2.title |
| 19 | + |
| 20 | + times1 = timeline1.reference_times |
| 21 | + times2 = timeline2.reference_times |
| 22 | + return false unless times1.size == times2.size |
| 23 | + |
| 24 | + times1.each_with_index do |time1, index| |
| 25 | + time2 = times2[index] |
| 26 | + return false unless time1.lesson_plan_item.actable_type == time2.lesson_plan_item.actable_type |
| 27 | + return false unless time1.lesson_plan_item.title == time2.lesson_plan_item.title |
| 28 | + return false unless days_equal_or_offset_by(time1.start_at, time2.start_at, time_shift) |
| 29 | + return false unless days_equal_or_offset_by(time1.bonus_end_at, time2.bonus_end_at, time_shift) |
| 30 | + return false unless days_equal_or_offset_by(time1.end_at, time2.end_at, time_shift) |
| 31 | + end |
| 32 | + |
| 33 | + true |
| 34 | + end |
| 35 | + # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity |
| 36 | + |
| 37 | + private |
| 38 | + |
| 39 | + # Checks whether two times are equal or differ by some given number of days. |
| 40 | + # |
| 41 | + # @param time1 [ActiveSupport::TimeWithZone, nil] a time |
| 42 | + # @param time2 [ActiveSupport::TimeWithZone, nil] the time to compare against `time1` |
| 43 | + # @param offset [ActiveSupport::Duration] number of days expected to be between `time1` and `time2` |
| 44 | + # @return [Boolean] `true` if `time1` and `time2` are equal, or differ by `offset` days |
| 45 | + def days_equal_or_offset_by(time1, time2, offset) |
| 46 | + return true if time1 == time2 |
| 47 | + |
| 48 | + days_between(time1, time2) == offset |
| 49 | + end |
| 50 | + |
| 51 | + # Returns the number of days between two `ActiveSupport::TimeWithZone` times. |
| 52 | + # It returns `0.days` if either time is `nil`. |
| 53 | + # |
| 54 | + # @param time1 [ActiveSupport::TimeWithZone, nil] a time |
| 55 | + # @param time2 [ActiveSupport::TimeWithZone, nil] the time to differ against `time1` |
| 56 | + # @return [ActiveSupport::Duration] the number of days between `time1` and `time2` |
| 57 | + def days_between(time1, time2) |
| 58 | + return 0.days if time1.blank? || time2.blank? |
| 59 | + |
| 60 | + date1 = time1.to_date |
| 61 | + date2 = time2.to_date |
| 62 | + (date1 - date2).to_i.days |
| 63 | + end |
| 64 | +end |
| 65 | + |
| 66 | +RSpec::Matchers.define :be_similar_to_timeline do |expected, time_shift| |
| 67 | + include ReferenceTimelinesTestHelper |
| 68 | + |
| 69 | + match do |actual| |
| 70 | + expect(similar_timelines?(actual, expected, time_shift)).to be_truthy |
| 71 | + end |
| 72 | +end |
| 73 | + |
| 74 | +RSpec.configure do |config| |
| 75 | + config.include ReferenceTimelinesTestHelper |
| 76 | +end |
0 commit comments