spec_selector 0.1.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
 - checksums.yaml.gz.sig +0 -0
 - data.tar.gz.sig +0 -0
 - data/README.md +48 -0
 - data/lib/spec_selector.rb +92 -0
 - data/lib/spec_selector/data_map.rb +38 -0
 - data/lib/spec_selector/data_presentation.rb +179 -0
 - data/lib/spec_selector/format.rb +99 -0
 - data/lib/spec_selector/helpers.rb +63 -0
 - data/lib/spec_selector/initialize.rb +83 -0
 - data/lib/spec_selector/instructions.rb +139 -0
 - data/lib/spec_selector/scripts/rerun.sh +40 -0
 - data/lib/spec_selector/state.rb +142 -0
 - data/lib/spec_selector/terminal.rb +45 -0
 - data/lib/spec_selector/ui.rb +169 -0
 - data/license.md +8 -0
 - data/spec/factories.rb +89 -0
 - data/spec/shared.rb +145 -0
 - data/spec/spec_helper.rb +47 -0
 - data/spec/spec_selector_spec.rb +165 -0
 - data/spec/spec_selector_util/data_map_spec.rb +98 -0
 - data/spec/spec_selector_util/data_presentation_spec.rb +314 -0
 - data/spec/spec_selector_util/format_spec.rb +213 -0
 - data/spec/spec_selector_util/helpers_spec.rb +222 -0
 - data/spec/spec_selector_util/initialize_spec.rb +93 -0
 - data/spec/spec_selector_util/ui_spec.rb +459 -0
 - metadata +96 -0
 - metadata.gz.sig +1 -0
 
| 
         @@ -0,0 +1,45 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module SpecSelectorUtil
         
     | 
| 
      
 4 
     | 
    
         
            +
              # The Terminal module contains methods concerned with terminal display
         
     | 
| 
      
 5 
     | 
    
         
            +
              # function.
         
     | 
| 
      
 6 
     | 
    
         
            +
              module Terminal
         
     | 
| 
      
 7 
     | 
    
         
            +
                def clear_frame
         
     | 
| 
      
 8 
     | 
    
         
            +
                  system("printf '\e[H'")
         
     | 
| 
      
 9 
     | 
    
         
            +
                  system("printf '\e[3J'")
         
     | 
| 
      
 10 
     | 
    
         
            +
                  system("printf '\e[0J'")
         
     | 
| 
      
 11 
     | 
    
         
            +
                end
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                def hide_cursor
         
     | 
| 
      
 14 
     | 
    
         
            +
                  system("printf '\e[?25l'")
         
     | 
| 
      
 15 
     | 
    
         
            +
                end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                def reveal_cursor
         
     | 
| 
      
 18 
     | 
    
         
            +
                  system("printf '\e[?25h'")
         
     | 
| 
      
 19 
     | 
    
         
            +
                end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                def term_height
         
     | 
| 
      
 22 
     | 
    
         
            +
                  $stdout.winsize[0]
         
     | 
| 
      
 23 
     | 
    
         
            +
                end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                def open_alt_buffer
         
     | 
| 
      
 26 
     | 
    
         
            +
                  system('tput smcup')
         
     | 
| 
      
 27 
     | 
    
         
            +
                end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                def close_alt_buffer
         
     | 
| 
      
 30 
     | 
    
         
            +
                  system('tput rmcup')
         
     | 
| 
      
 31 
     | 
    
         
            +
                end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                def reset_cursor
         
     | 
| 
      
 34 
     | 
    
         
            +
                  system("printf '\e[H'")
         
     | 
| 
      
 35 
     | 
    
         
            +
                end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                def term_width
         
     | 
| 
      
 38 
     | 
    
         
            +
                  $stdout.winsize[1]
         
     | 
| 
      
 39 
     | 
    
         
            +
                end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                def position_cursor(row, col)
         
     | 
| 
      
 42 
     | 
    
         
            +
                  system("printf '\e[#{row};#{col}H'")
         
     | 
| 
      
 43 
     | 
    
         
            +
                end
         
     | 
| 
      
 44 
     | 
    
         
            +
              end
         
     | 
| 
      
 45 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,169 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module SpecSelectorUtil
         
     | 
| 
      
 4 
     | 
    
         
            +
              # The UI module contains methods used to bind and process user input.
         
     | 
| 
      
 5 
     | 
    
         
            +
              module UI
         
     | 
| 
      
 6 
     | 
    
         
            +
                DIRECTION_KEYS = ["\e[A", "\e[B"].freeze
         
     | 
| 
      
 7 
     | 
    
         
            +
                TREE_NAVIGATION_KEYS = ["\r", "\x7F", "\e"].freeze
         
     | 
| 
      
 8 
     | 
    
         
            +
                OPTION_KEYS = [
         
     | 
| 
      
 9 
     | 
    
         
            +
                  /t/i, /f/i, /p/i, /q/i, /i/i, /r/i, /m/i, /c/i, /a/i, /v/i
         
     | 
| 
      
 10 
     | 
    
         
            +
                ].freeze
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                def exit_only
         
     | 
| 
      
 13 
     | 
    
         
            +
                  q_to_exit
         
     | 
| 
      
 14 
     | 
    
         
            +
                  loop { quit if user_input.match?(/q/i) }
         
     | 
| 
      
 15 
     | 
    
         
            +
                end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                def selector
         
     | 
| 
      
 18 
     | 
    
         
            +
                  set_selected
         
     | 
| 
      
 19 
     | 
    
         
            +
                  @example_count > 1 ? display_list : display_example
         
     | 
| 
      
 20 
     | 
    
         
            +
                  navigate
         
     | 
| 
      
 21 
     | 
    
         
            +
                end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                def set_selected
         
     | 
| 
      
 24 
     | 
    
         
            +
                  @list ||= @active_map[:top_level]
         
     | 
| 
      
 25 
     | 
    
         
            +
                  @selected ||= @list.first
         
     | 
| 
      
 26 
     | 
    
         
            +
                end
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                def navigate
         
     | 
| 
      
 29 
     | 
    
         
            +
                  @selector_index = @list.index(@selected) || 0
         
     | 
| 
      
 30 
     | 
    
         
            +
                  loop { bind_input }
         
     | 
| 
      
 31 
     | 
    
         
            +
                end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                def bind_input
         
     | 
| 
      
 34 
     | 
    
         
            +
                  input = user_input
         
     | 
| 
      
 35 
     | 
    
         
            +
                  direction_keys(input) if DIRECTION_KEYS.include?(input)
         
     | 
| 
      
 36 
     | 
    
         
            +
                  tree_nav_keys(input) if TREE_NAVIGATION_KEYS.include?(input)
         
     | 
| 
      
 37 
     | 
    
         
            +
                  option_keys(input) if OPTION_KEYS.any? { |key| input.match?(key) }
         
     | 
| 
      
 38 
     | 
    
         
            +
                end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                def quit
         
     | 
| 
      
 41 
     | 
    
         
            +
                  close_alt_buffer if @instructions
         
     | 
| 
      
 42 
     | 
    
         
            +
                  clear_frame
         
     | 
| 
      
 43 
     | 
    
         
            +
                  delete_filter_data
         
     | 
| 
      
 44 
     | 
    
         
            +
                  reveal_cursor
         
     | 
| 
      
 45 
     | 
    
         
            +
                  exit
         
     | 
| 
      
 46 
     | 
    
         
            +
                end
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                def top_level_list
         
     | 
| 
      
 49 
     | 
    
         
            +
                  exit_instruction_page if @instructions
         
     | 
| 
      
 50 
     | 
    
         
            +
                  @example_display = false
         
     | 
| 
      
 51 
     | 
    
         
            +
                  @selected = nil
         
     | 
| 
      
 52 
     | 
    
         
            +
                  @list = @active_map[:top_level]
         
     | 
| 
      
 53 
     | 
    
         
            +
                  set_selected
         
     | 
| 
      
 54 
     | 
    
         
            +
                  display_list
         
     | 
| 
      
 55 
     | 
    
         
            +
                end
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                def select_item
         
     | 
| 
      
 58 
     | 
    
         
            +
                  return if @example_display
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                  if example?(@selected)
         
     | 
| 
      
 61 
     | 
    
         
            +
                    display_example
         
     | 
| 
      
 62 
     | 
    
         
            +
                    return
         
     | 
| 
      
 63 
     | 
    
         
            +
                  end
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
                  @list = @active_map[@selected.metadata[:block]]
         
     | 
| 
      
 66 
     | 
    
         
            +
                  @selected = nil
         
     | 
| 
      
 67 
     | 
    
         
            +
                  set_selected
         
     | 
| 
      
 68 
     | 
    
         
            +
                  display_list
         
     | 
| 
      
 69 
     | 
    
         
            +
                end
         
     | 
| 
      
 70 
     | 
    
         
            +
             
     | 
| 
      
 71 
     | 
    
         
            +
                def exit_instruction_page_only
         
     | 
| 
      
 72 
     | 
    
         
            +
                  exit_instruction_page
         
     | 
| 
      
 73 
     | 
    
         
            +
                  refresh_display
         
     | 
| 
      
 74 
     | 
    
         
            +
                end
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
      
 76 
     | 
    
         
            +
                def top_fail
         
     | 
| 
      
 77 
     | 
    
         
            +
                  exit_instruction_page if @instructions
         
     | 
| 
      
 78 
     | 
    
         
            +
                  return if @failed.empty?
         
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
      
 80 
     | 
    
         
            +
                  @selected = @failed.first
         
     | 
| 
      
 81 
     | 
    
         
            +
                  display_example
         
     | 
| 
      
 82 
     | 
    
         
            +
                end
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
                def back
         
     | 
| 
      
 85 
     | 
    
         
            +
                  return if top_level?
         
     | 
| 
      
 86 
     | 
    
         
            +
             
     | 
| 
      
 87 
     | 
    
         
            +
                  parent_list
         
     | 
| 
      
 88 
     | 
    
         
            +
                  set_selected
         
     | 
| 
      
 89 
     | 
    
         
            +
                  display_list
         
     | 
| 
      
 90 
     | 
    
         
            +
                end
         
     | 
| 
      
 91 
     | 
    
         
            +
             
     | 
| 
      
 92 
     | 
    
         
            +
                def parent_list
         
     | 
| 
      
 93 
     | 
    
         
            +
                  if @example_display
         
     | 
| 
      
 94 
     | 
    
         
            +
                    @example_display = false
         
     | 
| 
      
 95 
     | 
    
         
            +
                    @list = @active_map[@selected.example_group.metadata[:block]]
         
     | 
| 
      
 96 
     | 
    
         
            +
                  else
         
     | 
| 
      
 97 
     | 
    
         
            +
                    data = parent_data(@selected.metadata)
         
     | 
| 
      
 98 
     | 
    
         
            +
                    p_data = parent_data(data)
         
     | 
| 
      
 99 
     | 
    
         
            +
                    parent_key = p_data ? p_data[:block] : :top_level
         
     | 
| 
      
 100 
     | 
    
         
            +
                    @list = @active_map[parent_key]
         
     | 
| 
      
 101 
     | 
    
         
            +
                    @selected = @groups[data[:block]]
         
     | 
| 
      
 102 
     | 
    
         
            +
                  end
         
     | 
| 
      
 103 
     | 
    
         
            +
                end
         
     | 
| 
      
 104 
     | 
    
         
            +
             
     | 
| 
      
 105 
     | 
    
         
            +
                def direction_keys(input)
         
     | 
| 
      
 106 
     | 
    
         
            +
                  exit_instruction_page if @instructions
         
     | 
| 
      
 107 
     | 
    
         
            +
                  dir = input == "\e[A" ? -1 : 1
         
     | 
| 
      
 108 
     | 
    
         
            +
                  @selector_index = (@selector_index + dir) % @list.length
         
     | 
| 
      
 109 
     | 
    
         
            +
                  @selected = @list[@selector_index]
         
     | 
| 
      
 110 
     | 
    
         
            +
                  @example_display ? display_example : display_list
         
     | 
| 
      
 111 
     | 
    
         
            +
                end
         
     | 
| 
      
 112 
     | 
    
         
            +
             
     | 
| 
      
 113 
     | 
    
         
            +
                def tree_nav_keys(input)
         
     | 
| 
      
 114 
     | 
    
         
            +
                  exit_instruction_page_only if @instructions && input != "\e"
         
     | 
| 
      
 115 
     | 
    
         
            +
             
     | 
| 
      
 116 
     | 
    
         
            +
                  case input
         
     | 
| 
      
 117 
     | 
    
         
            +
                  when "\r"
         
     | 
| 
      
 118 
     | 
    
         
            +
                    select_item
         
     | 
| 
      
 119 
     | 
    
         
            +
                  when "\x7F"
         
     | 
| 
      
 120 
     | 
    
         
            +
                    back
         
     | 
| 
      
 121 
     | 
    
         
            +
                  when "\e"
         
     | 
| 
      
 122 
     | 
    
         
            +
                    top_level_list
         
     | 
| 
      
 123 
     | 
    
         
            +
                  end
         
     | 
| 
      
 124 
     | 
    
         
            +
                end
         
     | 
| 
      
 125 
     | 
    
         
            +
             
     | 
| 
      
 126 
     | 
    
         
            +
                def option_keys(input)
         
     | 
| 
      
 127 
     | 
    
         
            +
                  case input
         
     | 
| 
      
 128 
     | 
    
         
            +
                  when /T/
         
     | 
| 
      
 129 
     | 
    
         
            +
                    top_fail!
         
     | 
| 
      
 130 
     | 
    
         
            +
                  when /t/
         
     | 
| 
      
 131 
     | 
    
         
            +
                    top_fail
         
     | 
| 
      
 132 
     | 
    
         
            +
                  when /p/i
         
     | 
| 
      
 133 
     | 
    
         
            +
                    toggle_passing
         
     | 
| 
      
 134 
     | 
    
         
            +
                  when /f/i
         
     | 
| 
      
 135 
     | 
    
         
            +
                    run_only_fails
         
     | 
| 
      
 136 
     | 
    
         
            +
                  when /q/i
         
     | 
| 
      
 137 
     | 
    
         
            +
                    quit
         
     | 
| 
      
 138 
     | 
    
         
            +
                  when /i/i
         
     | 
| 
      
 139 
     | 
    
         
            +
                    unless @instructions
         
     | 
| 
      
 140 
     | 
    
         
            +
                      view_instructions_page
         
     | 
| 
      
 141 
     | 
    
         
            +
                      return
         
     | 
| 
      
 142 
     | 
    
         
            +
                    end
         
     | 
| 
      
 143 
     | 
    
         
            +
             
     | 
| 
      
 144 
     | 
    
         
            +
                    exit_instruction_page_only
         
     | 
| 
      
 145 
     | 
    
         
            +
                  when /r/i
         
     | 
| 
      
 146 
     | 
    
         
            +
                    rerun
         
     | 
| 
      
 147 
     | 
    
         
            +
                  when /^a$/i
         
     | 
| 
      
 148 
     | 
    
         
            +
                    rerun_all
         
     | 
| 
      
 149 
     | 
    
         
            +
                  when /m/i
         
     | 
| 
      
 150 
     | 
    
         
            +
                    return if @instructions
         
     | 
| 
      
 151 
     | 
    
         
            +
             
     | 
| 
      
 152 
     | 
    
         
            +
                    @selected.metadata[:include] ? filter_remove : filter_include
         
     | 
| 
      
 153 
     | 
    
         
            +
                    refresh_display
         
     | 
| 
      
 154 
     | 
    
         
            +
                  when /^c$/i
         
     | 
| 
      
 155 
     | 
    
         
            +
                    clear_filter
         
     | 
| 
      
 156 
     | 
    
         
            +
                  when /v/i
         
     | 
| 
      
 157 
     | 
    
         
            +
                    view_inclusion_filter
         
     | 
| 
      
 158 
     | 
    
         
            +
                  end
         
     | 
| 
      
 159 
     | 
    
         
            +
                end
         
     | 
| 
      
 160 
     | 
    
         
            +
             
     | 
| 
      
 161 
     | 
    
         
            +
                def user_input
         
     | 
| 
      
 162 
     | 
    
         
            +
                  input = $stdin.getch
         
     | 
| 
      
 163 
     | 
    
         
            +
                  return input unless IO.select([$stdin], nil, nil, 0.000001)
         
     | 
| 
      
 164 
     | 
    
         
            +
             
     | 
| 
      
 165 
     | 
    
         
            +
                  input << $stdin.read_nonblock(2)
         
     | 
| 
      
 166 
     | 
    
         
            +
                  input
         
     | 
| 
      
 167 
     | 
    
         
            +
                end
         
     | 
| 
      
 168 
     | 
    
         
            +
              end
         
     | 
| 
      
 169 
     | 
    
         
            +
            end
         
     | 
    
        data/license.md
    ADDED
    
    | 
         @@ -0,0 +1,8 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            The MIT License (MIT)
         
     | 
| 
      
 2 
     | 
    
         
            +
            Copyright © 2021 Trevor Almon
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
         
     | 
    
        data/spec/factories.rb
    ADDED
    
    | 
         @@ -0,0 +1,89 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module TestObjects
         
     | 
| 
      
 4 
     | 
    
         
            +
              RSN = RSpec::Core::Notifications
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
              class Example < RSpec::Core::Example
         
     | 
| 
      
 7 
     | 
    
         
            +
                attr_accessor :metadata, :execution_result, :description, :example_group
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                def initialize
         
     | 
| 
      
 10 
     | 
    
         
            +
                end
         
     | 
| 
      
 11 
     | 
    
         
            +
              end
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
              class ExecutionResult < RSpec::Core::Example::ExecutionResult
         
     | 
| 
      
 14 
     | 
    
         
            +
                attr_accessor :status
         
     | 
| 
      
 15 
     | 
    
         
            +
              end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
              class ExampleGroup < RSpec::Core::ExampleGroup
         
     | 
| 
      
 18 
     | 
    
         
            +
                attr_accessor :examples, :metadata, :description
         
     | 
| 
      
 19 
     | 
    
         
            +
              end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
              class SummaryNotification < RSN::SummaryNotification
         
     | 
| 
      
 22 
     | 
    
         
            +
                attr_accessor :example_count,
         
     | 
| 
      
 23 
     | 
    
         
            +
                              :duration,
         
     | 
| 
      
 24 
     | 
    
         
            +
                              :load_time,
         
     | 
| 
      
 25 
     | 
    
         
            +
                              :errors_outside_of_example_count
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                :examples
         
     | 
| 
      
 28 
     | 
    
         
            +
              end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
              class SkippedExampleNotification < RSN::SkippedExampleNotification
         
     | 
| 
      
 31 
     | 
    
         
            +
                attr_accessor :example
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                def fully_formatted(_)
         
     | 
| 
      
 34 
     | 
    
         
            +
                  "\npending example"
         
     | 
| 
      
 35 
     | 
    
         
            +
                end
         
     | 
| 
      
 36 
     | 
    
         
            +
              end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
              class FailedExampleNotification < RSN::FailedExampleNotification
         
     | 
| 
      
 39 
     | 
    
         
            +
                attr_accessor :example
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                def initialize
         
     | 
| 
      
 42 
     | 
    
         
            +
                end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                def fully_formatted(_)
         
     | 
| 
      
 45 
     | 
    
         
            +
                  "\nfailed example"
         
     | 
| 
      
 46 
     | 
    
         
            +
                end
         
     | 
| 
      
 47 
     | 
    
         
            +
              end
         
     | 
| 
      
 48 
     | 
    
         
            +
            end
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
            FactoryBot.define do
         
     | 
| 
      
 51 
     | 
    
         
            +
              factory :execution_result, class: 'TestObjects::ExecutionResult' do
         
     | 
| 
      
 52 
     | 
    
         
            +
                status { :passed }
         
     | 
| 
      
 53 
     | 
    
         
            +
              end
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
              factory :example, class: 'TestObjects::Example' do
         
     | 
| 
      
 56 
     | 
    
         
            +
                execution_result { build(:execution_result) }
         
     | 
| 
      
 57 
     | 
    
         
            +
                description { 'passed' }
         
     | 
| 
      
 58 
     | 
    
         
            +
                metadata { {} }
         
     | 
| 
      
 59 
     | 
    
         
            +
              end
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
              factory :example_group, class: 'TestObjects::ExampleGroup' do
         
     | 
| 
      
 62 
     | 
    
         
            +
                examples { [build(:example)] }
         
     | 
| 
      
 63 
     | 
    
         
            +
                metadata { { block: self } }
         
     | 
| 
      
 64 
     | 
    
         
            +
                description do
         
     | 
| 
      
 65 
     | 
    
         
            +
                  if examples.all? { |ex| ex.execution_result.status == :passed }
         
     | 
| 
      
 66 
     | 
    
         
            +
                    'passing example group'
         
     | 
| 
      
 67 
     | 
    
         
            +
                  else
         
     | 
| 
      
 68 
     | 
    
         
            +
                    'non-passing example group'
         
     | 
| 
      
 69 
     | 
    
         
            +
                  end
         
     | 
| 
      
 70 
     | 
    
         
            +
                end
         
     | 
| 
      
 71 
     | 
    
         
            +
              end
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
      
 73 
     | 
    
         
            +
              factory :summary_notification, class: 'TestObjects::SummaryNotification' do
         
     | 
| 
      
 74 
     | 
    
         
            +
                example_count { 25 }
         
     | 
| 
      
 75 
     | 
    
         
            +
                duration { 1.5 }
         
     | 
| 
      
 76 
     | 
    
         
            +
                load_time { 0.5 }
         
     | 
| 
      
 77 
     | 
    
         
            +
                errors_outside_of_examples_count { 0 }
         
     | 
| 
      
 78 
     | 
    
         
            +
              end
         
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
      
 80 
     | 
    
         
            +
              factory :skipped_example_notification,
         
     | 
| 
      
 81 
     | 
    
         
            +
                      class: 'TestObjects::SkippedExampleNotification' do
         
     | 
| 
      
 82 
     | 
    
         
            +
                example
         
     | 
| 
      
 83 
     | 
    
         
            +
              end
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
              factory :failed_example_notification,
         
     | 
| 
      
 86 
     | 
    
         
            +
                      class: 'TestObjects::FailedExampleNotification' do
         
     | 
| 
      
 87 
     | 
    
         
            +
                example
         
     | 
| 
      
 88 
     | 
    
         
            +
              end
         
     | 
| 
      
 89 
     | 
    
         
            +
            end
         
     | 
    
        data/spec/shared.rb
    ADDED
    
    | 
         @@ -0,0 +1,145 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            RSpec.shared_context 'shared' do
         
     | 
| 
      
 2 
     | 
    
         
            +
              let(:spec_selector) { SpecSelector.new(StringIO.new) }
         
     | 
| 
      
 3 
     | 
    
         
            +
              let(:output) { spec_selector.ivar(:@output).string }
         
     | 
| 
      
 4 
     | 
    
         
            +
              let(:fail_result) { build(:execution_result, status: :failed) }
         
     | 
| 
      
 5 
     | 
    
         
            +
              let(:pending_result) { build(:execution_result, status: :pending) }
         
     | 
| 
      
 6 
     | 
    
         
            +
              let(:failed_example) { build(:example, execution_result: fail_result, metadata: { description: 'failed_example' }) }
         
     | 
| 
      
 7 
     | 
    
         
            +
              let(:pending_example) do
         
     | 
| 
      
 8 
     | 
    
         
            +
                build(:example, execution_result: pending_result, metadata: { description: 'pending_example' })
         
     | 
| 
      
 9 
     | 
    
         
            +
              end
         
     | 
| 
      
 10 
     | 
    
         
            +
              let(:passing_example) do
         
     | 
| 
      
 11 
     | 
    
         
            +
                build(:example, metadata: { description: 'passing_example' }, description: 'passing_example')
         
     | 
| 
      
 12 
     | 
    
         
            +
              end
         
     | 
| 
      
 13 
     | 
    
         
            +
              let(:pass_group) do
         
     | 
| 
      
 14 
     | 
    
         
            +
                build(:example_group, examples: [build(:example), build(:example)], metadata: { description: 'pass_group' })
         
     | 
| 
      
 15 
     | 
    
         
            +
              end
         
     | 
| 
      
 16 
     | 
    
         
            +
              let(:fail_group) { build(:example_group, examples: [failed_example, failed_example]) }
         
     | 
| 
      
 17 
     | 
    
         
            +
              let(:pending_group) do
         
     | 
| 
      
 18 
     | 
    
         
            +
                build(:example_group, examples: [pending_example, pending_example], metadata: { description: 'pending_group' })
         
     | 
| 
      
 19 
     | 
    
         
            +
              end
         
     | 
| 
      
 20 
     | 
    
         
            +
              let(:mixed_result_group) { build(:example_group, examples: [passing_example, failed_example, pending_example]) }
         
     | 
| 
      
 21 
     | 
    
         
            +
              let(:fail_subgroup) do
         
     | 
| 
      
 22 
     | 
    
         
            +
                build(
         
     | 
| 
      
 23 
     | 
    
         
            +
                  :example_group,
         
     | 
| 
      
 24 
     | 
    
         
            +
                  metadata: {
         
     | 
| 
      
 25 
     | 
    
         
            +
                    parent_example_group: {}
         
     | 
| 
      
 26 
     | 
    
         
            +
                  },
         
     | 
| 
      
 27 
     | 
    
         
            +
                  examples: [failed_example, failed_example]
         
     | 
| 
      
 28 
     | 
    
         
            +
                )
         
     | 
| 
      
 29 
     | 
    
         
            +
              end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
              let(:pending_subgroup) do
         
     | 
| 
      
 32 
     | 
    
         
            +
                build(
         
     | 
| 
      
 33 
     | 
    
         
            +
                  :example_group,
         
     | 
| 
      
 34 
     | 
    
         
            +
                  metadata: {
         
     | 
| 
      
 35 
     | 
    
         
            +
                    parent_example_group: {}
         
     | 
| 
      
 36 
     | 
    
         
            +
                  },
         
     | 
| 
      
 37 
     | 
    
         
            +
                  examples: [pending_example, pending_example]
         
     | 
| 
      
 38 
     | 
    
         
            +
                )
         
     | 
| 
      
 39 
     | 
    
         
            +
              end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
              let(:fail_parent_group) { build(:example_group, examples: [], metadata: { description: 'fail_parent_group' }) }
         
     | 
| 
      
 42 
     | 
    
         
            +
              let(:pending_parent_group) { build(:example_group, examples: [], metadata: { description: 'pending_parent_group' }) }
         
     | 
| 
      
 43 
     | 
    
         
            +
              let(:pass_subgroup) do
         
     | 
| 
      
 44 
     | 
    
         
            +
                build(
         
     | 
| 
      
 45 
     | 
    
         
            +
                  :example_group,
         
     | 
| 
      
 46 
     | 
    
         
            +
                  metadata: { parent_example_group: pass_parent_group.metadata, description: 'pass_subgroup' },
         
     | 
| 
      
 47 
     | 
    
         
            +
                  examples: [passing_example, passing_example]
         
     | 
| 
      
 48 
     | 
    
         
            +
                )
         
     | 
| 
      
 49 
     | 
    
         
            +
              end
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
              let(:pass_parent_group) { build(:example_group, examples: [], metadata: { description: 'pass_parent_group' }) }
         
     | 
| 
      
 52 
     | 
    
         
            +
              let(:mixed_list) { [pass_group, fail_group] }
         
     | 
| 
      
 53 
     | 
    
         
            +
              let(:mixed_map) do
         
     | 
| 
      
 54 
     | 
    
         
            +
                {
         
     | 
| 
      
 55 
     | 
    
         
            +
                  :top_level => [pass_group, fail_group],
         
     | 
| 
      
 56 
     | 
    
         
            +
                  pass_group.metadata[:block] => pass_group.examples,
         
     | 
| 
      
 57 
     | 
    
         
            +
                  fail_group.metadata[:block] => fail_group.examples
         
     | 
| 
      
 58 
     | 
    
         
            +
                }
         
     | 
| 
      
 59 
     | 
    
         
            +
              end
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
              let(:pending_map) do
         
     | 
| 
      
 62 
     | 
    
         
            +
                {
         
     | 
| 
      
 63 
     | 
    
         
            +
                  :top_level => [pending_group],
         
     | 
| 
      
 64 
     | 
    
         
            +
                  pending_group.metadata[:block] => pending_group.examples
         
     | 
| 
      
 65 
     | 
    
         
            +
                }
         
     | 
| 
      
 66 
     | 
    
         
            +
              end
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
              let(:deep_map) do
         
     | 
| 
      
 69 
     | 
    
         
            +
                {
         
     | 
| 
      
 70 
     | 
    
         
            +
                  :top_level => [pending_parent_group, pass_parent_group, fail_parent_group],
         
     | 
| 
      
 71 
     | 
    
         
            +
                  pending_parent_group.metadata[:block] => [pending_subgroup],
         
     | 
| 
      
 72 
     | 
    
         
            +
                  pass_parent_group.metadata[:block] => [pass_subgroup],
         
     | 
| 
      
 73 
     | 
    
         
            +
                  fail_parent_group.metadata[:block] => [fail_subgroup],
         
     | 
| 
      
 74 
     | 
    
         
            +
                  pending_subgroup.metadata[:block] => pending_subgroup.examples,
         
     | 
| 
      
 75 
     | 
    
         
            +
                  pass_subgroup.metadata[:block] => pass_subgroup.examples,
         
     | 
| 
      
 76 
     | 
    
         
            +
                  fail_subgroup.metadata[:block] => fail_subgroup.examples
         
     | 
| 
      
 77 
     | 
    
         
            +
                }
         
     | 
| 
      
 78 
     | 
    
         
            +
              end
         
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
      
 80 
     | 
    
         
            +
              let(:all_passing_map) do
         
     | 
| 
      
 81 
     | 
    
         
            +
                {
         
     | 
| 
      
 82 
     | 
    
         
            +
                  top_level: [pass_group, pass_group],
         
     | 
| 
      
 83 
     | 
    
         
            +
                  pass_group.metadata[:block] => pass_group.examples,
         
     | 
| 
      
 84 
     | 
    
         
            +
                  pass_group.metadata[:block] => pass_group.examples
         
     | 
| 
      
 85 
     | 
    
         
            +
                }
         
     | 
| 
      
 86 
     | 
    
         
            +
              end
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
              def allow_methods(*methods)
         
     | 
| 
      
 89 
     | 
    
         
            +
                methods.each do |method|
         
     | 
| 
      
 90 
     | 
    
         
            +
                  allow(spec_selector).to receive(method)
         
     | 
| 
      
 91 
     | 
    
         
            +
                end
         
     | 
| 
      
 92 
     | 
    
         
            +
              end
         
     | 
| 
      
 93 
     | 
    
         
            +
             
     | 
| 
      
 94 
     | 
    
         
            +
              def ivars_set(ivar_hash)
         
     | 
| 
      
 95 
     | 
    
         
            +
                ivar_hash.each do |ivar, value|
         
     | 
| 
      
 96 
     | 
    
         
            +
                  spec_selector.ivar_set(ivar, value)
         
     | 
| 
      
 97 
     | 
    
         
            +
                end
         
     | 
| 
      
 98 
     | 
    
         
            +
              end
         
     | 
| 
      
 99 
     | 
    
         
            +
             
     | 
| 
      
 100 
     | 
    
         
            +
              def ivar_set(sym, value)
         
     | 
| 
      
 101 
     | 
    
         
            +
                spec_selector.ivar_set(sym, value)
         
     | 
| 
      
 102 
     | 
    
         
            +
              end
         
     | 
| 
      
 103 
     | 
    
         
            +
             
     | 
| 
      
 104 
     | 
    
         
            +
              def ivar(sym)
         
     | 
| 
      
 105 
     | 
    
         
            +
                spec_selector.ivar(sym)
         
     | 
| 
      
 106 
     | 
    
         
            +
              end
         
     | 
| 
      
 107 
     | 
    
         
            +
             
     | 
| 
      
 108 
     | 
    
         
            +
              def more_data?(readable)
         
     | 
| 
      
 109 
     | 
    
         
            +
                IO.select([readable], nil, nil, 0.000001)
         
     | 
| 
      
 110 
     | 
    
         
            +
              end
         
     | 
| 
      
 111 
     | 
    
         
            +
             
     | 
| 
      
 112 
     | 
    
         
            +
              def summary_settings(example)
         
     | 
| 
      
 113 
     | 
    
         
            +
                case example
         
     | 
| 
      
 114 
     | 
    
         
            +
                when failed_example
         
     | 
| 
      
 115 
     | 
    
         
            +
                  notification_type = :failed_example_notification
         
     | 
| 
      
 116 
     | 
    
         
            +
                  summary_list = :@failure_summaries
         
     | 
| 
      
 117 
     | 
    
         
            +
                  ivar = :@failed
         
     | 
| 
      
 118 
     | 
    
         
            +
                when pending_example
         
     | 
| 
      
 119 
     | 
    
         
            +
                  notification_type = :skipped_example_notification
         
     | 
| 
      
 120 
     | 
    
         
            +
                  summary_list = :@pending_summaries
         
     | 
| 
      
 121 
     | 
    
         
            +
                  ivar = :@pending
         
     | 
| 
      
 122 
     | 
    
         
            +
                end
         
     | 
| 
      
 123 
     | 
    
         
            +
             
     | 
| 
      
 124 
     | 
    
         
            +
                ivars_set(:@selected => example, ivar => [example])
         
     | 
| 
      
 125 
     | 
    
         
            +
                notification = build(notification_type, example: example)
         
     | 
| 
      
 126 
     | 
    
         
            +
                spec_selector.ivar(summary_list)[example] = notification
         
     | 
| 
      
 127 
     | 
    
         
            +
              end
         
     | 
| 
      
 128 
     | 
    
         
            +
             
     | 
| 
      
 129 
     | 
    
         
            +
              def expect_full_instructions_to_be_displayed
         
     | 
| 
      
 130 
     | 
    
         
            +
                expect(output).to include('Press I to hide instructions')
         
     | 
| 
      
 131 
     | 
    
         
            +
                expect(output).to include('Press F to exclude passing examples')
         
     | 
| 
      
 132 
     | 
    
         
            +
                expect(output).to include('Press ↑ or ↓ to navigate list')
         
     | 
| 
      
 133 
     | 
    
         
            +
                expect(output).to include('Press [enter] to select')
         
     | 
| 
      
 134 
     | 
    
         
            +
                expect(output).to include('Press Q to exit')
         
     | 
| 
      
 135 
     | 
    
         
            +
              end
         
     | 
| 
      
 136 
     | 
    
         
            +
             
     | 
| 
      
 137 
     | 
    
         
            +
              def expect_full_instructions_to_be_hidden
         
     | 
| 
      
 138 
     | 
    
         
            +
                expect(output).to include('Press I to view instructions')
         
     | 
| 
      
 139 
     | 
    
         
            +
                expect(output).not_to include('Press I to hide instructions')
         
     | 
| 
      
 140 
     | 
    
         
            +
                expect(output).not_to include('Press F to exclude passing examples')
         
     | 
| 
      
 141 
     | 
    
         
            +
                expect(output).not_to include('Press ↑ or ↓ to navigate list')
         
     | 
| 
      
 142 
     | 
    
         
            +
                expect(output).not_to include('Press [enter] to select')
         
     | 
| 
      
 143 
     | 
    
         
            +
                expect(output).not_to include('Press Q to exit')
         
     | 
| 
      
 144 
     | 
    
         
            +
              end
         
     | 
| 
      
 145 
     | 
    
         
            +
            end
         
     |