tty-spinner 0.8.0 → 0.9.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.
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