forked from atom/atom
-
Notifications
You must be signed in to change notification settings - Fork 0
/
spec-helper.coffee
281 lines (228 loc) · 9.68 KB
/
spec-helper.coffee
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
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
require 'jasmine-json'
require '../src/window'
require '../vendor/jasmine-jquery'
path = require 'path'
_ = require 'underscore-plus'
fs = require 'fs-plus'
Grim = require 'grim'
pathwatcher = require 'pathwatcher'
FindParentDir = require 'find-parent-dir'
TextEditor = require '../src/text-editor'
TextEditorElement = require '../src/text-editor-element'
TokenizedBuffer = require '../src/tokenized-buffer'
clipboard = require '../src/safe-clipboard'
jasmineStyle = document.createElement('style')
jasmineStyle.textContent = atom.themes.loadStylesheet(atom.themes.resolveStylesheet('../static/jasmine'))
document.head.appendChild(jasmineStyle)
fixturePackagesPath = path.resolve(__dirname, './fixtures/packages')
atom.packages.packageDirPaths.unshift(fixturePackagesPath)
document.querySelector('html').style.overflow = 'auto'
document.body.style.overflow = 'auto'
Set.prototype.jasmineToString = ->
result = "Set {"
first = true
@forEach (element) ->
result += ", " unless first
result += element.toString()
first = false
result + "}"
Set.prototype.isEqual = (other) ->
if other instanceof Set
return false if @size isnt other.size
values = @values()
until (next = values.next()).done
return false unless other.has(next.value)
true
else
false
jasmine.getEnv().addEqualityTester(_.isEqual) # Use underscore's definition of equality for toEqual assertions
if process.env.CI
jasmine.getEnv().defaultTimeoutInterval = 60000
else
jasmine.getEnv().defaultTimeoutInterval = 5000
{resourcePath, testPaths} = atom.getLoadSettings()
if specPackagePath = FindParentDir.sync(testPaths[0], 'package.json')
packageMetadata = require(path.join(specPackagePath, 'package.json'))
specPackageName = packageMetadata.name
if specDirectory = FindParentDir.sync(testPaths[0], 'fixtures')
specProjectPath = path.join(specDirectory, 'fixtures')
else
specProjectPath = path.join(__dirname, 'fixtures')
beforeEach ->
documentTitle = null
atom.project.setPaths([specProjectPath])
window.resetTimeouts()
spyOn(_._, "now").andCallFake -> window.now
spyOn(window, "setTimeout").andCallFake window.fakeSetTimeout
spyOn(window, "clearTimeout").andCallFake window.fakeClearTimeout
spy = spyOn(atom.packages, 'resolvePackagePath').andCallFake (packageName) ->
if specPackageName and packageName is specPackageName
resolvePackagePath(specPackagePath)
else
resolvePackagePath(packageName)
resolvePackagePath = _.bind(spy.originalValue, atom.packages)
# prevent specs from modifying Atom's menus
spyOn(atom.menu, 'sendToBrowserProcess')
# reset config before each spec
atom.config.set "core.destroyEmptyPanes", false
atom.config.set "editor.fontFamily", "Courier"
atom.config.set "editor.fontSize", 16
atom.config.set "editor.autoIndent", false
atom.config.set "core.disabledPackages", ["package-that-throws-an-exception",
"package-with-broken-package-json", "package-with-broken-keymap"]
atom.config.set "editor.useShadowDOM", true
advanceClock(1000)
window.setTimeout.reset()
# make editor display updates synchronous
TextEditorElement::setUpdatedSynchronously(true)
spyOn(pathwatcher.File.prototype, "detectResurrectionAfterDelay").andCallFake -> @detectResurrection()
spyOn(TextEditor.prototype, "shouldPromptToSave").andReturn false
# make tokenization synchronous
TokenizedBuffer.prototype.chunkSize = Infinity
spyOn(TokenizedBuffer.prototype, "tokenizeInBackground").andCallFake -> @tokenizeNextChunk()
clipboardContent = 'initial clipboard content'
spyOn(clipboard, 'writeText').andCallFake (text) -> clipboardContent = text
spyOn(clipboard, 'readText').andCallFake -> clipboardContent
addCustomMatchers(this)
afterEach ->
atom.reset()
document.getElementById('jasmine-content').innerHTML = '' unless window.debugContent
warnIfLeakingPathSubscriptions()
waits(0) # yield to ui thread to make screen update more frequently
warnIfLeakingPathSubscriptions = ->
watchedPaths = pathwatcher.getWatchedPaths()
if watchedPaths.length > 0
console.error("WARNING: Leaking subscriptions for paths: " + watchedPaths.join(", "))
pathwatcher.closeAllWatchers()
ensureNoDeprecatedFunctionsCalled = ->
deprecations = Grim.getDeprecations()
if deprecations.length > 0
originalPrepareStackTrace = Error.prepareStackTrace
Error.prepareStackTrace = (error, stack) ->
output = []
for deprecation in deprecations
output.push "#{deprecation.originName} is deprecated. #{deprecation.message}"
output.push _.multiplyString("-", output[output.length - 1].length)
for stack in deprecation.getStacks()
for {functionName, location} in stack
output.push "#{functionName} -- #{location}"
output.push ""
output.join("\n")
error = new Error("Deprecated function(s) #{deprecations.map(({originName}) -> originName).join ', '}) were called.")
error.stack
Error.prepareStackTrace = originalPrepareStackTrace
throw error
emitObject = jasmine.StringPrettyPrinter.prototype.emitObject
jasmine.StringPrettyPrinter.prototype.emitObject = (obj) ->
if obj.inspect
@append obj.inspect()
else
emitObject.call(this, obj)
jasmine.unspy = (object, methodName) ->
throw new Error("Not a spy") unless object[methodName].hasOwnProperty('originalValue')
object[methodName] = object[methodName].originalValue
jasmine.attachToDOM = (element) ->
jasmineContent = document.querySelector('#jasmine-content')
jasmineContent.appendChild(element) unless jasmineContent.contains(element)
deprecationsSnapshot = null
jasmine.snapshotDeprecations = ->
deprecationsSnapshot = _.clone(Grim.deprecations)
jasmine.restoreDeprecationsSnapshot = ->
Grim.deprecations = deprecationsSnapshot
jasmine.useRealClock = ->
jasmine.unspy(window, 'setTimeout')
jasmine.unspy(window, 'clearTimeout')
jasmine.unspy(_._, 'now')
addCustomMatchers = (spec) ->
spec.addMatchers
toBeInstanceOf: (expected) ->
beOrNotBe = if @isNot then "not be" else "be"
this.message = => "Expected #{jasmine.pp(@actual)} to #{beOrNotBe} instance of #{expected.name} class"
@actual instanceof expected
toHaveLength: (expected) ->
if not @actual?
this.message = => "Expected object #{@actual} has no length method"
false
else
haveOrNotHave = if @isNot then "not have" else "have"
this.message = => "Expected object with length #{@actual.length} to #{haveOrNotHave} length #{expected}"
@actual.length is expected
toExistOnDisk: (expected) ->
toOrNotTo = this.isNot and "not to" or "to"
@message = -> return "Expected path '#{@actual}' #{toOrNotTo} exist."
fs.existsSync(@actual)
toHaveFocus: ->
toOrNotTo = this.isNot and "not to" or "to"
if not document.hasFocus()
console.error "Specs will fail because the Dev Tools have focus. To fix this close the Dev Tools or click the spec runner."
@message = -> return "Expected element '#{@actual}' or its descendants #{toOrNotTo} have focus."
element = @actual
element = element.get(0) if element.jquery
element is document.activeElement or element.contains(document.activeElement)
toShow: ->
toOrNotTo = this.isNot and "not to" or "to"
element = @actual
element = element.get(0) if element.jquery
@message = -> return "Expected element '#{element}' or its descendants #{toOrNotTo} show."
element.style.display in ['block', 'inline-block', 'static', 'fixed']
toEqualPath: (expected) ->
actualPath = path.normalize(@actual)
expectedPath = path.normalize(expected)
@message = -> return "Expected path '#{actualPath}' to be equal to '#{expectedPath}'."
actualPath is expectedPath
window.waitsForPromise = (args...) ->
label = null
if args.length > 1
{shouldReject, timeout, label} = args[0]
else
shouldReject = false
label ?= 'promise to be resolved or rejected'
fn = _.last(args)
window.waitsFor label, timeout, (moveOn) ->
promise = fn()
if shouldReject
promise.catch.call(promise, moveOn)
promise.then ->
jasmine.getEnv().currentSpec.fail("Expected promise to be rejected, but it was resolved")
moveOn()
else
promise.then(moveOn)
promise.catch.call promise, (error) ->
jasmine.getEnv().currentSpec.fail("Expected promise to be resolved, but it was rejected with: #{error?.message} #{jasmine.pp(error)}")
moveOn()
window.resetTimeouts = ->
window.now = 0
window.timeoutCount = 0
window.intervalCount = 0
window.timeouts = []
window.intervalTimeouts = {}
window.fakeSetTimeout = (callback, ms) ->
id = ++window.timeoutCount
window.timeouts.push([id, window.now + ms, callback])
id
window.fakeClearTimeout = (idToClear) ->
window.timeouts = window.timeouts.filter ([id]) -> id isnt idToClear
window.fakeSetInterval = (callback, ms) ->
id = ++window.intervalCount
action = ->
callback()
window.intervalTimeouts[id] = window.fakeSetTimeout(action, ms)
window.intervalTimeouts[id] = window.fakeSetTimeout(action, ms)
id
window.fakeClearInterval = (idToClear) ->
window.fakeClearTimeout(@intervalTimeouts[idToClear])
window.advanceClock = (delta=1) ->
window.now += delta
callbacks = []
window.timeouts = window.timeouts.filter ([id, strikeTime, callback]) ->
if strikeTime <= window.now
callbacks.push(callback)
false
else
true
callback() for callback in callbacks
exports.mockLocalStorage = ->
items = {}
spyOn(global.localStorage, 'setItem').andCallFake (key, item) -> items[key] = item.toString(); undefined
spyOn(global.localStorage, 'getItem').andCallFake (key) -> items[key] ? null
spyOn(global.localStorage, 'removeItem').andCallFake (key) -> delete items[key]; undefined