mirror of
https://github.com/pulsar-edit/pulsar.git
synced 2024-11-10 10:17:11 +03:00
323 lines
12 KiB
CoffeeScript
323 lines
12 KiB
CoffeeScript
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'
|
|
{CompositeDisposable} = require 'event-kit'
|
|
|
|
TextEditor = require '../src/text-editor'
|
|
TextEditorElement = require '../src/text-editor-element'
|
|
TextMateLanguageMode = require '../src/text-mate-language-mode'
|
|
{clipboard} = require 'electron'
|
|
|
|
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
|
|
|
|
{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 = require('os').tmpdir()
|
|
|
|
beforeEach ->
|
|
# Do not clobber recent project history
|
|
spyOn(Object.getPrototypeOf(atom.history), 'saveState').andReturn(Promise.resolve())
|
|
|
|
atom.project.setPaths([specProjectPath])
|
|
|
|
window.resetTimeouts()
|
|
spyOn(_._, "now").andCallFake -> window.now
|
|
spyOn(Date, '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"]
|
|
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
|
|
TextMateLanguageMode.prototype.chunkSize = Infinity
|
|
spyOn(TextMateLanguageMode.prototype, "tokenizeInBackground").andCallFake -> @tokenizeNextChunk()
|
|
|
|
# Without this spy, TextEditor.onDidTokenize callbacks would not be called
|
|
# after the buffer's language mode changed, because by the time the editor
|
|
# called its new language mode's onDidTokenize method, the language mode
|
|
# would already be fully tokenized.
|
|
spyOn(TextEditor.prototype, "onDidTokenize").andCallFake (callback) ->
|
|
new CompositeDisposable(
|
|
@emitter.on("did-tokenize", callback),
|
|
@onDidChangeGrammar =>
|
|
languageMode = @buffer.getLanguageMode()
|
|
if languageMode.tokenizeInBackground?.originalValue
|
|
callback()
|
|
)
|
|
|
|
clipboardContent = 'initial clipboard content'
|
|
spyOn(clipboard, 'writeText').andCallFake (text) -> clipboardContent = text
|
|
spyOn(clipboard, 'readText').andCallFake -> clipboardContent
|
|
|
|
addCustomMatchers(this)
|
|
|
|
afterEach ->
|
|
ensureNoDeprecatedFunctionCalls()
|
|
ensureNoDeprecatedStylesheets()
|
|
|
|
waitsForPromise ->
|
|
atom.reset()
|
|
|
|
runs ->
|
|
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()
|
|
|
|
ensureNoDeprecatedFunctionCalls = ->
|
|
deprecations = _.clone(Grim.getDeprecations())
|
|
Grim.clearDeprecations()
|
|
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
|
|
|
|
ensureNoDeprecatedStylesheets = ->
|
|
deprecations = _.clone(atom.styles.getDeprecations())
|
|
atom.styles.clearDeprecations()
|
|
for sourcePath, deprecation of deprecations
|
|
title =
|
|
if sourcePath isnt 'undefined'
|
|
"Deprecated stylesheet at '#{sourcePath}':"
|
|
else
|
|
"Deprecated stylesheet:"
|
|
throw new Error("#{title}\n#{deprecation.message}")
|
|
|
|
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)
|
|
|
|
grimDeprecationsSnapshot = null
|
|
stylesDeprecationsSnapshot = null
|
|
jasmine.snapshotDeprecations = ->
|
|
grimDeprecationsSnapshot = _.clone(Grim.deprecations)
|
|
stylesDeprecationsSnapshot = _.clone(atom.styles.deprecationsBySourcePath)
|
|
|
|
jasmine.restoreDeprecationsSnapshot = ->
|
|
Grim.deprecations = grimDeprecationsSnapshot
|
|
atom.styles.deprecationsBySourcePath = stylesDeprecationsSnapshot
|
|
|
|
jasmine.useRealClock = ->
|
|
jasmine.unspy(window, 'setTimeout')
|
|
jasmine.unspy(window, 'clearTimeout')
|
|
jasmine.unspy(_._, 'now')
|
|
jasmine.unspy(Date, 'now')
|
|
|
|
# The clock is halfway mocked now in a sad and terrible way... only setTimeout
|
|
# and clearTimeout are included. This method will also include setInterval. We
|
|
# would do this everywhere if didn't cause us to break a bunch of package tests.
|
|
jasmine.useMockClock = ->
|
|
spyOn(window, 'setInterval').andCallFake(fakeSetInterval)
|
|
spyOn(window, 'clearInterval').andCallFake(fakeClearInterval)
|
|
|
|
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=0) ->
|
|
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
|