eyes_selenium 3.15.14 → 3.15.15
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 +4 -4
- data/lib/applitools/selenium/target.rb +41 -16
- data/lib/applitools/selenium/visual_grid/eyes_connector.rb +13 -9
- data/lib/applitools/selenium/visual_grid/render_task.rb +1 -1
- data/lib/applitools/selenium/visual_grid/vg_match_window_data.rb +65 -0
- data/lib/applitools/selenium/visual_grid/vg_resource.rb +1 -0
- data/lib/applitools/selenium/visual_grid/visual_grid_eyes.rb +37 -21
- data/lib/applitools/version.rb +1 -1
- metadata +5 -4
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: b56e73c95e87c0156bb7972b15dd18abda92fe0b839b12bc0439f1c251810339
         | 
| 4 | 
            +
              data.tar.gz: b491c7ef0ee74076b3880888f8fa52994395ef620c74f81a256caaa6fe489270
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: e74a81d70aee821c5f5016b988fc70130aa7d53cf73ad30a49c020752d62ce4570dae56ec07558e7f5f91ec0e13aec2fc85fda0317f1e49e26477eef58ae8caa
         | 
| 7 | 
            +
              data.tar.gz: 00660a727243097379a333ab2e15df66097808658dced225f63734cdf5e89860754e480c060a444f8d2b39f5aef32c451b466c3017e8ed37053a34ef9b01080c
         | 
| @@ -19,7 +19,7 @@ module Applitools | |
| 19 19 | 
             
                  end
         | 
| 20 20 |  | 
| 21 21 | 
             
                  attr_accessor :element, :frames, :region_to_check, :coordinate_type, :options, :ignored_regions,
         | 
| 22 | 
            -
                    :floating_regions, :frame_or_element
         | 
| 22 | 
            +
                    :floating_regions, :frame_or_element, :regions
         | 
| 23 23 |  | 
| 24 24 | 
             
                  private :frame_or_element, :frame_or_element=
         | 
| 25 25 |  | 
| @@ -32,6 +32,7 @@ module Applitools | |
| 32 32 | 
             
                      send_dom: nil,
         | 
| 33 33 | 
             
                      script_hooks: { beforeCaptureScreenshot: '' }
         | 
| 34 34 | 
             
                    }
         | 
| 35 | 
            +
                    self.regions = {}
         | 
| 35 36 | 
             
                    reset_for_fullscreen
         | 
| 36 37 | 
             
                  end
         | 
| 37 38 |  | 
| @@ -55,18 +56,26 @@ module Applitools | |
| 55 56 | 
             
                                         when Applitools::Region
         | 
| 56 57 | 
             
                                           proc { args.first.padding(requested_padding) }
         | 
| 57 58 | 
             
                                         when Applitools::Selenium::Element, ::Selenium::WebDriver::Element
         | 
| 58 | 
            -
                                           proc do
         | 
| 59 | 
            +
                                           proc do |_driver, return_element = false|
         | 
| 59 60 | 
             
                                             region = args.first
         | 
| 60 | 
            -
                                              | 
| 61 | 
            -
                                                | 
| 62 | 
            -
             | 
| 61 | 
            +
                                             padding_proc = proc do |region|
         | 
| 62 | 
            +
                                               Applitools::Region.from_location_size(
         | 
| 63 | 
            +
                                                   region.location, region.size
         | 
| 64 | 
            +
                                               ).padding(requested_padding)
         | 
| 65 | 
            +
                                             end
         | 
| 66 | 
            +
                                             next region, padding_proc if return_element
         | 
| 67 | 
            +
                                             padding_proc.call(region)
         | 
| 63 68 | 
             
                                           end
         | 
| 64 69 | 
             
                                         else
         | 
| 65 | 
            -
                                           proc do |driver|
         | 
| 70 | 
            +
                                           proc do |driver, return_element = false|
         | 
| 66 71 | 
             
                                             region = driver.find_element(*args)
         | 
| 67 | 
            -
                                              | 
| 68 | 
            -
                                                | 
| 69 | 
            -
             | 
| 72 | 
            +
                                             padding_proc = proc do |region|
         | 
| 73 | 
            +
                                               Applitools::Region.from_location_size(
         | 
| 74 | 
            +
                                                   region.location, region.size
         | 
| 75 | 
            +
                                               ).padding(requested_padding)
         | 
| 76 | 
            +
                                             end
         | 
| 77 | 
            +
                                             next region, padding_proc if return_element
         | 
| 78 | 
            +
                                             padding_proc.call(region)
         | 
| 70 79 | 
             
                                           end
         | 
| 71 80 | 
             
                                         end
         | 
| 72 81 |  | 
| @@ -105,14 +114,30 @@ module Applitools | |
| 105 114 | 
             
                                        end
         | 
| 106 115 | 
             
                    value = case args.first
         | 
| 107 116 | 
             
                            when Applitools::FloatingRegion
         | 
| 108 | 
            -
                               | 
| 109 | 
            -
                            when :: | 
| 110 | 
            -
                               | 
| 117 | 
            +
                              args.first.padding(requested_padding)
         | 
| 118 | 
            +
                            when ::Applitools::Region
         | 
| 119 | 
            +
                              Applitools::FloatingRegion.any(args.shift, *args).padding(requested_padding)
         | 
| 120 | 
            +
                            when ::Selenium::WebDriver::Element, Applitools::Selenium::Element
         | 
| 121 | 
            +
                              proc do |_driver, return_element = false|
         | 
| 122 | 
            +
                                args_dup = args.dup
         | 
| 123 | 
            +
                                region = args_dup.shift
         | 
| 124 | 
            +
                                padding_proc = proc do |region|
         | 
| 125 | 
            +
                                  Applitools::FloatingRegion.any(region, *args_dup).padding(requested_padding)
         | 
| 126 | 
            +
                                end
         | 
| 127 | 
            +
                                next region, padding_proc if return_element
         | 
| 128 | 
            +
                                padding_proc.call(region)
         | 
| 129 | 
            +
                              end
         | 
| 111 130 | 
             
                            else
         | 
| 112 | 
            -
                              proc do |driver|
         | 
| 113 | 
            -
                                 | 
| 114 | 
            -
             | 
| 115 | 
            -
                                 | 
| 131 | 
            +
                              proc do |driver, return_element = false|
         | 
| 132 | 
            +
                                args_dup = args.dup
         | 
| 133 | 
            +
                                region = driver.find_element(args_dup.shift, args_dup.shift)
         | 
| 134 | 
            +
                                padding_proc = proc do |region|
         | 
| 135 | 
            +
                                  Applitools::FloatingRegion.any(
         | 
| 136 | 
            +
                                      region, *args_dup
         | 
| 137 | 
            +
                                  ).padding(requested_padding)
         | 
| 138 | 
            +
                                end
         | 
| 139 | 
            +
                                next region, padding_proc if return_element
         | 
| 140 | 
            +
                                padding_proc.call(region)
         | 
| 116 141 | 
             
                              end
         | 
| 117 142 | 
             
                            end
         | 
| 118 143 | 
             
                    floating_regions << value
         | 
| @@ -39,10 +39,10 @@ module Applitools | |
| 39 39 | 
             
                    target_to_check = target.finalize
         | 
| 40 40 | 
             
                    timeout = target_to_check.options[:timeout] || USE_DEFAULT_MATCH_TIMEOUT
         | 
| 41 41 |  | 
| 42 | 
            -
                    match_data = Applitools:: | 
| 42 | 
            +
                    match_data = Applitools::Selenium::VgMatchWindowData.new
         | 
| 43 43 | 
             
                    match_data.tag = name
         | 
| 44 44 | 
             
                    update_default_settings(match_data)
         | 
| 45 | 
            -
                    match_data.read_target(target_to_check, driver)
         | 
| 45 | 
            +
                    match_data.read_target(target_to_check, driver, selector_regions)
         | 
| 46 46 | 
             
                    check_result = check_window_base(
         | 
| 47 47 | 
             
                        dummy_region_provider, timeout, match_data
         | 
| 48 48 | 
             
                    )
         | 
| @@ -81,6 +81,10 @@ module Applitools | |
| 81 81 | 
             
                    render_status['domLocation']
         | 
| 82 82 | 
             
                  end
         | 
| 83 83 |  | 
| 84 | 
            +
                  def selector_regions
         | 
| 85 | 
            +
                    render_status['selectorRegions']
         | 
| 86 | 
            +
                  end
         | 
| 87 | 
            +
             | 
| 84 88 | 
             
                  def match_level_keys
         | 
| 85 89 | 
             
                    %w(match_level exact scale remainder).map(&:to_sym)
         | 
| 86 90 | 
             
                  end
         | 
| @@ -160,13 +164,13 @@ module Applitools | |
| 160 164 | 
             
                    a_title = title
         | 
| 161 165 | 
             
                    # logger.info 'Done!'
         | 
| 162 166 | 
             
                    Applitools::AppOutputWithScreenshot.new(
         | 
| 163 | 
            -
             | 
| 164 | 
            -
             | 
| 165 | 
            -
             | 
| 166 | 
            -
             | 
| 167 | 
            -
             | 
| 168 | 
            -
             | 
| 169 | 
            -
             | 
| 167 | 
            +
                      Applitools::AppOutput.new(a_title, '').tap do |o|
         | 
| 168 | 
            +
                        o.location = region.location unless region.empty?
         | 
| 169 | 
            +
                        o.dom_url = dom_url
         | 
| 170 | 
            +
                        o.screenshot_url = screenshot_url if respond_to?(:screenshot_url) && !screenshot_url.nil?
         | 
| 171 | 
            +
                      end,
         | 
| 172 | 
            +
                      nil,
         | 
| 173 | 
            +
                      true
         | 
| 170 174 | 
             
                    )
         | 
| 171 175 | 
             
                  end
         | 
| 172 176 | 
             
                end
         | 
| @@ -128,7 +128,7 @@ module Applitools | |
| 128 128 | 
             
                    self.all_blobs = data["blobs"]
         | 
| 129 129 | 
             
                    self.resource_urls = data["resourceUrls"]
         | 
| 130 130 | 
             
                    self.request_resources = Applitools::Selenium::RenderResources.new
         | 
| 131 | 
            -
             | 
| 131 | 
            +
             | 
| 132 132 | 
             
                    all_blobs.map {|blob| Applitools::Selenium::VGResource.parse_blob_from_script(blob)}.each do |blob|
         | 
| 133 133 | 
             
                      request_resources[blob.url] = blob
         | 
| 134 134 | 
             
                    end
         | 
| @@ -0,0 +1,65 @@ | |
| 1 | 
            +
            module Applitools
         | 
| 2 | 
            +
              module Selenium
         | 
| 3 | 
            +
                class VgMatchWindowData < Applitools::MatchWindowData
         | 
| 4 | 
            +
                  attr_accessor :target, :selector_regions
         | 
| 5 | 
            +
                  def read_target(target, driver, selector_regions)
         | 
| 6 | 
            +
                    self.target = target
         | 
| 7 | 
            +
                    self.selector_regions = selector_regions
         | 
| 8 | 
            +
                    # options
         | 
| 9 | 
            +
                    target_options_to_read.each do |field|
         | 
| 10 | 
            +
                      a_value = target.options[field.to_sym]
         | 
| 11 | 
            +
                      send("#{field}=", a_value) unless a_value.nil?
         | 
| 12 | 
            +
                    end
         | 
| 13 | 
            +
                    # ignored regions
         | 
| 14 | 
            +
                    if target.respond_to? :ignored_regions
         | 
| 15 | 
            +
                      target.ignored_regions.each do |r|
         | 
| 16 | 
            +
                        @need_convert_ignored_regions_coordinates = true unless @need_convert_ignored_regions_coordinates
         | 
| 17 | 
            +
                        case r
         | 
| 18 | 
            +
                        when Proc
         | 
| 19 | 
            +
                          region, padding_proc = r.call(driver, true)
         | 
| 20 | 
            +
                          # require 'pry'
         | 
| 21 | 
            +
                          # binding.pry
         | 
| 22 | 
            +
                          region = selector_regions[target.regions[region]]
         | 
| 23 | 
            +
                          retrieved_region = Applitools::Region.new(region['x'], region['y'], region['width'], region['height'])
         | 
| 24 | 
            +
                          @ignored_regions << padding_proc.call(retrieved_region) if padding_proc.is_a? Proc
         | 
| 25 | 
            +
                        when Applitools::Region
         | 
| 26 | 
            +
                          @ignored_regions << r
         | 
| 27 | 
            +
                        end
         | 
| 28 | 
            +
                      end
         | 
| 29 | 
            +
                    end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                    # # floating regions
         | 
| 32 | 
            +
                    return unless target.respond_to? :floating_regions
         | 
| 33 | 
            +
                    target.floating_regions.each do |r|
         | 
| 34 | 
            +
                      case r
         | 
| 35 | 
            +
                      when Proc
         | 
| 36 | 
            +
                        region, padding_proc = r.call(driver, true)
         | 
| 37 | 
            +
                        region = selector_regions[target.regions[region]]
         | 
| 38 | 
            +
                        retrieved_region = Applitools::Region.new(region['x'], region['y'], region['width'], region['height'])
         | 
| 39 | 
            +
                        floating_region = padding_proc.call(retrieved_region) if padding_proc.is_a? Proc
         | 
| 40 | 
            +
                        raise Applitools::EyesError.new "Wrong floating region: #{region.class}" unless
         | 
| 41 | 
            +
                            floating_region.is_a? Applitools::FloatingRegion
         | 
| 42 | 
            +
                        @floating_regions << floating_region
         | 
| 43 | 
            +
                        @need_convert_floating_regions_coordinates = true
         | 
| 44 | 
            +
                      when Applitools::FloatingRegion
         | 
| 45 | 
            +
                        @floating_regions << r
         | 
| 46 | 
            +
                        @need_convert_floating_regions_coordinates = true
         | 
| 47 | 
            +
                      end
         | 
| 48 | 
            +
                    end
         | 
| 49 | 
            +
                  end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                  def convert_ignored_regions_coordinates
         | 
| 52 | 
            +
                    return unless @need_convert_ignored_regions_coordinates
         | 
| 53 | 
            +
                    self.ignored_regions = @ignored_regions.map(&:with_padding).map(&:to_hash)
         | 
| 54 | 
            +
                    @need_convert_ignored_regions_coordinates = false
         | 
| 55 | 
            +
                  end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                  def convert_floating_regions_coordinates
         | 
| 58 | 
            +
                    return unless @need_convert_floating_regions_coordinates
         | 
| 59 | 
            +
                    self.floating_regions = @floating_regions
         | 
| 60 | 
            +
                    @need_convert_floating_regions_coordinates = false
         | 
| 61 | 
            +
                  end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                end
         | 
| 64 | 
            +
              end
         | 
| 65 | 
            +
            end
         | 
| @@ -100,29 +100,48 @@ module Applitools | |
| 100 100 | 
             
                  end
         | 
| 101 101 |  | 
| 102 102 | 
             
                  def get_regions_x_paths(target)
         | 
| 103 | 
            -
                    regions_hash = collect_selenium_regions(target).each do |k,v|
         | 
| 104 | 
            -
             | 
| 105 | 
            -
             | 
| 106 | 
            -
             | 
| 107 | 
            -
             | 
| 108 | 
            -
             | 
| 109 | 
            -
             | 
| 110 | 
            -
             | 
| 111 | 
            -
             | 
| 103 | 
            +
                    # regions_hash = collect_selenium_regions(target).each do |k,v|
         | 
| 104 | 
            +
                    #   v.map! do |el|
         | 
| 105 | 
            +
                    #     if [::Selenium::WebDriver::Element, Applitools::Selenium::Element].include?(el.class)
         | 
| 106 | 
            +
                    #       xpath = driver.execute_script(Applitools::Selenium::Scripts::GET_ELEMENT_XPATH_JS, el)
         | 
| 107 | 
            +
                    #       web_element_region = Applitools::Selenium::WebElementRegion.new(xpath, k)
         | 
| 108 | 
            +
                    #       self.region_to_check = web_element_region if k ==:target && size_mod == 'selector'
         | 
| 109 | 
            +
                    #       web_element_region
         | 
| 110 | 
            +
                    #     end
         | 
| 111 | 
            +
                    #   end.compact!
         | 
| 112 | 
            +
                    # end
         | 
| 113 | 
            +
                    result = []
         | 
| 114 | 
            +
                    regions_hash = collect_selenium_regions(target).each do |el, v|
         | 
| 115 | 
            +
                      if [::Selenium::WebDriver::Element, Applitools::Selenium::Element].include?(el.class)
         | 
| 116 | 
            +
                        xpath = driver.execute_script(Applitools::Selenium::Scripts::GET_ELEMENT_XPATH_JS, el)
         | 
| 117 | 
            +
                        web_element_region = Applitools::Selenium::WebElementRegion.new(xpath, v)
         | 
| 118 | 
            +
                        self.region_to_check = web_element_region if v == :target && size_mod == 'selector'
         | 
| 119 | 
            +
                        result << web_element_region
         | 
| 120 | 
            +
                        target.regions[el] = result.size - 1
         | 
| 121 | 
            +
                      end
         | 
| 112 122 | 
             
                    end
         | 
| 113 | 
            -
                     | 
| 123 | 
            +
                    result
         | 
| 114 124 | 
             
                  end
         | 
| 115 125 |  | 
| 116 126 | 
             
                  def collect_selenium_regions(target)
         | 
| 117 | 
            -
                    selenium_regions = { | 
| 118 | 
            -
                    ignore_regions = target.ignored_regions
         | 
| 119 | 
            -
                    floating_regions = target.floating_regions
         | 
| 127 | 
            +
                    selenium_regions = {}
         | 
| 128 | 
            +
                    # ignore_regions = target.ignored_regions
         | 
| 129 | 
            +
                    # floating_regions = target.floating_regions
         | 
| 120 130 | 
             
                    target_element = target.region_to_check
         | 
| 121 131 | 
             
                    setup_size_mode(target_element)
         | 
| 122 132 | 
             
                    # selenium_regions.map do |r|
         | 
| 123 133 | 
             
                    #   element_or_region(r)
         | 
| 124 134 | 
             
                    # end
         | 
| 125 | 
            -
                     | 
| 135 | 
            +
                    target.ignored_regions.each do |r|
         | 
| 136 | 
            +
                      selenium_regions[element_or_region(r)] = :ignore
         | 
| 137 | 
            +
                    end
         | 
| 138 | 
            +
                    target.floating_regions.each do |r|
         | 
| 139 | 
            +
                      selenium_regions[element_or_region(r)] = :floating
         | 
| 140 | 
            +
                    end
         | 
| 141 | 
            +
                    selenium_regions[region_to_check] = :target if size_mod == 'selector'
         | 
| 142 | 
            +
             | 
| 143 | 
            +
                    # selenium_regions[:ignore] += [target.ignored_regions.map { |r| element_or_region(r) }].flatten.compact
         | 
| 144 | 
            +
                    # selenium_regions[:target] << region_to_check if size_mod == 'selector'
         | 
| 126 145 | 
             
                    selenium_regions
         | 
| 127 146 | 
             
                  end
         | 
| 128 147 |  | 
| @@ -145,7 +164,8 @@ module Applitools | |
| 145 164 |  | 
| 146 165 | 
             
                  def element_or_region(target_element)
         | 
| 147 166 | 
             
                    if target_element.respond_to?(:call)
         | 
| 148 | 
            -
                      target_element.call(driver)
         | 
| 167 | 
            +
                      region, _padding_proc = target_element.call(driver, true)
         | 
| 168 | 
            +
                      region
         | 
| 149 169 | 
             
                    else
         | 
| 150 170 | 
             
                      target_element
         | 
| 151 171 | 
             
                    end
         | 
| @@ -153,9 +173,7 @@ module Applitools | |
| 153 173 |  | 
| 154 174 | 
             
                  def close(throw_exception = true)
         | 
| 155 175 | 
             
                    return false if test_list.empty?
         | 
| 156 | 
            -
                    test_list.each | 
| 157 | 
            -
                      t.close
         | 
| 158 | 
            -
                    end
         | 
| 176 | 
            +
                    test_list.each(&:close)
         | 
| 159 177 |  | 
| 160 178 | 
             
                    while (!((states = test_list.map(&:state_name).uniq).count == 1 && states.first == :completed)) do
         | 
| 161 179 | 
             
                      sleep 0.5
         | 
| @@ -180,9 +198,7 @@ module Applitools | |
| 180 198 | 
             
                  end
         | 
| 181 199 |  | 
| 182 200 | 
             
                  def abort_if_not_closed
         | 
| 183 | 
            -
                    test_list.each | 
| 184 | 
            -
                      t.abort_if_not_closed
         | 
| 185 | 
            -
                    end
         | 
| 201 | 
            +
                    test_list.each(&:abort_if_not_closed)
         | 
| 186 202 | 
             
                  end
         | 
| 187 203 |  | 
| 188 204 | 
             
                  def open?
         | 
    
        data/lib/applitools/version.rb
    CHANGED
    
    
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: eyes_selenium
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 3.15. | 
| 4 | 
            +
              version: 3.15.15
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Applitools Team
         | 
| 8 8 | 
             
            autorequire: 
         | 
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2019-05- | 
| 11 | 
            +
            date: 2019-05-17 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: eyes_core
         | 
| @@ -16,14 +16,14 @@ dependencies: | |
| 16 16 | 
             
                requirements:
         | 
| 17 17 | 
             
                - - '='
         | 
| 18 18 | 
             
                  - !ruby/object:Gem::Version
         | 
| 19 | 
            -
                    version: 3.15. | 
| 19 | 
            +
                    version: 3.15.15
         | 
| 20 20 | 
             
              type: :runtime
         | 
| 21 21 | 
             
              prerelease: false
         | 
| 22 22 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 23 23 | 
             
                requirements:
         | 
| 24 24 | 
             
                - - '='
         | 
| 25 25 | 
             
                  - !ruby/object:Gem::Version
         | 
| 26 | 
            -
                    version: 3.15. | 
| 26 | 
            +
                    version: 3.15.15
         | 
| 27 27 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 28 28 | 
             
              name: selenium-webdriver
         | 
| 29 29 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| @@ -129,6 +129,7 @@ files: | |
| 129 129 | 
             
            - lib/applitools/selenium/visual_grid/resource_cache.rb
         | 
| 130 130 | 
             
            - lib/applitools/selenium/visual_grid/running_test.rb
         | 
| 131 131 | 
             
            - lib/applitools/selenium/visual_grid/thread_pool.rb
         | 
| 132 | 
            +
            - lib/applitools/selenium/visual_grid/vg_match_window_data.rb
         | 
| 132 133 | 
             
            - lib/applitools/selenium/visual_grid/vg_resource.rb
         | 
| 133 134 | 
             
            - lib/applitools/selenium/visual_grid/vg_task.rb
         | 
| 134 135 | 
             
            - lib/applitools/selenium/visual_grid/visual_grid_eyes.rb
         |