achoo 0.4.2 → 0.5
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +13 -0
- data/README.rdoc +26 -22
- data/lib/achoo/achievo/hour_administration_form.rb +2 -3
- data/lib/achoo/achievo/hour_registration_form.rb +12 -15
- data/lib/achoo/achievo/hour_registration_form_ranged.rb +5 -2
- data/lib/achoo/achievo/lock_month_form.rb +1 -5
- data/lib/achoo/achievo/login_form.rb +3 -3
- data/lib/achoo/app.rb +27 -28
- data/lib/achoo/awake.rb +1 -1
- data/lib/achoo/extensions.rb +4 -0
- data/lib/achoo/ical.rb +6 -2
- data/lib/achoo/plugin/awake.rb +23 -0
- data/lib/achoo/plugin/ical.rb +47 -0
- data/lib/achoo/plugin/vcs.rb +26 -0
- data/lib/achoo/plugin_base.rb +6 -0
- data/lib/achoo/rc_loader.rb +9 -0
- data/lib/achoo/temporal/timespan.rb +3 -3
- data/lib/achoo/term.rb +7 -3
- data/lib/achoo/term/table.rb +5 -6
- data/lib/achoo/ui/commands.rb +12 -12
- data/lib/achoo/ui/date_chooser.rb +1 -1
- data/lib/achoo/ui/exception_handling.rb +1 -1
- data/lib/achoo/ui/register_hours.rb +50 -50
- data/lib/achoo/vcs/git.rb +5 -3
- data/test/acceptance/test_flexi_time.rb +41 -0
- data/test/acceptance/test_lock_month.rb +39 -0
- data/test/acceptance/test_register_hours.rb +161 -0
- data/test/lib/achievo_mock.rb +76 -0
- data/test/lib/achoo_runner.rb +54 -0
- data/test/lib/test_helpers.rb +43 -0
- data/test/unit/test_achievo_date_field.rb +34 -0
- data/test/unit/test_awake.rb +98 -0
- data/test/unit/test_extensions_array.rb +25 -0
- data/test/unit/test_system_cstruct.rb +28 -0
- data/test/unit/test_system_log_entry.rb +31 -0
- data/test/unit/test_term_menu.rb +71 -0
- data/test/unit/test_term_table.rb +52 -0
- data/test/unit/test_timespan.rb +48 -0
- data/test/unit/test_ui_date_chooser.rb +36 -0
- metadata +101 -74
@@ -0,0 +1,161 @@
|
|
1
|
+
require 'test_helpers'
|
2
|
+
|
3
|
+
class TestLockMonth < Test::Unit::TestCase
|
4
|
+
|
5
|
+
include AcceptanceBase
|
6
|
+
|
7
|
+
def setup
|
8
|
+
super
|
9
|
+
|
10
|
+
time_registration_html = %q{
|
11
|
+
<html>
|
12
|
+
<head></head>
|
13
|
+
<body>
|
14
|
+
<form name="entryform">
|
15
|
+
|
16
|
+
<input type="hidden" name="action" value="foo">
|
17
|
+
<input type="hidden" name="atkaction" value="bar">
|
18
|
+
|
19
|
+
<select name="activitydate[day]">
|
20
|
+
<option value="1" >Saturday 1</option>
|
21
|
+
<option value="2" >Sunday 2</option>
|
22
|
+
<option value="3" >Monday 3</option>
|
23
|
+
<option value="4" >Tuesday 4</option>
|
24
|
+
<option value="5" >Wednesday 5</option>
|
25
|
+
<option value="6" >Thursday 6</option>
|
26
|
+
<option value="7" >Friday 7</option>
|
27
|
+
<option value="8" >Saturday 8</option>
|
28
|
+
<option value="9" >Sunday 9</option>
|
29
|
+
<option value="10" >Monday 10</option>
|
30
|
+
<option value="11" >Tuesday 11</option>
|
31
|
+
<option value="12" >Wednesday 12</option>
|
32
|
+
<option value="13" >Thursday 13</option>
|
33
|
+
<option value="14" >Friday 14</option>
|
34
|
+
<option value="15" >Saturday 15</option>
|
35
|
+
<option value="16" >Sunday 16</option>
|
36
|
+
<option value="17" >Monday 17</option>
|
37
|
+
<option value="18" >Tuesday 18</option>
|
38
|
+
<option value="19" >Wednesday 19</option>
|
39
|
+
<option value="20" >Thursday 20</option>
|
40
|
+
<option value="21" >Friday 21</option>
|
41
|
+
<option value="22" >Saturday 22</option>
|
42
|
+
<option value="23" >Sunday 23</option>
|
43
|
+
<option value="24" >Monday 24</option>
|
44
|
+
<option value="25" selected>Tuesday 25</option>
|
45
|
+
<option value="26" >Wednesday 26</option>
|
46
|
+
<option value="27" >Thursday 27</option>
|
47
|
+
<option value="28" >Friday 28</option>
|
48
|
+
<option value="29" >Saturday 29</option>
|
49
|
+
<option value="30" >Sunday 30</option>
|
50
|
+
<option value="31" >Monday 31</option>
|
51
|
+
</select>
|
52
|
+
|
53
|
+
<select name="activitydate[month]">
|
54
|
+
<option value="1" >January</option>
|
55
|
+
<option value="2" >February</option>
|
56
|
+
<option value="3" >March</option>
|
57
|
+
<option value="4" >April</option>
|
58
|
+
<option value="5" selected>May</option>
|
59
|
+
<option value="6" >June</option>
|
60
|
+
<option value="7" >July</option>
|
61
|
+
<option value="8" >August</option>
|
62
|
+
<option value="9" >September</option>
|
63
|
+
<option value="10" >October</option>
|
64
|
+
<option value="11" >November</option>
|
65
|
+
<option value="12" >December</option>
|
66
|
+
</select>
|
67
|
+
|
68
|
+
<input type="text" name="activitydate[year]" value="2010">
|
69
|
+
|
70
|
+
<a href="FIX">Select range</a>
|
71
|
+
|
72
|
+
<select name="projectid">
|
73
|
+
<option value="project.id='1'" >foo: Foo</option>
|
74
|
+
<option value="project.id='2'" >bar: Bar</option>
|
75
|
+
</select>
|
76
|
+
|
77
|
+
<a href="FIX">Select project</a>
|
78
|
+
|
79
|
+
<select name="phaseid"></select>
|
80
|
+
|
81
|
+
<textarea name='remark'></textarea>
|
82
|
+
|
83
|
+
<input name="time" value="">
|
84
|
+
|
85
|
+
<select name="workperiod">
|
86
|
+
<option value="workperiod.id='1'" selected>Normal</option>
|
87
|
+
<option value="workperiod.id='2'" >Overtime</option>
|
88
|
+
</select>
|
89
|
+
|
90
|
+
<select name="billpercent">
|
91
|
+
<option value="billpercent.id='1'" >Normal (100%)</option>
|
92
|
+
<option value="billpercent.id='2'" >Overtime (200%)</option>
|
93
|
+
</select>
|
94
|
+
|
95
|
+
<input type="submit" value="Save">
|
96
|
+
</form>
|
97
|
+
</body>
|
98
|
+
</html>
|
99
|
+
}
|
100
|
+
|
101
|
+
phase_html = %q{The phase <input type="hidden" name="phaseid" value="phase.id='1'">}
|
102
|
+
|
103
|
+
@server.register(:get, '/time_registration', [ 200, nil, time_registration_html ])
|
104
|
+
@server.register(:post, '/dispatch.php', [ 200, nil, phase_html ])
|
105
|
+
end
|
106
|
+
|
107
|
+
def test_register_hours
|
108
|
+
achoo(:verbose => $DEBUG) do
|
109
|
+
expect '1. Register hours'
|
110
|
+
expect_main_prompt
|
111
|
+
puts '1'
|
112
|
+
|
113
|
+
expect /Date \(\[today\] \| \?\)>/
|
114
|
+
puts '2010-05-25'
|
115
|
+
|
116
|
+
expect 'Recently used projects'
|
117
|
+
expect '1. foo: Foo'
|
118
|
+
expect '2. bar: Bar'
|
119
|
+
puts '2'
|
120
|
+
|
121
|
+
expect 'Phases'
|
122
|
+
expect '1. The phase'
|
123
|
+
|
124
|
+
expect 'Remark>'
|
125
|
+
puts 'A nice remark'
|
126
|
+
|
127
|
+
expect 'Hours [7:30]>'
|
128
|
+
puts '2'
|
129
|
+
|
130
|
+
expect 'Do you want to change the defaults for worktime period and/or billing percentage? [N/y]>'
|
131
|
+
puts 'y'
|
132
|
+
|
133
|
+
expect 'Worktime periods'
|
134
|
+
expect '1. Normal'
|
135
|
+
expect '2. Overtime'
|
136
|
+
expect 'Period [1]>'
|
137
|
+
puts '2'
|
138
|
+
|
139
|
+
expect 'Billing options'
|
140
|
+
expect '1. Normal (100%)'
|
141
|
+
expect '2. Overtime (200%)'
|
142
|
+
expect 'Billing [1]>'
|
143
|
+
puts '2'
|
144
|
+
|
145
|
+
expect ' date: "2010-05-25"'
|
146
|
+
expect ' project: "bar: Bar"'
|
147
|
+
expect ' phase: "The phase"'
|
148
|
+
expect ' remark: "A nice remark"'
|
149
|
+
expect ' hours: "2"'
|
150
|
+
expect ' worktime: "Overtime"'
|
151
|
+
expect ' billing: "Overtime (200%)"'
|
152
|
+
|
153
|
+
expect 'Submit? [Y/n]>'
|
154
|
+
puts 'n'
|
155
|
+
|
156
|
+
expect 'Cancelled'
|
157
|
+
expect_main_prompt
|
158
|
+
puts 'q'
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# From http://dynamicorange.com/2009/02/18/ruby-mock-web-server/
|
2
|
+
|
3
|
+
require 'logger'
|
4
|
+
require 'rack'
|
5
|
+
require 'thin'
|
6
|
+
|
7
|
+
class AchievoMock
|
8
|
+
|
9
|
+
def initialize(port=4000)
|
10
|
+
@expectations = []
|
11
|
+
@server = Thin::Server.new('127.0.0.1', port, self)
|
12
|
+
@server.silent = true
|
13
|
+
@thread = Thread.new { @server.start }
|
14
|
+
sleep 1
|
15
|
+
@logger = Logger.new(File.dirname(__FILE__) + '/../../tmp/test.log')
|
16
|
+
end
|
17
|
+
|
18
|
+
def stop
|
19
|
+
@server.stop!
|
20
|
+
Thread.kill(@thread)
|
21
|
+
end
|
22
|
+
|
23
|
+
def register(method, path, response, env={})
|
24
|
+
env.merge!({
|
25
|
+
'REQUEST_METHOD' => method.to_s.upcase,
|
26
|
+
'REQUEST_PATH' => path,
|
27
|
+
})
|
28
|
+
response[1] = { 'Content-Type' => 'text/html', 'Content-Length' => response[2].length.to_s }
|
29
|
+
response[2] = [response[2]]
|
30
|
+
@logger.debug "Registered #{[env, response]}"
|
31
|
+
@expectations << [env, response]
|
32
|
+
end
|
33
|
+
|
34
|
+
def clear
|
35
|
+
@expectations = []
|
36
|
+
end
|
37
|
+
|
38
|
+
def call(env)
|
39
|
+
@logger.debug "Got request: #{env}"
|
40
|
+
|
41
|
+
if @expectations.empty?
|
42
|
+
@logger.debug('Got an unexpected request')
|
43
|
+
return error_page('Not expecting any request')
|
44
|
+
end
|
45
|
+
|
46
|
+
response = @expectations.first[1]
|
47
|
+
if expectation_satisified?(env, @expectations.shift)
|
48
|
+
@logger.debug "Responding with: #{response}"
|
49
|
+
return response
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def error_page(msg)
|
54
|
+
[500,
|
55
|
+
{ 'Content-Type' => 'text/plain',
|
56
|
+
'Content-Length' => msg.length.to_s
|
57
|
+
},
|
58
|
+
[msg]
|
59
|
+
]
|
60
|
+
end
|
61
|
+
|
62
|
+
def expectation_satisified?(env, expectation)
|
63
|
+
expectationEnv = expectation[0]
|
64
|
+
matched = false
|
65
|
+
expectationEnv.each do |envKey, value|
|
66
|
+
matched = true
|
67
|
+
if value != env[envKey]
|
68
|
+
matched = false
|
69
|
+
@logger.debug('Unmet expectation')
|
70
|
+
return error_page('Expectation not met')
|
71
|
+
end
|
72
|
+
end
|
73
|
+
matched
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'expect'
|
2
|
+
require 'pty'
|
3
|
+
require 'test/unit'
|
4
|
+
|
5
|
+
class AchooRunner
|
6
|
+
|
7
|
+
include Test::Unit::Assertions
|
8
|
+
|
9
|
+
attr_accessor :reader
|
10
|
+
attr_accessor :writer
|
11
|
+
attr_accessor :pid
|
12
|
+
|
13
|
+
def initialize(reader, writer, pid)
|
14
|
+
self.reader = reader
|
15
|
+
self.writer = writer
|
16
|
+
self.pid = pid
|
17
|
+
end
|
18
|
+
|
19
|
+
def puts(str); writer.puts str; end
|
20
|
+
|
21
|
+
def expect(pattern)
|
22
|
+
stat = reader.expect(pattern, 3)
|
23
|
+
raise "Didn't find '#{pattern}' before timeout" if stat.nil?
|
24
|
+
stat
|
25
|
+
end
|
26
|
+
|
27
|
+
def expect_main_prompt
|
28
|
+
expect('[1]>')
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def achoo(opts, &block)
|
33
|
+
|
34
|
+
options = {
|
35
|
+
:verbose => false,
|
36
|
+
}.merge(opts)
|
37
|
+
|
38
|
+
rc_file = File.dirname(__FILE__) << '/dot_achoo'
|
39
|
+
File.chmod(0600, rc_file)
|
40
|
+
cmd = 'ruby -Ilib bin/achoo --log --rcfile ' << rc_file
|
41
|
+
|
42
|
+
PTY.spawn(cmd) do |r, w, pid|
|
43
|
+
w.sync = true
|
44
|
+
$expect_verbose = options[:verbose]
|
45
|
+
|
46
|
+
runner = AchooRunner.new(r, w, pid)
|
47
|
+
begin
|
48
|
+
runner.instance_eval(&block)
|
49
|
+
rescue Exception => e
|
50
|
+
runner.flunk e
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
#require 'redgreen' # FIX Seems to be problems with ruby 1.9.2 due to minitest/unit
|
2
|
+
require 'shoulda'
|
3
|
+
require 'test/unit'
|
4
|
+
|
5
|
+
require 'achievo_mock'
|
6
|
+
require 'achoo_runner'
|
7
|
+
|
8
|
+
module AcceptanceBase
|
9
|
+
|
10
|
+
@@main_html = %q{
|
11
|
+
<html>
|
12
|
+
<head></head>
|
13
|
+
<frameset>
|
14
|
+
<frame name="menu" src="/menu">
|
15
|
+
<frameset>
|
16
|
+
</html>
|
17
|
+
}
|
18
|
+
|
19
|
+
@@menu_html = %q{
|
20
|
+
<html>
|
21
|
+
<head></head>
|
22
|
+
<body>
|
23
|
+
<a class="menuItemLevel2"
|
24
|
+
onclick="window.open('time_registration')">Time Registration</a>
|
25
|
+
<a class="menuItemLevel2"
|
26
|
+
onclick="window.open('lock_months')">Lock months</a>
|
27
|
+
<a class="menuItemLevel2"
|
28
|
+
onclick="window.open('holiday_report')">Holiday report</a>
|
29
|
+
</body>
|
30
|
+
</html>
|
31
|
+
}
|
32
|
+
|
33
|
+
def setup
|
34
|
+
@server = AchievoMock.new()
|
35
|
+
@server.register(:get, '/', [ 200, nil, @@main_html ])
|
36
|
+
@server.register(:get, '/menu', [ 200, nil, @@menu_html ])
|
37
|
+
end
|
38
|
+
|
39
|
+
def teardown
|
40
|
+
@server.stop
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'achoo/achievo'
|
2
|
+
require 'test_helpers'
|
3
|
+
|
4
|
+
class TestTermMenu < Test::Unit::TestCase
|
5
|
+
|
6
|
+
context 'Include' do
|
7
|
+
setup do
|
8
|
+
@a = Class.new do
|
9
|
+
include Achoo::Achievo::DateField('foo', 'bar')
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
should 'mixin foo accessor' do
|
14
|
+
assert @a.instance_methods.include?('foo')
|
15
|
+
end
|
16
|
+
|
17
|
+
should 'mixin foo= accessor' do
|
18
|
+
assert @a.instance_methods.include?('foo=')
|
19
|
+
end
|
20
|
+
|
21
|
+
should 'mixin foo_day_field accessor' do
|
22
|
+
assert @a.instance_methods.include?('foo_day_field')
|
23
|
+
end
|
24
|
+
|
25
|
+
should 'mixin foo_month_field accessor' do
|
26
|
+
assert @a.instance_methods.include?('foo_month_field')
|
27
|
+
end
|
28
|
+
|
29
|
+
should 'mixin foo_year_field accessor' do
|
30
|
+
assert @a.instance_methods.include?('foo_year_field')
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'achoo/awake'
|
2
|
+
require 'achoo/system'
|
3
|
+
require 'stringio'
|
4
|
+
require 'test_helpers'
|
5
|
+
require 'time'
|
6
|
+
|
7
|
+
class Achoo::Awake
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
log = [
|
11
|
+
[ '1990-03-01 07:00', :boot],
|
12
|
+
[ '1990-03-01 11:40', :suspend],
|
13
|
+
# Crash
|
14
|
+
[ '1990-03-01 13:00', :boot],
|
15
|
+
[ '1990-03-01 18:00', :halt],
|
16
|
+
#
|
17
|
+
[ '1990-03-02 07:00', :boot],
|
18
|
+
[ '1990-03-02 08:00', :suspend],
|
19
|
+
[ '1990-03-02 09:00', :awake],
|
20
|
+
[ '1990-03-02 10:00', :suspend],
|
21
|
+
[ '1990-03-03 11:00', :awake],
|
22
|
+
[ '1990-03-03 12:00', :halt],
|
23
|
+
#
|
24
|
+
[ '1990-03-04 07:00', :boot],
|
25
|
+
# Crash
|
26
|
+
[ '1990-03-05 07:00', :boot],
|
27
|
+
[ '1990-03-05 08:00', :suspend],
|
28
|
+
[ '1990-03-05 09:00', :awake],
|
29
|
+
# Crash
|
30
|
+
[ '1990-03-05 10:00', :boot],
|
31
|
+
].reverse
|
32
|
+
log.unshift(['1990-03-05 18:00', :now])
|
33
|
+
|
34
|
+
log.each {|entry| entry[0] = Time.parse(entry[0])}
|
35
|
+
log.collect! {|entry| Achoo::System::LogEntry.new(*entry)}
|
36
|
+
|
37
|
+
@sessions = sessions(log)
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
class TestAwake < Test::Unit::TestCase
|
44
|
+
|
45
|
+
def setup
|
46
|
+
@awake = Achoo::Awake.new
|
47
|
+
end
|
48
|
+
|
49
|
+
#def teardown
|
50
|
+
#end
|
51
|
+
|
52
|
+
def test_all
|
53
|
+
$stdout = StringIO.new
|
54
|
+
|
55
|
+
@awake.all
|
56
|
+
expected = %q{Powered on: (0+08:00) Mon 5. Mar 1990 10:00 - 18:00
|
57
|
+
Powered on: (?+??:??) Mon 5. Mar 1990 07:00 - ?
|
58
|
+
Awake: (?+??:??) Mon 5. Mar 1990 09:00 - ?
|
59
|
+
Awake: (0+01:00) Mon 5. Mar 1990 07:00 - 08:00
|
60
|
+
Powered on: (?+??:??) Sun 4. Mar 1990 07:00 - ?
|
61
|
+
Powered on: (1+05:00) Fri 2. Mar 1990 07:00 - Sat 3. Mar 1990 12:00
|
62
|
+
Awake: (0+01:00) Sat 3. Mar 1990 11:00 - 12:00
|
63
|
+
Awake: (0+01:00) Fri 2. Mar 1990 09:00 - 10:00
|
64
|
+
Awake: (0+01:00) Fri 2. Mar 1990 07:00 - 08:00
|
65
|
+
Powered on: (0+05:00) Thu 1. Mar 1990 13:00 - 18:00
|
66
|
+
Powered on: (0+04:40) Thu 1. Mar 1990 07:00 - 11:40
|
67
|
+
}
|
68
|
+
actual = $stdout.string
|
69
|
+
$stdout = STDOUT
|
70
|
+
assert_equal(expected, actual)
|
71
|
+
end
|
72
|
+
|
73
|
+
def test_at_1
|
74
|
+
$stdout = StringIO.new
|
75
|
+
|
76
|
+
@awake.at(Date.new(1990, 3, 2))
|
77
|
+
expected = %q{Powered on: (1+05:00) Fri 2. Mar 1990 07:00 - Sat 3. Mar 1990 12:00
|
78
|
+
Awake: (0+01:00) Fri 2. Mar 1990 09:00 - 10:00
|
79
|
+
Awake: (0+01:00) Fri 2. Mar 1990 07:00 - 08:00
|
80
|
+
}
|
81
|
+
actual = $stdout.string
|
82
|
+
$stdout = STDOUT
|
83
|
+
assert_equal(expected, actual)
|
84
|
+
end
|
85
|
+
|
86
|
+
def test_at_2
|
87
|
+
$stdout = StringIO.new
|
88
|
+
|
89
|
+
@awake.at(Date.new(1990, 3, 3))
|
90
|
+
expected = %q{Powered on: (1+05:00) Fri 2. Mar 1990 07:00 - Sat 3. Mar 1990 12:00
|
91
|
+
Awake: (0+01:00) Sat 3. Mar 1990 11:00 - 12:00
|
92
|
+
}
|
93
|
+
actual = $stdout.string
|
94
|
+
$stdout = STDOUT
|
95
|
+
assert_equal(expected, actual)
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|