bugsnag-maze-runner 6.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/bin/bugsnag-print-load-paths +6 -0
- data/bin/download-logs +76 -0
- data/bin/maze-runner +136 -0
- data/bin/upload-app +56 -0
- data/lib/features/scripts/await-android-emulator.sh +11 -0
- data/lib/features/scripts/clear-android-app-data.sh +8 -0
- data/lib/features/scripts/force-stop-android-app.sh +8 -0
- data/lib/features/scripts/install-android-app.sh +15 -0
- data/lib/features/scripts/launch-android-app.sh +38 -0
- data/lib/features/scripts/launch-android-emulator.sh +15 -0
- data/lib/features/steps/android_steps.rb +51 -0
- data/lib/features/steps/app_automator_steps.rb +228 -0
- data/lib/features/steps/aws_sam_steps.rb +212 -0
- data/lib/features/steps/breadcrumb_steps.rb +50 -0
- data/lib/features/steps/browser_steps.rb +93 -0
- data/lib/features/steps/build_api_steps.rb +25 -0
- data/lib/features/steps/document_server_steps.rb +7 -0
- data/lib/features/steps/error_reporting_steps.rb +342 -0
- data/lib/features/steps/feature_flag_steps.rb +190 -0
- data/lib/features/steps/header_steps.rb +72 -0
- data/lib/features/steps/log_steps.rb +29 -0
- data/lib/features/steps/multipart_request_steps.rb +142 -0
- data/lib/features/steps/network_steps.rb +75 -0
- data/lib/features/steps/payload_steps.rb +234 -0
- data/lib/features/steps/proxy_steps.rb +34 -0
- data/lib/features/steps/query_parameter_steps.rb +31 -0
- data/lib/features/steps/request_assertion_steps.rb +107 -0
- data/lib/features/steps/runner_steps.rb +406 -0
- data/lib/features/steps/session_tracking_steps.rb +116 -0
- data/lib/features/steps/value_steps.rb +119 -0
- data/lib/features/support/env.rb +7 -0
- data/lib/features/support/internal_hooks.rb +260 -0
- data/lib/maze/appium_server.rb +112 -0
- data/lib/maze/assertions/request_set_assertions.rb +97 -0
- data/lib/maze/aws/sam.rb +112 -0
- data/lib/maze/bitbar_devices.rb +84 -0
- data/lib/maze/bitbar_utils.rb +112 -0
- data/lib/maze/browser_stack_devices.rb +160 -0
- data/lib/maze/browser_stack_utils.rb +164 -0
- data/lib/maze/browsers_bs.yml +220 -0
- data/lib/maze/browsers_cbt.yml +100 -0
- data/lib/maze/bugsnag_config.rb +42 -0
- data/lib/maze/capabilities.rb +126 -0
- data/lib/maze/checks/assert_check.rb +91 -0
- data/lib/maze/checks/noop_check.rb +34 -0
- data/lib/maze/compare.rb +161 -0
- data/lib/maze/configuration.rb +174 -0
- data/lib/maze/docker.rb +108 -0
- data/lib/maze/document_server.rb +46 -0
- data/lib/maze/driver/appium.rb +217 -0
- data/lib/maze/driver/browser.rb +138 -0
- data/lib/maze/driver/resilient_appium.rb +51 -0
- data/lib/maze/errors.rb +20 -0
- data/lib/maze/helper.rb +118 -0
- data/lib/maze/hooks/appium_hooks.rb +216 -0
- data/lib/maze/hooks/browser_hooks.rb +68 -0
- data/lib/maze/hooks/command_hooks.rb +9 -0
- data/lib/maze/hooks/hooks.rb +61 -0
- data/lib/maze/interactive_cli.rb +173 -0
- data/lib/maze/logger.rb +73 -0
- data/lib/maze/macos_utils.rb +14 -0
- data/lib/maze/network.rb +49 -0
- data/lib/maze/option/parser.rb +245 -0
- data/lib/maze/option/processor.rb +143 -0
- data/lib/maze/option/validator.rb +184 -0
- data/lib/maze/option.rb +64 -0
- data/lib/maze/plugins/bugsnag_reporting_plugin.rb +49 -0
- data/lib/maze/plugins/cucumber_report_plugin.rb +101 -0
- data/lib/maze/plugins/global_retry_plugin.rb +38 -0
- data/lib/maze/proxy.rb +114 -0
- data/lib/maze/request_list.rb +82 -0
- data/lib/maze/retry_handler.rb +76 -0
- data/lib/maze/runner.rb +149 -0
- data/lib/maze/sauce_labs_utils.rb +96 -0
- data/lib/maze/server.rb +207 -0
- data/lib/maze/servlets/base_servlet.rb +22 -0
- data/lib/maze/servlets/command_servlet.rb +44 -0
- data/lib/maze/servlets/log_servlet.rb +64 -0
- data/lib/maze/servlets/reflective_servlet.rb +69 -0
- data/lib/maze/servlets/servlet.rb +160 -0
- data/lib/maze/smart_bear_utils.rb +71 -0
- data/lib/maze/store.rb +15 -0
- data/lib/maze/terminating_server.rb +129 -0
- data/lib/maze/timers.rb +51 -0
- data/lib/maze/wait.rb +35 -0
- data/lib/maze.rb +27 -0
- metadata +371 -0
@@ -0,0 +1,100 @@
|
|
1
|
+
# Selenium capabilities for browserNames available on CrossbrowserNameTesting
|
2
|
+
---
|
3
|
+
ie_8:
|
4
|
+
browserName: "Internet Explorer"
|
5
|
+
version: "8"
|
6
|
+
platform: "Windows 7"
|
7
|
+
|
8
|
+
ie_9:
|
9
|
+
browserName: "Internet Explorer"
|
10
|
+
version: "9"
|
11
|
+
platform: "Windows 7"
|
12
|
+
|
13
|
+
ie_10:
|
14
|
+
browserName: "Internet Explorer"
|
15
|
+
version: "10"
|
16
|
+
os: "windows"
|
17
|
+
platform: "Windows 7 64-Bit"
|
18
|
+
|
19
|
+
ie_11:
|
20
|
+
browserName: "Internet Explorer"
|
21
|
+
version: "11"
|
22
|
+
platform: "Windows 7 64-Bit"
|
23
|
+
|
24
|
+
edge_14:
|
25
|
+
browserName: "MicrosoftEdge"
|
26
|
+
version: "14"
|
27
|
+
platform: "Windows 10"
|
28
|
+
|
29
|
+
edge_15:
|
30
|
+
browserName: "MicrosoftEdge"
|
31
|
+
version: "15"
|
32
|
+
platform: "Windows 10"
|
33
|
+
|
34
|
+
safari_8:
|
35
|
+
browserName: "Safari"
|
36
|
+
version: "8"
|
37
|
+
platform: "Mac OSX 10.10"
|
38
|
+
|
39
|
+
safari_10:
|
40
|
+
browserName: "Safari"
|
41
|
+
version: "10"
|
42
|
+
platform: "Mac OSX 10.12"
|
43
|
+
|
44
|
+
safari_13:
|
45
|
+
browserName: "Safari"
|
46
|
+
version: "13"
|
47
|
+
platform: "Mac OSX 10.15"
|
48
|
+
|
49
|
+
safari_14:
|
50
|
+
browserName: "Safari"
|
51
|
+
version: "14"
|
52
|
+
platform: "MacOS 11.0"
|
53
|
+
|
54
|
+
iphone_7_simulator:
|
55
|
+
browserName: "Safari"
|
56
|
+
deviceName: "iPhone 7 Simulator"
|
57
|
+
platformVersion': "10.0"
|
58
|
+
platformName': "iOS"
|
59
|
+
|
60
|
+
iphone_xr:
|
61
|
+
browserName: "Safari"
|
62
|
+
deviceName': "iPhone XR"
|
63
|
+
platformVersion': "12.1"
|
64
|
+
platformName': "iOS"
|
65
|
+
|
66
|
+
android_s7:
|
67
|
+
browserName: "Chrome"
|
68
|
+
deviceName: "Galaxy S7"
|
69
|
+
platformVersion: "7.0"
|
70
|
+
platformName: "Android"
|
71
|
+
|
72
|
+
firefox_30:
|
73
|
+
browserName: "Firefox"
|
74
|
+
version: "30"
|
75
|
+
platform: "Windows 7"
|
76
|
+
|
77
|
+
firefox_56:
|
78
|
+
browserName: "Firefox"
|
79
|
+
version: "56"
|
80
|
+
platform: "Mac OSX 10.13"
|
81
|
+
|
82
|
+
firefox_95:
|
83
|
+
browserName: "Firefox"
|
84
|
+
version: "95"
|
85
|
+
platform: "Windows 10"
|
86
|
+
|
87
|
+
chrome_43:
|
88
|
+
browserName: "Chrome"
|
89
|
+
version: "43"
|
90
|
+
platform: "Windows 7"
|
91
|
+
|
92
|
+
chrome_61:
|
93
|
+
browserName: "Chrome"
|
94
|
+
version: "61"
|
95
|
+
platform: "Windows 10"
|
96
|
+
|
97
|
+
chrome_96:
|
98
|
+
browserName: "Chrome"
|
99
|
+
version: "96"
|
100
|
+
platform: "Windows 10"
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'bugsnag'
|
2
|
+
|
3
|
+
# Contains logic for running Bugsnag
|
4
|
+
module Maze
|
5
|
+
class BugsnagConfig
|
6
|
+
class << self
|
7
|
+
def start_bugsnag(cucumber_config)
|
8
|
+
# Use MAZE_BUGSNAG_API_KEY explicitly to avoid collisions with test env
|
9
|
+
return unless Maze.config.enable_bugsnag && ENV['MAZE_BUGSNAG_API_KEY']
|
10
|
+
|
11
|
+
Bugsnag.configure do |config|
|
12
|
+
config.api_key = ENV['MAZE_BUGSNAG_API_KEY']
|
13
|
+
config.discard_classes << 'Test::Unit::AssertionFailedError'
|
14
|
+
config.add_metadata(:'test driver', {
|
15
|
+
'driver type': Maze.driver.class,
|
16
|
+
'device farm': Maze.config.farm,
|
17
|
+
'capabilities': Maze.config.capabilities
|
18
|
+
}) if Maze.driver
|
19
|
+
config.add_metadata(:'buildkite', {
|
20
|
+
'pipeline': ENV['BUILDKITE_PIPELINE_NAME'],
|
21
|
+
'repo': ENV['BUILDKITE_REPO'],
|
22
|
+
'build url': ENV['BUILDKITE_BUILD_URL'],
|
23
|
+
'branch': ENV['BUILDKITE_BRANCH'],
|
24
|
+
'builder': ENV['BUILDKITE_BUILD_CREATOR'],
|
25
|
+
'message': ENV['BUILDKITE_MESSAGE'],
|
26
|
+
'step': ENV['BUILDKITE_LABEL']
|
27
|
+
}) if ENV['BUILDKITE']
|
28
|
+
config.project_root = Dir.pwd
|
29
|
+
end
|
30
|
+
|
31
|
+
Bugsnag.start_session
|
32
|
+
|
33
|
+
at_exit do
|
34
|
+
if $!
|
35
|
+
Bugsnag.notify($!)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Maze
|
4
|
+
# Appium capabilities for each target farm
|
5
|
+
class Capabilities
|
6
|
+
class << self
|
7
|
+
# @param device_type [String] A key from @see BrowserStackDevices::DEVICE_HASH
|
8
|
+
# @param local_id [String] unique key for the tunnel instance
|
9
|
+
# @param capabilities_option [String] extra capabilities provided on the command line
|
10
|
+
def for_browser_stack_device(device_type, local_id, appium_version, capabilities_option)
|
11
|
+
capabilities = {
|
12
|
+
'browserstack.console' => 'errors',
|
13
|
+
'browserstack.localIdentifier' => local_id,
|
14
|
+
'browserstack.local' => 'true',
|
15
|
+
'noReset' => 'true'
|
16
|
+
}
|
17
|
+
capabilities.merge! BrowserStackDevices::DEVICE_HASH[device_type]
|
18
|
+
capabilities.merge! JSON.parse(capabilities_option)
|
19
|
+
capabilities['browserstack.appium_version'] = appium_version unless appium_version.nil?
|
20
|
+
capabilities
|
21
|
+
end
|
22
|
+
|
23
|
+
# @param browser_type [String] A key from @see browsers_bs.yml
|
24
|
+
# @param local_id [String] unique key for the tunnel instance
|
25
|
+
# @param capabilities_option [String] extra capabilities provided on the command line
|
26
|
+
def for_browser_stack_browser(browser_type, local_id, capabilities_option)
|
27
|
+
capabilities = Selenium::WebDriver::Remote::Capabilities.new
|
28
|
+
capabilities['browserstack.local'] = 'true'
|
29
|
+
capabilities['browserstack.localIdentifier'] = local_id
|
30
|
+
capabilities['browserstack.console'] = 'errors'
|
31
|
+
browsers = YAML.safe_load(File.read("#{__dir__}/browsers_bs.yml"))
|
32
|
+
capabilities.merge! browsers[browser_type]
|
33
|
+
capabilities.merge! JSON.parse(capabilities_option)
|
34
|
+
capabilities
|
35
|
+
end
|
36
|
+
|
37
|
+
# @param browser_type [String] A key from @see browsers_cbt.yml
|
38
|
+
# @param local_id [String] unique key for the SB tunnel instance
|
39
|
+
# @param capabilities_option [String] extra capabilities provided on the command line
|
40
|
+
def for_cbt_browser(browser_type, local_id, capabilities_option)
|
41
|
+
capabilities = Selenium::WebDriver::Remote::Capabilities.new
|
42
|
+
capabilities['tunnel_name'] = local_id
|
43
|
+
browsers = YAML.safe_load(File.read("#{__dir__}/browsers_cbt.yml"))
|
44
|
+
capabilities.merge! browsers[browser_type]
|
45
|
+
capabilities.merge! JSON.parse(capabilities_option)
|
46
|
+
capabilities
|
47
|
+
end
|
48
|
+
|
49
|
+
# @param device_type [String]
|
50
|
+
def for_bitbar_device(bitbar_api_key, device_type, platform, platform_version, capabilities_option)
|
51
|
+
capabilities = {
|
52
|
+
'bitbar_apiKey' => bitbar_api_key,
|
53
|
+
'bitbar_testrun' => "#{platform} #{platform_version}",
|
54
|
+
'bitbar_findDevice' => false,
|
55
|
+
'bitbar_testTimeout' => 7200,
|
56
|
+
'disabledAnimations' => 'true',
|
57
|
+
'noReset' => 'true'
|
58
|
+
}
|
59
|
+
capabilities.merge! BitBarDevices.get_device(device_type, platform, platform_version, bitbar_api_key)
|
60
|
+
capabilities.merge! JSON.parse(capabilities_option)
|
61
|
+
capabilities
|
62
|
+
end
|
63
|
+
|
64
|
+
# Constructs Appium capabilities for running on a local Android or iOS device.
|
65
|
+
# @param platform [String] 'ios' or 'android'
|
66
|
+
# @param capabilities_option [String] extra capabilities provided on the command line
|
67
|
+
# @param team_id [String] Apple Team Id, for iOS only
|
68
|
+
# @param udid [String] device UDID, for iOS only
|
69
|
+
# noinspection RubyStringKeysInHashInspection
|
70
|
+
def for_local(platform, capabilities_option, team_id = nil, udid = nil)
|
71
|
+
capabilities = case platform.downcase
|
72
|
+
when 'android'
|
73
|
+
{
|
74
|
+
'platformName' => 'Android',
|
75
|
+
'automationName' => 'UiAutomator2',
|
76
|
+
'autoGrantPermissions' => 'true',
|
77
|
+
'noReset' => 'true'
|
78
|
+
}
|
79
|
+
when 'ios'
|
80
|
+
{
|
81
|
+
'platformName' => 'iOS',
|
82
|
+
'automationName' => 'XCUITest',
|
83
|
+
'deviceName' => udid,
|
84
|
+
'xcodeOrgId' => team_id,
|
85
|
+
'xcodeSigningId' => 'iPhone Developer',
|
86
|
+
'udid' => udid,
|
87
|
+
'noReset' => 'true',
|
88
|
+
'waitForQuiescence' => false,
|
89
|
+
'newCommandTimeout' => 0
|
90
|
+
}
|
91
|
+
when 'macos'
|
92
|
+
{
|
93
|
+
'platformName' => 'Mac'
|
94
|
+
}
|
95
|
+
else
|
96
|
+
raise "Unsupported platform: #{platform}"
|
97
|
+
end
|
98
|
+
common = {
|
99
|
+
'os' => platform,
|
100
|
+
'autoAcceptAlerts': 'true'
|
101
|
+
}
|
102
|
+
capabilities.merge! common
|
103
|
+
capabilities.merge! JSON.parse(capabilities_option)
|
104
|
+
end
|
105
|
+
|
106
|
+
def for_sauce_labs_device(device_name, os, os_version, tunnel_id, appium_version, capabilities_option)
|
107
|
+
capabilities = {
|
108
|
+
'noReset' => true,
|
109
|
+
'deviceOrientation' => 'portrait',
|
110
|
+
'tunnelIdentifier' => tunnel_id,
|
111
|
+
'browserName' => '',
|
112
|
+
'autoAcceptAlerts' => true,
|
113
|
+
'sendKeyStrategy' => 'setValue',
|
114
|
+
'waitForQuiescence' => false,
|
115
|
+
'newCommandTimeout' => 0
|
116
|
+
}
|
117
|
+
capabilities['deviceName'] = device_name unless device_name.nil?
|
118
|
+
capabilities['platformName'] = os unless os.nil?
|
119
|
+
capabilities['platformVersion'] = os_version unless os_version.nil?
|
120
|
+
capabilities.merge! JSON.parse(capabilities_option)
|
121
|
+
capabilities['appiumVersion'] = appium_version unless appium_version.nil?
|
122
|
+
capabilities
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'test/unit'
|
3
|
+
|
4
|
+
module Maze
|
5
|
+
module Checks
|
6
|
+
# Assertion-backed data verification checks
|
7
|
+
class AssertCheck
|
8
|
+
include Test::Unit::Assertions
|
9
|
+
|
10
|
+
def true(test, message = nil)
|
11
|
+
assert_true(test, message)
|
12
|
+
end
|
13
|
+
|
14
|
+
def false(test, message = nil)
|
15
|
+
assert_false(test, message)
|
16
|
+
end
|
17
|
+
|
18
|
+
def nil(test, message = nil)
|
19
|
+
assert_nil(test, message)
|
20
|
+
end
|
21
|
+
|
22
|
+
def not_nil(test, message = nil)
|
23
|
+
assert_not_nil(test, message)
|
24
|
+
end
|
25
|
+
|
26
|
+
def match(pattern, string, message = nil)
|
27
|
+
assert_match(pattern, string, message)
|
28
|
+
end
|
29
|
+
|
30
|
+
def equal(expected, act, message = nil)
|
31
|
+
assert_equal(expected, act, message)
|
32
|
+
end
|
33
|
+
|
34
|
+
def not_equal(expected, act, message = nil)
|
35
|
+
assert_not_equal(expected, act, message)
|
36
|
+
end
|
37
|
+
|
38
|
+
def operator(operand1, operator, operand2, message = nil)
|
39
|
+
assert_operator(operand1, operator, operand2, message)
|
40
|
+
end
|
41
|
+
|
42
|
+
def kind_of(klass, object, message = nil)
|
43
|
+
assert_kind_of(klass, object, message)
|
44
|
+
end
|
45
|
+
|
46
|
+
def block(message = 'block failed', &block)
|
47
|
+
assert_block(message, &block)
|
48
|
+
end
|
49
|
+
|
50
|
+
def include(collection, object, message = nil)
|
51
|
+
assert_include(collection, object, message)
|
52
|
+
end
|
53
|
+
alias includes include
|
54
|
+
|
55
|
+
def not_include(collection, object, message = nil)
|
56
|
+
assert_not_include(collection, object, message)
|
57
|
+
end
|
58
|
+
alias not_includes not_include
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# Wrapper for Maze.check.true to avoid making a breaking change.
|
64
|
+
# @deprecated TODO Remove in v7
|
65
|
+
def assert_true(value, message = nil)
|
66
|
+
Maze.check.true value, message
|
67
|
+
end
|
68
|
+
|
69
|
+
# Wrapper for Maze.check.false to avoid making a breaking change.
|
70
|
+
# @deprecated TODO Remove in v7
|
71
|
+
def assert_false(value, message = nil)
|
72
|
+
Maze.check.false value, message
|
73
|
+
end
|
74
|
+
|
75
|
+
# Wrapper for Maze.check.not_nil to avoid making a breaking change.
|
76
|
+
# @deprecated TODO Remove in v7
|
77
|
+
def assert_not_nil(value, message = nil)
|
78
|
+
Maze.check.not_nil value, message
|
79
|
+
end
|
80
|
+
|
81
|
+
# Wrapper for Maze.check.not_nil to avoid making a breaking change.
|
82
|
+
# @deprecated TODO Remove in v7
|
83
|
+
def assert_block(message = 'block failed', &block)
|
84
|
+
Maze.check.block(message, &block)
|
85
|
+
end
|
86
|
+
|
87
|
+
# Wrapper for Maze.check.true to avoid making a breaking change.
|
88
|
+
# @deprecated TODO Remove in v7
|
89
|
+
def assert_equal(value, message = nil)
|
90
|
+
Maze.check.equal value, message
|
91
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Maze
|
4
|
+
module Checks
|
5
|
+
# Assertion-backed data verification checks
|
6
|
+
class NoopCheck
|
7
|
+
def true(_test, _message = nil) end
|
8
|
+
|
9
|
+
def false(_test, _message = nil) end
|
10
|
+
|
11
|
+
def nil(_test, _message = nil) end
|
12
|
+
|
13
|
+
def not_nil(_test, _message = nil) end
|
14
|
+
|
15
|
+
def match(_pattern, _string, _message = nil) end
|
16
|
+
|
17
|
+
def equal(_expected, _actual, _message = nil) end
|
18
|
+
|
19
|
+
def not_equal(_expected, _actual, _message = nil) end
|
20
|
+
|
21
|
+
def operator(_operand1, _operator, _operand2, _message = nil) end
|
22
|
+
|
23
|
+
def kind_of(_klass, _object, _message = nil) end
|
24
|
+
|
25
|
+
def block(_message = 'block failed', &_block) end
|
26
|
+
|
27
|
+
def include(_collection, _object, _message = nil) end
|
28
|
+
alias includes include
|
29
|
+
|
30
|
+
def not_include(_collection, _object, _message = nil) end
|
31
|
+
alias not_includes not_include
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/maze/compare.rb
ADDED
@@ -0,0 +1,161 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Maze
|
4
|
+
# Routines for conducting comparisons
|
5
|
+
module Compare
|
6
|
+
# Provides a way to delivering results of element comparisons to test steps
|
7
|
+
class Result
|
8
|
+
# @!attribute [r] reasons
|
9
|
+
# @return [Array] An array of reasons for a comparison result
|
10
|
+
attr_reader :reasons
|
11
|
+
|
12
|
+
# @!attribute [w] equal
|
13
|
+
# @param [Boolean] Indicates if the comparison indicated the elements were equal
|
14
|
+
attr_writer :equal
|
15
|
+
|
16
|
+
# @!attribute [r] keys
|
17
|
+
# @return [Array] An array of keys checked
|
18
|
+
attr_reader :keys
|
19
|
+
|
20
|
+
# Creates the Result object
|
21
|
+
def initialize
|
22
|
+
@equal = true
|
23
|
+
@keys = []
|
24
|
+
@reasons = []
|
25
|
+
end
|
26
|
+
|
27
|
+
# Indicates if the values compared to produced this result were equal
|
28
|
+
#
|
29
|
+
# @return [Boolean] Whether the elements were equal
|
30
|
+
def equal?
|
31
|
+
@equal
|
32
|
+
end
|
33
|
+
|
34
|
+
# Returns the keys traversed in order to compare the end values
|
35
|
+
#
|
36
|
+
# @return [String] The keypath used in the test
|
37
|
+
def keypath
|
38
|
+
keys.length > 0 ? keys.reverse.join('.') : '<root>'
|
39
|
+
end
|
40
|
+
|
41
|
+
# Provides a standard assertion of equality, with standardised output
|
42
|
+
def assert_equal
|
43
|
+
Maze.check.true(@equal, "The compared fields do not match:\n #{result.reasons.join('\n')}")
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class << self
|
48
|
+
# Compares two objects for value equality, traversing to compare each
|
49
|
+
# nested object.
|
50
|
+
#
|
51
|
+
# @param obj1 [Any] The first object to compare
|
52
|
+
# @param obj2 [Any] The second object to compare
|
53
|
+
# @param result [Result|nil] Optional. Used for comparing recursively
|
54
|
+
#
|
55
|
+
# @return [Result] The result of comparing the objects
|
56
|
+
def value(obj1, obj2, result = nil)
|
57
|
+
result ||= Result.new
|
58
|
+
return result if obj1 == 'IGNORE'
|
59
|
+
|
60
|
+
if obj1 == 'NUMBER'
|
61
|
+
if obj2.is_a?(Numeric)
|
62
|
+
return result
|
63
|
+
else
|
64
|
+
result.equal = false
|
65
|
+
result.reasons << "A Number was expected, '#{obj2.class} received"
|
66
|
+
return result
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
unless obj1.class == obj2.class
|
71
|
+
result.equal = false
|
72
|
+
result.reasons << "Object types differ - expected '#{obj1.class}', received '#{obj2.class}'"
|
73
|
+
return result
|
74
|
+
end
|
75
|
+
|
76
|
+
case obj1
|
77
|
+
when Array
|
78
|
+
array(obj1, obj2, result)
|
79
|
+
when Hash
|
80
|
+
hash(obj1, obj2, result)
|
81
|
+
when String
|
82
|
+
string(obj1, obj2, result)
|
83
|
+
else
|
84
|
+
result.reasons << "#{obj1} is not equal to #{obj2}" unless (result.equal = (obj1 == obj2))
|
85
|
+
end
|
86
|
+
result
|
87
|
+
end
|
88
|
+
|
89
|
+
# Compares two arrays for value equality, traversing and comparing each element.
|
90
|
+
# Results are written to the given Result object.
|
91
|
+
#
|
92
|
+
# @param array1 [Array] The first array to compare
|
93
|
+
# @param array2 [Array] The second array to compare
|
94
|
+
# @param result [Result] The Result to store the results
|
95
|
+
def array(array1, array2, result)
|
96
|
+
unless array1.length == array2.length
|
97
|
+
result.equal = false
|
98
|
+
result.reasons << "Expected #{array1.length} items in array, received #{array2.length}"
|
99
|
+
return
|
100
|
+
end
|
101
|
+
|
102
|
+
array1.each_with_index do |obj1, index|
|
103
|
+
value(obj1, array2[index], result)
|
104
|
+
unless result.equal?
|
105
|
+
result.keys << index.to_s
|
106
|
+
break
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# Compares two hashes for value equality, traversing and comparing each key-value pair.
|
112
|
+
# Results are written to the given Result object.
|
113
|
+
#
|
114
|
+
# @param hash1 [Hash] The first hash to compare
|
115
|
+
# @param hash2 [Hash] The second hash to compare
|
116
|
+
# @param result [Result] The Result to store the results
|
117
|
+
def hash(hash1, hash2, result)
|
118
|
+
unless hash1.keys.length == hash2.keys.length
|
119
|
+
result.equal = false
|
120
|
+
missing = hash1.keys - hash2.keys
|
121
|
+
unexpected = hash2.keys - hash1.keys
|
122
|
+
result.reasons << "Missing keys from hash: #{missing.join(',')}" unless missing.empty?
|
123
|
+
result.reasons << "Unexpected keys in hash: #{unexpected.join(',')}" unless unexpected.empty?
|
124
|
+
return
|
125
|
+
end
|
126
|
+
|
127
|
+
hash1.each do |key, value|
|
128
|
+
value(value, hash2[key], result)
|
129
|
+
unless result.equal?
|
130
|
+
result.keys << key
|
131
|
+
break
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
# Compares two strings, writing the results to the given Result object.
|
137
|
+
#
|
138
|
+
# @param template [String] The expected string
|
139
|
+
# @param str2 [String] The string to compare
|
140
|
+
# @param result [Result] The Result to store the results
|
141
|
+
def string(template, str2, result)
|
142
|
+
return if template == str2 || regex_match(template, str2)
|
143
|
+
|
144
|
+
result.equal = false
|
145
|
+
result.reasons << "'#{str2}' does not match '#{template}'"
|
146
|
+
end
|
147
|
+
|
148
|
+
# Matches a string against a regex
|
149
|
+
#
|
150
|
+
# @param template [String] The regex to test with
|
151
|
+
# @param value [String] The value to test
|
152
|
+
#
|
153
|
+
# @return [Boolean] Whether the regex produced a match
|
154
|
+
def regex_match(template, value)
|
155
|
+
regex = template
|
156
|
+
regex = "^#{regex}$" unless regex.start_with?('^') || regex.end_with?('$')
|
157
|
+
value =~ /#{regex}/
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|