rainbow_formatter 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'formatter/custom/car'
4
+ require 'formatter/custom/dog'
5
+ require 'formatter/custom/monkey'
6
+ require 'formatter/custom/tina_bike'
7
+ require 'formatter/custom/tina_dream'
8
+
9
+ module Formatter
10
+ class Configuration
11
+ attr_accessor :formatter
12
+
13
+ def initialize
14
+ @formatter = :tina_bike
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Formatter
4
+ module Custom
5
+ module Car
6
+ # rubocop:disable Metrics/LineLength
7
+ def ascii_array
8
+ [" - __\r\n -- ~( @\\ \\ \r\n--- _________]_[__/_>________\r\n / ____ \\ <> | ____ \\\r\n =\\_/ __ \\_\\_______|_/ __ \\__D\r\n________(__)_____________(__)____",
9
+ " - __\r\n -- ~( @\\ \\\r\n --- _________]_[__/_>________\r\n / ____ \\ <> | ____ \\\r\n =\\_/ __ \\_\\_______|_/ __ \\__D\r\n________(__)_____________(__)____"]
10
+ end
11
+ # rubocop:enable Metrics/LineLength
12
+
13
+ def rainbow_mp3
14
+ File.expand_path('../../../data/tina_dream.mp3', __dir__)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Formatter
4
+ module Custom
5
+ module Dog
6
+ # rubocop:disable Metrics/LineLength
7
+ def ascii_array
8
+ [" ;~~,__\r\n :-....,-------'`-'._.'\r\n `-,,, , ,'~~'\r\n ; ,'~.__; /\r\n :| :|\r\n `-' `-'",
9
+ " ;~~,__\r\n :-....,-------'`-'._.'\r\n `-,,, , ,'~~'\r\n ; ,'~.__; /--.\r\n :| :| :|``(;\r\n `-'`-' `-'",
10
+ " ;~~,__\r\n :-....,-------'`-'._.'\r\n `-,,, , ;'~~'\r\n ,'_,'~.__; '--.\r\n //' ````(;\r\n `-'",
11
+ " .--~~,__\r\n :-....,-------`~~'._.'\r\n `-,,, ,_ ;'~U'\r\n _,-' ,'`-__; '--.\r\n (_/'~~ ''''(;"]
12
+ end
13
+ # rubocop:enable Metrics/LineLength
14
+
15
+ def rainbow_mp3
16
+ File.expand_path('../../../data/tina_dream.mp3', __dir__)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Formatter
4
+ module Custom
5
+ module Monkey
6
+ # rubocop:disable Metrics/LineLength
7
+ def ascii_array
8
+ [" .-\"-. \r\n _/.-.-.\\\r\n /|( o o )|\\\r\n | // \" \\\\ |\r\n / / \\'---'/ \\ \\\r\n \\ \\_/`\"\"\"`\\_/ /\r\n \\ /",
9
+ " .-\"-.\r\n _/_-.-_\\\r\n / __} {__ \\\r\n / // \" \\\\ \\\r\n / / \\'---'/ \\ \\\r\n \\ \\_/`\"\"\"`\\_/ /\r\n \\ /"]
10
+ end
11
+ # rubocop:enable Metrics/LineLength
12
+
13
+ def rainbow_mp3
14
+ File.expand_path('../../../data/tina_dream.mp3', __dir__)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Formatter
4
+ module Custom
5
+ module TinaBike
6
+ # rubocop:disable Metrics/LineLength
7
+ def ascii_array
8
+ [" - /\\**/\\ \r\n -- _( o_o ) \r\n --- (_/ u--u) \r\n /==( ___||)\r\n /d\\\"P\\ )=\\d\\\"Pb, \r\n 8 || ===/8=== || 8| \r\n P, ,d P, ,d` \r\n `\"\"\" `\"\"\"",
9
+ " - /\\**/\\ \n -- _( o_o ) \n --- (_/ u--u) \n /==( ___||)\n /d\\\"P\\ )=\\d\\\"Pb, \n 8 || ===/8=== || 8|\n P, ,d P, ,d`\n `\"\"\" `\"\"\""]
10
+ end
11
+ # rubocop:enable Metrics/LineLength
12
+
13
+ def rainbow_mp3
14
+ File.expand_path('../../../data/tina_bike.mp3', __dir__)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Formatter
4
+ module Custom
5
+ module TinaDream
6
+ # rubocop:disable Metrics/LineLength
7
+ def ascii_array
8
+ [" \r\n /\\_____/\\\r\n / o o \\ \r\n ( == ^ == )\r\n ) (\r\n ( )\r\n ( ( ) ( ) )\r\n (__(__)___(__)__)",
9
+ " \r\n /\\_____/\\\r\n / -- -- \\ \r\n ( == ^ == )\r\n ) (\r\n ( )\r\n ( ( ) ( ) )\r\n (__(__)___(__)__)",
10
+ " \r\n /\\_____/\\\r\n / -- -- \\ \r\n ( == o == )\r\n ) (\r\n ( )\r\n ( ( ) ( ) )\r\n (__(__)___(__)__)",
11
+ " ZzzZ \r\n /\\_____/\\\r\n / -- -- \\ \r\n ( == o == )\r\n ) (\r\n ( )\r\n ( ( ) ( ) )\r\n (__(__)___(__)__)",
12
+ " ZzzZz \r\n /\\_____/\\\r\n / -- -- \\ \r\n ( == o == )\r\n ) (\r\n ( )\r\n ( ( ) ( ) )\r\n (__(__)___(__)__)",
13
+ " ZzzZzz \r\n /\\_____/\\\r\n / -- -- \\ \r\n ( == o == )\r\n ) (\r\n ( )\r\n ( ( ) ( ) )\r\n (__(__)___(__)__)",
14
+ " ZzzZzzZ \r\n /\\_____/\\\r\n / -- -- \\ \r\n ( == o == )\r\n ) (\r\n ( )\r\n ( ( ) ( ) )\r\n (__(__)___(__)__)",
15
+ " ZzzZzzZz \r\n /\\_____/\\\r\n / -- -- \\ \r\n ( == o == )\r\n ) (\r\n ( )\r\n ( ( ) ( ) )\r\n (__(__)___(__)__)",
16
+ " ZzzZzzZzz \r\n /\\_____/\\\r\n / -- -- \\ \r\n ( == o == )\r\n ) (\r\n ( )\r\n ( ( ) ( ) )\r\n (__(__)___(__)__)",
17
+ " \r\n /\\_____/\\\r\n / 0 0 \\ \r\n ( == o == )\r\n ) (\r\n ( )\r\n ( ( ) ( ) )\r\n (__(__)___(__)__)"]
18
+ end
19
+ # rubocop:enable Metrics/LineLength
20
+
21
+ def rainbow_mp3
22
+ File.expand_path('../../../data/tina_dream.mp3', __dir__)
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Formatter
4
+ module InstaFail
5
+ def progress_lines
6
+ # Display last failed example output
7
+ errors_dump = []
8
+ errors_dump.concat(@failed_examples.last.fully_formatted(@failure_count).split("\n")) if @failed_examples.size.positive?
9
+ super.concat(errors_dump)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Formatter
4
+ module Music
5
+ def osx?
6
+ platform.downcase.include?('darwin')
7
+ end
8
+
9
+ def linux?
10
+ platform.downcase.include?('linux')
11
+ end
12
+
13
+ def kernel=(kernel)
14
+ @kernel = kernel
15
+ end
16
+
17
+ def kernel
18
+ @kernel ||= Kernel
19
+ end
20
+
21
+ def platform=(platform)
22
+ @platform = platform
23
+ end
24
+
25
+ def platform
26
+ @platform ||= RUBY_PLATFORM
27
+ end
28
+
29
+ def start(input)
30
+ super
31
+ @music_thread = Thread.new { start_music_or_kill(Thread.current) }
32
+ wait_for_music_to_start(@music_thread)
33
+ end
34
+
35
+ def dump_summary(*args)
36
+ kill_music
37
+ super
38
+ end
39
+
40
+ private
41
+
42
+ def kill_music
43
+ if @music_thread && @music_thread['music_pid']
44
+ @music_thread.kill
45
+ Process.kill('KILL', @music_thread['music_pid'])
46
+ end
47
+ end
48
+
49
+ def linux_player
50
+ %w[mpg321 mpg123].find do |player|
51
+ kernel.system("which #{player} >/dev/null 2>&1 && type #{player} >/dev/null 2>&1")
52
+ end
53
+ end
54
+
55
+ def music_command
56
+ # this isn't really threadsafe but it'll work if we're careful
57
+ return @music_command if @music_command
58
+
59
+ if osx?
60
+ @music_command = "afplay #{rainbow_mp3}"
61
+ elsif linux? && linux_player
62
+ @music_command = "#{linux_player} #{rainbow_mp3} >/dev/null 2>&1"
63
+ end
64
+ end
65
+
66
+ def start_music_or_kill(thread)
67
+ thread.exit unless File.exist?(rainbow_mp3) && music_command
68
+ loop do
69
+ thread['music_pid'] = kernel.spawn(music_command)
70
+ thread['started_playing'] ||= true
71
+ Process.wait(thread['music_pid'])
72
+ end
73
+ end
74
+
75
+ def wait_for_music_to_start(music_thread)
76
+ sleep 0.001 while !music_thread['started_playing'] && music_thread.status
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Formatter
4
+ module Verbose
5
+ def progress_lines
6
+ label = "running: #{example_name}"
7
+ label = label.slice(0, terminal_width - 3).concat('...') if label.size > terminal_width
8
+ super.concat(
9
+ [
10
+ format("%-#{terminal_width}s", finished? ? '' : label)
11
+ ]
12
+ )
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Formatter
4
+ VERSION = '0.1.5'
5
+ end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'formatter/configuration'
4
+ require 'formatter/common'
5
+ require 'ostruct'
6
+ require 'pry'
7
+
8
+ class RainbowFormatter
9
+ include Formatter::Common
10
+
11
+ attr_reader :example_name, :ascii_array, :output
12
+
13
+ RSpec::Core::Formatters.register self, :start, :example_started, :example_passed, :example_pending, :example_failed,
14
+ :start_dump, :dump_summary
15
+
16
+ BUNDLED_MODES = {
17
+ tina_bike: Formatter::Custom::TinaBike,
18
+ tina_dream: Formatter::Custom::TinaDream,
19
+ car: Formatter::Custom::Car,
20
+ dog: Formatter::Custom::Dog,
21
+ monkey: Formatter::Custom::Monkey
22
+ }.freeze
23
+
24
+ def initialize(output)
25
+ @output = output
26
+ @current = @color_index = @passing_count = @failure_count = @pending_count = @animation_index = 0
27
+ @example_results = []
28
+ @failed_examples = []
29
+ @pending_examples = []
30
+ setup_formatter
31
+ end
32
+
33
+ def setup_formatter
34
+ formatter = RainbowFormatter.configuration.formatter
35
+ formatter = BUNDLED_MODES.dig(formatter) unless formatter.is_a?(Module)
36
+ singleton_class.send(:include, formatter)
37
+ end
38
+
39
+ def self.configuration
40
+ @configuration ||= Formatter::Configuration.new
41
+ end
42
+
43
+ def self.configure
44
+ yield configuration if block_given?
45
+ end
46
+
47
+ def start(start_notification)
48
+ # TODO: Lazy fix for specs.
49
+ @example_count = if start_notification.is_a?(Integer)
50
+ start_notification
51
+ else
52
+ start_notification.count
53
+ end
54
+ end
55
+
56
+ def start_dump(_notification)
57
+ @current = @example_count
58
+ end
59
+
60
+ def example_started(notification)
61
+ notification = notification.example if notification.respond_to?(:example)
62
+ @example_name = notification.full_description
63
+ end
64
+
65
+ def example_passed(_notification)
66
+ tick
67
+ end
68
+
69
+ def example_pending(notification)
70
+ @pending_examples << notification
71
+ @pending_count += 1
72
+ tick(mark: PENDING)
73
+ end
74
+
75
+ def example_failed(notification)
76
+ @failed_examples << notification
77
+ @failure_count += 1
78
+ tick(mark: FAIL)
79
+ end
80
+
81
+ def dump_summary(notification)
82
+ duration = notification.duration
83
+ summary = "\nYou've rainbowified for #{format_duration(duration)}\n".split(//).map { |c| rainbowify(c) }
84
+ output.puts summary.join
85
+ output.puts notification.fully_formatted
86
+ dump_commands_to_rerun_failed_examples if respond_to?(:dump_commands_to_rerun_failed_examples)
87
+ end
88
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rainbow_formatter'
4
+ require 'formatter/insta_fail'
5
+
6
+ RainbowInstaFailFormatter = Class.new(RainbowFormatter) do
7
+ include Formatter::InstaFail
8
+
9
+ RSpec::Core::Formatters.register(self, :example_passed, :example_pending, :example_failed, :start_dump, :start)
10
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rainbow_formatter'
4
+ require 'formatter/music'
5
+
6
+ RainbowMusicFormatter = Class.new(RainbowFormatter) do
7
+ include Formatter::Music
8
+
9
+ RSpec::Core::Formatters.register(self, :example_passed, :example_pending, :example_failed, :start_dump, :start)
10
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rainbow_formatter'
4
+ require 'formatter/verbose'
5
+
6
+ RainbowVerboseFormatter = Class.new(RainbowFormatter) do
7
+ include Formatter::Verbose
8
+
9
+ RSpec::Core::Formatters.register(self, :example_passed, :example_pending, :example_failed, :start_dump, :start)
10
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ $LOAD_PATH.push File.expand_path('lib', __dir__)
4
+ require 'formatter/version'
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = 'rainbow_formatter'
8
+ s.version = Formatter::VERSION
9
+ s.authors = ['Federico Farina']
10
+ s.email = ['federicojosefarina@gmail.com']
11
+ s.platform = Gem::Platform::RUBY
12
+ s.homepage = 'https://github.com/fedefa/rainbow-formatter'
13
+ s.summary = 'Customizable RSpec formatter'
14
+ s.description = 'Customizable ascii-music RSpec formattter'
15
+ s.license = 'MIT'
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
20
+ s.require_paths = ['lib']
21
+
22
+ s.add_dependency 'rspec', '>= 3', '< 4'
23
+
24
+ s.add_development_dependency 'pry'
25
+ s.add_development_dependency 'rake'
26
+ end
@@ -0,0 +1,156 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require 'stringio'
5
+
6
+ describe RainbowFormatter do
7
+ before do
8
+ rspec_bin = $PROGRAM_NAME.split('/').last
9
+ @output = StringIO.new
10
+ if rspec_bin == 'rspec'
11
+ @formatter = RainbowFormatter.new(@output)
12
+ @example = RSpec::Core::ExampleGroup.describe.example
13
+ else
14
+ formatter_options = OpenStruct.new(colour: true, dry_run: false, autospec: nil)
15
+ @formatter = RainbowFormatter.new(formatter_options, @output)
16
+ @example = Spec::Example::ExampleProxy.new('should pass')
17
+ @formatter.instance_variable_set(:@example_group, OpenStruct.new(description: 'group'))
18
+ end
19
+ @formatter.start(2)
20
+ @formatter.example_started(@example)
21
+ end
22
+
23
+ shared_examples 'a test' do
24
+ it 'should call the increment method' do
25
+ expect(@formatter).to receive(:tick)
26
+ @formatter.example_passed(@example)
27
+ end
28
+ end
29
+
30
+ describe 'passed, pending and failed' do
31
+ before do
32
+ allow(@formatter).to receive(:tick)
33
+ end
34
+
35
+ describe 'example_passed' do
36
+ it_behaves_like 'a test'
37
+ it 'should update the scoreboard' do
38
+ expect(@formatter.scoreboard.size).to eq(4)
39
+ end
40
+ end
41
+
42
+ describe 'example_pending' do
43
+ it_behaves_like 'a test'
44
+
45
+ it 'should increment the pending count' do
46
+ expect do
47
+ @formatter.example_pending(@example)
48
+ end.to change(@formatter, :pending_count).by(1)
49
+ end
50
+ end
51
+
52
+ describe 'example_failed' do
53
+ it_behaves_like 'a test'
54
+
55
+ it 'should increment the failure count' do
56
+ expect do
57
+ @formatter.example_failed(@example)
58
+ end.to change(@formatter, :failure_count).by(1)
59
+ end
60
+
61
+ it 'should return finished if the specs are finished' do
62
+ @formatter.example_failed(@example)
63
+ allow(@formatter).to receive(:finished?).and_return(true)
64
+ end
65
+ end
66
+ end
67
+
68
+ describe 'tick' do
69
+ before do
70
+ allow(@formatter).to receive(:current).and_return(1)
71
+ allow(@formatter).to receive(:example_count).and_return(2)
72
+ @formatter.tick
73
+ end
74
+
75
+ it 'should increment the current' do
76
+ expect(@formatter.current).to eql(1)
77
+ end
78
+
79
+ it 'should store the marks in an array' do
80
+ expect(@formatter.example_results).to include('=')
81
+ end
82
+ end
83
+
84
+ describe 'rainbowify' do
85
+ it 'should increment the color index count' do
86
+ expect do
87
+ @formatter.rainbowify('=')
88
+ end.to change(@formatter, :color_index).by(1)
89
+ end
90
+ end
91
+
92
+ describe 'highlight' do
93
+ it 'should rainbowify passing examples' do
94
+ expect(@formatter.highlight('=')).to eq("\e[38;5;154m-\e[0m")
95
+ end
96
+
97
+ it 'should mark failing examples as red' do
98
+ expect(@formatter.highlight('*')).to eq("\e[31m*\e[0m")
99
+ end
100
+
101
+ it 'should mark pending examples as yellow' do
102
+ expect(@formatter.highlight('!')).to eq("\e[33m!\e[0m")
103
+ end
104
+ end
105
+
106
+ describe 'start' do
107
+ it 'should set the total amount of specs' do
108
+ expect(@formatter.example_count).to eq(2)
109
+ end
110
+
111
+ it 'should set the current to 0' do
112
+ expect(@formatter.current).to eq(0)
113
+ end
114
+ end
115
+
116
+ describe '#format_duration' do
117
+ it 'should return just seconds for sub 60 seconds' do
118
+ expect(@formatter.format_duration(5.3)).to eq('5.3 seconds')
119
+ end
120
+
121
+ it 'should remove that extra zero if it is not needed' do
122
+ expect(@formatter.format_duration(1.0)).to eq('1 second')
123
+ end
124
+
125
+ it 'should plurlaize seconds' do
126
+ expect(@formatter.format_duration(1.1)).to eq('1.1 seconds')
127
+ end
128
+
129
+ it 'add a minute if it is just over 60 seconds' do
130
+ expect(@formatter.format_duration(63.2543456456)).to eq('1 minute and 3.25 seconds')
131
+ end
132
+
133
+ it 'should pluralize minutes' do
134
+ expect(@formatter.format_duration(987.34)).to eq('16 minutes and 27.34 seconds')
135
+ end
136
+ end
137
+
138
+ describe 'example width' do
139
+ [15, 36, 60].each do |n|
140
+ context "for #{n} examples" do
141
+ before { @formatter.start(n) }
142
+
143
+ [0.25, 0.5, 0.75].each do |p|
144
+ i = (n * p).to_i
145
+ before { i.times { @formatter.tick } }
146
+
147
+ context "when in example #{i}" do
148
+ it 'should return 1 as the example width' do
149
+ expect(@formatter.example_width).to eq(1)
150
+ end
151
+ end
152
+ end
153
+ end
154
+ end
155
+ end
156
+ end