bucky-core 0.9.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.
Files changed (100) hide show
  1. checksums.yaml +7 -0
  2. data/.circleci/config.yml +66 -0
  3. data/.codeclimate.yml +48 -0
  4. data/.dockerignore +11 -0
  5. data/.gitignore +40 -0
  6. data/.rspec +3 -0
  7. data/.rubocop.yml +76 -0
  8. data/.rubocop_todo.yml +51 -0
  9. data/Dockerfile +38 -0
  10. data/Dockerfile.system-test +44 -0
  11. data/Gemfile +4 -0
  12. data/Gemfile.lock +115 -0
  13. data/LICENSE +201 -0
  14. data/README.md +246 -0
  15. data/Rakefile +8 -0
  16. data/bin/console +15 -0
  17. data/bin/setup +8 -0
  18. data/bucky-core.gemspec +47 -0
  19. data/docker-compose.dev-with-bm.yml +28 -0
  20. data/docker-compose.dev.yml +18 -0
  21. data/docker-compose.system-test.yml +21 -0
  22. data/docker/nginx/Dockerfile +7 -0
  23. data/docker/nginx/nginx.conf +22 -0
  24. data/docker/nginx/public/index.html +19 -0
  25. data/docker/nginx/public/test_page.html +12 -0
  26. data/exe/bucky +214 -0
  27. data/lib/bucky.rb +3 -0
  28. data/lib/bucky/core/database/db_connector.rb +29 -0
  29. data/lib/bucky/core/database/test_data_operator.rb +195 -0
  30. data/lib/bucky/core/exception/bucky_exception.rb +39 -0
  31. data/lib/bucky/core/report/screen_shot_generator.rb +24 -0
  32. data/lib/bucky/core/test_core/test_case_loader.rb +162 -0
  33. data/lib/bucky/core/test_core/test_class_generator.rb +129 -0
  34. data/lib/bucky/core/test_core/test_manager.rb +70 -0
  35. data/lib/bucky/core/test_core/test_result.rb +92 -0
  36. data/lib/bucky/test_equipment/evidence/evidence_generator.rb +36 -0
  37. data/lib/bucky/test_equipment/pageobject/base_pageobject.rb +55 -0
  38. data/lib/bucky/test_equipment/pageobject/pages.rb +61 -0
  39. data/lib/bucky/test_equipment/selenium_handler/webdriver_handler.rb +66 -0
  40. data/lib/bucky/test_equipment/test_case/abst_test_case.rb +49 -0
  41. data/lib/bucky/test_equipment/test_case/e2e_test_case.rb +70 -0
  42. data/lib/bucky/test_equipment/test_case/linkstatus_test_case.rb +28 -0
  43. data/lib/bucky/test_equipment/user_operation/user_operation_helper.rb +97 -0
  44. data/lib/bucky/test_equipment/user_operation/user_operation_logger.rb +15 -0
  45. data/lib/bucky/test_equipment/user_operation/user_operator.rb +61 -0
  46. data/lib/bucky/test_equipment/verifications/abst_verification.rb +13 -0
  47. data/lib/bucky/test_equipment/verifications/e2e_verification.rb +106 -0
  48. data/lib/bucky/test_equipment/verifications/js_error_checker.rb +23 -0
  49. data/lib/bucky/test_equipment/verifications/service_verifications.rb +62 -0
  50. data/lib/bucky/test_equipment/verifications/status_checker.rb +180 -0
  51. data/lib/bucky/tools/lint.rb +69 -0
  52. data/lib/bucky/utils/bucky_logger.rb +25 -0
  53. data/lib/bucky/utils/bucky_output.rb +23 -0
  54. data/lib/bucky/utils/config.rb +55 -0
  55. data/lib/bucky/utils/requests.rb +33 -0
  56. data/lib/bucky/utils/yaml_load.rb +24 -0
  57. data/lib/bucky/version.rb +7 -0
  58. data/system_testing/test_bucky_project/.bucky_home +2 -0
  59. data/system_testing/test_bucky_project/config/bucky_config.yml +6 -0
  60. data/system_testing/test_bucky_project/config/e2e_config.yml +15 -0
  61. data/system_testing/test_bucky_project/config/linkstatus_config.yml +3 -0
  62. data/system_testing/test_bucky_project/config/test_db_config.yml +8 -0
  63. data/system_testing/test_bucky_project/services/README.md +1 -0
  64. data/system_testing/test_bucky_project/services/service_a/pc/pageobject/index.rb +13 -0
  65. data/system_testing/test_bucky_project/services/service_a/pc/parts/index.yml +6 -0
  66. data/system_testing/test_bucky_project/services/service_a/pc/scenarios/e2e/pc_e2e.yml +68 -0
  67. data/system_testing/test_bucky_project/services/service_a/pc/scenarios/e2e/setup_each_pc_e2e.yml +20 -0
  68. data/system_testing/test_bucky_project/services/service_a/pc/scenarios/e2e/setup_teardown_each_pc_e2e.yml +35 -0
  69. data/system_testing/test_bucky_project/services/service_a/pc/scenarios/e2e/teardown_each_pc_e2e.yml +20 -0
  70. data/system_testing/test_bucky_project/services/service_a/pc/scenarios/linkstatus/pc_link.yml +10 -0
  71. data/system_testing/test_bucky_project/services/service_a/sp/pageobject/index.rb +14 -0
  72. data/system_testing/test_bucky_project/services/service_a/sp/parts/index.yml +6 -0
  73. data/system_testing/test_bucky_project/services/service_a/sp/scenarios/e2e/sp_e2e_test.yml +26 -0
  74. data/system_testing/test_bucky_project/services/service_a/sp/scenarios/linkstatus/sp_link.yml +10 -0
  75. data/system_testing/test_bucky_project/services/service_a/tablet/pageobject/index.rb +14 -0
  76. data/system_testing/test_bucky_project/services/service_a/tablet/parts/index.yml +6 -0
  77. data/system_testing/test_bucky_project/services/service_a/tablet/scenarios/e2e/tablet_e2e_test.yml +26 -0
  78. data/system_testing/test_bucky_project/system/evidences/README.md +1 -0
  79. data/system_testing/test_bucky_project/system/evidences/screen_shots/README.md +1 -0
  80. data/system_testing/test_bucky_project/system/logs/README.md +1 -0
  81. data/system_testing/test_specification.md +38 -0
  82. data/system_testing/testing_code/command.bats +42 -0
  83. data/system_testing/testing_code/e2e.bats +75 -0
  84. data/system_testing/testing_code/linkstatus.bats +24 -0
  85. data/template/make_page/pc/pageobject/sample_page.rb +23 -0
  86. data/template/make_page/pc/parts/sample_page.yml +9 -0
  87. data/template/make_page/sp/pageobject/sample_page.rb +24 -0
  88. data/template/make_page/sp/parts/sample_page.yml +9 -0
  89. data/template/make_page/tablet/pageobject/sample_page.rb +24 -0
  90. data/template/make_page/tablet/parts/sample_page.yml +9 -0
  91. data/template/new/.bucky_home +2 -0
  92. data/template/new/config/bucky_config.yml +6 -0
  93. data/template/new/config/e2e_config.yml +15 -0
  94. data/template/new/config/linkstatus_config.yml +3 -0
  95. data/template/new/config/test_db_config.yml +8 -0
  96. data/template/new/services/README.md +1 -0
  97. data/template/new/system/evidences/README.md +1 -0
  98. data/template/new/system/evidences/screen_shots/README.md +1 -0
  99. data/template/new/system/logs/README.md +1 -0
  100. metadata +415 -0
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'net/http'
4
+ require_relative '../verifications/status_checker'
5
+ require_relative './abst_test_case'
6
+
7
+ module Bucky
8
+ module TestEquipment
9
+ module TestCase
10
+ class LinkstatusTestCase < Bucky::TestEquipment::TestCase::AbstTestCase
11
+ include Bucky::TestEquipment::Verifications::StatusChecker
12
+
13
+ class << self
14
+ def startup; end
15
+
16
+ def shutdown; end
17
+ end
18
+
19
+ def setup; end
20
+
21
+ def teardown
22
+ # Call abst_test_case.teardown to get elappsed time of every test case
23
+ super
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bucky
4
+ module TestEquipment
5
+ module UserOperation
6
+ class UserOperationHelper
7
+ def initialize(args)
8
+ @app = args[:app]
9
+ @device = args[:device]
10
+ @driver = args[:driver]
11
+ @pages = args[:pages]
12
+ end
13
+
14
+ # Open url
15
+ # @param [Hash]
16
+ def go(args)
17
+ @driver.navigate.to args[:url]
18
+ end
19
+
20
+ def back(_)
21
+ @driver.navigate.back
22
+ end
23
+
24
+ def input(args)
25
+ @pages.get_part(args).send_keys args[:word]
26
+ end
27
+
28
+ # Clear textbox
29
+ def clear(args)
30
+ @pages.get_part(args).clear
31
+ end
32
+
33
+ def click(args)
34
+ elem = @pages.get_part(args)
35
+ elem.location_once_scrolled_into_view
36
+ sleep 1
37
+ elem.click
38
+ end
39
+
40
+ def refresh(_)
41
+ @driver.navigate.refresh
42
+ end
43
+
44
+ def switch_next_window(_)
45
+ @driver.switch_to.window(@driver.window_handles.last)
46
+ end
47
+
48
+ def back_to_window(_)
49
+ @driver.switch_to.window(@driver.window_handles.first)
50
+ end
51
+
52
+ # Close window
53
+ def close(_)
54
+ @driver.close
55
+ end
56
+
57
+ def stop(_)
58
+ puts 'stop. please enter to continue'
59
+ gets
60
+ end
61
+
62
+ def choose(args)
63
+ option = Selenium::WebDriver::Support::Select.new(@pages.get_part(args))
64
+ if args.key?(:text)
65
+ type = :text
66
+ selected = args[type].to_s
67
+ elsif args.key?(:value)
68
+ type = :value
69
+ selected = args[type].to_s
70
+ elsif args.key?(:index)
71
+ type = :index
72
+ selected = args[type].to_i
73
+ else
74
+ raise StandardError, "Included invalid key #{args.keys}"
75
+ end
76
+ option.select_by(type, selected)
77
+ end
78
+
79
+ # Alert accept
80
+ def accept_alert(_)
81
+ a = @driver.switch_to.alert
82
+ a.accept
83
+ end
84
+
85
+ def wait(args)
86
+ # Indent
87
+ print ' ' * 6
88
+ args[:sec].times do |count|
89
+ print "#{count + 1} "
90
+ sleep 1
91
+ end
92
+ puts ''
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bucky
4
+ module TestEquipment
5
+ module UserOperation
6
+ class UserOperationLogger
7
+ class << self
8
+ def get_user_opr_log(op_args)
9
+ op_args.to_s << "\n"
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../core/exception/bucky_exception'
4
+ require_relative './user_operation_helper'
5
+ require_relative '../../utils/bucky_logger'
6
+
7
+ module Bucky
8
+ module TestEquipment
9
+ module UserOperation
10
+ class UserOperator
11
+ include Bucky::Utils::BuckyLogger
12
+
13
+ def initialize(args)
14
+ @operation_helper = Bucky::TestEquipment::UserOperation::UserOperationHelper.new(args)
15
+ @pages = args[:pages]
16
+ end
17
+
18
+ # Call user operation by argument
19
+ # @param [String] operation
20
+ # @param [String] test_case_name
21
+ # @param [Hash] args
22
+ def method_missing(operation, test_case_name, **args)
23
+ @operation = operation
24
+ @test_case_name = test_case_name
25
+ Bucky::Utils::BuckyLogger.write(test_case_name, args)
26
+
27
+ # Call method of UserOperationHelper
28
+ return @operation_helper.send(@operation, args) if @operation_helper.methods.include?(@operation)
29
+
30
+ # Call method of page object
31
+ # e.g) {page: 'top', operation: 'input_freeword', word: 'testing word'}
32
+ return page_method(args) if args.key?(:page) && !args.key?(:part)
33
+
34
+ # Call method of part
35
+ part_mothod(args) if args.key?(:part)
36
+ rescue StandardError => e
37
+ Bucky::Core::Exception::WebdriverException.handle(e)
38
+ end
39
+
40
+ private
41
+
42
+ def page_method(args)
43
+ @pages.send(args[:page]).send(@operation, args)
44
+ end
45
+
46
+ def part_mothod(args)
47
+ # Multiple parts is saved as hash
48
+ # e.g){page: 'top', part: {locate: 'rosen_tokyo', num: 1}, operate: 'click'}
49
+ if args[:part].class == Hash
50
+ part_name = args[:part][:locate]
51
+ num = args[:part][:num]
52
+ @pages.send(args[:page]).send(part_name)[num].send(@operation)
53
+ # e.g.){page: 'top', part: 'rosen_tokyo', operate: 'click'}
54
+ else
55
+ @pages.send(args[:page]).send(args[:part]).send(@operation)
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test/unit'
4
+
5
+ module Bucky
6
+ module TestEquipment
7
+ module Verifications
8
+ class AbstVerification
9
+ include Test::Unit::Assertions
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../utils/bucky_logger'
4
+ require_relative '../../utils/bucky_output'
5
+ require_relative './abst_verification'
6
+ require_relative '../evidence/evidence_generator'
7
+
8
+ module Bucky
9
+ module TestEquipment
10
+ module Verifications
11
+ class E2eVerification < Bucky::TestEquipment::Verifications::AbstVerification
12
+ include Bucky::Utils::BuckyLogger
13
+ include Bucky::Utils::BuckyOutput
14
+ using StringColorize
15
+
16
+ def initialize(driver, pages, test_case_name)
17
+ @driver = driver
18
+ @pages = pages
19
+ @evidence = Bucky::TestEquipment::Evidence::E2eEvidence.new(driver: driver, test_case: test_case_name)
20
+ end
21
+
22
+ def pages_getter
23
+ @pages
24
+ end
25
+
26
+ # Check whether title of web page matches expected value
27
+ # @param [Hash]
28
+ def assert_title(**args)
29
+ Bucky::Utils::BuckyLogger.write('assert_title', args)
30
+ verify_rescue { assert_equal(args[:expect]&.to_s, @driver.title, 'Not Expected Title.') }
31
+ end
32
+
33
+ # Check whether text of web element matches expected value
34
+ # @param [Hash]
35
+ def assert_text(**args)
36
+ Bucky::Utils::BuckyLogger.write('assert_text', args)
37
+ part = @pages.get_part(args)
38
+ verify_rescue { assert_equal(args[:expect]&.to_s, part.text, 'Not Expected Text.') }
39
+ end
40
+
41
+ # Check whether text of web element contains expected value
42
+ # @param [Hash]
43
+ def assert_contained_text(**args)
44
+ Bucky::Utils::BuckyLogger.write('assert_contained_text', args)
45
+ part = @pages.get_part(args)
46
+ verify_rescue { assert(part.text.include?(args[:expect]&.to_s), "Not Contain Expected Text.\nexpect: #{args[:expect].to_s.bg_green.black}\nactual: #{part.text.to_s.bg_red.black}") }
47
+ end
48
+
49
+ # Check whether url contains excepted value
50
+ # @param [Hash]
51
+ def assert_contained_url(**args)
52
+ Bucky::Utils::BuckyLogger.write('assert_contained_url', args)
53
+ verify_rescue { assert(@driver.current_url.include?(args[:expect]&.to_s), "Not Contain Expected URL.\nexpect: #{args[:expect].to_s.bg_green.black}\nactual: #{@driver.current_url.to_s.bg_red.black}") }
54
+ end
55
+
56
+ # Check whether attribute of web element contains excepted value.
57
+ # @param [Hash]
58
+ def assert_contained_attribute(**args)
59
+ Bucky::Utils::BuckyLogger.write('assert_contained_attribute', args)
60
+ part = @pages.get_part(args)
61
+ verify_rescue { assert(part[args[:attribute]].include?(args[:expect]&.to_s), "Not Contain Expected Attribute.\nexpect: #{args[:expect].to_s.bg_green.black}\nactual: #{part[args[:attribute]].to_s.bg_red.black}") }
62
+ end
63
+
64
+ # Check whether text of part is number
65
+ # @param [Hash]
66
+ def assert_is_number(**args)
67
+ Bucky::Utils::BuckyLogger.write('assert is number', args)
68
+ text = @pages.get_part(args).text.sub(',', '')
69
+ verify_rescue { assert(text.to_i.to_s == text.to_s, "Not number.\nactual: #{text.bg_red.black}") }
70
+ end
71
+
72
+ # Check whether style property includes display:block
73
+ # @param [Hash]
74
+ def assert_display(**args)
75
+ Bucky::Utils::BuckyLogger.write('assert display', args)
76
+ verify_rescue { assert_true(@pages.get_part(args).displayed?, "No display this parts.\nURL: #{@driver.current_url}\npage: #{args[:page]}\npart: #{args[:part]}") }
77
+ end
78
+
79
+ # Check whether web element exists
80
+ # @param [Hash]
81
+ def assert_exist_part(**args)
82
+ Bucky::Utils::BuckyLogger.write('assert_exist_part', args)
83
+ verify_rescue { assert_true(@pages.part_exist?(args), "This part is not exist.\nURL: #{@driver.current_url}\npage: #{args[:page]}\npart: #{args[:part]}") }
84
+ end
85
+
86
+ # Check whether web element don't exist
87
+ # @param [Hash]
88
+ def assert_not_exist_part(**args)
89
+ Bucky::Utils::BuckyLogger.write('assert_not_exist_part', args)
90
+ verify_rescue { assert_false(@pages.part_exist?(args), "This part is exist.\nURL: #{@driver.current_url}\npage: #{args[:page]}\npart: #{args[:part]}") }
91
+ end
92
+
93
+ private
94
+
95
+ # Common exception method
96
+ # @param [Method] &assert
97
+ def verify_rescue
98
+ yield
99
+ rescue StandardError => e
100
+ @evidence.save_evidence(e)
101
+ raise e
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test/unit'
4
+
5
+ module Bucky
6
+ module TestEquipment
7
+ module Verifications
8
+ module JsErrorChecker
9
+ include Test::Unit::Assertions
10
+
11
+ # Check Javascript Error in page
12
+ # @param [Webdriver] driver
13
+ def assert_no_js_error(driver)
14
+ js_errors = driver.execute_script(
15
+ 'return window.JSErrorCollector_errors ? window.JSErrorCollector_errors.pump() : []'
16
+ )
17
+ # Empty is ok
18
+ assert_empty(js_errors, '[JS Error]')
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../verifications/e2e_verification'
4
+
5
+ module Bucky
6
+ module TestEquipment
7
+ module Verifications
8
+ class ServiceVerifications
9
+ attr_reader :e2e_verification
10
+
11
+ # @param [String] @service
12
+ # @param [String] @device (pc, sp)
13
+ # @param [Selenium::WebDriver::Remote::Driver] @driver
14
+ # @param [Bucky::TestEquipment::PageObject::Pages] @pages
15
+ # @param [String] @test_case_name
16
+ def initialize(args)
17
+ @service = args[:service]
18
+ @device = args[:device]
19
+ @driver = args[:driver]
20
+ @pages = args[:pages]
21
+ @test_case_name = args[:method_name]
22
+ collect_verifications
23
+ @e2e_verification = Bucky::TestEquipment::Verifications::E2eVerification.new(@driver, @pages, @test_case_name)
24
+ end
25
+
26
+ def method_missing(verification, **args)
27
+ if e2e_verification.respond_to? verification
28
+ puts " #{verification} is defined in E2eVerificationClass."
29
+ e2e_verification.send(verification, args)
30
+ elsif args.key?(:page)
31
+ send(args[:page]).send(verification, args)
32
+ else
33
+ raise StandardError, "Undefined verification method or invalid arguments. #{verification},#{args}"
34
+ end
35
+ rescue StandardError => e
36
+ raise e
37
+ end
38
+
39
+ private
40
+
41
+ # Load page and define page verification method
42
+ def collect_verifications
43
+ module_service_name = @service.split('_').map(&:capitalize).join
44
+ Dir.glob("#{$bucky_home_dir}/services/#{@service}/#{@device}/verifications/*.rb").each do |file|
45
+ require file
46
+
47
+ page_name = file.split('/')[-1].sub('.rb', '')
48
+ page_class_name = page_name.split('_').map(&:capitalize).join
49
+
50
+ # Get instance of page object
51
+ page_class = eval(format('Services::%<module_service_name>s::%<device>s::Verifications::%<page_class_name>s', module_service_name: module_service_name, device: @device.capitalize, page_class_name: page_class_name))
52
+ page_instance = page_class.new(@driver, @pages, @test_case_name)
53
+
54
+ self.class.class_eval do
55
+ define_method(page_name) { page_instance }
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,180 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test/unit'
4
+ require 'nokogiri'
5
+ require 'parallel'
6
+ require_relative '../../utils/requests'
7
+ require_relative '../../utils/config'
8
+
9
+ REDIRECT_LIMIT = 5
10
+
11
+ # Check http status code from response
12
+ # 2xx -> OK
13
+ # 3xx -> request again
14
+ # 4xx~5xx -> NG
15
+
16
+ module Bucky
17
+ module TestEquipment
18
+ module Verifications
19
+ module StatusChecker
20
+ include Test::Unit::Assertions
21
+ include Bucky::Utils::Requests
22
+
23
+ # Check http status code
24
+ # @param [String] url
25
+ # @return [String] message
26
+ def http_status_check(args)
27
+ url = args[:url]
28
+ device = args[:device]
29
+ link_check_max_times = args[:link_check_max_times]
30
+ url_log = args[:url_log]
31
+ redirect_count = args[:redirect_count]
32
+ redirect_url_list = args[:redirect_url_list]
33
+
34
+ # If number of requests is over redirect limit
35
+ return { error_message: "\n[Redirect Error] #{url} is redirected more than #{REDIRECT_LIMIT}" } if redirect_count > REDIRECT_LIMIT
36
+
37
+ check_result = check_log_and_get_response(url, device, link_check_max_times, url_log)
38
+ # If result include response continue to check, else return result
39
+ !check_result.key?(:response) ? (return check_result) : response = check_result[:response]
40
+
41
+ # Store original url
42
+ redirect_url_list << url
43
+ case response.code
44
+ when /2[0-9]{2}/
45
+ url_log[url][:entity] = response.entity
46
+ puts " #{url} ... [#{response.code}:OK]"
47
+ { entity: response.entity }
48
+ when /3[0-9]{2}/
49
+ fqdn = url[%r{^(https?:\/\/([a-zA-Z0-9\-_.]+))}]
50
+ redirect_url = response['location']
51
+ # Add fqdn if location doesn't include fqdn
52
+ redirect_url = fqdn << redirect_url unless redirect_url.include?('http')
53
+ puts " #{url} ... redirect to #{redirect_url} [#{response.code}:RD]"
54
+ http_status_check_args = { url: redirect_url, device: device, link_check_max_times: link_check_max_times, url_log: url_log, redirect_count: redirect_count + 1, redirect_url_list: redirect_url_list }
55
+ http_status_check(http_status_check_args)
56
+ when /(4|5)[0-9]{2}/
57
+ url_log[url][:error_message] = "[Status Error] http status returned #{response.code}.\ncheck this url: #{redirect_url_list.join(' -> ')}"
58
+ puts " #{url} ... [#{response.code}:NG]"
59
+ { error_message: url_log[url][:error_message] }
60
+ else
61
+ url_log[url][:error_message] = "[Status Code Invalid Error] Status Code is Invalid. \n Status:#{response.code}"
62
+ { error_message: url_log[url][:error_message] }
63
+ end
64
+ end
65
+
66
+ def link_status_check(args)
67
+ url = args[:url]
68
+ device = args[:device]
69
+ exclude_urls = args[:exclude_urls]
70
+ link_check_max_times = args[:link_check_max_times]
71
+ url_log = args[:url_log]
72
+ only_same_fqdn = args[:only_same_fqdn] ||= true
73
+
74
+ # Extract base url and check if it is valid
75
+ url_reg = %r{^(https?://([a-zA-Z0-9\-_.]+))}
76
+ url_obj = url.match(url_reg)
77
+ raise "Invalid URL #{url}" unless url_obj
78
+
79
+ base_url = url_obj[1]
80
+ base_fqdn = url_obj[2]
81
+
82
+ # Check base url
83
+ http_status_check_args = { url: url, device: device, link_check_max_times: link_check_max_times, url_log: url_log, redirect_count: 0, redirect_url_list: [] }
84
+ base_response = http_status_check(http_status_check_args)
85
+ assert_nil(base_response[:error_message], "Response of base URL is incorrect.\n#{base_response[:error_message]}")
86
+
87
+ # Collect links
88
+ links_args = { base_url: base_url, base_fqdn: base_fqdn, url_reg: url_reg, only_same_fqdn: only_same_fqdn, entity: base_response[:entity] }
89
+ links = make_target_links(links_args)
90
+ links = exclude(links, exclude_urls) unless exclude_urls.nil?
91
+
92
+ errors = []
93
+ Parallel.each(links.uniq, in_threads: Bucky::Utils::Config.instance[:linkstatus_parallel_num]) do |link|
94
+ http_status_check_args[:url] = link
95
+ http_status_check_args[:redirect_url_list] = []
96
+ link_response = http_status_check(http_status_check_args)
97
+ errors << link_response[:error_message] if link_response[:error_message]
98
+ end
99
+ assert_empty(errors, errors.join("\n"))
100
+ end
101
+
102
+ def make_target_links(args)
103
+ base_url = args[:base_url]
104
+ base_fqdn = args[:base_fqdn]
105
+ url_reg = args[:url_reg]
106
+ only_same_fqdn = args[:only_same_fqdn]
107
+ entity = args[:entity]
108
+ doc = Nokogiri::HTML.parse(entity)
109
+ links = []
110
+ doc.xpath('//a').each do |node|
111
+ href = node.attr('href')
112
+ next if exclude_href?(href)
113
+
114
+ # Add fqdn if href doesn't include fqdn
115
+ unless url_reg.match?(href)
116
+ links << base_url + href
117
+ next
118
+ end
119
+
120
+ href_fqdn = href.match(url_reg)[2]
121
+ if only_same_fqdn == false
122
+ links << href
123
+ elsif base_fqdn == href_fqdn
124
+ links << href
125
+ end
126
+ end
127
+ links
128
+ end
129
+
130
+ # Exclude non test target url
131
+ def exclude(links, exclude_urls)
132
+ excluded_links = links - exclude_urls
133
+
134
+ # Exclude url if it has "*" in the last of it
135
+ exclude_urls.each do |ex_url|
136
+ next unless ex_url.end_with?('*')
137
+
138
+ excluded_links.delete_if { |l| l.start_with?(ex_url.delete('*')) }
139
+ end
140
+
141
+ excluded_links
142
+ end
143
+
144
+ private
145
+
146
+ def exclude_href?(href)
147
+ return true if href.nil?
148
+
149
+ exclude_regexps = [/^javascript.+/, /^tel:\d.+/, /^mailto:.+/]
150
+ exclude_regexps.keep_if { |reg| reg.match?(href) }
151
+ return true unless exclude_regexps.empty?
152
+
153
+ false
154
+ end
155
+
156
+ # Check result hash and submit request or return result.
157
+ # Return: 1.(if request submitted) respons 2. (code 2xx)entity 3. (reach max check times)error message
158
+ def check_log_and_get_response(url, device, link_check_max_times, url_log)
159
+ unless url_log.key?(url)
160
+ response = get_response(url, device, Bucky::Utils::Config.instance[:linkstatus_open_timeout], Bucky::Utils::Config.instance[:linkstatus_read_timeout])
161
+ url_log[url] = { code: response.code, entity: nil, error_message: nil, count: 1 }
162
+ return { response: response }
163
+ end
164
+
165
+ if url_log[url][:code].match?(/2[0-9]{2}/)
166
+ puts " #{url} is already [#{url_log[url][:code]}:OK]"
167
+ return { entity: url_log[url][:entity] }
168
+ elsif url_log[url][:count] >= link_check_max_times
169
+ puts " #{url} reach maximum check times [#{url_log[url][:code]}:NG]"
170
+ return { error_message: url_log[url][:error_message] }
171
+ else
172
+ response = get_response(url, device, Bucky::Utils::Config.instance[:linkstatus_open_timeout], Bucky::Utils::Config.instance[:linkstatus_read_timeout])
173
+ url_log[url][:count] += 1
174
+ return { response: response }
175
+ end
176
+ end
177
+ end
178
+ end
179
+ end
180
+ end