tty-spinner 0.8.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +120 -0
  3. data/README.md +28 -10
  4. data/Rakefile +8 -0
  5. data/examples/auto_spin.rb +8 -0
  6. data/examples/basic.rb +8 -0
  7. data/examples/clear.rb +9 -0
  8. data/examples/color.rb +12 -0
  9. data/examples/error.rb +9 -0
  10. data/examples/formats.rb +11 -0
  11. data/examples/hide_cursor.rb +12 -0
  12. data/examples/multi/basic.rb +13 -0
  13. data/examples/multi/basic_top_level.rb +13 -0
  14. data/examples/multi/custom_style.rb +26 -0
  15. data/examples/multi/files.rb +14 -0
  16. data/examples/multi/jobs.rb +10 -0
  17. data/examples/multi/multi.rb +17 -0
  18. data/examples/multi/multi_top_level.rb +18 -0
  19. data/examples/multi/pause.rb +26 -0
  20. data/examples/multi/threaded.rb +30 -0
  21. data/examples/pause.rb +19 -0
  22. data/examples/run.rb +18 -0
  23. data/examples/success.rb +9 -0
  24. data/examples/threaded.rb +11 -0
  25. data/examples/update.rb +11 -0
  26. data/lib/tty-spinner.rb +0 -2
  27. data/lib/tty/spinner.rb +40 -30
  28. data/lib/tty/spinner/formats.rb +3 -3
  29. data/lib/tty/spinner/multi.rb +31 -12
  30. data/lib/tty/spinner/version.rb +2 -2
  31. data/spec/spec_helper.rb +51 -0
  32. data/spec/unit/auto_spin_spec.rb +27 -0
  33. data/spec/unit/clear_spec.rb +18 -0
  34. data/spec/unit/error_spec.rb +46 -0
  35. data/spec/unit/events_spec.rb +37 -0
  36. data/spec/unit/formats_spec.rb +9 -0
  37. data/spec/unit/frames_spec.rb +33 -0
  38. data/spec/unit/hide_cursor_spec.rb +53 -0
  39. data/spec/unit/job_spec.rb +14 -0
  40. data/spec/unit/join_spec.rb +12 -0
  41. data/spec/unit/multi/auto_spin_spec.rb +34 -0
  42. data/spec/unit/multi/error_spec.rb +109 -0
  43. data/spec/unit/multi/line_inset_spec.rb +59 -0
  44. data/spec/unit/multi/on_spec.rb +13 -0
  45. data/spec/unit/multi/register_spec.rb +47 -0
  46. data/spec/unit/multi/spin_spec.rb +103 -0
  47. data/spec/unit/multi/stop_spec.rb +97 -0
  48. data/spec/unit/multi/success_spec.rb +110 -0
  49. data/spec/unit/new_spec.rb +26 -0
  50. data/spec/unit/pause_spec.rb +25 -0
  51. data/spec/unit/reset_spec.rb +21 -0
  52. data/spec/unit/run_spec.rb +32 -0
  53. data/spec/unit/spin_spec.rb +90 -0
  54. data/spec/unit/stop_spec.rb +64 -0
  55. data/spec/unit/success_spec.rb +46 -0
  56. data/spec/unit/update_spec.rb +87 -0
  57. data/tasks/console.rake +11 -0
  58. data/tasks/coverage.rake +11 -0
  59. data/tasks/spec.rake +29 -0
  60. data/tty-spinner.gemspec +27 -0
  61. metadata +62 -9
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+
3
+ RSpec.describe TTY::Spinner, '#auto_spin' do
4
+ let(:output) { StringIO.new('', 'w+') }
5
+
6
+ it "starts and auto spins" do
7
+ spinner = TTY::Spinner.new(output: output, interval: 100)
8
+ allow(spinner).to receive(:spin)
9
+
10
+ spinner.auto_spin
11
+ sleep 0.1
12
+ spinner.stop
13
+
14
+ expect(spinner).to have_received(:spin).at_least(5).times
15
+ end
16
+
17
+ it "restores cursor when erorr is raised" do
18
+ spinner = TTY::Spinner.new(output: output, hide_cursor: true)
19
+
20
+ spinner.auto_spin {
21
+ raise 'boom'
22
+ }
23
+
24
+ output.rewind
25
+ expect(output.read).to start_with("\e[?25l").and end_with("\e[?25h")
26
+ end
27
+ end
@@ -0,0 +1,18 @@
1
+ # encoding: utf-8
2
+
3
+ RSpec.describe TTY::Spinner, ':clear' do
4
+ let(:output) { StringIO.new('', 'w+') }
5
+
6
+ it "clears output when done" do
7
+ spinner = TTY::Spinner.new(clear: true, output: output)
8
+ 3.times { spinner.spin }
9
+ spinner.stop('Done!')
10
+ output.rewind
11
+ expect(output.read).to eq([
12
+ "\e[1G|",
13
+ "\e[1G/",
14
+ "\e[1G-",
15
+ "\e[0m\e[2K\e[1G"
16
+ ].join)
17
+ end
18
+ end
@@ -0,0 +1,46 @@
1
+ # coding: utf-8
2
+
3
+ RSpec.describe TTY::Spinner, '#error' do
4
+ let(:output) { StringIO.new('', 'w+') }
5
+
6
+ it "marks spinner as error" do
7
+ spinner = TTY::Spinner.new(output: output)
8
+ 3.times { spinner.spin }
9
+ spinner.error
10
+ output.rewind
11
+ expect(output.read).to eq([
12
+ "\e[1G|",
13
+ "\e[1G/",
14
+ "\e[1G-",
15
+ "\e[0m\e[2K",
16
+ "\e[1G#{TTY::Spinner::CROSS}\n"
17
+ ].join)
18
+
19
+ expect(spinner.error?).to be(true)
20
+ end
21
+
22
+ it "marks spinner as error with message" do
23
+ spinner = TTY::Spinner.new(output: output)
24
+ 3.times { spinner.spin }
25
+ spinner.error('Error')
26
+ output.rewind
27
+ expect(output.read).to eq([
28
+ "\e[1G|",
29
+ "\e[1G/",
30
+ "\e[1G-",
31
+ "\e[0m\e[2K",
32
+ "\e[1G#{TTY::Spinner::CROSS} Error\n"
33
+ ].join)
34
+
35
+ expect(spinner.error?).to be(true)
36
+ end
37
+
38
+ it "changes error spinner marker" do
39
+ spinner = TTY::Spinner.new(error_mark: 'x', output: output)
40
+ spinner.error('(error)')
41
+ output.rewind
42
+ expect(output.read).to eq("\e[0m\e[2K\e[1Gx (error)\n")
43
+
44
+ expect(spinner.error?).to be(true)
45
+ end
46
+ end
@@ -0,0 +1,37 @@
1
+ # coding: utf-8
2
+
3
+ RSpec.describe TTY::Spinner, 'events' do
4
+ let(:output) { StringIO.new('', 'w+') }
5
+
6
+ it "emits :done event" do
7
+ events = []
8
+ spinner = TTY::Spinner.new(output: output)
9
+ spinner.on(:done) { events << :done }
10
+
11
+ spinner.stop
12
+
13
+ expect(events).to eq([:done])
14
+ end
15
+
16
+ it "emits :success event" do
17
+ events = []
18
+ spinner = TTY::Spinner.new(output: output)
19
+ spinner.on(:done) { events << :done }
20
+ spinner.on(:success) { events << :success }
21
+
22
+ spinner.success
23
+
24
+ expect(events).to match_array([:done, :success])
25
+ end
26
+
27
+ it "emits :error event" do
28
+ events = []
29
+ spinner = TTY::Spinner.new(output: output)
30
+ spinner.on(:done) { events << :done }
31
+ spinner.on(:error) { events << :error }
32
+
33
+ spinner.error
34
+
35
+ expect(events).to match_array([:done, :error])
36
+ end
37
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe TTY::Formats::FORMATS do
4
+ TTY::Formats::FORMATS.each do |token, properties|
5
+ specify "#{token} contains proper defaults properties" do
6
+ expect(properties.keys.sort).to eq(%i[frames interval])
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,33 @@
1
+ # encoding: utf-8
2
+
3
+ RSpec.describe TTY::Spinner, ':frames' do
4
+ let(:output) { StringIO.new('', 'w+') }
5
+
6
+ it "uses custom frames from string" do
7
+ frames = ".o0@*"
8
+ spinner = TTY::Spinner.new(frames: frames, output: output)
9
+ 5.times { spinner.spin }
10
+ output.rewind
11
+ expect(output.read).to eq([
12
+ "\e[1G.",
13
+ "\e[1Go",
14
+ "\e[1G0",
15
+ "\e[1G@",
16
+ "\e[1G*"
17
+ ].join)
18
+ end
19
+
20
+ it "uses custom frames from array" do
21
+ frames = [".", "o", "0", "@", "*"]
22
+ spinner = TTY::Spinner.new(frames: frames, output: output)
23
+ 5.times { spinner.spin }
24
+ output.rewind
25
+ expect(output.read).to eq([
26
+ "\e[1G.",
27
+ "\e[1Go",
28
+ "\e[1G0",
29
+ "\e[1G@",
30
+ "\e[1G*"
31
+ ].join)
32
+ end
33
+ end
@@ -0,0 +1,53 @@
1
+ # coding: utf-8
2
+
3
+ RSpec.describe TTY::Spinner, ':hide_cursor' do
4
+ let(:output) { StringIO.new('', 'w+') }
5
+
6
+ it "hides cursor" do
7
+ spinner = TTY::Spinner.new(output: output, hide_cursor: true)
8
+ 4.times { spinner.spin }
9
+ spinner.stop
10
+ output.rewind
11
+ expect(output.read).to eq([
12
+ "\e[?25l\e[1G|",
13
+ "\e[1G/",
14
+ "\e[1G-",
15
+ "\e[1G\\",
16
+ "\e[0m\e[2K",
17
+ "\e[1G\\\n",
18
+ "\e[?25h"
19
+ ].join)
20
+ end
21
+
22
+ it "restores cursor on success" do
23
+ spinner = TTY::Spinner.new(output: output, hide_cursor: true)
24
+ 4.times { spinner.spin }
25
+ spinner.success('success')
26
+ output.rewind
27
+ expect(output.read).to eq([
28
+ "\e[?25l\e[1G|",
29
+ "\e[1G/",
30
+ "\e[1G-",
31
+ "\e[1G\\",
32
+ "\e[0m\e[2K",
33
+ "\e[1G\u2714 success\n",
34
+ "\e[?25h"
35
+ ].join)
36
+ end
37
+
38
+ it "restores cursor on error" do
39
+ spinner = TTY::Spinner.new(output: output, hide_cursor: true)
40
+ 4.times { spinner.spin }
41
+ spinner.error('error')
42
+ output.rewind
43
+ expect(output.read).to eq([
44
+ "\e[?25l\e[1G|",
45
+ "\e[1G/",
46
+ "\e[1G-",
47
+ "\e[1G\\",
48
+ "\e[0m\e[2K",
49
+ "\e[1G\u2716 error\n",
50
+ "\e[?25h"
51
+ ].join)
52
+ end
53
+ end
@@ -0,0 +1,14 @@
1
+ # encoding: utf-8
2
+
3
+ RSpec.describe TTY::Spinner, '#job' do
4
+ it "adds and executes job" do
5
+ spinner = TTY::Spinner.new("[:spinner] :title")
6
+ called = []
7
+ work = proc { |sp| called << sp }
8
+ spinner.job(&work)
9
+
10
+ spinner.execute_job
11
+
12
+ expect(called).to eq([spinner])
13
+ end
14
+ end
@@ -0,0 +1,12 @@
1
+ # coding: utf-8
2
+
3
+ RSpec.describe TTY::Spinner, '#join' do
4
+ let(:output) { StringIO.new('', 'w+') }
5
+
6
+ it "raises exception when not spinning" do
7
+ spinner = TTY::Spinner.new(output: output)
8
+ expect {
9
+ spinner.join
10
+ }.to raise_error(TTY::Spinner::NotSpinningError)
11
+ end
12
+ end
@@ -0,0 +1,34 @@
1
+ # encoding: utf-8
2
+
3
+ RSpec.describe TTY::Spinner::Multi, '#auto_spin' do
4
+ let(:output) { StringIO.new('', 'w+') }
5
+
6
+ it "doesn't auto spin top level spinner" do
7
+ spinners = TTY::Spinner::Multi.new("Top level spinner", output: output)
8
+ allow(spinners.top_spinner).to receive(:auto_spin)
9
+
10
+ spinners.auto_spin
11
+
12
+ expect(spinners.top_spinner).to_not have_received(:auto_spin)
13
+ end
14
+
15
+ it "raises an exception when called without a top spinner" do
16
+ spinners = TTY::Spinner::Multi.new(output: output)
17
+
18
+ expect {
19
+ spinners.auto_spin
20
+ }.to raise_error(RuntimeError, /No top level spinner/)
21
+ end
22
+
23
+ it "auto spins top level & child spinners with jobs" do
24
+ spinners = TTY::Spinner::Multi.new("top", output: output)
25
+ jobs = []
26
+
27
+ spinners.register("one") { |sp| jobs << 'one'; sp.success }
28
+ spinners.register("two") { |sp| jobs << 'two'; sp.success }
29
+
30
+ spinners.auto_spin
31
+
32
+ expect(jobs).to match_array(['one', 'two'])
33
+ end
34
+ end
@@ -0,0 +1,109 @@
1
+ # encoding: utf-8
2
+
3
+ RSpec.describe TTY::Spinner::Multi, '#error' do
4
+ let(:output) { StringIO.new('', 'w+') }
5
+
6
+ it 'stops registerd multi spinner and emits an :error message' do
7
+ spinners = TTY::Spinner::Multi.new(":spinner", output: output)
8
+ callbacks = []
9
+ sp1 = spinners.register "[:spinner] one"
10
+ sp2 = spinners.register "[:spinner] two"
11
+
12
+ expect(sp1.error?).to eq(false)
13
+ expect(sp2.error?).to eq(false)
14
+
15
+ spinners.on(:error) { callbacks << :error }
16
+ .on(:done) { callbacks << :done }
17
+ .on(:success) { callbacks << :success }
18
+
19
+ spinners.error
20
+
21
+ expect(sp1.error?).to eq(true)
22
+ expect(sp2.error?).to eq(true)
23
+ expect(callbacks).to match_array([:done, :error])
24
+ end
25
+
26
+ it 'stops unregistered top level spinner and emits an :error message' do
27
+ spinners = TTY::Spinner::Multi.new(output: output)
28
+ callbacks = []
29
+ sp1 = spinners.register "[:spinner] one"
30
+ sp2 = spinners.register "[:spinner] two"
31
+
32
+ expect(sp1.error?).to eq(false)
33
+ expect(sp2.error?).to eq(false)
34
+
35
+ spinners.on(:error) { callbacks << :error }
36
+ .on(:done) { callbacks << :done }
37
+ .on(:success) { callbacks << :success }
38
+
39
+ spinners.error
40
+
41
+ expect(sp1.error?).to eq(true)
42
+ expect(sp2.error?).to eq(true)
43
+ expect(callbacks).to match_array([:done, :error])
44
+ end
45
+
46
+ it 'stops registed spinners under top level and emits an error message' do
47
+ spinners = TTY::Spinner::Multi.new(":spinner", output: output)
48
+ callbacks = []
49
+ sp1 = spinners.register "[:spinner] one"
50
+ sp2 = spinners.register "[:spinner] two"
51
+
52
+ expect(sp1.error?).to eq(false)
53
+ expect(sp2.error?).to eq(false)
54
+
55
+ spinners.on(:error) { callbacks << :error }
56
+ .on(:done) { callbacks << :done }
57
+ .on(:success) { callbacks << :success }
58
+
59
+ sp1.error
60
+ sp2.error
61
+
62
+ expect(spinners.error?).to eq(true)
63
+ expect(callbacks).to match_array([:done, :error])
64
+ end
65
+
66
+ it 'stops registed spinners under top level and emits an error message' do
67
+ spinners = TTY::Spinner::Multi.new(output: output)
68
+ callbacks = []
69
+ sp1 = spinners.register "[:spinner] one"
70
+ sp2 = spinners.register "[:spinner] two"
71
+
72
+ expect(sp1.error?).to eq(false)
73
+ expect(sp2.error?).to eq(false)
74
+
75
+ spinners.on(:error) { callbacks << :error }
76
+ .on(:done) { callbacks << :done }
77
+ .on(:success) { callbacks << :success }
78
+
79
+ sp1.error
80
+ sp2.error
81
+
82
+ expect(spinners.error?).to eq(true)
83
+ expect(callbacks).to match_array([:done, :error])
84
+ end
85
+
86
+ it 'returns true when any spinner failed' do
87
+ spinners = TTY::Spinner::Multi.new(output: output)
88
+ sp1 = spinners.register("one")
89
+ sp2 = spinners.register("two")
90
+
91
+ sp1.success
92
+ sp2.error
93
+
94
+ expect(spinners.error?).to eq(true)
95
+ end
96
+
97
+ it "updates top spinner error state based on child spinners jobs failure" do
98
+ spinners = TTY::Spinner::Multi.new("top", output: output)
99
+
100
+ spinners.register("one") { |sp| sp.success }
101
+ spinners.register("two") { |sp| sp.error }
102
+
103
+ expect(spinners.error?).to eq(false)
104
+
105
+ spinners.auto_spin
106
+
107
+ expect(spinners.error?).to eq(true)
108
+ end
109
+ end
@@ -0,0 +1,59 @@
1
+ # encoding: utf-8
2
+
3
+ RSpec.describe TTY::Spinner::Multi, '#line_inset' do
4
+ let(:output) { StringIO.new('', 'w+') }
5
+
6
+ it "doesn't create inset when no top level spinner" do
7
+ spinners = TTY::Spinner::Multi.new(output: output)
8
+
9
+ spinner = spinners.register 'example'
10
+
11
+ expect(spinners.line_inset(spinner)).to eq('')
12
+ end
13
+
14
+ it "defaults to the empty string for the top level spinner" do
15
+ spinners = TTY::Spinner::Multi.new("Top level spinner", output: output)
16
+
17
+ expect(spinners.line_inset(1))
18
+ .to eq(TTY::Spinner::Multi::DEFAULT_INSET[:top])
19
+ end
20
+
21
+ it "returns four spaces when there is a top level spinner" do
22
+ spinners = TTY::Spinner::Multi.new("Top level spinner", output: output)
23
+
24
+ spinners.register 'middle'
25
+ spinners.register 'bottom'
26
+
27
+ expect(spinners.line_inset(2))
28
+ .to eq(TTY::Spinner::Multi::DEFAULT_INSET[:middle])
29
+ end
30
+
31
+ it "decorates last spinner" do
32
+ spinners = TTY::Spinner::Multi.new("Top spinner", output: output)
33
+
34
+ spinners.register 'middle'
35
+ spinners.register 'bottom'
36
+
37
+ expect(spinners.line_inset(3))
38
+ .to eq(TTY::Spinner::Multi::DEFAULT_INSET[:bottom])
39
+ end
40
+
41
+ it "allows customization" do
42
+ opts = {
43
+ output: output,
44
+ indent: 4,
45
+ style: {
46
+ top: ". ",
47
+ middle: "--",
48
+ bottom: "---",
49
+ }
50
+ }
51
+ spinners = TTY::Spinner::Multi.new("Top level spinner", opts)
52
+ spinners.register ""
53
+ spinners.register ""
54
+
55
+ expect(spinners.line_inset(1)).to eq(". ")
56
+ expect(spinners.line_inset(2)).to eq("--")
57
+ expect(spinners.line_inset(3)).to eq("---")
58
+ end
59
+ end