-
Notifications
You must be signed in to change notification settings - Fork 0
/
sketch.io
189 lines (152 loc) · 4.55 KB
/
sketch.io
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
#
# expect.io API sketches
# A small unit testing library for Io
#
# TODO
# > More assertions
# > Don't allow assertions outside tests? then we could expose a single `test` global
# > Smaller Lobby footprint
# > Tape output
# > Auto wire up report to print header and footer without explicit
# method calls
# > Parallel tests
# > Skip feature
# > Only feature
# > Decide how to impl matcher messages - see comments on `equal`
# > Exit 0/1
# Assertions
Matchers := Object clone
# TODO choose an API for exposing a message
# > return list(result, message) [current choice]
# > magic last argument contains message - I think this has problems for interpolate.
# > set last matcher message on Matchers (self lastMessage := ...)
# > throw with message on fail (no message on pass)
# > ?
Matchers equal := method(a, b,
list(a == b, "#{a} should equal #{b}" interpolate)
)
#
# Reporter
#
DevReporter := Object clone do(
# Describe the state of a test
# TODO extract from reporting logic
TestState := Object clone
TestState init := method(
self passCount := 0
self failCount := 0
)
TestState onPass := method(passCount = passCount + 1)
TestState onFail := method(failCount = failCount + 1)
TestState allPassed := method(failCount == 0)
suiteState := TestState clone
# Stack of active test cases (just in case we get nested tests)
activeTestStates := list()
# List of completed test cases
completedTestStates := list()
reportStart := method(
"# Starting expect.io tests\n" println
)
reportTestStart := method(desc,
"## Test: #{desc}" interpolate println
activeTestStates push(TestState clone)
)
reportTestEnd := method(desc,
state := activeTestStates pop
completedTestStates push(state)
" - Complete: #{state passCount} ok, #{state failCount} not ok" interpolate println
)
reportFail := method(msg,
suiteState onFail
activeTestStates last onFail
" - failed: #{msg}" interpolate println
)
reportPass := method(msg,
suiteState onPass
activeTestStates last onPass
" - passed: #{msg}" interpolate println
)
reportEnd := method(
"\n# Test report" println
# Assertions
passCount := suiteState passCount
failCount := suiteState failCount
assertCount := passCount + failCount
asserts := if(assertCount == 1, "assertion", "assertions")
# Tests
testCount := completedTestStates size
tests := if(testCount == 1, "test", "tests")
testPassCount := completedTestStates select(allPassed) size
testFailCount := testCount - testPassCount
# Suite
suitePassed := suiteState allPassed
suiteResultDesc := if(suitePassed, "passed", "failed")
"Ran #{testCount} tests" interpolate println
" - #{testPassCount} passed" interpolate println
" - #{testFailCount} failed" interpolate println
"\n... with #{assertCount} total #{asserts}" interpolate println
" - #{passCount} passed" interpolate println
" - #{failCount} failed" interpolate println
"\nThe suite #{suiteResultDesc}" interpolate println
)
)
# Test runner
reporter := DevReporter clone
getDefaultMessage := method(name, actual, expected,
"#{name}: expected #{expected} but got #{actual}" interpolate
)
runMatcher := method(name, target, other,
# "runMatcher #{name} #{target} #{other}" interpolate println
testResult := Matchers perform(name, target, other)
# " -> #{testResult}" interpolate println
# Matchers can return result or (result, message)
if (testResult isKindOf(list)) then(
isPass := testResult first
msg := testResult second
) else (
isPass := testResult
msg := getDefaultMessage(name, target, other)
)
if (isPass) then (
reporter reportPass(msg)
) else (
reporter reportFail(msg)
)
)
test := method(desc, /* testCase, */
reporter reportTestStart(desc)
call evalArgAt(1)
reporter reportTestEnd(desc)
)
#
# assert api
#
assert := Object clone
assert forward := method(a, b,
methodName := call message name
runMatcher(methodName, a, b)
)
#
# expect/should api
#
Object proxyFor := method(target,
proxy := Object clone
proxy target := target
# Forward missing method calls to assert
# Assumes tests have arity 2. I think we can work around
# that using performWithArgList if necessary.
proxy forward := method(other,
methodName := call message name
# "proxy #{target} #{other}" interpolate println
runMatcher(methodName, target, other)
)
proxy
)
Object should := method(
proxyFor(call target)
)
Object expect := method(target,
wrapper := Object clone
wrapper to := proxyFor(target)
wrapper
)