bucky-core 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
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