1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
|
# frozen_string_literal: true
require 'active_support/inflector'
require 'rainbow/refinement'
module QA
module Support
module Repeater
using Rainbow
DEFAULT_MAX_WAIT_TIME = 60
RepeaterConditionExceededError = Class.new(RuntimeError)
RetriesExceededError = Class.new(RepeaterConditionExceededError)
WaitExceededError = Class.new(RepeaterConditionExceededError)
def repeat_until(
max_attempts: nil,
max_duration: nil,
reload_page: nil,
sleep_interval: 0,
raise_on_failure: true,
retry_on_exception: false,
log: true,
message: nil
)
attempts = 0
start = Time.now
begin
while remaining_attempts?(attempts, max_attempts) && remaining_time?(start, max_duration)
# start logging from the second attempt
if log && attempts == 1
msg = ["Retrying action with:"]
msg << "max_attempts: #{max_attempts};" if max_attempts
msg << "max_duration: #{max_duration};" if max_duration
msg << "reload_page: #{reload_page};" if reload_page
msg << "sleep_interval: #{sleep_interval};"
msg << "raise_on_failure: #{raise_on_failure};"
msg << "retry_on_exception: #{retry_on_exception}"
QA::Runtime::Logger.debug(msg.join(' '))
end
QA::Runtime::Logger.debug("Attempt number #{attempts + 1}".bg(:yellow).black) if log && max_attempts && attempts > 0
result = yield
if result
log_completion(log, attempts)
return result
end
sleep_and_reload_if_needed(sleep_interval, reload_page)
attempts += 1
end
rescue StandardError, RSpec::Expectations::ExpectationNotMetError
raise unless retry_on_exception
attempts += 1
raise unless remaining_attempts?(attempts, max_attempts) && remaining_time?(start, max_duration)
sleep_and_reload_if_needed(sleep_interval, reload_page)
retry
end
if raise_on_failure
unless remaining_attempts?(attempts, max_attempts)
raise(
RetriesExceededError,
"#{message || 'Retry'} failed after #{max_attempts} #{'attempt'.pluralize(max_attempts)}"
)
end
raise(
WaitExceededError,
"#{message || 'Wait'} failed after #{max_duration} #{'second'.pluralize(max_duration)}"
)
end
log_completion(log, attempts)
false
end
private
def sleep_and_reload_if_needed(sleep_interval, reload_page)
sleep(sleep_interval)
reload_page.refresh if reload_page
end
def remaining_attempts?(attempts, max_attempts)
max_attempts ? attempts < max_attempts : true
end
def remaining_time?(start, max_duration)
max_duration ? Time.now - start < max_duration : true
end
# Log completion if more than one attempt performed
#
# @param [Boolean] log
# @param [Integer] attempts
# @return [void]
def log_completion(log, attempts)
return unless log && attempts > 0
QA::Runtime::Logger.debug('ended retry')
end
end
end
end
|