mirror of
https://github.com/atom/atom.git
synced 2026-02-11 23:25:03 -05:00
Merge branch 'master' into pr/8232
This commit is contained in:
@@ -1,15 +1,9 @@
|
||||
{$} = require '../src/space-pen-extensions'
|
||||
|
||||
describe '"atom" protocol URL', ->
|
||||
it 'sends the file relative in the package as response', ->
|
||||
called = false
|
||||
callback = -> called = true
|
||||
$.ajax
|
||||
url: 'atom://async/package.json'
|
||||
success: callback
|
||||
# In old versions of jQuery, ajax calls to custom protocol would always
|
||||
# be treated as error eventhough the browser thinks it's a success
|
||||
# request.
|
||||
error: callback
|
||||
request = new XMLHttpRequest()
|
||||
request.addEventListener('load', -> called = true)
|
||||
request.open('GET', 'atom://async/package.json', true)
|
||||
request.send()
|
||||
|
||||
waitsFor 'request to be done', -> called is true
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
path = require 'path'
|
||||
_ = require 'underscore-plus'
|
||||
{View, $, $$} = require '../src/space-pen-extensions'
|
||||
grim = require 'grim'
|
||||
marked = require 'marked'
|
||||
|
||||
listen = require '../src/delegated-listener'
|
||||
|
||||
formatStackTrace = (spec, message='', stackTrace) ->
|
||||
return stackTrace unless stackTrace
|
||||
|
||||
@@ -30,30 +31,43 @@ formatStackTrace = (spec, message='', stackTrace) ->
|
||||
lines.join('\n').trim()
|
||||
|
||||
module.exports =
|
||||
class AtomReporter extends View
|
||||
@content: ->
|
||||
@div class: 'spec-reporter', =>
|
||||
@div class: 'padded pull-right', =>
|
||||
@button outlet: 'reloadButton', class: 'btn btn-small reload-button', 'Reload Specs'
|
||||
@div outlet: 'coreArea', class: 'symbol-area', =>
|
||||
@div outlet: 'coreHeader', class: 'symbol-header'
|
||||
@ul outlet: 'coreSummary', class: 'symbol-summary list-unstyled'
|
||||
@div outlet: 'bundledArea', class: 'symbol-area', =>
|
||||
@div outlet: 'bundledHeader', class: 'symbol-header'
|
||||
@ul outlet: 'bundledSummary', class: 'symbol-summary list-unstyled'
|
||||
@div outlet: 'userArea', class: 'symbol-area', =>
|
||||
@div outlet: 'userHeader', class: 'symbol-header'
|
||||
@ul outlet: 'userSummary', class: 'symbol-summary list-unstyled'
|
||||
@div outlet: "status", class: 'status alert alert-info', =>
|
||||
@div outlet: "time", class: 'time'
|
||||
@div outlet: "specCount", class: 'spec-count'
|
||||
@div outlet: "message", class: 'message'
|
||||
@div outlet: "results", class: 'results'
|
||||
class AtomReporter
|
||||
|
||||
@div outlet: "deprecations", class: 'status alert alert-warning', style: 'display: none', =>
|
||||
@span outlet: 'deprecationStatus', '0 deprecations'
|
||||
@div class: 'deprecation-toggle'
|
||||
@div outlet: 'deprecationList', class: 'deprecation-list'
|
||||
constructor: ->
|
||||
@element = document.createElement('div')
|
||||
@element.innerHTML = """
|
||||
<div class="spec-reporter">
|
||||
<div class="padded pull-right">
|
||||
<button outlet="reloadButton" class="btn btn-small reload-button">Reload Specs</button>
|
||||
</div>
|
||||
<div outlet="coreArea" class="symbol-area">
|
||||
<div outlet="coreHeader" class="symbol-header"></div>
|
||||
<ul outlet="coreSummary"class="symbol-summary list-unstyled"></ul>
|
||||
</div>
|
||||
<div outlet="bundledArea" class="symbol-area">
|
||||
<div outlet="bundledHeader" class="symbol-header"></div>
|
||||
<ul outlet="bundledSummary"class="symbol-summary list-unstyled"></ul>
|
||||
</div>
|
||||
<div outlet="userArea" class="symbol-area">
|
||||
<div outlet="userHeader" class="symbol-header"></div>
|
||||
<ul outlet="userSummary"class="symbol-summary list-unstyled"></ul>
|
||||
</div>
|
||||
<div outlet="status" class="status alert alert-info">
|
||||
<div outlet="time" class="time"></div>
|
||||
<div outlet="specCount" class="spec-count"></div>
|
||||
<div outlet="message" class="message"></div>
|
||||
</div>
|
||||
<div outlet="results" class="results"></div>
|
||||
<div outlet="deprecations" class="status alert alert-warning" style="display: none">
|
||||
<span outlet="deprecationStatus">0 deprecations</span>
|
||||
<div class="deprecation-toggle"></div>
|
||||
</div>
|
||||
<div outlet="deprecationList" class="deprecation-list"></div>
|
||||
</div>
|
||||
"""
|
||||
|
||||
for element in @element.querySelectorAll('[outlet]')
|
||||
this[element.getAttribute('outlet')] = element
|
||||
|
||||
startedAt: null
|
||||
runningSpecCount: 0
|
||||
@@ -71,20 +85,18 @@ class AtomReporter extends View
|
||||
specs = runner.specs()
|
||||
@totalSpecCount = specs.length
|
||||
@addSpecs(specs)
|
||||
$(document.body).append this
|
||||
|
||||
@on 'click', '.stack-trace', ->
|
||||
$(this).toggleClass('expanded')
|
||||
|
||||
@reloadButton.on 'click', -> require('ipc').send('call-window-method', 'restart')
|
||||
document.body.appendChild(@element)
|
||||
|
||||
reportRunnerResults: (runner) ->
|
||||
@updateSpecCounts()
|
||||
@status.addClass('alert-success').removeClass('alert-info') if @failedCount is 0
|
||||
if @failedCount is 0
|
||||
@status.classList.add('alert-success')
|
||||
@status.classList.remove('alert-info')
|
||||
|
||||
if @failedCount is 1
|
||||
@message.text "#{@failedCount} failure"
|
||||
@message.textContent = "#{@failedCount} failure"
|
||||
else
|
||||
@message.text "#{@failedCount} failures"
|
||||
@message.textConent = "#{@failedCount} failures"
|
||||
|
||||
reportSuiteResults: (suite) ->
|
||||
|
||||
@@ -100,170 +112,214 @@ class AtomReporter extends View
|
||||
addDeprecations: (spec) ->
|
||||
deprecations = grim.getDeprecations()
|
||||
@deprecationCount += deprecations.length
|
||||
@deprecations.show() if @deprecationCount > 0
|
||||
@deprecations.style.display = '' if @deprecationCount > 0
|
||||
if @deprecationCount is 1
|
||||
@deprecationStatus.text("1 deprecation")
|
||||
@deprecationStatus.textContent = "1 deprecation"
|
||||
else
|
||||
@deprecationStatus.text("#{@deprecationCount} deprecations")
|
||||
@deprecationStatus.textContent = "#{@deprecationCount} deprecations"
|
||||
|
||||
for deprecation in deprecations
|
||||
@deprecationList.append $$ ->
|
||||
@div class: 'padded', =>
|
||||
@div class: 'result-message fail deprecation-message', =>
|
||||
@raw marked(deprecation.message)
|
||||
@deprecationList.appendChild(@buildDeprecationElement(spec, deprecation))
|
||||
|
||||
for stack in deprecation.getStacks()
|
||||
fullStack = stack.map ({functionName, location}) ->
|
||||
if functionName is '<unknown>'
|
||||
" at #{location}"
|
||||
else
|
||||
" at #{functionName} (#{location})"
|
||||
@pre class: 'stack-trace padded', formatStackTrace(spec, deprecation.message, fullStack.join('\n'))
|
||||
grim.clearDeprecations()
|
||||
|
||||
handleEvents: ->
|
||||
$(document).on "click", ".spec-toggle", ({currentTarget}) ->
|
||||
element = $(currentTarget)
|
||||
specFailures = element.parent().find('.spec-failures')
|
||||
specFailures.toggle()
|
||||
element.toggleClass('folded')
|
||||
false
|
||||
buildDeprecationElement: (spec, deprecation) ->
|
||||
div = document.createElement('div')
|
||||
div.className = 'padded'
|
||||
div.innerHTML = """
|
||||
<div class="result-message fail deprecation-message">
|
||||
#{marked(deprecation.message)}
|
||||
</div>
|
||||
"""
|
||||
|
||||
$(document).on "click", ".deprecation-toggle", ({currentTarget}) ->
|
||||
element = $(currentTarget)
|
||||
deprecationList = $(document).find('.deprecation-list')
|
||||
deprecationList.toggle()
|
||||
element.toggleClass('folded')
|
||||
false
|
||||
for stack in deprecation.getStacks()
|
||||
fullStack = stack.map ({functionName, location}) ->
|
||||
if functionName is '<unknown>'
|
||||
" at #{location}"
|
||||
else
|
||||
" at #{functionName} (#{location})"
|
||||
pre = document.createElement('pre')
|
||||
pre.className = 'stack-trace padded'
|
||||
pre.textContent = formatStackTrace(spec, deprecation.message, fullStack.join('\n'))
|
||||
div.appendChild(pre)
|
||||
|
||||
div
|
||||
|
||||
handleEvents: ->
|
||||
listen document, 'click', '.spec-toggle', (event) ->
|
||||
specFailures = event.currentTarget.parentElement.querySelector('.spec-failures')
|
||||
|
||||
if specFailures.style.display is 'none'
|
||||
specFailures.style.display = ''
|
||||
event.currentTarget.classList.remove('folded')
|
||||
else
|
||||
specFailures.style.display = 'none'
|
||||
event.currentTarget.classList.add('folded')
|
||||
|
||||
event.preventDefault()
|
||||
|
||||
listen document, 'click', '.deprecation-list', (event) ->
|
||||
deprecationList = event.currentTarget.parentElement.querySelector('.deprecation-list')
|
||||
|
||||
if deprecationList.style.display is 'none'
|
||||
deprecationList.style.display = ''
|
||||
event.currentTarget.classList.remove('folded')
|
||||
else
|
||||
deprecationList.style.display = 'none'
|
||||
event.currentTarget.classList.add('folded')
|
||||
|
||||
event.preventDefault()
|
||||
|
||||
listen document, 'click', '.stack-trace', (event) ->
|
||||
event.currentTarget.classList.toggle('expanded')
|
||||
|
||||
@reloadButton.addEventListener('click', -> require('ipc').send('call-window-method', 'restart'))
|
||||
|
||||
updateSpecCounts: ->
|
||||
if @skippedCount
|
||||
specCount = "#{@completeSpecCount - @skippedCount}/#{@totalSpecCount - @skippedCount} (#{@skippedCount} skipped)"
|
||||
else
|
||||
specCount = "#{@completeSpecCount}/#{@totalSpecCount}"
|
||||
@specCount[0].textContent = specCount
|
||||
@specCount.textContent = specCount
|
||||
|
||||
updateStatusView: (spec) ->
|
||||
if @failedCount > 0
|
||||
@status.addClass('alert-danger').removeClass('alert-info')
|
||||
@status.classList.add('alert-danger')
|
||||
@status.classList.remove('alert-info')
|
||||
|
||||
@updateSpecCounts()
|
||||
|
||||
rootSuite = spec.suite
|
||||
rootSuite = rootSuite.parentSuite while rootSuite.parentSuite
|
||||
@message.text rootSuite.description
|
||||
@message.textContent = rootSuite.description
|
||||
|
||||
time = "#{Math.round((spec.endedAt - @startedAt) / 10)}"
|
||||
time = "0#{time}" if time.length < 3
|
||||
@time[0].textContent = "#{time[0...-2]}.#{time[-2..]}s"
|
||||
@time.textContent = "#{time[0...-2]}.#{time[-2..]}s"
|
||||
|
||||
addSpecs: (specs) ->
|
||||
coreSpecs = 0
|
||||
bundledPackageSpecs = 0
|
||||
userPackageSpecs = 0
|
||||
for spec in specs
|
||||
symbol = $$ -> @li id: "spec-summary-#{spec.id}", class: "spec-summary pending"
|
||||
symbol = document.createElement('li')
|
||||
symbol.setAttribute('id', "spec-summary-#{spec.id}")
|
||||
symbol.className = "spec-summary pending"
|
||||
switch spec.specType
|
||||
when 'core'
|
||||
coreSpecs++
|
||||
@coreSummary.append symbol
|
||||
@coreSummary.appendChild symbol
|
||||
when 'bundled'
|
||||
bundledPackageSpecs++
|
||||
@bundledSummary.append symbol
|
||||
@bundledSummary.appendChild symbol
|
||||
when 'user'
|
||||
userPackageSpecs++
|
||||
@userSummary.append symbol
|
||||
@userSummary.appendChild symbol
|
||||
|
||||
if coreSpecs > 0
|
||||
@coreHeader.text("Core Specs (#{coreSpecs})")
|
||||
@coreHeader.textContent = "Core Specs (#{coreSpecs})"
|
||||
else
|
||||
@coreArea.hide()
|
||||
@coreArea.style.display = 'none'
|
||||
if bundledPackageSpecs > 0
|
||||
@bundledHeader.text("Bundled Package Specs (#{bundledPackageSpecs})")
|
||||
@bundledHeader.textContent = "Bundled Package Specs (#{bundledPackageSpecs})"
|
||||
else
|
||||
@bundledArea.hide()
|
||||
@bundledArea.style.display = 'none'
|
||||
if userPackageSpecs > 0
|
||||
if coreSpecs is 0 and bundledPackageSpecs is 0
|
||||
# Package specs being run, show a more descriptive label
|
||||
{specDirectory} = specs[0]
|
||||
packageFolderName = path.basename(path.dirname(specDirectory))
|
||||
packageName = _.undasherize(_.uncamelcase(packageFolderName))
|
||||
@userHeader.text("#{packageName} Specs")
|
||||
@userHeader.textContent = "#{packageName} Specs"
|
||||
else
|
||||
@userHeader.text("User Package Specs (#{userPackageSpecs})")
|
||||
@userHeader.textContent = "User Package Specs (#{userPackageSpecs})"
|
||||
else
|
||||
@userArea.hide()
|
||||
@userArea.style.display = 'none'
|
||||
|
||||
specStarted: (spec) ->
|
||||
@runningSpecCount++
|
||||
|
||||
specComplete: (spec) ->
|
||||
specSummaryElement = $("#spec-summary-#{spec.id}")
|
||||
specSummaryElement.removeClass('pending')
|
||||
specSummaryElement.setTooltip(title: spec.getFullName(), container: '.spec-reporter')
|
||||
specSummaryElement = document.getElementById("spec-summary-#{spec.id}")
|
||||
specSummaryElement.classList.remove('pending')
|
||||
|
||||
results = spec.results()
|
||||
if results.skipped
|
||||
specSummaryElement.addClass("skipped")
|
||||
specSummaryElement.classList.add("skipped")
|
||||
@skippedCount++
|
||||
else if results.passed()
|
||||
specSummaryElement.addClass("passed")
|
||||
specSummaryElement.classList.add("passed")
|
||||
@passedCount++
|
||||
else
|
||||
specSummaryElement.addClass("failed")
|
||||
specSummaryElement.classList.add("failed")
|
||||
|
||||
specView = new SpecResultView(spec)
|
||||
specView.attach()
|
||||
@failedCount++
|
||||
@addDeprecations(spec)
|
||||
|
||||
class SuiteResultView extends View
|
||||
@content: ->
|
||||
@div class: 'suite', =>
|
||||
@div outlet: 'description', class: 'description'
|
||||
|
||||
initialize: (@suite) ->
|
||||
@attr('id', "suite-view-#{@suite.id}")
|
||||
@description.text(@suite.description)
|
||||
class SuiteResultView
|
||||
constructor: (@suite) ->
|
||||
@element = document.createElement('div')
|
||||
@element.className = 'suite'
|
||||
@element.setAttribute('id', "suite-view-#{@suite.id}")
|
||||
@description = document.createElement('div')
|
||||
@description.className = 'description'
|
||||
@description.textContent = @suite.description
|
||||
@element.appendChild(@description)
|
||||
|
||||
attach: ->
|
||||
(@parentSuiteView() or $('.results')).append this
|
||||
(@parentSuiteView() or document.querySelector('.results')).appendChild(@element)
|
||||
|
||||
parentSuiteView: ->
|
||||
return unless @suite.parentSuite
|
||||
|
||||
if not suiteView = $("#suite-view-#{@suite.parentSuite.id}").view()
|
||||
unless suiteViewElement = document.querySelector("#suite-view-#{@suite.parentSuite.id}")
|
||||
suiteView = new SuiteResultView(@suite.parentSuite)
|
||||
suiteView.attach()
|
||||
suiteViewElement = suiteView.element
|
||||
|
||||
suiteView
|
||||
suiteViewElement
|
||||
|
||||
class SpecResultView extends View
|
||||
@content: ->
|
||||
@div class: 'spec', =>
|
||||
@div class: 'spec-toggle'
|
||||
@div outlet: 'description', class: 'description'
|
||||
@div outlet: 'specFailures', class: 'spec-failures'
|
||||
class SpecResultView
|
||||
constructor: (@spec) ->
|
||||
@element = document.createElement('div')
|
||||
@element.className = 'spec'
|
||||
@element.innerHTML = """
|
||||
<div class='spec-toggle'></div>
|
||||
<div outlet='description' class='description'></div>
|
||||
<div outlet='specFailures' class='spec-failures'></div>
|
||||
"""
|
||||
@description = @element.querySelector('[outlet="description"]')
|
||||
@specFailures = @element.querySelector('[outlet="specFailures"]')
|
||||
|
||||
initialize: (@spec) ->
|
||||
@addClass("spec-view-#{@spec.id}")
|
||||
@element.classList.add("spec-view-#{@spec.id}")
|
||||
|
||||
description = @spec.description
|
||||
description = "it #{description}" if description.indexOf('it ') isnt 0
|
||||
@description.text(description)
|
||||
@description.textContent = description
|
||||
|
||||
for result in @spec.results().getItems() when not result.passed()
|
||||
stackTrace = formatStackTrace(@spec, result.message, result.trace.stack)
|
||||
@specFailures.append $$ ->
|
||||
@div result.message, class: 'result-message fail'
|
||||
@pre stackTrace, class: 'stack-trace padded' if stackTrace
|
||||
|
||||
resultElement = document.createElement('div')
|
||||
resultElement.className = 'result-message fail'
|
||||
resultElement.textContent = result.message
|
||||
@specFailures.appendChild(resultElement)
|
||||
|
||||
if stackTrace
|
||||
traceElement = document.createElement('pre')
|
||||
traceElement.className = 'stack-trace padded'
|
||||
traceElement.textContent = stackTrace
|
||||
@specFailures.appendChild(traceElement)
|
||||
|
||||
attach: ->
|
||||
@parentSuiteView().append this
|
||||
@parentSuiteView().appendChild(@element)
|
||||
|
||||
parentSuiteView: ->
|
||||
if not suiteView = $("#suite-view-#{@spec.suite.id}").view()
|
||||
unless suiteViewElement = document.querySelector("#suite-view-#{@spec.suite.id}")
|
||||
suiteView = new SuiteResultView(@spec.suite)
|
||||
suiteView.attach()
|
||||
suiteViewElement = suiteView.element
|
||||
|
||||
suiteView
|
||||
suiteViewElement
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
{$, $$} = require '../src/space-pen-extensions'
|
||||
Exec = require('child_process').exec
|
||||
path = require 'path'
|
||||
Package = require '../src/package'
|
||||
@@ -223,3 +222,31 @@ describe "the `atom` global", ->
|
||||
spyOn(atom, "pickFolder").andCallFake (callback) -> callback(null)
|
||||
atom.addProjectFolder()
|
||||
expect(atom.project.getPaths()).toEqual(initialPaths)
|
||||
|
||||
describe "::unloadEditorWindow()", ->
|
||||
it "saves the serialized state of the window so it can be deserialized after reload", ->
|
||||
workspaceState = atom.workspace.serialize()
|
||||
syntaxState = atom.grammars.serialize()
|
||||
projectState = atom.project.serialize()
|
||||
|
||||
atom.unloadEditorWindow()
|
||||
|
||||
expect(atom.state.workspace).toEqual workspaceState
|
||||
expect(atom.state.grammars).toEqual syntaxState
|
||||
expect(atom.state.project).toEqual projectState
|
||||
expect(atom.saveSync).toHaveBeenCalled()
|
||||
|
||||
describe "::removeEditorWindow()", ->
|
||||
it "unsubscribes from all buffers", ->
|
||||
waitsForPromise ->
|
||||
atom.workspace.open("sample.js")
|
||||
|
||||
runs ->
|
||||
buffer = atom.workspace.getActivePaneItem().buffer
|
||||
pane = atom.workspace.getActivePane()
|
||||
pane.splitRight(copyActiveItem: true)
|
||||
expect(atom.workspace.getTextEditors().length).toBe 2
|
||||
|
||||
atom.removeEditorWindow()
|
||||
|
||||
expect(buffer.getSubscriptionCount()).toBe 0
|
||||
|
||||
@@ -1,34 +1,81 @@
|
||||
path = require 'path'
|
||||
fs = require 'fs-plus'
|
||||
temp = require 'temp'
|
||||
installer = require '../src/command-installer'
|
||||
CommandInstaller = require '../src/command-installer'
|
||||
|
||||
describe "install(commandPath, callback)", ->
|
||||
commandFilePath = temp.openSync("atom-command").path
|
||||
commandName = path.basename(commandFilePath)
|
||||
installationPath = temp.mkdirSync("atom-bin")
|
||||
installationFilePath = path.join(installationPath, commandName)
|
||||
describe "CommandInstaller on #darwin", ->
|
||||
[installer, resourcesPath, installationPath, atomBinPath, apmBinPath] = []
|
||||
|
||||
beforeEach ->
|
||||
fs.chmodSync(commandFilePath, '755')
|
||||
spyOn(installer, 'getInstallDirectory').andReturn installationPath
|
||||
installationPath = temp.mkdirSync("atom-bin")
|
||||
|
||||
describe "on #darwin", ->
|
||||
it "symlinks the command and makes it executable", ->
|
||||
expect(fs.isFileSync(commandFilePath)).toBeTruthy()
|
||||
expect(fs.isFileSync(installationFilePath)).toBeFalsy()
|
||||
resourcesPath = temp.mkdirSync('atom-app')
|
||||
atomBinPath = path.join(resourcesPath, 'app', 'atom.sh')
|
||||
apmBinPath = path.join(resourcesPath, 'app', 'apm', 'node_modules', '.bin', 'apm')
|
||||
fs.writeFileSync(atomBinPath, "")
|
||||
fs.writeFileSync(apmBinPath, "")
|
||||
fs.chmodSync(atomBinPath, '755')
|
||||
fs.chmodSync(apmBinPath, '755')
|
||||
|
||||
installDone = false
|
||||
installError = null
|
||||
installer.createSymlink commandFilePath, false, (error) ->
|
||||
installDone = true
|
||||
installError = error
|
||||
spyOn(CommandInstaller::, 'getResourcesDirectory').andReturn(resourcesPath)
|
||||
spyOn(CommandInstaller::, 'getInstallDirectory').andReturn(installationPath)
|
||||
|
||||
waitsFor ->
|
||||
installDone
|
||||
describe "when using a stable version of atom", ->
|
||||
beforeEach ->
|
||||
installer = new CommandInstaller("2.0.2")
|
||||
|
||||
it "symlinks the atom command as 'atom'", ->
|
||||
installedAtomPath = path.join(installationPath, 'atom')
|
||||
|
||||
expect(fs.isFileSync(installedAtomPath)).toBeFalsy()
|
||||
|
||||
waitsFor (done) ->
|
||||
installer.installAtomCommand(false, done)
|
||||
|
||||
runs ->
|
||||
expect(installError).toBeNull()
|
||||
expect(fs.isFileSync(installationFilePath)).toBeTruthy()
|
||||
expect(fs.realpathSync(installationFilePath)).toBe fs.realpathSync(commandFilePath)
|
||||
expect(fs.isExecutableSync(installationFilePath)).toBeTruthy()
|
||||
expect(fs.realpathSync(installedAtomPath)).toBe fs.realpathSync(atomBinPath)
|
||||
expect(fs.isExecutableSync(installedAtomPath)).toBe true
|
||||
expect(fs.isFileSync(path.join(installationPath, 'atom-beta'))).toBe false
|
||||
|
||||
it "symlinks the apm command as 'apm'", ->
|
||||
installedApmPath = path.join(installationPath, 'apm')
|
||||
|
||||
expect(fs.isFileSync(installedApmPath)).toBeFalsy()
|
||||
|
||||
waitsFor (done) ->
|
||||
installer.installApmCommand(false, done)
|
||||
|
||||
runs ->
|
||||
expect(fs.realpathSync(installedApmPath)).toBe fs.realpathSync(apmBinPath)
|
||||
expect(fs.isExecutableSync(installedApmPath)).toBeTruthy()
|
||||
expect(fs.isFileSync(path.join(installationPath, 'apm-beta'))).toBe false
|
||||
|
||||
describe "when using a beta version of atom", ->
|
||||
beforeEach ->
|
||||
installer = new CommandInstaller("2.2.0-beta.0")
|
||||
|
||||
it "symlinks the atom command as 'atom-beta'", ->
|
||||
installedAtomPath = path.join(installationPath, 'atom-beta')
|
||||
|
||||
expect(fs.isFileSync(installedAtomPath)).toBeFalsy()
|
||||
|
||||
waitsFor (done) ->
|
||||
installer.installAtomCommand(false, done)
|
||||
|
||||
runs ->
|
||||
expect(fs.realpathSync(installedAtomPath)).toBe fs.realpathSync(atomBinPath)
|
||||
expect(fs.isExecutableSync(installedAtomPath)).toBe true
|
||||
expect(fs.isFileSync(path.join(installationPath, 'atom'))).toBe false
|
||||
|
||||
it "symlinks the apm command as 'apm-beta'", ->
|
||||
installedApmPath = path.join(installationPath, 'apm-beta')
|
||||
|
||||
expect(fs.isFileSync(installedApmPath)).toBeFalsy()
|
||||
|
||||
waitsFor (done) ->
|
||||
installer.installApmCommand(false, done)
|
||||
|
||||
runs ->
|
||||
expect(fs.realpathSync(installedApmPath)).toBe fs.realpathSync(apmBinPath)
|
||||
expect(fs.isExecutableSync(installedApmPath)).toBeTruthy()
|
||||
expect(fs.isFileSync(path.join(installationPath, 'apm'))).toBe false
|
||||
|
||||
@@ -115,6 +115,15 @@ describe "CommandRegistry", ->
|
||||
grandchild.dispatchEvent(dispatchedEvent)
|
||||
expect(dispatchedEvent.abortKeyBinding).toHaveBeenCalled()
|
||||
|
||||
it "copies non-standard properties from the original event to the synthetic event", ->
|
||||
syntheticEvent = null
|
||||
registry.add '.child', 'command', (event) -> syntheticEvent = event
|
||||
|
||||
dispatchedEvent = new CustomEvent('command', bubbles: true)
|
||||
dispatchedEvent.nonStandardProperty = 'testing'
|
||||
grandchild.dispatchEvent(dispatchedEvent)
|
||||
expect(syntheticEvent.nonStandardProperty).toBe 'testing'
|
||||
|
||||
it "allows listeners to be removed via a disposable returned by ::add", ->
|
||||
calls = []
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ path = require 'path'
|
||||
temp = require 'temp'
|
||||
CSON = require 'season'
|
||||
fs = require 'fs-plus'
|
||||
Grim = require 'grim'
|
||||
|
||||
describe "Config", ->
|
||||
dotAtomPath = null
|
||||
@@ -364,16 +363,6 @@ describe "Config", ->
|
||||
expect(atom.config.save).not.toHaveBeenCalled()
|
||||
expect(atom.config.get('foo.bar.baz', scope: ['.source.coffee'])).toBe 55
|
||||
|
||||
it "deprecates passing a scope selector as the first argument", ->
|
||||
atom.config.setDefaults("foo", bar: baz: 10)
|
||||
atom.config.set('foo.bar.baz', 55, scopeSelector: '.source.coffee')
|
||||
|
||||
spyOn(Grim, 'deprecate')
|
||||
atom.config.unset('.source.coffee', 'foo.bar.baz')
|
||||
expect(Grim.deprecate).toHaveBeenCalled()
|
||||
|
||||
expect(atom.config.get('foo.bar.baz', scope: ['.source.coffee'])).toBe 10
|
||||
|
||||
describe ".onDidChange(keyPath, {scope})", ->
|
||||
[observeHandler, observeSubscription] = []
|
||||
|
||||
@@ -458,15 +447,6 @@ describe "Config", ->
|
||||
expect(changeSpy).toHaveBeenCalledWith({oldValue: 12, newValue: undefined})
|
||||
changeSpy.reset()
|
||||
|
||||
it 'deprecates using a scope descriptor as an optional first argument', ->
|
||||
keyPath = "foo.bar.baz"
|
||||
spyOn(Grim, 'deprecate')
|
||||
atom.config.onDidChange [".source.coffee", ".string.quoted.double.coffee"], keyPath, changeSpy = jasmine.createSpy()
|
||||
expect(Grim.deprecate).toHaveBeenCalled()
|
||||
|
||||
atom.config.set("foo.bar.baz", 12)
|
||||
expect(changeSpy).toHaveBeenCalledWith({oldValue: undefined, newValue: 12})
|
||||
|
||||
describe ".observe(keyPath, {scope})", ->
|
||||
[observeHandler, observeSubscription] = []
|
||||
|
||||
@@ -535,16 +515,6 @@ describe "Config", ->
|
||||
expect(observeHandler).toHaveBeenCalledWith("value 2")
|
||||
expect(otherHandler).not.toHaveBeenCalledWith("value 2")
|
||||
|
||||
it "deprecates using a scope descriptor as the first argument", ->
|
||||
spyOn(Grim, 'deprecate')
|
||||
atom.config.observe([".some.scope"], "foo.bar.baz", observeHandler)
|
||||
atom.config.observe([".another.scope"], "foo.bar.baz", otherHandler)
|
||||
expect(Grim.deprecate).toHaveBeenCalled()
|
||||
|
||||
atom.config.set('foo.bar.baz', "value 2", scopeSelector: ".some")
|
||||
expect(observeHandler).toHaveBeenCalledWith("value 2")
|
||||
expect(otherHandler).not.toHaveBeenCalledWith("value 2")
|
||||
|
||||
it 'calls the callback when properties with more specific selectors are removed', ->
|
||||
changeSpy = jasmine.createSpy()
|
||||
atom.config.observe("foo.bar.baz", scope: [".source.coffee", ".string.quoted.double.coffee"], changeSpy)
|
||||
@@ -1058,10 +1028,6 @@ describe "Config", ->
|
||||
atom.config.setDefaults("foo.bar.baz", a: 2)
|
||||
expect(updatedCallback.callCount).toBe 1
|
||||
|
||||
it "sets a default when the setting's key contains an escaped dot", ->
|
||||
atom.config.setDefaults("foo", 'a\\.b': 1, b: 2)
|
||||
expect(atom.config.get("foo")).toEqual 'a\\.b': 1, b: 2
|
||||
|
||||
describe ".setSchema(keyPath, schema)", ->
|
||||
it 'creates a properly nested schema', ->
|
||||
schema =
|
||||
@@ -1629,135 +1595,3 @@ describe "Config", ->
|
||||
|
||||
expect(atom.config.set('foo.bar.arr', ['two', 'three'])).toBe true
|
||||
expect(atom.config.get('foo.bar.arr')).toEqual ['two', 'three']
|
||||
|
||||
describe "Deprecated Methods", ->
|
||||
describe ".getDefault(keyPath)", ->
|
||||
it "returns a clone of the default value", ->
|
||||
atom.config.setDefaults("foo", same: 1, changes: 1)
|
||||
|
||||
spyOn(Grim, 'deprecate')
|
||||
expect(atom.config.getDefault('foo.same')).toBe 1
|
||||
expect(atom.config.getDefault('foo.changes')).toBe 1
|
||||
expect(Grim.deprecate.callCount).toBe 2
|
||||
|
||||
atom.config.set('foo.same', 2)
|
||||
atom.config.set('foo.changes', 3)
|
||||
|
||||
expect(atom.config.getDefault('foo.same')).toBe 1
|
||||
expect(atom.config.getDefault('foo.changes')).toBe 1
|
||||
expect(Grim.deprecate.callCount).toBe 4
|
||||
|
||||
initialDefaultValue = [1, 2, 3]
|
||||
atom.config.setDefaults("foo", bar: initialDefaultValue)
|
||||
expect(atom.config.getDefault('foo.bar')).toEqual initialDefaultValue
|
||||
expect(atom.config.getDefault('foo.bar')).not.toBe initialDefaultValue
|
||||
expect(Grim.deprecate.callCount).toBe 6
|
||||
|
||||
describe "when scoped settings are used", ->
|
||||
it "returns the global default when no scoped default set", ->
|
||||
atom.config.setDefaults("foo", bar: baz: 10)
|
||||
|
||||
spyOn(Grim, 'deprecate')
|
||||
expect(atom.config.getDefault('.source.coffee', 'foo.bar.baz')).toBe 10
|
||||
expect(Grim.deprecate).toHaveBeenCalled()
|
||||
|
||||
it "returns the scoped settings not including the user's config file", ->
|
||||
atom.config.setDefaults("foo", bar: baz: 10)
|
||||
atom.config.set("foo.bar.baz", 42, scopeSelector: ".source.coffee", source: "some-source")
|
||||
|
||||
spyOn(Grim, 'deprecate')
|
||||
expect(atom.config.getDefault('.source.coffee', 'foo.bar.baz')).toBe 42
|
||||
expect(Grim.deprecate.callCount).toBe 1
|
||||
|
||||
atom.config.set('foo.bar.baz', 55, scopeSelector: '.source.coffee')
|
||||
expect(atom.config.getDefault('.source.coffee', 'foo.bar.baz')).toBe 42
|
||||
expect(Grim.deprecate.callCount).toBe 2
|
||||
|
||||
describe ".isDefault(keyPath)", ->
|
||||
it "returns true when the value of the key path is its default value", ->
|
||||
atom.config.setDefaults("foo", same: 1, changes: 1)
|
||||
|
||||
spyOn(Grim, 'deprecate')
|
||||
expect(atom.config.isDefault('foo.same')).toBe true
|
||||
expect(atom.config.isDefault('foo.changes')).toBe true
|
||||
expect(Grim.deprecate.callCount).toBe 2
|
||||
|
||||
atom.config.set('foo.same', 2)
|
||||
atom.config.set('foo.changes', 3)
|
||||
|
||||
expect(atom.config.isDefault('foo.same')).toBe false
|
||||
expect(atom.config.isDefault('foo.changes')).toBe false
|
||||
expect(Grim.deprecate.callCount).toBe 4
|
||||
|
||||
describe "when scoped settings are used", ->
|
||||
it "returns false when a scoped setting was set by the user", ->
|
||||
spyOn(Grim, 'deprecate')
|
||||
expect(atom.config.isDefault('.source.coffee', 'foo.bar.baz')).toBe true
|
||||
expect(Grim.deprecate.callCount).toBe 1
|
||||
|
||||
atom.config.set("foo.bar.baz", 42, scopeSelector: ".source.coffee", source: "something-else")
|
||||
expect(atom.config.isDefault('.source.coffee', 'foo.bar.baz')).toBe true
|
||||
expect(Grim.deprecate.callCount).toBe 2
|
||||
|
||||
atom.config.set('foo.bar.baz', 55, scopeSelector: '.source.coffee')
|
||||
expect(atom.config.isDefault('.source.coffee', 'foo.bar.baz')).toBe false
|
||||
expect(Grim.deprecate.callCount).toBe 3
|
||||
|
||||
describe ".toggle(keyPath)", ->
|
||||
beforeEach ->
|
||||
jasmine.snapshotDeprecations()
|
||||
|
||||
afterEach ->
|
||||
jasmine.restoreDeprecationsSnapshot()
|
||||
|
||||
it "negates the boolean value of the current key path value", ->
|
||||
atom.config.set('foo.a', 1)
|
||||
atom.config.toggle('foo.a')
|
||||
expect(atom.config.get('foo.a')).toBe false
|
||||
|
||||
atom.config.set('foo.a', '')
|
||||
atom.config.toggle('foo.a')
|
||||
expect(atom.config.get('foo.a')).toBe true
|
||||
|
||||
atom.config.set('foo.a', null)
|
||||
atom.config.toggle('foo.a')
|
||||
expect(atom.config.get('foo.a')).toBe true
|
||||
|
||||
atom.config.set('foo.a', true)
|
||||
atom.config.toggle('foo.a')
|
||||
expect(atom.config.get('foo.a')).toBe false
|
||||
|
||||
describe ".getSettings()", ->
|
||||
it "returns all settings including defaults", ->
|
||||
atom.config.setDefaults("foo", bar: baz: 10)
|
||||
atom.config.set("foo.ok", 12)
|
||||
|
||||
jasmine.snapshotDeprecations()
|
||||
expect(atom.config.getSettings().foo).toEqual
|
||||
ok: 12
|
||||
bar:
|
||||
baz: 10
|
||||
jasmine.restoreDeprecationsSnapshot()
|
||||
|
||||
describe ".getPositiveInt(keyPath, defaultValue)", ->
|
||||
beforeEach ->
|
||||
jasmine.snapshotDeprecations()
|
||||
|
||||
afterEach ->
|
||||
jasmine.restoreDeprecationsSnapshot()
|
||||
|
||||
it "returns the proper coerced value", ->
|
||||
atom.config.set('editor.preferredLineLength', 0)
|
||||
expect(atom.config.getPositiveInt('editor.preferredLineLength', 80)).toBe 1
|
||||
|
||||
it "returns the proper coerced value", ->
|
||||
atom.config.set('editor.preferredLineLength', -1234)
|
||||
expect(atom.config.getPositiveInt('editor.preferredLineLength', 80)).toBe 1
|
||||
|
||||
it "returns the default value when a string is passed in", ->
|
||||
atom.config.set('editor.preferredLineLength', 'abcd')
|
||||
expect(atom.config.getPositiveInt('editor.preferredLineLength', 80)).toBe 80
|
||||
|
||||
it "returns the default value when null is passed in", ->
|
||||
atom.config.set('editor.preferredLineLength', null)
|
||||
expect(atom.config.getPositiveInt('editor.preferredLineLength', 80)).toBe 80
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
{$$} = require '../src/space-pen-extensions'
|
||||
|
||||
ContextMenuManager = require '../src/context-menu-manager'
|
||||
|
||||
describe "ContextMenuManager", ->
|
||||
@@ -158,32 +156,3 @@ describe "ContextMenuManager", ->
|
||||
catch error
|
||||
addError = error
|
||||
expect(addError.message).toContain('<>')
|
||||
|
||||
describe "when the menus are specified in a legacy format", ->
|
||||
beforeEach ->
|
||||
jasmine.snapshotDeprecations()
|
||||
|
||||
afterEach ->
|
||||
jasmine.restoreDeprecationsSnapshot()
|
||||
|
||||
it "allows items to be specified in the legacy format for now", ->
|
||||
contextMenu.add '.parent':
|
||||
'A': 'a'
|
||||
'Separator 1': '-'
|
||||
'B':
|
||||
'C': 'c'
|
||||
'Separator 2': '-'
|
||||
'D': 'd'
|
||||
|
||||
expect(contextMenu.templateForElement(parent)).toEqual [
|
||||
{label: 'A', command: 'a'}
|
||||
{type: 'separator'}
|
||||
{
|
||||
label: 'B'
|
||||
submenu: [
|
||||
{label: 'C', command: 'c'}
|
||||
{type: 'separator'}
|
||||
{label: 'D', command: 'd'}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
@@ -47,14 +47,6 @@ describe "DisplayBuffer", ->
|
||||
buffer.insert([0, 0], oneHundredLines)
|
||||
expect(displayBuffer.getLineCount()).toBe 100 + originalLineCount
|
||||
|
||||
it "reassigns the scrollTop if it exceeds the max possible value after lines are removed", ->
|
||||
displayBuffer.setHeight(50)
|
||||
displayBuffer.setLineHeightInPixels(10)
|
||||
displayBuffer.setScrollTop(80)
|
||||
|
||||
buffer.delete([[8, 0], [10, 0]])
|
||||
expect(displayBuffer.getScrollTop()).toBe 60
|
||||
|
||||
it "updates the display buffer prior to invoking change handlers registered on the buffer", ->
|
||||
buffer.onDidChange -> expect(displayBuffer2.tokenizedLineForScreenRow(0).text).toBe "testing"
|
||||
displayBuffer2 = new DisplayBuffer({buffer, tabLength})
|
||||
@@ -240,7 +232,8 @@ describe "DisplayBuffer", ->
|
||||
describe "when a newline is inserted, deleted, and re-inserted at the end of a wrapped line (regression)", ->
|
||||
it "correctly renders the original wrapped line", ->
|
||||
buffer = atom.project.buildBufferSync(null, '')
|
||||
displayBuffer = new DisplayBuffer({buffer, tabLength, editorWidthInChars: 30, softWrapped: true})
|
||||
displayBuffer = new DisplayBuffer({buffer, tabLength, editorWidthInChars: 30})
|
||||
displayBuffer.setSoftWrapped(true)
|
||||
|
||||
buffer.insert([0, 0], "the quick brown fox jumps over the lazy dog.")
|
||||
buffer.insert([0, Infinity], '\n')
|
||||
@@ -281,6 +274,9 @@ describe "DisplayBuffer", ->
|
||||
|
||||
describe ".setEditorWidthInChars(length)", ->
|
||||
it "changes the length at which lines are wrapped and emits a change event for all screen lines", ->
|
||||
tokensText = (tokens) ->
|
||||
_.pluck(tokens, 'value').join('')
|
||||
|
||||
displayBuffer.setEditorWidthInChars(40)
|
||||
expect(tokensText displayBuffer.tokenizedLineForScreenRow(4).tokens).toBe ' left = [], right = [];'
|
||||
expect(tokensText displayBuffer.tokenizedLineForScreenRow(5).tokens).toBe ' while(items.length > 0) {'
|
||||
@@ -293,18 +289,6 @@ describe "DisplayBuffer", ->
|
||||
displayBuffer.setEditorWidthInChars(-1)
|
||||
expect(displayBuffer.editorWidthInChars).not.toBe -1
|
||||
|
||||
it "sets ::scrollLeft to 0 and keeps it there when soft wrapping is enabled", ->
|
||||
displayBuffer.setDefaultCharWidth(10)
|
||||
displayBuffer.setWidth(85)
|
||||
|
||||
displayBuffer.setSoftWrapped(false)
|
||||
displayBuffer.setScrollLeft(Infinity)
|
||||
expect(displayBuffer.getScrollLeft()).toBeGreaterThan 0
|
||||
displayBuffer.setSoftWrapped(true)
|
||||
expect(displayBuffer.getScrollLeft()).toBe 0
|
||||
displayBuffer.setScrollLeft(10)
|
||||
expect(displayBuffer.getScrollLeft()).toBe 0
|
||||
|
||||
describe "primitive folding", ->
|
||||
beforeEach ->
|
||||
displayBuffer.destroy()
|
||||
@@ -734,21 +718,6 @@ describe "DisplayBuffer", ->
|
||||
expect(displayBuffer.clipScreenPosition([0, 1], clip: 'forward')).toEqual [0, tabLength]
|
||||
expect(displayBuffer.clipScreenPosition([0, tabLength], clip: 'forward')).toEqual [0, tabLength]
|
||||
|
||||
describe "::screenPositionForPixelPosition(pixelPosition)", ->
|
||||
it "clips pixel positions above buffer start", ->
|
||||
displayBuffer.setLineHeightInPixels(20)
|
||||
expect(displayBuffer.screenPositionForPixelPosition(top: -Infinity, left: -Infinity)).toEqual [0, 0]
|
||||
expect(displayBuffer.screenPositionForPixelPosition(top: -Infinity, left: Infinity)).toEqual [0, 0]
|
||||
expect(displayBuffer.screenPositionForPixelPosition(top: -1, left: Infinity)).toEqual [0, 0]
|
||||
expect(displayBuffer.screenPositionForPixelPosition(top: 0, left: Infinity)).toEqual [0, 29]
|
||||
|
||||
it "clips pixel positions below buffer end", ->
|
||||
displayBuffer.setLineHeightInPixels(20)
|
||||
expect(displayBuffer.screenPositionForPixelPosition(top: Infinity, left: -Infinity)).toEqual [12, 2]
|
||||
expect(displayBuffer.screenPositionForPixelPosition(top: Infinity, left: Infinity)).toEqual [12, 2]
|
||||
expect(displayBuffer.screenPositionForPixelPosition(top: displayBuffer.getHeight() + 1, left: 0)).toEqual [12, 2]
|
||||
expect(displayBuffer.screenPositionForPixelPosition(top: displayBuffer.getHeight() - 1, left: 0)).toEqual [12, 0]
|
||||
|
||||
describe "::screenPositionForBufferPosition(bufferPosition, options)", ->
|
||||
it "clips the specified buffer position", ->
|
||||
expect(displayBuffer.screenPositionForBufferPosition([0, 2])).toEqual [0, 2]
|
||||
@@ -1182,20 +1151,6 @@ describe "DisplayBuffer", ->
|
||||
expect(marker1.getProperties()).toEqual a: 1, b: 2
|
||||
expect(marker2.getProperties()).toEqual a: 1, b: 3
|
||||
|
||||
describe "Marker::getPixelRange()", ->
|
||||
it "returns the start and end positions of the marker based on the line height and character widths assigned to the DisplayBuffer", ->
|
||||
marker = displayBuffer.markScreenRange([[5, 10], [6, 4]])
|
||||
|
||||
displayBuffer.setLineHeightInPixels(20)
|
||||
displayBuffer.setDefaultCharWidth(10)
|
||||
|
||||
for char in ['r', 'e', 't', 'u', 'r', 'n']
|
||||
displayBuffer.setScopedCharWidth(["source.js", "keyword.control.js"], char, 11)
|
||||
|
||||
{start, end} = marker.getPixelRange()
|
||||
expect(start.top).toBe 5 * 20
|
||||
expect(start.left).toBe (4 * 10) + (6 * 11)
|
||||
|
||||
describe 'when there are multiple DisplayBuffers for a buffer', ->
|
||||
describe 'when a marker is created', ->
|
||||
it 'the second display buffer will not emit a marker-created event when the marker has been deleted in the first marker-created event', ->
|
||||
@@ -1250,157 +1205,18 @@ describe "DisplayBuffer", ->
|
||||
expect(displayBuffer.getDecorations(class: 'two').length).toEqual 0
|
||||
expect(displayBuffer.getDecorations(class: 'one').length).toEqual 1
|
||||
|
||||
describe "::setScrollTop", ->
|
||||
beforeEach ->
|
||||
displayBuffer.setLineHeightInPixels(10)
|
||||
|
||||
it "disallows negative values", ->
|
||||
displayBuffer.setHeight(displayBuffer.getScrollHeight() + 100)
|
||||
expect(displayBuffer.setScrollTop(-10)).toBe 0
|
||||
expect(displayBuffer.getScrollTop()).toBe 0
|
||||
|
||||
it "disallows values that would make ::getScrollBottom() exceed ::getScrollHeight()", ->
|
||||
displayBuffer.setHeight(50)
|
||||
maxScrollTop = displayBuffer.getScrollHeight() - displayBuffer.getHeight()
|
||||
|
||||
expect(displayBuffer.setScrollTop(maxScrollTop)).toBe maxScrollTop
|
||||
expect(displayBuffer.getScrollTop()).toBe maxScrollTop
|
||||
|
||||
expect(displayBuffer.setScrollTop(maxScrollTop + 50)).toBe maxScrollTop
|
||||
expect(displayBuffer.getScrollTop()).toBe maxScrollTop
|
||||
|
||||
describe "editor.scrollPastEnd", ->
|
||||
describe "when editor.scrollPastEnd is false", ->
|
||||
beforeEach ->
|
||||
atom.config.set("editor.scrollPastEnd", false)
|
||||
displayBuffer.setLineHeightInPixels(10)
|
||||
|
||||
it "does not add the height of the view to the scroll height", ->
|
||||
lineHeight = displayBuffer.getLineHeightInPixels()
|
||||
originalScrollHeight = displayBuffer.getScrollHeight()
|
||||
displayBuffer.setHeight(50)
|
||||
|
||||
expect(displayBuffer.getScrollHeight()).toBe originalScrollHeight
|
||||
|
||||
describe "when editor.scrollPastEnd is true", ->
|
||||
beforeEach ->
|
||||
atom.config.set("editor.scrollPastEnd", true)
|
||||
displayBuffer.setLineHeightInPixels(10)
|
||||
|
||||
it "adds the height of the view to the scroll height", ->
|
||||
lineHeight = displayBuffer.getLineHeightInPixels()
|
||||
originalScrollHeight = displayBuffer.getScrollHeight()
|
||||
displayBuffer.setHeight(50)
|
||||
|
||||
expect(displayBuffer.getScrollHeight()).toEqual(originalScrollHeight + displayBuffer.height - (lineHeight * 3))
|
||||
|
||||
describe "::setScrollLeft", ->
|
||||
beforeEach ->
|
||||
displayBuffer.setLineHeightInPixels(10)
|
||||
displayBuffer.setDefaultCharWidth(10)
|
||||
|
||||
it "disallows negative values", ->
|
||||
displayBuffer.setWidth(displayBuffer.getScrollWidth() + 100)
|
||||
expect(displayBuffer.setScrollLeft(-10)).toBe 0
|
||||
expect(displayBuffer.getScrollLeft()).toBe 0
|
||||
|
||||
it "disallows values that would make ::getScrollRight() exceed ::getScrollWidth()", ->
|
||||
displayBuffer.setWidth(50)
|
||||
maxScrollLeft = displayBuffer.getScrollWidth() - displayBuffer.getWidth()
|
||||
|
||||
expect(displayBuffer.setScrollLeft(maxScrollLeft)).toBe maxScrollLeft
|
||||
expect(displayBuffer.getScrollLeft()).toBe maxScrollLeft
|
||||
|
||||
expect(displayBuffer.setScrollLeft(maxScrollLeft + 50)).toBe maxScrollLeft
|
||||
expect(displayBuffer.getScrollLeft()).toBe maxScrollLeft
|
||||
|
||||
describe "::scrollToScreenPosition(position, [options])", ->
|
||||
beforeEach ->
|
||||
displayBuffer.setLineHeightInPixels(10)
|
||||
displayBuffer.setDefaultCharWidth(10)
|
||||
displayBuffer.setHorizontalScrollbarHeight(0)
|
||||
displayBuffer.setHeight(50)
|
||||
displayBuffer.setWidth(150)
|
||||
|
||||
it "sets the scroll top and scroll left so the given screen position is in view", ->
|
||||
displayBuffer.scrollToScreenPosition([8, 20])
|
||||
expect(displayBuffer.getScrollBottom()).toBe (9 + displayBuffer.getVerticalScrollMargin()) * 10
|
||||
expect(displayBuffer.getScrollRight()).toBe (20 + displayBuffer.getHorizontalScrollMargin()) * 10
|
||||
it "triggers ::onDidRequestAutoscroll with the logical coordinates along with the options", ->
|
||||
scrollSpy = jasmine.createSpy("::onDidRequestAutoscroll")
|
||||
displayBuffer.onDidRequestAutoscroll(scrollSpy)
|
||||
|
||||
displayBuffer.scrollToScreenPosition([8, 20])
|
||||
expect(displayBuffer.getScrollBottom()).toBe (9 + displayBuffer.getVerticalScrollMargin()) * 10
|
||||
expect(displayBuffer.getScrollRight()).toBe (20 + displayBuffer.getHorizontalScrollMargin()) * 10
|
||||
displayBuffer.scrollToScreenPosition([8, 20], center: true)
|
||||
displayBuffer.scrollToScreenPosition([8, 20], center: false, reversed: true)
|
||||
|
||||
describe "when the 'center' option is true", ->
|
||||
it "vertically scrolls to center the given position vertically", ->
|
||||
displayBuffer.scrollToScreenPosition([8, 20], center: true)
|
||||
expect(displayBuffer.getScrollTop()).toBe (8 * 10) + 5 - (50 / 2)
|
||||
expect(displayBuffer.getScrollRight()).toBe (20 + displayBuffer.getHorizontalScrollMargin()) * 10
|
||||
|
||||
it "does not scroll vertically if the position is already in view", ->
|
||||
displayBuffer.scrollToScreenPosition([4, 20], center: true)
|
||||
expect(displayBuffer.getScrollTop()).toBe 0
|
||||
|
||||
describe "scroll width", ->
|
||||
cursorWidth = 1
|
||||
beforeEach ->
|
||||
displayBuffer.setDefaultCharWidth(10)
|
||||
|
||||
it "recomputes the scroll width when the default character width changes", ->
|
||||
expect(displayBuffer.getScrollWidth()).toBe 10 * 65 + cursorWidth
|
||||
|
||||
displayBuffer.setDefaultCharWidth(12)
|
||||
expect(displayBuffer.getScrollWidth()).toBe 12 * 65 + cursorWidth
|
||||
|
||||
it "recomputes the scroll width when the max line length changes", ->
|
||||
buffer.insert([6, 12], ' ')
|
||||
expect(displayBuffer.getScrollWidth()).toBe 10 * 66 + cursorWidth
|
||||
|
||||
buffer.delete([[6, 10], [6, 12]], ' ')
|
||||
expect(displayBuffer.getScrollWidth()).toBe 10 * 64 + cursorWidth
|
||||
|
||||
it "recomputes the scroll width when the scoped character widths change", ->
|
||||
operatorWidth = 20
|
||||
displayBuffer.setScopedCharWidth(['source.js', 'keyword.operator.js'], '<', operatorWidth)
|
||||
expect(displayBuffer.getScrollWidth()).toBe 10 * 64 + operatorWidth + cursorWidth
|
||||
|
||||
it "recomputes the scroll width when the scoped character widths change in a batch", ->
|
||||
operatorWidth = 20
|
||||
|
||||
displayBuffer.onDidChangeCharacterWidths changedSpy = jasmine.createSpy()
|
||||
|
||||
displayBuffer.batchCharacterMeasurement ->
|
||||
displayBuffer.setScopedCharWidth(['source.js', 'keyword.operator.js'], '<', operatorWidth)
|
||||
displayBuffer.setScopedCharWidth(['source.js', 'keyword.operator.js'], '?', operatorWidth)
|
||||
|
||||
expect(displayBuffer.getScrollWidth()).toBe 10 * 63 + operatorWidth * 2 + cursorWidth
|
||||
expect(changedSpy.callCount).toBe 1
|
||||
|
||||
describe "::getVisibleRowRange()", ->
|
||||
beforeEach ->
|
||||
displayBuffer.setLineHeightInPixels(10)
|
||||
displayBuffer.setHeight(100)
|
||||
|
||||
it "returns the first and the last visible rows", ->
|
||||
displayBuffer.setScrollTop(0)
|
||||
|
||||
expect(displayBuffer.getVisibleRowRange()).toEqual [0, 9]
|
||||
|
||||
it "includes partially visible rows in the range", ->
|
||||
displayBuffer.setScrollTop(5)
|
||||
|
||||
expect(displayBuffer.getVisibleRowRange()).toEqual [0, 10]
|
||||
|
||||
it "returns an empty range when lineHeight is 0", ->
|
||||
displayBuffer.setLineHeightInPixels(0)
|
||||
|
||||
expect(displayBuffer.getVisibleRowRange()).toEqual [0, 0]
|
||||
|
||||
it "ends at last buffer row even if there's more space available", ->
|
||||
displayBuffer.setHeight(150)
|
||||
displayBuffer.setScrollTop(60)
|
||||
|
||||
expect(displayBuffer.getVisibleRowRange()).toEqual [0, 13]
|
||||
expect(scrollSpy).toHaveBeenCalledWith(screenRange: [[8, 20], [8, 20]], options: {})
|
||||
expect(scrollSpy).toHaveBeenCalledWith(screenRange: [[8, 20], [8, 20]], options: {center: true})
|
||||
expect(scrollSpy).toHaveBeenCalledWith(screenRange: [[8, 20], [8, 20]], options: {center: false, reversed: true})
|
||||
|
||||
describe "::decorateMarker", ->
|
||||
describe "when decorating gutters", ->
|
||||
|
||||
60
spec/dom-element-pool-spec.coffee
Normal file
60
spec/dom-element-pool-spec.coffee
Normal file
@@ -0,0 +1,60 @@
|
||||
DOMElementPool = require '../src/dom-element-pool'
|
||||
{contains} = require 'underscore-plus'
|
||||
|
||||
describe "DOMElementPool", ->
|
||||
domElementPool = null
|
||||
|
||||
beforeEach ->
|
||||
domElementPool = new DOMElementPool
|
||||
|
||||
it "builds DOM nodes, recycling them when they are freed", ->
|
||||
[div, span1, span2, span3, span4, span5, textNode] = elements = [
|
||||
domElementPool.buildElement("div")
|
||||
domElementPool.buildElement("span")
|
||||
domElementPool.buildElement("span")
|
||||
domElementPool.buildElement("span")
|
||||
domElementPool.buildElement("span")
|
||||
domElementPool.buildElement("span")
|
||||
domElementPool.buildText("Hello world!")
|
||||
]
|
||||
|
||||
div.appendChild(span1)
|
||||
span1.appendChild(span2)
|
||||
div.appendChild(span3)
|
||||
span3.appendChild(span4)
|
||||
span4.appendChild(textNode)
|
||||
|
||||
domElementPool.freeElementAndDescendants(div)
|
||||
domElementPool.freeElementAndDescendants(span5)
|
||||
|
||||
expect(contains(elements, domElementPool.buildElement("div"))).toBe(true)
|
||||
expect(contains(elements, domElementPool.buildElement("span"))).toBe(true)
|
||||
expect(contains(elements, domElementPool.buildElement("span"))).toBe(true)
|
||||
expect(contains(elements, domElementPool.buildElement("span"))).toBe(true)
|
||||
expect(contains(elements, domElementPool.buildElement("span"))).toBe(true)
|
||||
expect(contains(elements, domElementPool.buildElement("span"))).toBe(true)
|
||||
expect(contains(elements, domElementPool.buildText("another text"))).toBe(true)
|
||||
|
||||
expect(contains(elements, domElementPool.buildElement("div"))).toBe(false)
|
||||
expect(contains(elements, domElementPool.buildElement("span"))).toBe(false)
|
||||
expect(contains(elements, domElementPool.buildText("unexisting"))).toBe(false)
|
||||
|
||||
it "forgets free nodes after being cleared", ->
|
||||
span = domElementPool.buildElement("span")
|
||||
div = domElementPool.buildElement("div")
|
||||
domElementPool.freeElementAndDescendants(span)
|
||||
domElementPool.freeElementAndDescendants(div)
|
||||
|
||||
domElementPool.clear()
|
||||
|
||||
expect(domElementPool.buildElement("span")).not.toBe(span)
|
||||
expect(domElementPool.buildElement("div")).not.toBe(div)
|
||||
|
||||
it "throws an error when trying to free the same node twice", ->
|
||||
div = domElementPool.buildElement("div")
|
||||
domElementPool.freeElementAndDescendants(div)
|
||||
expect(-> domElementPool.freeElementAndDescendants(div)).toThrow()
|
||||
|
||||
it "throws an error when trying to free an invalid element", ->
|
||||
expect(-> domElementPool.freeElementAndDescendants(null)).toThrow()
|
||||
expect(-> domElementPool.freeElementAndDescendants(undefined)).toThrow()
|
||||
73
spec/fake-lines-yardstick.coffee
Normal file
73
spec/fake-lines-yardstick.coffee
Normal file
@@ -0,0 +1,73 @@
|
||||
{Point} = require 'text-buffer'
|
||||
|
||||
module.exports =
|
||||
class FakeLinesYardstick
|
||||
constructor: (@model, @presenter) ->
|
||||
@characterWidthsByScope = {}
|
||||
|
||||
prepareScreenRowsForMeasurement: ->
|
||||
@presenter.getPreMeasurementState()
|
||||
|
||||
getScopedCharacterWidth: (scopeNames, char) ->
|
||||
@getScopedCharacterWidths(scopeNames)[char]
|
||||
|
||||
getScopedCharacterWidths: (scopeNames) ->
|
||||
scope = @characterWidthsByScope
|
||||
for scopeName in scopeNames
|
||||
scope[scopeName] ?= {}
|
||||
scope = scope[scopeName]
|
||||
scope.characterWidths ?= {}
|
||||
scope.characterWidths
|
||||
|
||||
setScopedCharacterWidth: (scopeNames, character, width) ->
|
||||
@getScopedCharacterWidths(scopeNames)[character] = width
|
||||
|
||||
pixelPositionForScreenPosition: (screenPosition, clip=true) ->
|
||||
screenPosition = Point.fromObject(screenPosition)
|
||||
screenPosition = @model.clipScreenPosition(screenPosition) if clip
|
||||
|
||||
targetRow = screenPosition.row
|
||||
targetColumn = screenPosition.column
|
||||
baseCharacterWidth = @model.getDefaultCharWidth()
|
||||
|
||||
top = targetRow * @model.getLineHeightInPixels()
|
||||
left = 0
|
||||
column = 0
|
||||
|
||||
iterator = @model.tokenizedLineForScreenRow(targetRow).getTokenIterator()
|
||||
while iterator.next()
|
||||
characterWidths = @getScopedCharacterWidths(iterator.getScopes())
|
||||
|
||||
valueIndex = 0
|
||||
text = iterator.getText()
|
||||
while valueIndex < text.length
|
||||
if iterator.isPairedCharacter()
|
||||
char = text
|
||||
charLength = 2
|
||||
valueIndex += 2
|
||||
else
|
||||
char = text[valueIndex]
|
||||
charLength = 1
|
||||
valueIndex++
|
||||
|
||||
break if column is targetColumn
|
||||
|
||||
left += characterWidths[char] ? baseCharacterWidth unless char is '\0'
|
||||
column += charLength
|
||||
|
||||
{top, left}
|
||||
|
||||
pixelRectForScreenRange: (screenRange) ->
|
||||
lineHeight = @model.getLineHeightInPixels()
|
||||
|
||||
if screenRange.end.row > screenRange.start.row
|
||||
top = @pixelPositionForScreenPosition(screenRange.start).top
|
||||
left = 0
|
||||
height = (screenRange.end.row - screenRange.start.row + 1) * lineHeight
|
||||
width = @presenter.getScrollWidth()
|
||||
else
|
||||
{top, left} = @pixelPositionForScreenPosition(screenRange.start, false)
|
||||
height = lineHeight
|
||||
width = @pixelPositionForScreenPosition(screenRange.end, false).left - left
|
||||
|
||||
{top, left, width, height}
|
||||
0
spec/fixtures/packages/package-with-cached-incompatible-native-module/main.js
vendored
Normal file
0
spec/fixtures/packages/package-with-cached-incompatible-native-module/main.js
vendored
Normal file
12
spec/fixtures/packages/package-with-cached-incompatible-native-module/package.json
vendored
Normal file
12
spec/fixtures/packages/package-with-cached-incompatible-native-module/package.json
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "package-with-cached-incompatible-native-module",
|
||||
"version": "1.0.0",
|
||||
"main": "./main.js",
|
||||
"_atomModuleCache": {
|
||||
"extensions": {
|
||||
".node": [
|
||||
"node_modules/native-module/build/Release/native.node"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
0
spec/fixtures/packages/package-with-ignored-incompatible-native-module/main.js
vendored
Normal file
0
spec/fixtures/packages/package-with-ignored-incompatible-native-module/main.js
vendored
Normal file
0
spec/fixtures/packages/package-with-ignored-incompatible-native-module/node_modules/compatible-native-module/main.js
generated
vendored
Normal file
0
spec/fixtures/packages/package-with-ignored-incompatible-native-module/node_modules/compatible-native-module/main.js
generated
vendored
Normal file
4
spec/fixtures/packages/package-with-ignored-incompatible-native-module/node_modules/compatible-native-module/package.json
generated
vendored
Normal file
4
spec/fixtures/packages/package-with-ignored-incompatible-native-module/node_modules/compatible-native-module/package.json
generated
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"name": "compatible-native-module",
|
||||
"main": "./main.js"
|
||||
}
|
||||
0
spec/fixtures/packages/package-with-ignored-incompatible-native-module/node_modules/native-module/build/Release/native.node
generated
vendored
Normal file
0
spec/fixtures/packages/package-with-ignored-incompatible-native-module/node_modules/native-module/build/Release/native.node
generated
vendored
Normal file
1
spec/fixtures/packages/package-with-ignored-incompatible-native-module/node_modules/native-module/main.js
generated
vendored
Normal file
1
spec/fixtures/packages/package-with-ignored-incompatible-native-module/node_modules/native-module/main.js
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
throw new Error("this simulates a native module's failure to load")
|
||||
4
spec/fixtures/packages/package-with-ignored-incompatible-native-module/node_modules/native-module/package.json
generated
vendored
Normal file
4
spec/fixtures/packages/package-with-ignored-incompatible-native-module/node_modules/native-module/package.json
generated
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"name": "native-module",
|
||||
"main": "./main.js"
|
||||
}
|
||||
12
spec/fixtures/packages/package-with-ignored-incompatible-native-module/package.json
vendored
Normal file
12
spec/fixtures/packages/package-with-ignored-incompatible-native-module/package.json
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "package-with-ignored-incompatible-native-module",
|
||||
"version": "1.0.0",
|
||||
"main": "./main.js",
|
||||
"_atomModuleCache": {
|
||||
"extensions": {
|
||||
".node": [
|
||||
"node_modules/compatible-native-module/build/Release/native.node"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
{Git} = require 'atom'
|
||||
{deprecate} = require 'grim'
|
||||
|
||||
deprecate('Fake task deprecation')
|
||||
module.exports = ->
|
||||
|
||||
@@ -11,7 +11,7 @@ describe "GitRepositoryProvider", ->
|
||||
it "returns a Promise that resolves to a GitRepository", ->
|
||||
waitsForPromise ->
|
||||
provider = new GitRepositoryProvider atom.project
|
||||
directory = new Directory path.join(__dirname, 'fixtures/git/master.git')
|
||||
directory = new Directory path.join(__dirname, 'fixtures', 'git', 'master.git')
|
||||
provider.repositoryForDirectory(directory).then (result) ->
|
||||
expect(result).toBeInstanceOf GitRepository
|
||||
expect(provider.pathToRepository[result.getPath()]).toBeTruthy()
|
||||
@@ -24,11 +24,11 @@ describe "GitRepositoryProvider", ->
|
||||
secondRepo = null
|
||||
|
||||
waitsForPromise ->
|
||||
directory = new Directory path.join(__dirname, 'fixtures/git/master.git')
|
||||
directory = new Directory path.join(__dirname, 'fixtures', 'git', 'master.git')
|
||||
provider.repositoryForDirectory(directory).then (result) -> firstRepo = result
|
||||
|
||||
waitsForPromise ->
|
||||
directory = new Directory path.join(__dirname, 'fixtures/git/master.git/objects')
|
||||
directory = new Directory path.join(__dirname, 'fixtures', 'git', 'master.git', 'objects')
|
||||
provider.repositoryForDirectory(directory).then (result) -> secondRepo = result
|
||||
|
||||
runs ->
|
||||
@@ -56,6 +56,21 @@ describe "GitRepositoryProvider", ->
|
||||
provider.repositoryForDirectory(directory).then (result) ->
|
||||
expect(result).toBe null
|
||||
|
||||
describe "when specified a Directory with a valid gitfile-linked repository", ->
|
||||
it "returns a Promise that resolves to a GitRepository", ->
|
||||
waitsForPromise ->
|
||||
provider = new GitRepositoryProvider atom.project
|
||||
gitDirPath = path.join(__dirname, 'fixtures', 'git', 'master.git')
|
||||
workDirPath = temp.mkdirSync('git-workdir')
|
||||
fs.writeFileSync(path.join(workDirPath, '.git'), 'gitdir: ' + gitDirPath+'\n')
|
||||
|
||||
directory = new Directory workDirPath
|
||||
provider.repositoryForDirectory(directory).then (result) ->
|
||||
expect(result).toBeInstanceOf GitRepository
|
||||
expect(provider.pathToRepository[result.getPath()]).toBeTruthy()
|
||||
expect(result.statusTask).toBeTruthy()
|
||||
expect(result.getType()).toBe 'git'
|
||||
|
||||
describe "when specified a Directory without existsSync()", ->
|
||||
directory = null
|
||||
provider = null
|
||||
|
||||
@@ -3,6 +3,7 @@ GitRepository = require '../src/git-repository'
|
||||
fs = require 'fs-plus'
|
||||
path = require 'path'
|
||||
Task = require '../src/task'
|
||||
Project = require '../src/project'
|
||||
|
||||
copyRepository = ->
|
||||
workingDirPath = temp.mkdirSync('atom-working-dir')
|
||||
@@ -276,7 +277,7 @@ describe "GitRepository", ->
|
||||
atom.workspace.open('file.txt')
|
||||
|
||||
runs ->
|
||||
project2 = atom.project.testSerialization()
|
||||
project2 = Project.deserialize(atom.project.serialize())
|
||||
buffer = project2.getBuffers()[0]
|
||||
|
||||
waitsFor ->
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
Gutter = require '../src/gutter'
|
||||
GutterContainerComponent = require '../src/gutter-container-component'
|
||||
DOMElementPool = require '../src/dom-element-pool'
|
||||
|
||||
describe "GutterContainerComponent", ->
|
||||
[gutterContainerComponent] = []
|
||||
@@ -22,9 +23,10 @@ describe "GutterContainerComponent", ->
|
||||
mockTestState
|
||||
|
||||
beforeEach ->
|
||||
domElementPool = new DOMElementPool
|
||||
mockEditor = {}
|
||||
mockMouseDown = ->
|
||||
gutterContainerComponent = new GutterContainerComponent({editor: mockEditor, onMouseDown: mockMouseDown})
|
||||
gutterContainerComponent = new GutterContainerComponent({editor: mockEditor, onMouseDown: mockMouseDown, domElementPool})
|
||||
|
||||
it "creates a DOM node with no child gutter nodes when it is initialized", ->
|
||||
expect(gutterContainerComponent.getDomNode() instanceof HTMLElement).toBe true
|
||||
|
||||
@@ -43,6 +43,5 @@ for arg in "$@"; do
|
||||
done
|
||||
|
||||
echo "Launching Atom" >&2
|
||||
echo ${atom_path} ${atom_args[@]} ${atom_switches[@]} >&2
|
||||
|
||||
exec ${atom_path} ${atom_args[@]} ${atom_switches[@]}
|
||||
echo "${atom_path}" ${atom_args[@]} ${atom_switches[@]} >&2
|
||||
exec "${atom_path}" ${atom_args[@]} ${atom_switches[@]}
|
||||
|
||||
@@ -9,7 +9,7 @@ webdriverio = require "../../../build/node_modules/webdriverio"
|
||||
|
||||
AtomPath = remote.process.argv[0]
|
||||
AtomLauncherPath = path.join(__dirname, "..", "helpers", "atom-launcher.sh")
|
||||
ChromedriverPath = path.resolve(__dirname, '..', '..', '..', 'atom-shell', 'chromedriver', 'chromedriver')
|
||||
ChromedriverPath = path.resolve(__dirname, '..', '..', '..', 'electron', 'chromedriver', 'chromedriver')
|
||||
SocketPath = path.join(temp.mkdirSync("socket-dir"), "atom-#{process.env.USER}.sock")
|
||||
ChromedriverPort = 9515
|
||||
ChromedriverURLBase = "/wd/hub"
|
||||
|
||||
@@ -6,20 +6,21 @@ return unless process.env.ATOM_INTEGRATION_TESTS_ENABLED
|
||||
# run them on Travis.
|
||||
return if process.env.TRAVIS
|
||||
|
||||
fs = require "fs"
|
||||
fs = require "fs-plus"
|
||||
path = require "path"
|
||||
temp = require("temp").track()
|
||||
runAtom = require "./helpers/start-atom"
|
||||
CSON = require "season"
|
||||
|
||||
describe "Starting Atom", ->
|
||||
[tempDirPath, otherTempDirPath, atomHome] = []
|
||||
atomHome = temp.mkdirSync('atom-home')
|
||||
[tempDirPath, otherTempDirPath] = []
|
||||
|
||||
beforeEach ->
|
||||
jasmine.useRealClock()
|
||||
|
||||
atomHome = temp.mkdirSync('atom-home')
|
||||
fs.writeFileSync(path.join(atomHome, 'config.cson'), fs.readFileSync(path.join(__dirname, 'fixtures', 'atom-home', 'config.cson')))
|
||||
fs.removeSync(path.join(atomHome, 'storage'))
|
||||
|
||||
tempDirPath = temp.mkdirSync("empty-dir")
|
||||
otherTempDirPath = temp.mkdirSync("another-temp-dir")
|
||||
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
fs = require 'fs'
|
||||
|
||||
module.exports.runSpecSuite = (specSuite, logFile, logErrors=true) ->
|
||||
{$, $$} = require '../src/space-pen-extensions'
|
||||
|
||||
window[key] = value for key, value of require '../vendor/jasmine'
|
||||
|
||||
{TerminalReporter} = require 'jasmine-tagged'
|
||||
@@ -25,7 +23,7 @@ module.exports.runSpecSuite = (specSuite, logFile, logErrors=true) ->
|
||||
log(str)
|
||||
onComplete: (runner) ->
|
||||
fs.closeSync(logStream) if logStream?
|
||||
if process.env.JANKY_SHA1
|
||||
if process.env.JANKY_SHA1 or process.env.CI
|
||||
grim = require 'grim'
|
||||
|
||||
if grim.getDeprecationsLength() > 0
|
||||
@@ -47,7 +45,9 @@ module.exports.runSpecSuite = (specSuite, logFile, logErrors=true) ->
|
||||
jasmineEnv.addReporter(timeReporter)
|
||||
jasmineEnv.setIncludedTags([process.platform])
|
||||
|
||||
$('body').append $$ -> @div id: 'jasmine-content'
|
||||
jasmineContent = document.createElement('div')
|
||||
jasmineContent.setAttribute('id', 'jasmine-content')
|
||||
document.body.appendChild(jasmineContent)
|
||||
|
||||
jasmineEnv.execute()
|
||||
|
||||
|
||||
184
spec/lines-yardstick-spec.coffee
Normal file
184
spec/lines-yardstick-spec.coffee
Normal file
@@ -0,0 +1,184 @@
|
||||
LinesYardstick = require "../src/lines-yardstick"
|
||||
{toArray} = require 'underscore-plus'
|
||||
|
||||
describe "LinesYardstick", ->
|
||||
[editor, mockPresenter, mockLineNodesProvider, createdLineNodes, linesYardstick] = []
|
||||
|
||||
beforeEach ->
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-javascript')
|
||||
|
||||
waitsForPromise ->
|
||||
atom.project.open('sample.js').then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
createdLineNodes = []
|
||||
availableScreenRows = {}
|
||||
screenRowsToMeasure = []
|
||||
|
||||
buildLineNode = (screenRow) ->
|
||||
tokenizedLine = editor.tokenizedLineForScreenRow(screenRow)
|
||||
iterator = tokenizedLine.getTokenIterator()
|
||||
lineNode = document.createElement("div")
|
||||
lineNode.style.whiteSpace = "pre"
|
||||
while iterator.next()
|
||||
span = document.createElement("span")
|
||||
span.className = iterator.getScopes().join(' ').replace(/\.+/g, ' ')
|
||||
span.textContent = iterator.getText()
|
||||
lineNode.appendChild(span)
|
||||
|
||||
jasmine.attachToDOM(lineNode)
|
||||
createdLineNodes.push(lineNode)
|
||||
lineNode
|
||||
|
||||
mockPresenter =
|
||||
setScreenRowsToMeasure: (screenRows) -> screenRowsToMeasure = screenRows
|
||||
clearScreenRowsToMeasure: -> setScreenRowsToMeasure = []
|
||||
getPreMeasurementState: ->
|
||||
state = {}
|
||||
for screenRow in screenRowsToMeasure
|
||||
tokenizedLine = editor.tokenizedLineForScreenRow(screenRow)
|
||||
state[tokenizedLine.id] = screenRow
|
||||
state
|
||||
|
||||
mockLineNodesProvider =
|
||||
updateSync: (state) -> availableScreenRows = state
|
||||
lineNodeForLineIdAndScreenRow: (lineId, screenRow) ->
|
||||
return if availableScreenRows[lineId] isnt screenRow
|
||||
|
||||
buildLineNode(screenRow)
|
||||
textNodesForLineIdAndScreenRow: (lineId, screenRow) ->
|
||||
lineNode = @lineNodeForLineIdAndScreenRow(lineId, screenRow)
|
||||
textNodes = []
|
||||
for span in lineNode.children
|
||||
for textNode in span.childNodes
|
||||
textNodes.push(textNode)
|
||||
textNodes
|
||||
|
||||
editor.setLineHeightInPixels(14)
|
||||
linesYardstick = new LinesYardstick(editor, mockPresenter, mockLineNodesProvider)
|
||||
|
||||
afterEach ->
|
||||
lineNode.remove() for lineNode in createdLineNodes
|
||||
atom.themes.removeStylesheet('test')
|
||||
|
||||
describe "::pixelPositionForScreenPosition(screenPosition)", ->
|
||||
it "converts screen positions to pixel positions", ->
|
||||
atom.styles.addStyleSheet """
|
||||
* {
|
||||
font-size: 12px;
|
||||
font-family: monospace;
|
||||
}
|
||||
.function {
|
||||
font-size: 16px
|
||||
}
|
||||
"""
|
||||
|
||||
expect(linesYardstick.pixelPositionForScreenPosition([0, 0])).toEqual({left: 0, top: 0})
|
||||
expect(linesYardstick.pixelPositionForScreenPosition([0, 1])).toEqual({left: 7, top: 0})
|
||||
expect(linesYardstick.pixelPositionForScreenPosition([0, 5])).toEqual({left: 37.8046875, top: 0})
|
||||
expect(linesYardstick.pixelPositionForScreenPosition([1, 6])).toEqual({left: 43.20703125, top: 14})
|
||||
expect(linesYardstick.pixelPositionForScreenPosition([1, 9])).toEqual({left: 72.20703125, top: 14})
|
||||
expect(linesYardstick.pixelPositionForScreenPosition([2, Infinity])).toEqual({left: 288.046875, top: 28})
|
||||
|
||||
it "reuses already computed pixel positions unless it is invalidated", ->
|
||||
atom.styles.addStyleSheet """
|
||||
* {
|
||||
font-size: 16px;
|
||||
font-family: monospace;
|
||||
}
|
||||
"""
|
||||
|
||||
expect(linesYardstick.pixelPositionForScreenPosition([1, 2])).toEqual({left: 19.203125, top: 14})
|
||||
expect(linesYardstick.pixelPositionForScreenPosition([2, 6])).toEqual({left: 57.609375, top: 28})
|
||||
expect(linesYardstick.pixelPositionForScreenPosition([5, 10])).toEqual({left: 95.609375, top: 70})
|
||||
|
||||
atom.styles.addStyleSheet """
|
||||
* {
|
||||
font-size: 20px;
|
||||
}
|
||||
"""
|
||||
|
||||
expect(linesYardstick.pixelPositionForScreenPosition([1, 2])).toEqual({left: 19.203125, top: 14})
|
||||
expect(linesYardstick.pixelPositionForScreenPosition([2, 6])).toEqual({left: 57.609375, top: 28})
|
||||
expect(linesYardstick.pixelPositionForScreenPosition([5, 10])).toEqual({left: 95.609375, top: 70})
|
||||
|
||||
linesYardstick.invalidateCache()
|
||||
|
||||
expect(linesYardstick.pixelPositionForScreenPosition([1, 2])).toEqual({left: 24.00390625, top: 14})
|
||||
expect(linesYardstick.pixelPositionForScreenPosition([2, 6])).toEqual({left: 72.01171875, top: 28})
|
||||
expect(linesYardstick.pixelPositionForScreenPosition([5, 10])).toEqual({left: 120.01171875, top: 70})
|
||||
|
||||
it "correctly handles RTL characters", ->
|
||||
atom.styles.addStyleSheet """
|
||||
* {
|
||||
font-size: 14px;
|
||||
font-family: monospace;
|
||||
}
|
||||
"""
|
||||
|
||||
editor.setText("السلام عليكم")
|
||||
expect(linesYardstick.pixelPositionForScreenPosition([0, 0]).left).toBe 0
|
||||
expect(linesYardstick.pixelPositionForScreenPosition([0, 1]).left).toBe 8
|
||||
expect(linesYardstick.pixelPositionForScreenPosition([0, 2]).left).toBe 16
|
||||
expect(linesYardstick.pixelPositionForScreenPosition([0, 5]).left).toBe 33
|
||||
expect(linesYardstick.pixelPositionForScreenPosition([0, 7]).left).toBe 50
|
||||
expect(linesYardstick.pixelPositionForScreenPosition([0, 9]).left).toBe 67
|
||||
expect(linesYardstick.pixelPositionForScreenPosition([0, 11]).left).toBe 84
|
||||
|
||||
it "doesn't measure invisible lines if it is explicitly told so", ->
|
||||
atom.styles.addStyleSheet """
|
||||
* {
|
||||
font-size: 12px;
|
||||
font-family: monospace;
|
||||
}
|
||||
"""
|
||||
|
||||
expect(linesYardstick.pixelPositionForScreenPosition([0, 0], true, true)).toEqual({left: 0, top: 0})
|
||||
expect(linesYardstick.pixelPositionForScreenPosition([0, 1], true, true)).toEqual({left: 0, top: 0})
|
||||
expect(linesYardstick.pixelPositionForScreenPosition([0, 5], true, true)).toEqual({left: 0, top: 0})
|
||||
|
||||
describe "::screenPositionForPixelPosition(pixelPosition)", ->
|
||||
it "converts pixel positions to screen positions", ->
|
||||
atom.styles.addStyleSheet """
|
||||
* {
|
||||
font-size: 12px;
|
||||
font-family: monospace;
|
||||
}
|
||||
.function {
|
||||
font-size: 16px
|
||||
}
|
||||
"""
|
||||
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 0, left: 12.5})).toEqual([0, 2])
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 14, left: 18.8})).toEqual([1, 3])
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 28, left: 100})).toEqual([2, 14])
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 32, left: 24.3})).toEqual([2, 3])
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 46, left: 66.5})).toEqual([3, 9])
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 80, left: 99.9})).toEqual([5, 14])
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 80, left: 224.4365234375})).toEqual([5, 29])
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 80, left: 225})).toEqual([5, 30])
|
||||
|
||||
it "clips pixel positions above buffer start", ->
|
||||
expect(linesYardstick.screenPositionForPixelPosition(top: -Infinity, left: -Infinity)).toEqual [0, 0]
|
||||
expect(linesYardstick.screenPositionForPixelPosition(top: -Infinity, left: Infinity)).toEqual [0, 0]
|
||||
expect(linesYardstick.screenPositionForPixelPosition(top: -1, left: Infinity)).toEqual [0, 0]
|
||||
expect(linesYardstick.screenPositionForPixelPosition(top: 0, left: Infinity)).toEqual [0, 29]
|
||||
|
||||
it "clips pixel positions below buffer end", ->
|
||||
expect(linesYardstick.screenPositionForPixelPosition(top: Infinity, left: -Infinity)).toEqual [12, 2]
|
||||
expect(linesYardstick.screenPositionForPixelPosition(top: Infinity, left: Infinity)).toEqual [12, 2]
|
||||
expect(linesYardstick.screenPositionForPixelPosition(top: (editor.getLastScreenRow() + 1) * 14, left: 0)).toEqual [12, 2]
|
||||
expect(linesYardstick.screenPositionForPixelPosition(top: editor.getLastScreenRow() * 14, left: 0)).toEqual [12, 0]
|
||||
|
||||
it "doesn't measure invisible lines if it is explicitly told so", ->
|
||||
atom.styles.addStyleSheet """
|
||||
* {
|
||||
font-size: 12px;
|
||||
font-family: monospace;
|
||||
}
|
||||
"""
|
||||
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 0, left: 13}, true)).toEqual([0, 0])
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 14, left: 20}, true)).toEqual([1, 0])
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 28, left: 100}, true)).toEqual([2, 0])
|
||||
@@ -52,7 +52,7 @@ describe "MenuManager", ->
|
||||
it "sends the current menu template and associated key bindings to the browser process", ->
|
||||
spyOn(menu, 'sendToBrowserProcess')
|
||||
menu.add [{label: "A", submenu: [{label: "B", command: "b"}]}]
|
||||
atom.keymap.add 'test', 'atom-workspace': 'ctrl-b': 'b'
|
||||
atom.keymaps.add 'test', 'atom-workspace': 'ctrl-b': 'b'
|
||||
menu.update()
|
||||
|
||||
waits 1
|
||||
@@ -64,8 +64,8 @@ describe "MenuManager", ->
|
||||
# more dynamic interaction between the currently focused element and the menu
|
||||
spyOn(menu, 'sendToBrowserProcess')
|
||||
menu.add [{label: "A", submenu: [{label: "B", command: "b"}]}]
|
||||
atom.keymap.add 'test', 'atom-workspace': 'ctrl-b': 'b'
|
||||
atom.keymap.add 'test', 'atom-text-editor': 'ctrl-b': 'unset!'
|
||||
atom.keymaps.add 'test', 'atom-workspace': 'ctrl-b': 'b'
|
||||
atom.keymaps.add 'test', 'atom-text-editor': 'ctrl-b': 'unset!'
|
||||
|
||||
waits 1
|
||||
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
path = require 'path'
|
||||
{$, $$} = require '../src/space-pen-extensions'
|
||||
Package = require '../src/package'
|
||||
{Disposable} = require 'atom'
|
||||
|
||||
describe "PackageManager", ->
|
||||
workspaceElement = null
|
||||
|
||||
createTestElement = (className) ->
|
||||
element = document.createElement('div')
|
||||
element.className = className
|
||||
element
|
||||
|
||||
beforeEach ->
|
||||
workspaceElement = atom.views.getView(atom.workspace)
|
||||
|
||||
@@ -58,17 +62,7 @@ describe "PackageManager", ->
|
||||
expect(console.warn.argsForCall[0][0]).toContain("Could not resolve")
|
||||
|
||||
describe "when the package is deprecated", ->
|
||||
grim = require 'grim'
|
||||
includeDeprecatedAPIs = null
|
||||
|
||||
beforeEach ->
|
||||
{includeDeprecatedAPIs} = grim
|
||||
|
||||
afterEach ->
|
||||
grim.includeDeprecatedAPIs = includeDeprecatedAPIs
|
||||
|
||||
it "returns null", ->
|
||||
grim.includeDeprecatedAPIs = false
|
||||
expect(atom.packages.loadPackage(path.join(__dirname, 'fixtures', 'packages', 'wordcount'))).toBeNull()
|
||||
expect(atom.packages.isDeprecatedPackage('wordcount', '2.1.9')).toBe true
|
||||
expect(atom.packages.isDeprecatedPackage('wordcount', '2.2.0')).toBe true
|
||||
@@ -170,24 +164,6 @@ describe "PackageManager", ->
|
||||
expect(atom.config.set('package-with-config-schema.numbers.one', '10')).toBe true
|
||||
expect(atom.config.get('package-with-config-schema.numbers.one')).toBe 10
|
||||
|
||||
describe "when a package has configDefaults", ->
|
||||
beforeEach ->
|
||||
jasmine.snapshotDeprecations()
|
||||
|
||||
afterEach ->
|
||||
jasmine.restoreDeprecationsSnapshot()
|
||||
|
||||
it "still assigns configDefaults from the module though deprecated", ->
|
||||
|
||||
expect(atom.config.get('package-with-config-defaults.numbers.one')).toBeUndefined()
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('package-with-config-defaults')
|
||||
|
||||
runs ->
|
||||
expect(atom.config.get('package-with-config-defaults.numbers.one')).toBe 1
|
||||
expect(atom.config.get('package-with-config-defaults.numbers.two')).toBe 2
|
||||
|
||||
describe "when the package metadata includes `activationCommands`", ->
|
||||
[mainModule, promise, workspaceCommandListener, registration] = []
|
||||
|
||||
@@ -209,33 +185,31 @@ describe "PackageManager", ->
|
||||
mainModule = null
|
||||
|
||||
it "defers requiring/activating the main module until an activation event bubbles to the root view", ->
|
||||
expect(promise.isFulfilled()).not.toBeTruthy()
|
||||
expect(Package.prototype.requireMainModule.callCount).toBe 0
|
||||
|
||||
workspaceElement.dispatchEvent(new CustomEvent('activation-command', bubbles: true))
|
||||
|
||||
waitsForPromise ->
|
||||
promise
|
||||
|
||||
runs ->
|
||||
expect(Package.prototype.requireMainModule.callCount).toBe 1
|
||||
|
||||
it "triggers the activation event on all handlers registered during activation", ->
|
||||
waitsForPromise ->
|
||||
atom.workspace.open()
|
||||
|
||||
runs ->
|
||||
editorView = atom.views.getView(atom.workspace.getActiveTextEditor()).__spacePenView
|
||||
legacyCommandListener = jasmine.createSpy("legacyCommandListener")
|
||||
editorView.command 'activation-command', legacyCommandListener
|
||||
editorElement = atom.views.getView(atom.workspace.getActiveTextEditor())
|
||||
editorCommandListener = jasmine.createSpy("editorCommandListener")
|
||||
atom.commands.add 'atom-text-editor', 'activation-command', editorCommandListener
|
||||
atom.commands.dispatch(editorView[0], 'activation-command')
|
||||
atom.commands.dispatch(editorElement, 'activation-command')
|
||||
expect(mainModule.activate.callCount).toBe 1
|
||||
expect(mainModule.legacyActivationCommandCallCount).toBe 1
|
||||
expect(mainModule.activationCommandCallCount).toBe 1
|
||||
expect(legacyCommandListener.callCount).toBe 1
|
||||
expect(editorCommandListener.callCount).toBe 1
|
||||
expect(workspaceCommandListener.callCount).toBe 1
|
||||
atom.commands.dispatch(editorView[0], 'activation-command')
|
||||
expect(mainModule.legacyActivationCommandCallCount).toBe 2
|
||||
atom.commands.dispatch(editorElement, 'activation-command')
|
||||
expect(mainModule.activationCommandCallCount).toBe 2
|
||||
expect(legacyCommandListener.callCount).toBe 2
|
||||
expect(editorCommandListener.callCount).toBe 2
|
||||
expect(workspaceCommandListener.callCount).toBe 2
|
||||
expect(mainModule.activate.callCount).toBe 1
|
||||
@@ -301,8 +275,8 @@ describe "PackageManager", ->
|
||||
promise = atom.packages.activatePackage('package-with-activation-hooks')
|
||||
|
||||
it "defers requiring/activating the main module until an triggering of an activation hook occurs", ->
|
||||
expect(promise.isFulfilled()).not.toBeTruthy()
|
||||
expect(Package.prototype.requireMainModule.callCount).toBe 0
|
||||
|
||||
atom.packages.triggerActivationHook('language-fictitious:grammar-used')
|
||||
|
||||
waitsForPromise ->
|
||||
@@ -399,36 +373,36 @@ describe "PackageManager", ->
|
||||
describe "keymap loading", ->
|
||||
describe "when the metadata does not contain a 'keymaps' manifest", ->
|
||||
it "loads all the .cson/.json files in the keymaps directory", ->
|
||||
element1 = $$ -> @div class: 'test-1'
|
||||
element2 = $$ -> @div class: 'test-2'
|
||||
element3 = $$ -> @div class: 'test-3'
|
||||
element1 = createTestElement('test-1')
|
||||
element2 = createTestElement('test-2')
|
||||
element3 = createTestElement('test-3')
|
||||
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element1[0])).toHaveLength 0
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element2[0])).toHaveLength 0
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element3[0])).toHaveLength 0
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element1)).toHaveLength 0
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element2)).toHaveLength 0
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element3)).toHaveLength 0
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage("package-with-keymaps")
|
||||
|
||||
runs ->
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element1[0])[0].command).toBe "test-1"
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element2[0])[0].command).toBe "test-2"
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element3[0])).toHaveLength 0
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element1)[0].command).toBe "test-1"
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element2)[0].command).toBe "test-2"
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element3)).toHaveLength 0
|
||||
|
||||
describe "when the metadata contains a 'keymaps' manifest", ->
|
||||
it "loads only the keymaps specified by the manifest, in the specified order", ->
|
||||
element1 = $$ -> @div class: 'test-1'
|
||||
element3 = $$ -> @div class: 'test-3'
|
||||
element1 = createTestElement('test-1')
|
||||
element3 = createTestElement('test-3')
|
||||
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element1[0])).toHaveLength 0
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element1)).toHaveLength 0
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage("package-with-keymaps-manifest")
|
||||
|
||||
runs ->
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element1[0])[0].command).toBe 'keymap-1'
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-n', target: element1[0])[0].command).toBe 'keymap-2'
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-y', target: element3[0])).toHaveLength 0
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element1)[0].command).toBe 'keymap-1'
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-n', target: element1)[0].command).toBe 'keymap-2'
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-y', target: element3)).toHaveLength 0
|
||||
|
||||
describe "when the keymap file is empty", ->
|
||||
it "does not throw an error on activation", ->
|
||||
@@ -440,9 +414,9 @@ describe "PackageManager", ->
|
||||
|
||||
describe "when the package's keymaps have been disabled", ->
|
||||
it "does not add the keymaps", ->
|
||||
element1 = $$ -> @div class: 'test-1'
|
||||
element1 = createTestElement('test-1')
|
||||
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element1[0])).toHaveLength 0
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element1)).toHaveLength 0
|
||||
|
||||
atom.config.set("core.packagesWithKeymapsDisabled", ["package-with-keymaps-manifest"])
|
||||
|
||||
@@ -450,11 +424,11 @@ describe "PackageManager", ->
|
||||
atom.packages.activatePackage("package-with-keymaps-manifest")
|
||||
|
||||
runs ->
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element1[0])).toHaveLength 0
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element1)).toHaveLength 0
|
||||
|
||||
describe "when the package's keymaps are disabled and re-enabled after it is activated", ->
|
||||
it "removes and re-adds the keymaps", ->
|
||||
element1 = $$ -> @div class: 'test-1'
|
||||
element1 = createTestElement('test-1')
|
||||
atom.packages.observePackagesWithKeymapsDisabled()
|
||||
|
||||
waitsForPromise ->
|
||||
@@ -462,10 +436,10 @@ describe "PackageManager", ->
|
||||
|
||||
runs ->
|
||||
atom.config.set("core.packagesWithKeymapsDisabled", ['package-with-keymaps-manifest'])
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element1[0])).toHaveLength 0
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element1)).toHaveLength 0
|
||||
|
||||
atom.config.set("core.packagesWithKeymapsDisabled", [])
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element1[0])[0].command).toBe 'keymap-1'
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element1)[0].command).toBe 'keymap-1'
|
||||
|
||||
describe "menu loading", ->
|
||||
beforeEach ->
|
||||
@@ -474,7 +448,7 @@ describe "PackageManager", ->
|
||||
|
||||
describe "when the metadata does not contain a 'menus' manifest", ->
|
||||
it "loads all the .cson/.json files in the menus directory", ->
|
||||
element = ($$ -> @div class: 'test-1')[0]
|
||||
element = createTestElement('test-1')
|
||||
|
||||
expect(atom.contextMenu.templateForElement(element)).toEqual []
|
||||
|
||||
@@ -491,7 +465,7 @@ describe "PackageManager", ->
|
||||
|
||||
describe "when the metadata contains a 'menus' manifest", ->
|
||||
it "loads only the menus specified by the manifest, in the specified order", ->
|
||||
element = ($$ -> @div class: 'test-1')[0]
|
||||
element = createTestElement('test-1')
|
||||
|
||||
expect(atom.contextMenu.templateForElement(element)).toEqual []
|
||||
|
||||
@@ -535,7 +509,8 @@ describe "PackageManager", ->
|
||||
expect(atom.themes.stylesheetElementForId(one)).not.toBeNull()
|
||||
expect(atom.themes.stylesheetElementForId(two)).not.toBeNull()
|
||||
expect(atom.themes.stylesheetElementForId(three)).toBeNull()
|
||||
expect($('#jasmine-content').css('font-size')).toBe '1px'
|
||||
|
||||
expect(getComputedStyle(document.querySelector('#jasmine-content')).fontSize).toBe '1px'
|
||||
|
||||
describe "when the metadata does not contain a 'styleSheets' manifest", ->
|
||||
it "loads all style sheets from the styles directory", ->
|
||||
@@ -562,7 +537,7 @@ describe "PackageManager", ->
|
||||
expect(atom.themes.stylesheetElementForId(two)).not.toBeNull()
|
||||
expect(atom.themes.stylesheetElementForId(three)).not.toBeNull()
|
||||
expect(atom.themes.stylesheetElementForId(four)).not.toBeNull()
|
||||
expect($('#jasmine-content').css('font-size')).toBe '3px'
|
||||
expect(getComputedStyle(document.querySelector('#jasmine-content')).fontSize).toBe '3px'
|
||||
|
||||
it "assigns the stylesheet's context based on the filename", ->
|
||||
waitsForPromise ->
|
||||
@@ -747,8 +722,8 @@ describe "PackageManager", ->
|
||||
|
||||
runs ->
|
||||
atom.packages.deactivatePackage('package-with-keymaps')
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: ($$ -> @div class: 'test-1')[0])).toHaveLength 0
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: ($$ -> @div class: 'test-2')[0])).toHaveLength 0
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: createTestElement('test-1'))).toHaveLength 0
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: createTestElement('test-2'))).toHaveLength 0
|
||||
|
||||
it "removes the package's stylesheets", ->
|
||||
waitsForPromise ->
|
||||
@@ -890,7 +865,7 @@ describe "PackageManager", ->
|
||||
# enabling of theme
|
||||
pack = atom.packages.enablePackage(packageName)
|
||||
|
||||
waitsFor ->
|
||||
waitsFor 'theme to enable', 500, ->
|
||||
pack in atom.packages.getActivePackages()
|
||||
|
||||
runs ->
|
||||
@@ -903,7 +878,7 @@ describe "PackageManager", ->
|
||||
|
||||
pack = atom.packages.disablePackage(packageName)
|
||||
|
||||
waitsFor ->
|
||||
waitsFor 'did-change-active-themes event to fire', 500, ->
|
||||
didChangeActiveThemesHandler.callCount is 1
|
||||
|
||||
runs ->
|
||||
@@ -911,66 +886,3 @@ describe "PackageManager", ->
|
||||
expect(atom.config.get('core.themes')).not.toContain packageName
|
||||
expect(atom.config.get('core.themes')).not.toContain packageName
|
||||
expect(atom.config.get('core.disabledPackages')).not.toContain packageName
|
||||
|
||||
describe "deleting non-bundled autocomplete packages", ->
|
||||
[autocompleteCSSPath, autocompletePlusPath] = []
|
||||
fs = require 'fs-plus'
|
||||
path = require 'path'
|
||||
|
||||
beforeEach ->
|
||||
fixturePath = path.resolve(__dirname, './fixtures/packages')
|
||||
autocompleteCSSPath = path.join(fixturePath, 'autocomplete-css')
|
||||
autocompletePlusPath = path.join(fixturePath, 'autocomplete-plus')
|
||||
|
||||
try
|
||||
fs.mkdirSync(autocompleteCSSPath)
|
||||
fs.writeFileSync(path.join(autocompleteCSSPath, 'package.json'), '{}')
|
||||
fs.symlinkSync(path.join(fixturePath, 'package-with-main'), autocompletePlusPath, 'dir')
|
||||
|
||||
expect(fs.isDirectorySync(autocompleteCSSPath)).toBe true
|
||||
expect(fs.isSymbolicLinkSync(autocompletePlusPath)).toBe true
|
||||
|
||||
jasmine.unspy(atom.packages, 'uninstallAutocompletePlus')
|
||||
|
||||
afterEach ->
|
||||
try
|
||||
fs.unlink autocompletePlusPath, ->
|
||||
|
||||
it "removes the packages", ->
|
||||
atom.packages.loadPackages()
|
||||
|
||||
waitsFor ->
|
||||
not fs.isDirectorySync(autocompleteCSSPath)
|
||||
|
||||
runs ->
|
||||
expect(fs.isDirectorySync(autocompleteCSSPath)).toBe false
|
||||
expect(fs.isSymbolicLinkSync(autocompletePlusPath)).toBe true
|
||||
|
||||
describe "when the deprecated sublime-tabs package is installed", ->
|
||||
grim = require 'grim'
|
||||
includeDeprecatedAPIs = null
|
||||
|
||||
beforeEach ->
|
||||
{includeDeprecatedAPIs} = grim
|
||||
grim.includeDeprecatedAPIs = false
|
||||
|
||||
afterEach ->
|
||||
grim.includeDeprecatedAPIs = includeDeprecatedAPIs
|
||||
|
||||
it "enables the tree-view and tabs package", ->
|
||||
atom.config.pushAtKeyPath('core.disabledPackages', 'tree-view')
|
||||
atom.config.pushAtKeyPath('core.disabledPackages', 'tabs')
|
||||
|
||||
spyOn(atom.packages, 'getAvailablePackagePaths').andReturn [
|
||||
path.join(__dirname, 'fixtures', 'packages', 'sublime-tabs')
|
||||
path.resolve(__dirname, '..', 'node_modules', 'tree-view')
|
||||
path.resolve(__dirname, '..', 'node_modules', 'tabs')
|
||||
]
|
||||
atom.packages.loadPackages()
|
||||
|
||||
waitsFor ->
|
||||
not atom.packages.isPackageDisabled('tree-view') and not atom.packages.isPackageDisabled('tabs')
|
||||
|
||||
runs ->
|
||||
expect(atom.packages.isPackageLoaded('tree-view')).toBe true
|
||||
expect(atom.packages.isPackageLoaded('tabs')).toBe true
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
{$} = require '../src/space-pen-extensions'
|
||||
path = require 'path'
|
||||
Package = require '../src/package'
|
||||
ThemePackage = require '../src/theme-package'
|
||||
@@ -7,6 +6,10 @@ describe "Package", ->
|
||||
describe "when the package contains incompatible native modules", ->
|
||||
beforeEach ->
|
||||
spyOn(atom, 'inDevMode').andReturn(false)
|
||||
items = {}
|
||||
spyOn(global.localStorage, 'setItem').andCallFake (key, item) -> items[key] = item; undefined
|
||||
spyOn(global.localStorage, 'getItem').andCallFake (key) -> items[key] ? null
|
||||
spyOn(global.localStorage, 'removeItem').andCallFake (key) -> delete items[key]; undefined
|
||||
|
||||
it "does not activate it", ->
|
||||
packagePath = atom.project.getDirectories()[0]?.resolve('packages/package-with-incompatible-native-module')
|
||||
@@ -15,16 +18,18 @@ describe "Package", ->
|
||||
expect(pack.incompatibleModules[0].name).toBe 'native-module'
|
||||
expect(pack.incompatibleModules[0].path).toBe path.join(packagePath, 'node_modules', 'native-module')
|
||||
|
||||
it "utilizes _atomModuleCache if present to determine the package's native dependencies", ->
|
||||
packagePath = atom.project.getDirectories()[0]?.resolve('packages/package-with-ignored-incompatible-native-module')
|
||||
pack = new Package(packagePath)
|
||||
expect(pack.getNativeModuleDependencyPaths().length).toBe(1) # doesn't see the incompatible module
|
||||
expect(pack.isCompatible()).toBe true
|
||||
|
||||
packagePath = atom.project.getDirectories()[0]?.resolve('packages/package-with-cached-incompatible-native-module')
|
||||
pack = new Package(packagePath)
|
||||
expect(pack.isCompatible()).toBe false
|
||||
|
||||
it "caches the incompatible native modules in local storage", ->
|
||||
packagePath = atom.project.getDirectories()[0]?.resolve('packages/package-with-incompatible-native-module')
|
||||
cacheKey = null
|
||||
cacheItem = null
|
||||
|
||||
spyOn(global.localStorage, 'setItem').andCallFake (key, item) ->
|
||||
cacheKey = key
|
||||
cacheItem = item
|
||||
spyOn(global.localStorage, 'getItem').andCallFake (key) ->
|
||||
return cacheItem if cacheKey is key
|
||||
|
||||
expect(new Package(packagePath).isCompatible()).toBe false
|
||||
expect(global.localStorage.getItem.callCount).toBe 1
|
||||
@@ -34,55 +39,121 @@ describe "Package", ->
|
||||
expect(global.localStorage.getItem.callCount).toBe 2
|
||||
expect(global.localStorage.setItem.callCount).toBe 1
|
||||
|
||||
describe "::rebuild()", ->
|
||||
beforeEach ->
|
||||
spyOn(atom, 'inDevMode').andReturn(false)
|
||||
items = {}
|
||||
spyOn(global.localStorage, 'setItem').andCallFake (key, item) -> items[key] = item; undefined
|
||||
spyOn(global.localStorage, 'getItem').andCallFake (key) -> items[key] ? null
|
||||
spyOn(global.localStorage, 'removeItem').andCallFake (key) -> delete items[key]; undefined
|
||||
|
||||
it "returns a promise resolving to the results of `apm rebuild`", ->
|
||||
packagePath = atom.project.getDirectories()[0]?.resolve('packages/package-with-index')
|
||||
pack = new Package(packagePath)
|
||||
rebuildCallbacks = []
|
||||
spyOn(pack, 'runRebuildProcess').andCallFake ((callback) -> rebuildCallbacks.push(callback))
|
||||
|
||||
promise = pack.rebuild()
|
||||
rebuildCallbacks[0]({code: 0, stdout: 'stdout output', stderr: 'stderr output'})
|
||||
|
||||
waitsFor (done) ->
|
||||
promise.then (result) ->
|
||||
expect(result).toEqual {code: 0, stdout: 'stdout output', stderr: 'stderr output'}
|
||||
done()
|
||||
|
||||
it "persists build failures in local storage", ->
|
||||
packagePath = atom.project.getDirectories()[0]?.resolve('packages/package-with-index')
|
||||
pack = new Package(packagePath)
|
||||
|
||||
expect(pack.isCompatible()).toBe true
|
||||
expect(pack.getBuildFailureOutput()).toBeNull()
|
||||
|
||||
rebuildCallbacks = []
|
||||
spyOn(pack, 'runRebuildProcess').andCallFake ((callback) -> rebuildCallbacks.push(callback))
|
||||
|
||||
pack.rebuild()
|
||||
rebuildCallbacks[0]({code: 13, stderr: 'It is broken'})
|
||||
|
||||
expect(pack.getBuildFailureOutput()).toBe 'It is broken'
|
||||
expect(pack.getIncompatibleNativeModules()).toEqual []
|
||||
expect(pack.isCompatible()).toBe false
|
||||
|
||||
# A different package instance has the same failure output (simulates reload)
|
||||
pack2 = new Package(packagePath)
|
||||
expect(pack2.getBuildFailureOutput()).toBe 'It is broken'
|
||||
expect(pack2.isCompatible()).toBe false
|
||||
|
||||
# Clears the build failure after a successful build
|
||||
pack.rebuild()
|
||||
rebuildCallbacks[1]({code: 0, stdout: 'It worked'})
|
||||
|
||||
expect(pack.getBuildFailureOutput()).toBeNull()
|
||||
expect(pack2.getBuildFailureOutput()).toBeNull()
|
||||
|
||||
it "sets cached incompatible modules to an empty array when the rebuild completes (there may be a build error, but rebuilding *deletes* native modules)", ->
|
||||
packagePath = atom.project.getDirectories()[0]?.resolve('packages/package-with-incompatible-native-module')
|
||||
pack = new Package(packagePath)
|
||||
|
||||
expect(pack.getIncompatibleNativeModules().length).toBeGreaterThan(0)
|
||||
|
||||
rebuildCallbacks = []
|
||||
spyOn(pack, 'runRebuildProcess').andCallFake ((callback) -> rebuildCallbacks.push(callback))
|
||||
|
||||
pack.rebuild()
|
||||
expect(pack.getIncompatibleNativeModules().length).toBeGreaterThan(0)
|
||||
rebuildCallbacks[0]({code: 0, stdout: 'It worked'})
|
||||
expect(pack.getIncompatibleNativeModules().length).toBe(0)
|
||||
|
||||
describe "theme", ->
|
||||
theme = null
|
||||
[editorElement, theme] = []
|
||||
|
||||
beforeEach ->
|
||||
$("#jasmine-content").append $("<atom-text-editor></atom-text-editor>")
|
||||
editorElement = document.createElement('atom-text-editor')
|
||||
jasmine.attachToDOM(editorElement)
|
||||
|
||||
afterEach ->
|
||||
theme.deactivate() if theme?
|
||||
|
||||
describe "when the theme contains a single style file", ->
|
||||
it "loads and applies css", ->
|
||||
expect($("atom-text-editor").css("padding-bottom")).not.toBe "1234px"
|
||||
expect(getComputedStyle(editorElement).paddingBottom).not.toBe "1234px"
|
||||
themePath = atom.project.getDirectories()[0]?.resolve('packages/theme-with-index-css')
|
||||
theme = new ThemePackage(themePath)
|
||||
theme.activate()
|
||||
expect($("atom-text-editor").css("padding-top")).toBe "1234px"
|
||||
expect(getComputedStyle(editorElement).paddingTop).toBe "1234px"
|
||||
|
||||
it "parses, loads and applies less", ->
|
||||
expect($("atom-text-editor").css("padding-bottom")).not.toBe "1234px"
|
||||
expect(getComputedStyle(editorElement).paddingBottom).not.toBe "1234px"
|
||||
themePath = atom.project.getDirectories()[0]?.resolve('packages/theme-with-index-less')
|
||||
theme = new ThemePackage(themePath)
|
||||
theme.activate()
|
||||
expect($("atom-text-editor").css("padding-top")).toBe "4321px"
|
||||
expect(getComputedStyle(editorElement).paddingTop).toBe "4321px"
|
||||
|
||||
describe "when the theme contains a package.json file", ->
|
||||
it "loads and applies stylesheets from package.json in the correct order", ->
|
||||
expect($("atom-text-editor").css("padding-top")).not.toBe("101px")
|
||||
expect($("atom-text-editor").css("padding-right")).not.toBe("102px")
|
||||
expect($("atom-text-editor").css("padding-bottom")).not.toBe("103px")
|
||||
expect(getComputedStyle(editorElement).paddingTop).not.toBe("101px")
|
||||
expect(getComputedStyle(editorElement).paddingRight).not.toBe("102px")
|
||||
expect(getComputedStyle(editorElement).paddingBottom).not.toBe("103px")
|
||||
|
||||
themePath = atom.project.getDirectories()[0]?.resolve('packages/theme-with-package-file')
|
||||
theme = new ThemePackage(themePath)
|
||||
theme.activate()
|
||||
expect($("atom-text-editor").css("padding-top")).toBe("101px")
|
||||
expect($("atom-text-editor").css("padding-right")).toBe("102px")
|
||||
expect($("atom-text-editor").css("padding-bottom")).toBe("103px")
|
||||
expect(getComputedStyle(editorElement).paddingTop).toBe("101px")
|
||||
expect(getComputedStyle(editorElement).paddingRight).toBe("102px")
|
||||
expect(getComputedStyle(editorElement).paddingBottom).toBe("103px")
|
||||
|
||||
describe "when the theme does not contain a package.json file and is a directory", ->
|
||||
it "loads all stylesheet files in the directory", ->
|
||||
expect($("atom-text-editor").css("padding-top")).not.toBe "10px"
|
||||
expect($("atom-text-editor").css("padding-right")).not.toBe "20px"
|
||||
expect($("atom-text-editor").css("padding-bottom")).not.toBe "30px"
|
||||
expect(getComputedStyle(editorElement).paddingTop).not.toBe "10px"
|
||||
expect(getComputedStyle(editorElement).paddingRight).not.toBe "20px"
|
||||
expect(getComputedStyle(editorElement).paddingBottom).not.toBe "30px"
|
||||
|
||||
themePath = atom.project.getDirectories()[0]?.resolve('packages/theme-without-package-file')
|
||||
theme = new ThemePackage(themePath)
|
||||
theme.activate()
|
||||
expect($("atom-text-editor").css("padding-top")).toBe "10px"
|
||||
expect($("atom-text-editor").css("padding-right")).toBe "20px"
|
||||
expect($("atom-text-editor").css("padding-bottom")).toBe "30px"
|
||||
expect(getComputedStyle(editorElement).paddingTop).toBe "10px"
|
||||
expect(getComputedStyle(editorElement).paddingRight).toBe "20px"
|
||||
expect(getComputedStyle(editorElement).paddingBottom).toBe "30px"
|
||||
|
||||
describe "reloading a theme", ->
|
||||
beforeEach ->
|
||||
|
||||
@@ -4,16 +4,13 @@ PaneAxis = require '../src/pane-axis'
|
||||
|
||||
describe "PaneContainerElement", ->
|
||||
describe "when panes are added or removed", ->
|
||||
[paneAxisElement, paneAxis] = []
|
||||
it "inserts or removes resize elements", ->
|
||||
childTagNames = ->
|
||||
child.nodeName.toLowerCase() for child in paneAxisElement.children
|
||||
|
||||
beforeEach ->
|
||||
paneAxis = new PaneAxis
|
||||
paneAxisElement = new PaneAxisElement().initialize(paneAxis)
|
||||
|
||||
childTagNames = ->
|
||||
child.nodeName.toLowerCase() for child in paneAxisElement.children
|
||||
|
||||
it "inserts or removes resize elements", ->
|
||||
expect(childTagNames()).toEqual []
|
||||
|
||||
paneAxis.addChild(new PaneAxis)
|
||||
@@ -44,6 +41,45 @@ describe "PaneContainerElement", ->
|
||||
'atom-pane-axis'
|
||||
]
|
||||
|
||||
it "transfers focus to the next pane if a focused pane is removed", ->
|
||||
container = new PaneContainer
|
||||
containerElement = atom.views.getView(container)
|
||||
leftPane = container.getActivePane()
|
||||
leftPaneElement = atom.views.getView(leftPane)
|
||||
rightPane = leftPane.splitRight()
|
||||
rightPaneElement = atom.views.getView(rightPane)
|
||||
|
||||
jasmine.attachToDOM(containerElement)
|
||||
rightPaneElement.focus()
|
||||
expect(document.activeElement).toBe rightPaneElement
|
||||
|
||||
rightPane.destroy()
|
||||
expect(document.activeElement).toBe leftPaneElement
|
||||
|
||||
describe "when a pane is split", ->
|
||||
it "builds appropriately-oriented atom-pane-axis elements", ->
|
||||
container = new PaneContainer
|
||||
containerElement = atom.views.getView(container)
|
||||
|
||||
pane1 = container.getRoot()
|
||||
pane2 = pane1.splitRight()
|
||||
pane3 = pane2.splitDown()
|
||||
|
||||
horizontalPanes = containerElement.querySelectorAll('atom-pane-container > atom-pane-axis.horizontal > atom-pane')
|
||||
expect(horizontalPanes.length).toBe 1
|
||||
expect(horizontalPanes[0]).toBe atom.views.getView(pane1)
|
||||
|
||||
verticalPanes = containerElement.querySelectorAll('atom-pane-container > atom-pane-axis.horizontal > atom-pane-axis.vertical > atom-pane')
|
||||
expect(verticalPanes.length).toBe 2
|
||||
expect(verticalPanes[0]).toBe atom.views.getView(pane2)
|
||||
expect(verticalPanes[1]).toBe atom.views.getView(pane3)
|
||||
|
||||
pane1.destroy()
|
||||
verticalPanes = containerElement.querySelectorAll('atom-pane-container > atom-pane-axis.vertical > atom-pane')
|
||||
expect(verticalPanes.length).toBe 2
|
||||
expect(verticalPanes[0]).toBe atom.views.getView(pane2)
|
||||
expect(verticalPanes[1]).toBe atom.views.getView(pane3)
|
||||
|
||||
describe "when the resize element is dragged ", ->
|
||||
[container, containerElement] = []
|
||||
|
||||
@@ -194,3 +230,96 @@ describe "PaneContainerElement", ->
|
||||
atom.commands.dispatch(atom.views.getView(rightPane), 'pane:decrease-size')
|
||||
expect(leftPane.getFlexScale()).toBe 1/1.1
|
||||
expect(rightPane.getFlexScale()).toBe 1/1.1
|
||||
|
||||
describe "changing focus directionally between panes", ->
|
||||
[containerElement, pane1, pane2, pane3, pane4, pane5, pane6, pane7, pane8, pane9] = []
|
||||
|
||||
beforeEach ->
|
||||
# Set up a grid of 9 panes, in the following arrangement, where the
|
||||
# numbers correspond to the variable names below.
|
||||
#
|
||||
# -------
|
||||
# |1|2|3|
|
||||
# -------
|
||||
# |4|5|6|
|
||||
# -------
|
||||
# |7|8|9|
|
||||
# -------
|
||||
|
||||
buildElement = (id) ->
|
||||
element = document.createElement('div')
|
||||
element.textContent = id
|
||||
element.tabIndex = -1
|
||||
element
|
||||
|
||||
container = new PaneContainer
|
||||
pane1 = container.getRoot()
|
||||
pane1.activateItem(buildElement('1'))
|
||||
pane4 = pane1.splitDown(items: [buildElement('4')])
|
||||
pane7 = pane4.splitDown(items: [buildElement('7')])
|
||||
|
||||
pane2 = pane1.splitRight(items: [buildElement('2')])
|
||||
pane3 = pane2.splitRight(items: [buildElement('3')])
|
||||
|
||||
pane5 = pane4.splitRight(items: [buildElement('5')])
|
||||
pane6 = pane5.splitRight(items: [buildElement('6')])
|
||||
|
||||
pane8 = pane7.splitRight(items: [buildElement('8')])
|
||||
pane9 = pane8.splitRight(items: [buildElement('9')])
|
||||
|
||||
containerElement = atom.views.getView(container)
|
||||
containerElement.style.height = '400px'
|
||||
containerElement.style.width = '400px'
|
||||
jasmine.attachToDOM(containerElement)
|
||||
|
||||
describe "::focusPaneViewAbove()", ->
|
||||
describe "when there are multiple rows above the focused pane", ->
|
||||
it "focuses up to the adjacent row", ->
|
||||
pane8.activate()
|
||||
containerElement.focusPaneViewAbove()
|
||||
expect(document.activeElement).toBe pane5.getActiveItem()
|
||||
|
||||
describe "when there are no rows above the focused pane", ->
|
||||
it "keeps the current pane focused", ->
|
||||
pane2.activate()
|
||||
containerElement.focusPaneViewAbove()
|
||||
expect(document.activeElement).toBe pane2.getActiveItem()
|
||||
|
||||
describe "::focusPaneViewBelow()", ->
|
||||
describe "when there are multiple rows below the focused pane", ->
|
||||
it "focuses down to the adjacent row", ->
|
||||
pane2.activate()
|
||||
containerElement.focusPaneViewBelow()
|
||||
expect(document.activeElement).toBe pane5.getActiveItem()
|
||||
|
||||
describe "when there are no rows below the focused pane", ->
|
||||
it "keeps the current pane focused", ->
|
||||
pane8.activate()
|
||||
containerElement.focusPaneViewBelow()
|
||||
expect(document.activeElement).toBe pane8.getActiveItem()
|
||||
|
||||
describe "::focusPaneViewOnLeft()", ->
|
||||
describe "when there are multiple columns to the left of the focused pane", ->
|
||||
it "focuses left to the adjacent column", ->
|
||||
pane6.activate()
|
||||
containerElement.focusPaneViewOnLeft()
|
||||
expect(document.activeElement).toBe pane5.getActiveItem()
|
||||
|
||||
describe "when there are no columns to the left of the focused pane", ->
|
||||
it "keeps the current pane focused", ->
|
||||
pane4.activate()
|
||||
containerElement.focusPaneViewOnLeft()
|
||||
expect(document.activeElement).toBe pane4.getActiveItem()
|
||||
|
||||
describe "::focusPaneViewOnRight()", ->
|
||||
describe "when there are multiple columns to the right of the focused pane", ->
|
||||
it "focuses right to the adjacent column", ->
|
||||
pane4.activate()
|
||||
containerElement.focusPaneViewOnRight()
|
||||
expect(document.activeElement).toBe pane5.getActiveItem()
|
||||
|
||||
describe "when there are no columns to the right of the focused pane", ->
|
||||
it "keeps the current pane focused", ->
|
||||
pane6.activate()
|
||||
containerElement.focusPaneViewOnRight()
|
||||
expect(document.activeElement).toBe pane6.getActiveItem()
|
||||
|
||||
@@ -21,7 +21,7 @@ describe "PaneContainer", ->
|
||||
it "preserves the focused pane across serialization", ->
|
||||
expect(pane3A.focused).toBe true
|
||||
|
||||
containerB = containerA.testSerialization()
|
||||
containerB = PaneContainer.deserialize(containerA.serialize())
|
||||
[pane1B, pane2B, pane3B] = containerB.getPanes()
|
||||
expect(pane3B.focused).toBe true
|
||||
|
||||
@@ -29,7 +29,7 @@ describe "PaneContainer", ->
|
||||
pane3A.activate()
|
||||
expect(containerA.getActivePane()).toBe pane3A
|
||||
|
||||
containerB = containerA.testSerialization()
|
||||
containerB = PaneContainer.deserialize(containerA.serialize())
|
||||
[pane1B, pane2B, pane3B] = containerB.getPanes()
|
||||
expect(containerB.getActivePane()).toBe pane3B
|
||||
|
||||
@@ -40,6 +40,32 @@ describe "PaneContainer", ->
|
||||
containerB = atom.deserializers.deserialize(state)
|
||||
expect(containerB.getActivePane()).toBe containerB.getPanes()[0]
|
||||
|
||||
describe "if there are empty panes after deserialization", ->
|
||||
beforeEach ->
|
||||
pane3A.getItems()[0].serialize = -> deserializer: 'Bogus'
|
||||
|
||||
describe "if the 'core.destroyEmptyPanes' config option is false (the default)", ->
|
||||
it "leaves the empty panes intact", ->
|
||||
state = containerA.serialize()
|
||||
containerB = atom.deserializers.deserialize(state)
|
||||
[leftPane, column] = containerB.getRoot().getChildren()
|
||||
[topPane, bottomPane] = column.getChildren()
|
||||
|
||||
expect(leftPane.getItems().length).toBe 1
|
||||
expect(topPane.getItems().length).toBe 1
|
||||
expect(bottomPane.getItems().length).toBe 0
|
||||
|
||||
describe "if the 'core.destroyEmptyPanes' config option is true", ->
|
||||
it "removes empty panes on deserialization", ->
|
||||
atom.config.set('core.destroyEmptyPanes', true)
|
||||
|
||||
state = containerA.serialize()
|
||||
containerB = atom.deserializers.deserialize(state)
|
||||
[leftPane, rightPane] = containerB.getRoot().getChildren()
|
||||
|
||||
expect(leftPane.getItems().length).toBe 1
|
||||
expect(rightPane.getItems().length).toBe 1
|
||||
|
||||
it "does not allow the root pane to be destroyed", ->
|
||||
container = new PaneContainer
|
||||
container.getRoot().destroy()
|
||||
@@ -223,3 +249,19 @@ describe "PaneContainer", ->
|
||||
['will', {item: item2, pane: pane2, index: 0}]
|
||||
['did', {item: item2, pane: pane2, index: 0}]
|
||||
]
|
||||
|
||||
describe "::saveAll()", ->
|
||||
it "saves all open pane items", ->
|
||||
container = new PaneContainer
|
||||
pane1 = container.getRoot()
|
||||
pane2 = pane1.splitRight()
|
||||
|
||||
pane1.addItem(item1 = {getURI: (-> ''), save: -> @saved = true})
|
||||
pane1.addItem(item2 = {getURI: (-> ''), save: -> @saved = true})
|
||||
pane2.addItem(item3 = {getURI: (-> ''), save: -> @saved = true})
|
||||
|
||||
container.saveAll()
|
||||
|
||||
expect(item1.saved).toBe true
|
||||
expect(item2.saved).toBe true
|
||||
expect(item3.saved).toBe true
|
||||
|
||||
@@ -1,334 +0,0 @@
|
||||
path = require 'path'
|
||||
temp = require 'temp'
|
||||
PaneContainer = require '../src/pane-container'
|
||||
PaneContainerView = require '../src/pane-container-view'
|
||||
PaneView = require '../src/pane-view'
|
||||
{Disposable} = require 'event-kit'
|
||||
{$, View, $$} = require '../src/space-pen-extensions'
|
||||
|
||||
describe "PaneContainerView", ->
|
||||
[TestView, container, pane1, pane2, pane3, deserializerDisposable] = []
|
||||
|
||||
beforeEach ->
|
||||
class TestView extends View
|
||||
deserializerDisposable = atom.deserializers.add(this)
|
||||
@deserialize: ({name}) -> new TestView(name)
|
||||
@content: -> @div tabindex: -1
|
||||
initialize: (@name) -> @text(@name)
|
||||
serialize: -> {deserializer: 'TestView', @name}
|
||||
getURI: -> path.join(temp.dir, @name)
|
||||
save: -> @saved = true
|
||||
isEqual: (other) -> @name is other?.name
|
||||
onDidChangeTitle: -> new Disposable(->)
|
||||
onDidChangeModified: -> new Disposable(->)
|
||||
|
||||
container = atom.views.getView(atom.workspace.paneContainer).__spacePenView
|
||||
pane1 = container.getRoot()
|
||||
pane1.activateItem(new TestView('1'))
|
||||
pane2 = pane1.splitRight(new TestView('2'))
|
||||
pane3 = pane2.splitDown(new TestView('3'))
|
||||
|
||||
afterEach ->
|
||||
deserializerDisposable.dispose()
|
||||
|
||||
describe ".getActivePaneView()", ->
|
||||
it "returns the most-recently focused pane", ->
|
||||
focusStealer = $$ -> @div tabindex: -1, "focus stealer"
|
||||
focusStealer.attachToDom()
|
||||
container.attachToDom()
|
||||
|
||||
pane2.focus()
|
||||
expect(container.getFocusedPane()).toBe pane2
|
||||
expect(container.getActivePaneView()).toBe pane2
|
||||
|
||||
focusStealer.focus()
|
||||
expect(container.getFocusedPane()).toBeUndefined()
|
||||
expect(container.getActivePaneView()).toBe pane2
|
||||
|
||||
pane3.focus()
|
||||
expect(container.getFocusedPane()).toBe pane3
|
||||
expect(container.getActivePaneView()).toBe pane3
|
||||
|
||||
describe ".eachPaneView(callback)", ->
|
||||
it "runs the callback with all current and future panes until the subscription is cancelled", ->
|
||||
panes = []
|
||||
subscription = container.eachPaneView (pane) -> panes.push(pane)
|
||||
expect(panes).toEqual [pane1, pane2, pane3]
|
||||
|
||||
panes = []
|
||||
pane4 = pane3.splitRight(pane3.copyActiveItem())
|
||||
expect(panes).toEqual [pane4]
|
||||
|
||||
panes = []
|
||||
subscription.off()
|
||||
pane4.splitDown()
|
||||
expect(panes).toEqual []
|
||||
|
||||
describe ".saveAll()", ->
|
||||
it "saves all open pane items", ->
|
||||
pane1.activateItem(new TestView('4'))
|
||||
|
||||
container.saveAll()
|
||||
|
||||
for pane in container.getPaneViews()
|
||||
for item in pane.getItems()
|
||||
expect(item.saved).toBeTruthy()
|
||||
|
||||
describe "serialization", ->
|
||||
it "can be serialized and deserialized, and correctly adjusts dimensions of deserialized panes after attach", ->
|
||||
newContainer = atom.views.getView(container.model.testSerialization()).__spacePenView
|
||||
expect(newContainer.find('atom-pane-axis.horizontal > :contains(1)')).toExist()
|
||||
expect(newContainer.find('atom-pane-axis.horizontal > atom-pane-axis.vertical > :contains(2)')).toExist()
|
||||
expect(newContainer.find('atom-pane-axis.horizontal > atom-pane-axis.vertical > :contains(3)')).toExist()
|
||||
|
||||
newContainer.height(200).width(300).attachToDom()
|
||||
expect(newContainer.find('atom-pane-axis.horizontal > :contains(1)').width()).toBe 150
|
||||
expect(newContainer.find('atom-pane-axis.horizontal > atom-pane-axis.vertical > :contains(2)').height()).toBe 100
|
||||
|
||||
describe "if there are empty panes after deserialization", ->
|
||||
beforeEach ->
|
||||
# only deserialize pane 1's view successfully
|
||||
TestView.deserialize = ({name}) -> new TestView(name) if name is '1'
|
||||
|
||||
describe "if the 'core.destroyEmptyPanes' config option is false (the default)", ->
|
||||
it "leaves the empty panes intact", ->
|
||||
newContainer = atom.views.getView(container.model.testSerialization()).__spacePenView
|
||||
expect(newContainer.find('atom-pane-axis.horizontal > :contains(1)')).toExist()
|
||||
expect(newContainer.find('atom-pane-axis.horizontal > atom-pane-axis.vertical > atom-pane').length).toBe 2
|
||||
|
||||
describe "if the 'core.destroyEmptyPanes' config option is true", ->
|
||||
it "removes empty panes on deserialization", ->
|
||||
atom.config.set('core.destroyEmptyPanes', true)
|
||||
newContainer = atom.views.getView(container.model.testSerialization()).__spacePenView
|
||||
expect(newContainer.find('atom-pane-axis.horizontal, atom-pane-axis.vertical')).not.toExist()
|
||||
expect(newContainer.find('> :contains(1)')).toExist()
|
||||
|
||||
describe "pane-container:active-pane-item-changed", ->
|
||||
[pane1, item1a, item1b, item2a, item2b, item3a, container, activeItemChangedHandler] = []
|
||||
beforeEach ->
|
||||
item1a = new TestView('1a')
|
||||
item1b = new TestView('1b')
|
||||
item2a = new TestView('2a')
|
||||
item2b = new TestView('2b')
|
||||
item3a = new TestView('3a')
|
||||
|
||||
container = atom.views.getView(new PaneContainer).__spacePenView
|
||||
pane1 = container.getRoot()
|
||||
pane1.activateItem(item1a)
|
||||
container.attachToDom()
|
||||
|
||||
activeItemChangedHandler = jasmine.createSpy("activeItemChangedHandler")
|
||||
container.on 'pane-container:active-pane-item-changed', activeItemChangedHandler
|
||||
|
||||
describe "when there is one pane", ->
|
||||
it "is triggered when a new pane item is added", ->
|
||||
pane1.activateItem(item1b)
|
||||
expect(activeItemChangedHandler.callCount).toBe 1
|
||||
expect(activeItemChangedHandler.argsForCall[0][1]).toEqual item1b
|
||||
|
||||
it "is not triggered when the active pane item is shown again", ->
|
||||
pane1.activateItem(item1a)
|
||||
expect(activeItemChangedHandler).not.toHaveBeenCalled()
|
||||
|
||||
it "is triggered when switching to an existing pane item", ->
|
||||
pane1.activateItem(item1b)
|
||||
activeItemChangedHandler.reset()
|
||||
|
||||
pane1.activateItem(item1a)
|
||||
expect(activeItemChangedHandler.callCount).toBe 1
|
||||
expect(activeItemChangedHandler.argsForCall[0][1]).toEqual item1a
|
||||
|
||||
it "is triggered when the active pane item is destroyed", ->
|
||||
pane1.activateItem(item1b)
|
||||
activeItemChangedHandler.reset()
|
||||
|
||||
pane1.destroyItem(item1b)
|
||||
expect(activeItemChangedHandler.callCount).toBe 1
|
||||
expect(activeItemChangedHandler.argsForCall[0][1]).toEqual item1a
|
||||
|
||||
it "is not triggered when an inactive pane item is destroyed", ->
|
||||
pane1.activateItem(item1b)
|
||||
activeItemChangedHandler.reset()
|
||||
|
||||
pane1.destroyItem(item1a)
|
||||
expect(activeItemChangedHandler).not.toHaveBeenCalled()
|
||||
|
||||
it "is triggered when all pane items are destroyed", ->
|
||||
pane1.destroyItem(item1a)
|
||||
expect(activeItemChangedHandler.callCount).toBe 1
|
||||
expect(activeItemChangedHandler.argsForCall[0][1]).toBe undefined
|
||||
|
||||
describe "when there are two panes", ->
|
||||
[pane2] = []
|
||||
|
||||
beforeEach ->
|
||||
pane2 = pane1.splitLeft(item2a)
|
||||
activeItemChangedHandler.reset()
|
||||
|
||||
it "is triggered when a new pane item is added to the active pane", ->
|
||||
pane2.activateItem(item2b)
|
||||
expect(activeItemChangedHandler.callCount).toBe 1
|
||||
expect(activeItemChangedHandler.argsForCall[0][1]).toEqual item2b
|
||||
|
||||
it "is not triggered when a new pane item is added to an inactive pane", ->
|
||||
pane1.activateItem(item1b)
|
||||
expect(activeItemChangedHandler).not.toHaveBeenCalled()
|
||||
|
||||
it "is triggered when the active pane's active item is destroyed", ->
|
||||
pane2.activateItem(item2b)
|
||||
activeItemChangedHandler.reset()
|
||||
|
||||
pane2.destroyItem(item2b)
|
||||
expect(activeItemChangedHandler.callCount).toBe 1
|
||||
expect(activeItemChangedHandler.argsForCall[0][1]).toEqual item2a
|
||||
|
||||
it "is not triggered when an inactive pane's active item is destroyed", ->
|
||||
pane1.activateItem(item1b)
|
||||
activeItemChangedHandler.reset()
|
||||
|
||||
pane1.destroyItem(item1b)
|
||||
expect(activeItemChangedHandler).not.toHaveBeenCalled()
|
||||
|
||||
it "is triggered when the active pane is destroyed", ->
|
||||
pane2.remove()
|
||||
expect(activeItemChangedHandler.callCount).toBe 1
|
||||
expect(activeItemChangedHandler.argsForCall[0][1]).toEqual item1a
|
||||
|
||||
it "is not triggered when an inactive pane is destroyed", ->
|
||||
pane1.remove()
|
||||
expect(activeItemChangedHandler).not.toHaveBeenCalled()
|
||||
|
||||
it "is triggered when the active pane is changed", ->
|
||||
pane1.activate()
|
||||
expect(activeItemChangedHandler.callCount).toBe 1
|
||||
expect(activeItemChangedHandler.argsForCall[0][1]).toEqual item1a
|
||||
|
||||
describe "when there are multiple panes", ->
|
||||
beforeEach ->
|
||||
pane2 = pane1.splitRight(item2a)
|
||||
activeItemChangedHandler.reset()
|
||||
|
||||
it "is triggered when a new pane is added", ->
|
||||
pane2.splitDown(item3a)
|
||||
expect(activeItemChangedHandler.callCount).toBe 1
|
||||
expect(activeItemChangedHandler.argsForCall[0][1]).toEqual item3a
|
||||
|
||||
it "is not triggered when an inactive pane is destroyed", ->
|
||||
pane3 = pane2.splitDown(item3a)
|
||||
activeItemChangedHandler.reset()
|
||||
|
||||
pane1.remove()
|
||||
pane2.remove()
|
||||
expect(activeItemChangedHandler).not.toHaveBeenCalled()
|
||||
|
||||
describe ".focusNextPaneView()", ->
|
||||
it "focuses the pane following the focused pane or the first pane if no pane has focus", ->
|
||||
container.attachToDom()
|
||||
container.focusNextPaneView()
|
||||
expect(pane1.activeItem).toMatchSelector ':focus'
|
||||
container.focusNextPaneView()
|
||||
expect(pane2.activeItem).toMatchSelector ':focus'
|
||||
container.focusNextPaneView()
|
||||
expect(pane3.activeItem).toMatchSelector ':focus'
|
||||
container.focusNextPaneView()
|
||||
expect(pane1.activeItem).toMatchSelector ':focus'
|
||||
|
||||
describe ".focusPreviousPaneView()", ->
|
||||
it "focuses the pane preceding the focused pane or the last pane if no pane has focus", ->
|
||||
container.attachToDom()
|
||||
container.getPaneViews()[0].focus() # activate first pane
|
||||
|
||||
container.focusPreviousPaneView()
|
||||
expect(pane3.activeItem).toMatchSelector ':focus'
|
||||
container.focusPreviousPaneView()
|
||||
expect(pane2.activeItem).toMatchSelector ':focus'
|
||||
container.focusPreviousPaneView()
|
||||
expect(pane1.activeItem).toMatchSelector ':focus'
|
||||
container.focusPreviousPaneView()
|
||||
expect(pane3.activeItem).toMatchSelector ':focus'
|
||||
|
||||
describe "changing focus directionally between panes", ->
|
||||
[pane1, pane2, pane3, pane4, pane5, pane6, pane7, pane8, pane9] = []
|
||||
|
||||
beforeEach ->
|
||||
# Set up a grid of 9 panes, in the following arrangement, where the
|
||||
# numbers correspond to the variable names below.
|
||||
#
|
||||
# -------
|
||||
# |1|2|3|
|
||||
# -------
|
||||
# |4|5|6|
|
||||
# -------
|
||||
# |7|8|9|
|
||||
# -------
|
||||
|
||||
container = atom.views.getView(new PaneContainer).__spacePenView
|
||||
pane1 = container.getRoot()
|
||||
pane1.activateItem(new TestView('1'))
|
||||
pane4 = pane1.splitDown(new TestView('4'))
|
||||
pane7 = pane4.splitDown(new TestView('7'))
|
||||
|
||||
pane2 = pane1.splitRight(new TestView('2'))
|
||||
pane3 = pane2.splitRight(new TestView('3'))
|
||||
|
||||
pane5 = pane4.splitRight(new TestView('5'))
|
||||
pane6 = pane5.splitRight(new TestView('6'))
|
||||
|
||||
pane8 = pane7.splitRight(new TestView('8'))
|
||||
pane9 = pane8.splitRight(new TestView('9'))
|
||||
|
||||
container.height(400)
|
||||
container.width(400)
|
||||
container.attachToDom()
|
||||
|
||||
describe ".focusPaneViewAbove()", ->
|
||||
describe "when there are multiple rows above the focused pane", ->
|
||||
it "focuses up to the adjacent row", ->
|
||||
pane8.focus()
|
||||
container.focusPaneViewAbove()
|
||||
expect(pane5.activeItem).toMatchSelector ':focus'
|
||||
|
||||
describe "when there are no rows above the focused pane", ->
|
||||
it "keeps the current pane focused", ->
|
||||
pane2.focus()
|
||||
container.focusPaneViewAbove()
|
||||
expect(pane2.activeItem).toMatchSelector ':focus'
|
||||
|
||||
describe ".focusPaneViewBelow()", ->
|
||||
describe "when there are multiple rows below the focused pane", ->
|
||||
it "focuses down to the adjacent row", ->
|
||||
pane2.focus()
|
||||
container.focusPaneViewBelow()
|
||||
expect(pane5.activeItem).toMatchSelector ':focus'
|
||||
|
||||
describe "when there are no rows below the focused pane", ->
|
||||
it "keeps the current pane focused", ->
|
||||
pane8.focus()
|
||||
container.focusPaneViewBelow()
|
||||
expect(pane8.activeItem).toMatchSelector ':focus'
|
||||
|
||||
describe ".focusPaneViewOnLeft()", ->
|
||||
describe "when there are multiple columns to the left of the focused pane", ->
|
||||
it "focuses left to the adjacent column", ->
|
||||
pane6.focus()
|
||||
container.focusPaneViewOnLeft()
|
||||
expect(pane5.activeItem).toMatchSelector ':focus'
|
||||
|
||||
describe "when there are no columns to the left of the focused pane", ->
|
||||
it "keeps the current pane focused", ->
|
||||
pane4.focus()
|
||||
container.focusPaneViewOnLeft()
|
||||
expect(pane4.activeItem).toMatchSelector ':focus'
|
||||
|
||||
describe ".focusPaneViewOnRight()", ->
|
||||
describe "when there are multiple columns to the right of the focused pane", ->
|
||||
it "focuses right to the adjacent column", ->
|
||||
pane4.focus()
|
||||
container.focusPaneViewOnRight()
|
||||
expect(pane5.activeItem).toMatchSelector ':focus'
|
||||
|
||||
describe "when there are no columns to the right of the focused pane", ->
|
||||
it "keeps the current pane focused", ->
|
||||
pane6.focus()
|
||||
container.focusPaneViewOnRight()
|
||||
expect(pane6.activeItem).toMatchSelector ':focus'
|
||||
197
spec/pane-element-spec.coffee
Normal file
197
spec/pane-element-spec.coffee
Normal file
@@ -0,0 +1,197 @@
|
||||
PaneContainer = require '../src/pane-container'
|
||||
|
||||
describe "PaneElement", ->
|
||||
[paneElement, container, pane] = []
|
||||
|
||||
beforeEach ->
|
||||
container = new PaneContainer
|
||||
pane = container.getRoot()
|
||||
paneElement = atom.views.getView(pane)
|
||||
|
||||
describe "when the pane's active status changes", ->
|
||||
it "adds or removes the .active class as appropriate", ->
|
||||
pane2 = pane.splitRight()
|
||||
expect(pane2.isActive()).toBe true
|
||||
|
||||
expect(paneElement.className).not.toMatch /active/
|
||||
pane.activate()
|
||||
expect(paneElement.className).toMatch /active/
|
||||
pane2.activate()
|
||||
expect(paneElement.className).not.toMatch /active/
|
||||
|
||||
describe "when the active item changes", ->
|
||||
it "hides all item elements except the active one", ->
|
||||
item1 = document.createElement('div')
|
||||
item2 = document.createElement('div')
|
||||
item3 = document.createElement('div')
|
||||
pane.addItem(item1)
|
||||
pane.addItem(item2)
|
||||
pane.addItem(item3)
|
||||
|
||||
expect(pane.getActiveItem()).toBe item1
|
||||
expect(item1.parentElement).toBeDefined()
|
||||
expect(item1.style.display).toBe ''
|
||||
expect(item2.parentElement).toBeNull()
|
||||
expect(item3.parentElement).toBeNull()
|
||||
|
||||
pane.activateItem(item2)
|
||||
expect(item2.parentElement).toBeDefined()
|
||||
expect(item1.style.display).toBe 'none'
|
||||
expect(item2.style.display).toBe ''
|
||||
expect(item3.parentElement).toBeNull()
|
||||
|
||||
pane.activateItem(item3)
|
||||
expect(item3.parentElement).toBeDefined()
|
||||
expect(item1.style.display).toBe 'none'
|
||||
expect(item2.style.display).toBe 'none'
|
||||
expect(item3.style.display).toBe ''
|
||||
|
||||
it "transfers focus to the new item if the previous item was focused", ->
|
||||
item1 = document.createElement('div')
|
||||
item1.tabIndex = -1
|
||||
item2 = document.createElement('div')
|
||||
item2.tabIndex = -1
|
||||
pane.addItem(item1)
|
||||
pane.addItem(item2)
|
||||
jasmine.attachToDOM(paneElement)
|
||||
paneElement.focus()
|
||||
|
||||
expect(document.activeElement).toBe item1
|
||||
pane.activateItem(item2)
|
||||
expect(document.activeElement).toBe item2
|
||||
|
||||
describe "if the active item is a model object", ->
|
||||
it "retrieves the associated view from atom.views and appends it to the itemViews div", ->
|
||||
class TestModel
|
||||
|
||||
atom.views.addViewProvider TestModel, (model) ->
|
||||
view = document.createElement('div')
|
||||
view.model = model
|
||||
view
|
||||
|
||||
item1 = new TestModel
|
||||
item2 = new TestModel
|
||||
pane.addItem(item1)
|
||||
pane.addItem(item2)
|
||||
|
||||
expect(paneElement.itemViews.children[0].model).toBe item1
|
||||
expect(paneElement.itemViews.children[0].style.display).toBe ''
|
||||
pane.activateItem(item2)
|
||||
expect(paneElement.itemViews.children[1].model).toBe item2
|
||||
expect(paneElement.itemViews.children[0].style.display).toBe 'none'
|
||||
expect(paneElement.itemViews.children[1].style.display).toBe ''
|
||||
|
||||
describe "when the new active implements .getPath()", ->
|
||||
it "adds the file path and file name as a data attribute on the pane", ->
|
||||
item1 = document.createElement('div')
|
||||
item1.getPath = -> '/foo/bar.txt'
|
||||
item2 = document.createElement('div')
|
||||
pane.addItem(item1)
|
||||
pane.addItem(item2)
|
||||
|
||||
expect(paneElement.dataset.activeItemPath).toBe '/foo/bar.txt'
|
||||
expect(paneElement.dataset.activeItemName).toBe 'bar.txt'
|
||||
|
||||
pane.activateItem(item2)
|
||||
|
||||
expect(paneElement.dataset.activeItemPath).toBeUndefined()
|
||||
expect(paneElement.dataset.activeItemName).toBeUndefined()
|
||||
|
||||
pane.activateItem(item1)
|
||||
expect(paneElement.dataset.activeItemPath).toBe '/foo/bar.txt'
|
||||
expect(paneElement.dataset.activeItemName).toBe 'bar.txt'
|
||||
|
||||
pane.destroyItems()
|
||||
expect(paneElement.dataset.activeItemPath).toBeUndefined()
|
||||
expect(paneElement.dataset.activeItemName).toBeUndefined()
|
||||
|
||||
describe "when an item is removed from the pane", ->
|
||||
describe "when the destroyed item is an element", ->
|
||||
it "removes the item from the itemViews div", ->
|
||||
item1 = document.createElement('div')
|
||||
item2 = document.createElement('div')
|
||||
pane.addItem(item1)
|
||||
pane.addItem(item2)
|
||||
paneElement = atom.views.getView(pane)
|
||||
|
||||
expect(item1.parentElement).toBe paneElement.itemViews
|
||||
pane.destroyItem(item1)
|
||||
expect(item1.parentElement).toBeNull()
|
||||
expect(item2.parentElement).toBe paneElement.itemViews
|
||||
pane.destroyItem(item2)
|
||||
expect(item2.parentElement).toBeNull()
|
||||
|
||||
describe "when the destroyed item is a model", ->
|
||||
it "removes the model's associated view", ->
|
||||
class TestModel
|
||||
|
||||
atom.views.addViewProvider TestModel, (model) ->
|
||||
view = document.createElement('div')
|
||||
model.element = view
|
||||
view.model = model
|
||||
view
|
||||
|
||||
item1 = new TestModel
|
||||
item2 = new TestModel
|
||||
pane.addItem(item1)
|
||||
pane.addItem(item2)
|
||||
|
||||
expect(item1.element.parentElement).toBe paneElement.itemViews
|
||||
pane.destroyItem(item1)
|
||||
expect(item1.element.parentElement).toBeNull()
|
||||
expect(item2.element.parentElement).toBe paneElement.itemViews
|
||||
pane.destroyItem(item2)
|
||||
expect(item2.element.parentElement).toBeNull()
|
||||
|
||||
describe "when the pane element is focused", ->
|
||||
it "transfers focus to the active view", ->
|
||||
item = document.createElement('div')
|
||||
item.tabIndex = -1
|
||||
pane.activateItem(item)
|
||||
jasmine.attachToDOM(paneElement)
|
||||
|
||||
expect(document.activeElement).toBe document.body
|
||||
paneElement.focus()
|
||||
expect(document.activeElement).toBe item
|
||||
|
||||
it "makes the pane active", ->
|
||||
pane.splitRight()
|
||||
expect(pane.isActive()).toBe false
|
||||
|
||||
jasmine.attachToDOM(paneElement)
|
||||
paneElement.focus()
|
||||
|
||||
expect(pane.isActive()).toBe true
|
||||
|
||||
describe "when the pane element is attached", ->
|
||||
it "focuses the pane element if isFocused() returns true on its model", ->
|
||||
pane.focus()
|
||||
jasmine.attachToDOM(paneElement)
|
||||
expect(document.activeElement).toBe paneElement
|
||||
|
||||
describe "drag and drop", ->
|
||||
buildDragEvent = (type, files) ->
|
||||
dataTransfer =
|
||||
files: files
|
||||
data: {}
|
||||
setData: (key, value) -> @data[key] = value
|
||||
getData: (key) -> @data[key]
|
||||
|
||||
event = new CustomEvent("drop")
|
||||
event.dataTransfer = dataTransfer
|
||||
event
|
||||
|
||||
describe "when a file is dragged to the pane", ->
|
||||
it "opens it", ->
|
||||
spyOn(atom, "open")
|
||||
event = buildDragEvent("drop", [{path: "/fake1"}, {path: "/fake2"}])
|
||||
paneElement.dispatchEvent(event)
|
||||
expect(atom.open.callCount).toBe 1
|
||||
expect(atom.open.argsForCall[0][0]).toEqual pathsToOpen: ['/fake1', '/fake2']
|
||||
|
||||
describe "when a non-file is dragged to the pane", ->
|
||||
it "does nothing", ->
|
||||
spyOn(atom, "open")
|
||||
event = buildDragEvent("drop", [])
|
||||
paneElement.dispatchEvent(event)
|
||||
expect(atom.open).not.toHaveBeenCalled()
|
||||
@@ -1,4 +1,4 @@
|
||||
{Model} = require 'theorist'
|
||||
{Emitter} = require 'event-kit'
|
||||
Pane = require '../src/pane'
|
||||
PaneAxis = require '../src/pane-axis'
|
||||
PaneContainer = require '../src/pane-container'
|
||||
@@ -6,13 +6,17 @@ PaneContainer = require '../src/pane-container'
|
||||
describe "Pane", ->
|
||||
deserializerDisposable = null
|
||||
|
||||
class Item extends Model
|
||||
class Item
|
||||
@deserialize: ({name, uri}) -> new this(name, uri)
|
||||
constructor: (@name, @uri) ->
|
||||
constructor: (@name, @uri) -> @emitter = new Emitter
|
||||
destroyed: false
|
||||
getURI: -> @uri
|
||||
getPath: -> @path
|
||||
serialize: -> {deserializer: 'Item', @name, @uri}
|
||||
isEqual: (other) -> @name is other?.name
|
||||
onDidDestroy: (fn) -> @emitter.on('did-destroy', fn)
|
||||
destroy: -> @destroyed = true; @emitter.emit('did-destroy')
|
||||
isDestroyed: -> @destroyed
|
||||
|
||||
beforeEach ->
|
||||
deserializerDisposable = atom.deserializers.add(Item)
|
||||
@@ -99,7 +103,7 @@ describe "Pane", ->
|
||||
|
||||
item = new Item("C")
|
||||
pane.addItem(item, 1)
|
||||
expect(events).toEqual [{item, index: 1}]
|
||||
expect(events).toEqual [{item, index: 1, moved: false}]
|
||||
|
||||
it "throws an exception if the item is already present on a pane", ->
|
||||
item = new Item("A")
|
||||
@@ -219,13 +223,13 @@ describe "Pane", ->
|
||||
events = []
|
||||
pane.onWillRemoveItem (event) -> events.push(event)
|
||||
pane.destroyItem(item2)
|
||||
expect(events).toEqual [{item: item2, index: 1, destroyed: true}]
|
||||
expect(events).toEqual [{item: item2, index: 1, moved: false, destroyed: true}]
|
||||
|
||||
it "invokes ::onDidRemoveItem() observers", ->
|
||||
events = []
|
||||
pane.onDidRemoveItem (event) -> events.push(event)
|
||||
pane.destroyItem(item2)
|
||||
expect(events).toEqual [{item: item2, index: 1, destroyed: true}]
|
||||
expect(events).toEqual [{item: item2, index: 1, moved: false, destroyed: true}]
|
||||
|
||||
describe "when the destroyed item is the active item and is the first item", ->
|
||||
it "activates the next item", ->
|
||||
@@ -513,14 +517,20 @@ describe "Pane", ->
|
||||
pane1.onWillRemoveItem (event) -> events.push(event)
|
||||
pane1.moveItemToPane(item2, pane2, 1)
|
||||
|
||||
expect(events).toEqual [{item: item2, index: 1, destroyed: false}]
|
||||
expect(events).toEqual [{item: item2, index: 1, moved: true, destroyed: false}]
|
||||
|
||||
it "invokes ::onDidRemoveItem() observers", ->
|
||||
events = []
|
||||
pane1.onDidRemoveItem (event) -> events.push(event)
|
||||
pane1.moveItemToPane(item2, pane2, 1)
|
||||
|
||||
expect(events).toEqual [{item: item2, index: 1, destroyed: false}]
|
||||
expect(events).toEqual [{item: item2, index: 1, moved: true, destroyed: false}]
|
||||
|
||||
it "does not invoke ::onDidAddPaneItem observers on the container", ->
|
||||
addedItems = []
|
||||
container.onDidAddPaneItem (item) -> addedItems.push(item)
|
||||
pane1.moveItemToPane(item2, pane2, 1)
|
||||
expect(addedItems).toEqual []
|
||||
|
||||
describe "when the moved item the last item in the source pane", ->
|
||||
beforeEach ->
|
||||
@@ -728,12 +738,12 @@ describe "Pane", ->
|
||||
pane = new Pane(params)
|
||||
|
||||
it "can serialize and deserialize the pane and all its items", ->
|
||||
newPane = pane.testSerialization()
|
||||
newPane = Pane.deserialize(pane.serialize())
|
||||
expect(newPane.getItems()).toEqual pane.getItems()
|
||||
|
||||
it "restores the active item on deserialization", ->
|
||||
pane.activateItemAtIndex(1)
|
||||
newPane = pane.testSerialization()
|
||||
newPane = Pane.deserialize(pane.serialize())
|
||||
expect(newPane.getActiveItem()).toEqual newPane.itemAtIndex(1)
|
||||
|
||||
it "does not include items that cannot be deserialized", ->
|
||||
@@ -741,11 +751,11 @@ describe "Pane", ->
|
||||
unserializable = {}
|
||||
pane.activateItem(unserializable)
|
||||
|
||||
newPane = pane.testSerialization()
|
||||
newPane = Pane.deserialize(pane.serialize())
|
||||
expect(newPane.getActiveItem()).toEqual pane.itemAtIndex(0)
|
||||
expect(newPane.getItems().length).toBe pane.getItems().length - 1
|
||||
|
||||
it "includes the pane's focus state in the serialized state", ->
|
||||
pane.focus()
|
||||
newPane = pane.testSerialization()
|
||||
newPane = Pane.deserialize(pane.serialize())
|
||||
expect(newPane.focused).toBe true
|
||||
|
||||
@@ -1,389 +0,0 @@
|
||||
PaneContainer = require '../src/pane-container'
|
||||
PaneView = require '../src/pane-view'
|
||||
fs = require 'fs-plus'
|
||||
{Emitter, Disposable} = require 'event-kit'
|
||||
{$, View} = require '../src/space-pen-extensions'
|
||||
path = require 'path'
|
||||
temp = require 'temp'
|
||||
|
||||
describe "PaneView", ->
|
||||
[container, containerModel, view1, view2, editor1, editor2, pane, paneModel, deserializerDisposable] = []
|
||||
|
||||
class TestView extends View
|
||||
@deserialize: ({id, text}) -> new TestView({id, text})
|
||||
@content: ({id, text}) -> @div class: 'test-view', id: id, tabindex: -1, text
|
||||
initialize: ({@id, @text}) ->
|
||||
@emitter = new Emitter
|
||||
serialize: -> {deserializer: 'TestView', @id, @text}
|
||||
getURI: -> @id
|
||||
isEqual: (other) -> other? and @id is other.id and @text is other.text
|
||||
changeTitle: ->
|
||||
@emitter.emit 'did-change-title', 'title'
|
||||
onDidChangeTitle: (callback) ->
|
||||
@emitter.on 'did-change-title', callback
|
||||
onDidChangeModified: -> new Disposable(->)
|
||||
|
||||
beforeEach ->
|
||||
jasmine.snapshotDeprecations()
|
||||
|
||||
deserializerDisposable = atom.deserializers.add(TestView)
|
||||
container = atom.views.getView(new PaneContainer).__spacePenView
|
||||
containerModel = container.model
|
||||
view1 = new TestView(id: 'view-1', text: 'View 1')
|
||||
view2 = new TestView(id: 'view-2', text: 'View 2')
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('sample.js').then (o) -> editor1 = o
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('sample.txt').then (o) -> editor2 = o
|
||||
|
||||
runs ->
|
||||
pane = container.getRoot()
|
||||
paneModel = pane.getModel()
|
||||
paneModel.addItems([view1, editor1, view2, editor2])
|
||||
|
||||
afterEach ->
|
||||
deserializerDisposable.dispose()
|
||||
jasmine.restoreDeprecationsSnapshot()
|
||||
|
||||
describe "when the active pane item changes", ->
|
||||
it "hides all item views except the active one", ->
|
||||
expect(pane.getActiveItem()).toBe view1
|
||||
expect(view1.css('display')).not.toBe 'none'
|
||||
|
||||
pane.activateItem(view2)
|
||||
expect(view1.css('display')).toBe 'none'
|
||||
expect(view2.css('display')).not.toBe 'none'
|
||||
|
||||
it "triggers 'pane:active-item-changed'", ->
|
||||
itemChangedHandler = jasmine.createSpy("itemChangedHandler")
|
||||
container.on 'pane:active-item-changed', itemChangedHandler
|
||||
|
||||
expect(pane.getActiveItem()).toBe view1
|
||||
paneModel.activateItem(view2)
|
||||
paneModel.activateItem(view2)
|
||||
|
||||
expect(itemChangedHandler.callCount).toBe 1
|
||||
expect(itemChangedHandler.argsForCall[0][1]).toBe view2
|
||||
itemChangedHandler.reset()
|
||||
|
||||
paneModel.activateItem(editor1)
|
||||
expect(itemChangedHandler).toHaveBeenCalled()
|
||||
expect(itemChangedHandler.argsForCall[0][1]).toBe editor1
|
||||
itemChangedHandler.reset()
|
||||
|
||||
it "transfers focus to the new active view if the previous view was focused", ->
|
||||
container.attachToDom()
|
||||
pane.focus()
|
||||
expect(pane.activeView).not.toBe view2
|
||||
expect(pane.activeView).toMatchSelector ':focus'
|
||||
paneModel.activateItem(view2)
|
||||
expect(view2).toMatchSelector ':focus'
|
||||
|
||||
describe "when the new activeItem is a model", ->
|
||||
it "shows the item's view or creates and shows a new view for the item if none exists", ->
|
||||
initialViewCount = pane.itemViews.find('.test-view').length
|
||||
|
||||
model1 =
|
||||
id: 'test-model-1'
|
||||
text: 'Test Model 1'
|
||||
serialize: -> {@id, @text}
|
||||
getViewClass: -> TestView
|
||||
|
||||
model2 =
|
||||
id: 'test-model-2'
|
||||
text: 'Test Model 2'
|
||||
serialize: -> {@id, @text}
|
||||
getViewClass: -> TestView
|
||||
|
||||
paneModel.activateItem(model1)
|
||||
paneModel.activateItem(model2)
|
||||
expect(pane.itemViews.find('.test-view').length).toBe initialViewCount + 2
|
||||
|
||||
paneModel.activatePreviousItem()
|
||||
expect(pane.itemViews.find('.test-view').length).toBe initialViewCount + 2
|
||||
|
||||
paneModel.destroyItem(model2)
|
||||
expect(pane.itemViews.find('.test-view').length).toBe initialViewCount + 1
|
||||
|
||||
paneModel.destroyItem(model1)
|
||||
expect(pane.itemViews.find('.test-view').length).toBe initialViewCount
|
||||
|
||||
describe "when the new activeItem is a view", ->
|
||||
it "appends it to the itemViews div if it hasn't already been appended and shows it", ->
|
||||
expect(pane.itemViews.find('#view-2')).not.toExist()
|
||||
paneModel.activateItem(view2)
|
||||
expect(pane.itemViews.find('#view-2')).toExist()
|
||||
paneModel.activateItem(view1)
|
||||
paneModel.activateItem(view2)
|
||||
expect(pane.itemViews.find('#view-2').length).toBe 1
|
||||
|
||||
describe "when the new activeItem implements ::getPath", ->
|
||||
beforeEach ->
|
||||
paneModel.activateItem(editor1)
|
||||
|
||||
it "adds the file path as a data attribute to the pane", ->
|
||||
expect(pane).toHaveAttr('data-active-item-path')
|
||||
|
||||
it "adds the file name as a data attribute to the pane", ->
|
||||
expect(pane).toHaveAttr('data-active-item-name')
|
||||
|
||||
describe "when the activeItem is destroyed", ->
|
||||
it "removes the data attributes", ->
|
||||
pane.destroyItems()
|
||||
expect(pane).not.toHaveAttr('data-active-item-path')
|
||||
expect(pane).not.toHaveAttr('data-active-item-name')
|
||||
|
||||
describe "when the new activeItem does not implement ::getPath", ->
|
||||
beforeEach ->
|
||||
paneModel.activateItem(editor1)
|
||||
paneModel.activateItem(document.createElement('div'))
|
||||
|
||||
it "does not add the file path as a data attribute to the pane", ->
|
||||
expect(pane).not.toHaveAttr('data-active-item-path')
|
||||
|
||||
it "does not add the file name as data attribute to the pane", ->
|
||||
expect(pane).not.toHaveAttr('data-active-item-name')
|
||||
|
||||
describe "when an item is destroyed", ->
|
||||
it "triggers the 'pane:item-removed' event with the item and its former index", ->
|
||||
itemRemovedHandler = jasmine.createSpy("itemRemovedHandler")
|
||||
pane.on 'pane:item-removed', itemRemovedHandler
|
||||
paneModel.destroyItem(editor1)
|
||||
expect(itemRemovedHandler).toHaveBeenCalled()
|
||||
expect(itemRemovedHandler.argsForCall[0][1..2]).toEqual [editor1, 1]
|
||||
|
||||
describe "when the destroyed item is a view", ->
|
||||
it "removes the item from the 'item-views' div", ->
|
||||
expect(view1.parent()).toMatchSelector pane.itemViews
|
||||
paneModel.destroyItem(view1)
|
||||
expect(view1.parent()).not.toMatchSelector pane.itemViews
|
||||
|
||||
describe "when the destroyed item is a model", ->
|
||||
it "removes the associated view", ->
|
||||
paneModel.activateItem(editor1)
|
||||
expect(pane.itemViews.find('atom-text-editor').length).toBe 1
|
||||
pane.destroyItem(editor1)
|
||||
expect(pane.itemViews.find('atom-text-editor').length).toBe 0
|
||||
|
||||
describe "when an item is moved within the same pane", ->
|
||||
it "emits a 'pane:item-moved' event with the item and the new index", ->
|
||||
pane.on 'pane:item-moved', itemMovedHandler = jasmine.createSpy("itemMovedHandler")
|
||||
paneModel.moveItem(view1, 2)
|
||||
expect(itemMovedHandler).toHaveBeenCalled()
|
||||
expect(itemMovedHandler.argsForCall[0][1..2]).toEqual [view1, 2]
|
||||
|
||||
describe "when an item is moved to another pane", ->
|
||||
it "detaches the item's view rather than removing it", ->
|
||||
container.attachToDom()
|
||||
expect(view1.is(':visible')).toBe true
|
||||
paneModel2 = paneModel.splitRight()
|
||||
view1.data('preservative', 1234)
|
||||
paneModel.moveItemToPane(view1, paneModel2, 1)
|
||||
expect(view1.data('preservative')).toBe 1234
|
||||
paneModel2.activateItemAtIndex(1)
|
||||
expect(view1.data('preservative')).toBe 1234
|
||||
expect(view1.is(':visible')).toBe true
|
||||
|
||||
describe "when the title of the active item changes", ->
|
||||
describe 'when there is no onDidChangeTitle method (deprecated)', ->
|
||||
beforeEach ->
|
||||
jasmine.snapshotDeprecations()
|
||||
|
||||
view1.onDidChangeTitle = null
|
||||
view2.onDidChangeTitle = null
|
||||
|
||||
pane.activateItem(view2)
|
||||
pane.activateItem(view1)
|
||||
|
||||
afterEach ->
|
||||
jasmine.restoreDeprecationsSnapshot()
|
||||
|
||||
it "emits pane:active-item-title-changed", ->
|
||||
activeItemTitleChangedHandler = jasmine.createSpy("activeItemTitleChangedHandler")
|
||||
pane.on 'pane:active-item-title-changed', activeItemTitleChangedHandler
|
||||
|
||||
expect(pane.getActiveItem()).toBe view1
|
||||
|
||||
view2.trigger 'title-changed'
|
||||
expect(activeItemTitleChangedHandler).not.toHaveBeenCalled()
|
||||
|
||||
view1.trigger 'title-changed'
|
||||
expect(activeItemTitleChangedHandler).toHaveBeenCalled()
|
||||
activeItemTitleChangedHandler.reset()
|
||||
|
||||
pane.activateItem(view2)
|
||||
view2.trigger 'title-changed'
|
||||
expect(activeItemTitleChangedHandler).toHaveBeenCalled()
|
||||
|
||||
describe 'when there is a onDidChangeTitle method', ->
|
||||
it "emits pane:active-item-title-changed", ->
|
||||
activeItemTitleChangedHandler = jasmine.createSpy("activeItemTitleChangedHandler")
|
||||
pane.on 'pane:active-item-title-changed', activeItemTitleChangedHandler
|
||||
|
||||
expect(pane.getActiveItem()).toBe view1
|
||||
view2.changeTitle()
|
||||
expect(activeItemTitleChangedHandler).not.toHaveBeenCalled()
|
||||
|
||||
view1.changeTitle()
|
||||
expect(activeItemTitleChangedHandler).toHaveBeenCalled()
|
||||
activeItemTitleChangedHandler.reset()
|
||||
|
||||
pane.activateItem(view2)
|
||||
view2.changeTitle()
|
||||
expect(activeItemTitleChangedHandler).toHaveBeenCalled()
|
||||
|
||||
describe "when an unmodifed buffer's path is deleted", ->
|
||||
it "removes the pane item", ->
|
||||
editor = null
|
||||
jasmine.unspy(window, 'setTimeout')
|
||||
filePath = path.join(temp.mkdirSync(), 'file.txt')
|
||||
fs.writeFileSync(filePath, '')
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open(filePath).then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
pane.activateItem(editor)
|
||||
expect(pane.items).toHaveLength(5)
|
||||
fs.removeSync(filePath)
|
||||
|
||||
waitsFor ->
|
||||
pane.items.length is 4
|
||||
|
||||
describe "when a pane is destroyed", ->
|
||||
[pane2, pane2Model] = []
|
||||
|
||||
beforeEach ->
|
||||
pane2Model = paneModel.splitRight() # Can't destroy the last pane, so we add another
|
||||
pane2 = atom.views.getView(pane2Model).__spacePenView
|
||||
|
||||
it "triggers a 'pane:removed' event with the pane", ->
|
||||
removedHandler = jasmine.createSpy("removedHandler")
|
||||
container.on 'pane:removed', removedHandler
|
||||
paneModel.destroy()
|
||||
expect(removedHandler).toHaveBeenCalled()
|
||||
expect(removedHandler.argsForCall[0][1]).toBe pane
|
||||
|
||||
describe "if the destroyed pane has focus", ->
|
||||
[paneToLeft, paneToRight] = []
|
||||
|
||||
it "focuses the next pane", ->
|
||||
container.attachToDom()
|
||||
pane2.activate()
|
||||
expect(pane.hasFocus()).toBe false
|
||||
expect(pane2.hasFocus()).toBe true
|
||||
pane2Model.destroy()
|
||||
expect(pane.hasFocus()).toBe true
|
||||
|
||||
describe "::getNextPane()", ->
|
||||
it "returns the next pane if one exists, wrapping around from the last pane to the first", ->
|
||||
pane.activateItem(editor1)
|
||||
expect(pane.getNextPane()).toBeUndefined
|
||||
pane2 = pane.splitRight(pane.copyActiveItem())
|
||||
expect(pane.getNextPane()).toBe pane2
|
||||
expect(pane2.getNextPane()).toBe pane
|
||||
|
||||
describe "when the pane's active status changes", ->
|
||||
[pane2, pane2Model] = []
|
||||
|
||||
beforeEach ->
|
||||
pane2Model = paneModel.splitRight(items: [pane.copyActiveItem()])
|
||||
pane2 = atom.views.getView(pane2Model).__spacePenView
|
||||
expect(pane2Model.isActive()).toBe true
|
||||
|
||||
it "adds or removes the .active class as appropriate", ->
|
||||
expect(pane).not.toHaveClass('active')
|
||||
paneModel.activate()
|
||||
expect(pane).toHaveClass('active')
|
||||
pane2Model.activate()
|
||||
expect(pane).not.toHaveClass('active')
|
||||
|
||||
it "triggers 'pane:became-active' or 'pane:became-inactive' according to the current status", ->
|
||||
pane.on 'pane:became-active', becameActiveHandler = jasmine.createSpy("becameActiveHandler")
|
||||
pane.on 'pane:became-inactive', becameInactiveHandler = jasmine.createSpy("becameInactiveHandler")
|
||||
paneModel.activate()
|
||||
|
||||
expect(becameActiveHandler.callCount).toBe 1
|
||||
expect(becameInactiveHandler.callCount).toBe 0
|
||||
|
||||
pane2Model.activate()
|
||||
expect(becameActiveHandler.callCount).toBe 1
|
||||
expect(becameInactiveHandler.callCount).toBe 1
|
||||
|
||||
describe "when the pane is focused", ->
|
||||
beforeEach ->
|
||||
container.attachToDom()
|
||||
|
||||
it "transfers focus to the active view", ->
|
||||
focusHandler = jasmine.createSpy("focusHandler")
|
||||
pane.getActiveItem().on 'focus', focusHandler
|
||||
pane.focus()
|
||||
expect(focusHandler).toHaveBeenCalled()
|
||||
|
||||
it "makes the pane active", ->
|
||||
paneModel.splitRight(items: [pane.copyActiveItem()])
|
||||
expect(paneModel.isActive()).toBe false
|
||||
pane.focus()
|
||||
expect(paneModel.isActive()).toBe true
|
||||
|
||||
describe "when a pane is split", ->
|
||||
it "builds the appropriateatom-pane-axis.horizontal and pane-column views", ->
|
||||
pane1 = pane
|
||||
pane1Model = pane.getModel()
|
||||
pane.activateItem(editor1)
|
||||
|
||||
pane2Model = pane1Model.splitRight(items: [pane1Model.copyActiveItem()])
|
||||
pane3Model = pane2Model.splitDown(items: [pane2Model.copyActiveItem()])
|
||||
pane2 = pane2Model._view
|
||||
pane2 = atom.views.getView(pane2Model).__spacePenView
|
||||
pane3 = atom.views.getView(pane3Model).__spacePenView
|
||||
|
||||
expect(container.find('> atom-pane-axis.horizontal > atom-pane').toArray()).toEqual [pane1[0]]
|
||||
expect(container.find('> atom-pane-axis.horizontal > atom-pane-axis.vertical > atom-pane').toArray()).toEqual [pane2[0], pane3[0]]
|
||||
|
||||
pane1Model.destroy()
|
||||
expect(container.find('> atom-pane-axis.vertical > atom-pane').toArray()).toEqual [pane2[0], pane3[0]]
|
||||
|
||||
describe "serialization", ->
|
||||
it "focuses the pane after attach only if had focus when serialized", ->
|
||||
container.attachToDom()
|
||||
pane.focus()
|
||||
|
||||
container2 = atom.views.getView(container.model.testSerialization()).__spacePenView
|
||||
pane2 = container2.getRoot()
|
||||
container2.attachToDom()
|
||||
expect(pane2).toMatchSelector(':has(:focus)')
|
||||
|
||||
$(document.activeElement).blur()
|
||||
container3 = atom.views.getView(container.model.testSerialization()).__spacePenView
|
||||
pane3 = container3.getRoot()
|
||||
container3.attachToDom()
|
||||
expect(pane3).not.toMatchSelector(':has(:focus)')
|
||||
|
||||
describe "drag and drop", ->
|
||||
buildDragEvent = (type, files) ->
|
||||
dataTransfer =
|
||||
files: files
|
||||
data: {}
|
||||
setData: (key, value) -> @data[key] = value
|
||||
getData: (key) -> @data[key]
|
||||
|
||||
event = new CustomEvent("drop")
|
||||
event.dataTransfer = dataTransfer
|
||||
event
|
||||
|
||||
describe "when a file is dragged to window", ->
|
||||
it "opens it", ->
|
||||
spyOn(atom, "open")
|
||||
event = buildDragEvent("drop", [ {path: "/fake1"}, {path: "/fake2"} ])
|
||||
pane[0].dispatchEvent(event)
|
||||
expect(atom.open.callCount).toBe 1
|
||||
expect(atom.open.argsForCall[0][0]).toEqual pathsToOpen: ['/fake1', '/fake2']
|
||||
|
||||
describe "when a non-file is dragged to window", ->
|
||||
it "does nothing", ->
|
||||
spyOn(atom, "open")
|
||||
event = buildDragEvent("drop", [])
|
||||
pane[0].dispatchEvent(event)
|
||||
expect(atom.open).not.toHaveBeenCalled()
|
||||
@@ -66,7 +66,7 @@ describe "Project", ->
|
||||
|
||||
runs ->
|
||||
expect(atom.project.getBuffers().length).toBe 1
|
||||
deserializedProject = atom.project.testSerialization()
|
||||
deserializedProject = Project.deserialize(atom.project.serialize())
|
||||
expect(deserializedProject.getBuffers().length).toBe 0
|
||||
|
||||
it "listens for destroyed events on deserialized buffers and removes them when they are destroyed", ->
|
||||
@@ -75,7 +75,7 @@ describe "Project", ->
|
||||
|
||||
runs ->
|
||||
expect(atom.project.getBuffers().length).toBe 1
|
||||
deserializedProject = atom.project.testSerialization()
|
||||
deserializedProject = Project.deserialize(atom.project.serialize())
|
||||
|
||||
expect(deserializedProject.getBuffers().length).toBe 1
|
||||
deserializedProject.getBuffers()[0].destroy()
|
||||
@@ -91,7 +91,7 @@ describe "Project", ->
|
||||
runs ->
|
||||
expect(atom.project.getBuffers().length).toBe 1
|
||||
fs.mkdirSync(pathToOpen)
|
||||
deserializedProject = atom.project.testSerialization()
|
||||
deserializedProject = Project.deserialize(atom.project.serialize())
|
||||
expect(deserializedProject.getBuffers().length).toBe 0
|
||||
|
||||
it "does not deserialize buffers when their path is inaccessible", ->
|
||||
@@ -104,7 +104,7 @@ describe "Project", ->
|
||||
runs ->
|
||||
expect(atom.project.getBuffers().length).toBe 1
|
||||
fs.chmodSync(pathToOpen, '000')
|
||||
deserializedProject = atom.project.testSerialization()
|
||||
deserializedProject = Project.deserialize(atom.project.serialize())
|
||||
expect(deserializedProject.getBuffers().length).toBe 0
|
||||
|
||||
describe "when an editor is saved and the project has no path", ->
|
||||
@@ -201,18 +201,6 @@ describe "Project", ->
|
||||
expect(editor.buffer.getPath()).toBeUndefined()
|
||||
expect(newBufferHandler).toHaveBeenCalledWith(editor.buffer)
|
||||
|
||||
it "returns number of read bytes as progress indicator", ->
|
||||
filePath = atom.project.getDirectories()[0]?.resolve 'a'
|
||||
totalBytes = 0
|
||||
promise = atom.project.open(filePath)
|
||||
promise.progress (bytesRead) -> totalBytes = bytesRead
|
||||
|
||||
waitsForPromise ->
|
||||
promise
|
||||
|
||||
runs ->
|
||||
expect(totalBytes).toBe fs.statSync(filePath).size
|
||||
|
||||
describe ".bufferForPath(path)", ->
|
||||
[buffer] = []
|
||||
beforeEach ->
|
||||
@@ -520,36 +508,3 @@ describe "Project", ->
|
||||
|
||||
randomPath = path.join("some", "random", "path")
|
||||
expect(atom.project.contains(randomPath)).toBe false
|
||||
|
||||
describe ".eachBuffer(callback)", ->
|
||||
beforeEach ->
|
||||
jasmine.snapshotDeprecations()
|
||||
atom.project.bufferForPathSync('a')
|
||||
|
||||
afterEach ->
|
||||
jasmine.restoreDeprecationsSnapshot()
|
||||
|
||||
it "invokes the callback for existing buffer", ->
|
||||
count = 0
|
||||
count = 0
|
||||
callbackBuffer = null
|
||||
callback = (buffer) ->
|
||||
callbackBuffer = buffer
|
||||
count++
|
||||
atom.project.eachBuffer(callback)
|
||||
expect(count).toBe 1
|
||||
expect(callbackBuffer).toBe atom.project.getBuffers()[0]
|
||||
|
||||
it "invokes the callback for new buffers", ->
|
||||
count = 0
|
||||
callbackBuffer = null
|
||||
callback = (buffer) ->
|
||||
callbackBuffer = buffer
|
||||
count++
|
||||
|
||||
atom.project.eachBuffer(callback)
|
||||
count = 0
|
||||
callbackBuffer = null
|
||||
atom.project.bufferForPathSync(require.resolve('./fixtures/sample.txt'))
|
||||
expect(count).toBe 1
|
||||
expect(callbackBuffer).toBe atom.project.getBuffers()[1]
|
||||
|
||||
@@ -1,212 +0,0 @@
|
||||
SelectListView = require '../src/select-list-view'
|
||||
{$, $$} = require '../src/space-pen-extensions'
|
||||
|
||||
describe "SelectListView", ->
|
||||
[selectList, items, list, filterEditorView] = []
|
||||
|
||||
beforeEach ->
|
||||
items = [
|
||||
["A", "Alpha"], ["B", "Bravo"], ["C", "Charlie"],
|
||||
["D", "Delta"], ["E", "Echo"], ["F", "Foxtrot"]
|
||||
]
|
||||
|
||||
selectList = new SelectListView
|
||||
selectList.setMaxItems(4)
|
||||
selectList.getFilterKey = -> 1
|
||||
selectList.viewForItem = (item) ->
|
||||
$$ -> @li item[1], class: item[0]
|
||||
|
||||
selectList.confirmed = jasmine.createSpy('confirmed hook')
|
||||
selectList.cancelled = jasmine.createSpy('cancelled hook')
|
||||
|
||||
selectList.setItems(items)
|
||||
{list, filterEditorView} = selectList
|
||||
|
||||
describe "when an array is assigned", ->
|
||||
it "populates the list with up to maxItems items, based on the liForElement function", ->
|
||||
expect(list.find('li').length).toBe selectList.maxItems
|
||||
expect(list.find('li:eq(0)')).toHaveText 'Alpha'
|
||||
expect(list.find('li:eq(0)')).toHaveClass 'A'
|
||||
|
||||
describe "viewForItem(item)", ->
|
||||
it "allows raw DOM elements to be returned", ->
|
||||
selectList.viewForItem = (item) ->
|
||||
li = document.createElement('li')
|
||||
li.classList.add(item[0])
|
||||
li.innerText = item[1]
|
||||
li
|
||||
|
||||
selectList.setItems(items)
|
||||
|
||||
expect(list.find('li').length).toBe selectList.maxItems
|
||||
expect(list.find('li:eq(0)')).toHaveText 'Alpha'
|
||||
expect(list.find('li:eq(0)')).toHaveClass 'A'
|
||||
expect(selectList.getSelectedItem()).toBe items[0]
|
||||
|
||||
it "allows raw HTML to be returned", ->
|
||||
selectList.viewForItem = (item) ->
|
||||
"<li>#{item}</li>"
|
||||
|
||||
selectList.setItems(['Bermuda', 'Bahama'])
|
||||
|
||||
expect(list.find('li:eq(0)')).toHaveText 'Bermuda'
|
||||
expect(selectList.getSelectedItem()).toBe 'Bermuda'
|
||||
|
||||
describe "when the text of the mini editor changes", ->
|
||||
beforeEach ->
|
||||
selectList.attachToDom()
|
||||
|
||||
it "filters the elements in the list based on the scoreElement function and selects the first item", ->
|
||||
filterEditorView.getEditor().insertText('la')
|
||||
window.advanceClock(selectList.inputThrottle)
|
||||
|
||||
expect(list.find('li').length).toBe 2
|
||||
expect(list.find('li:contains(Alpha)')).toExist()
|
||||
expect(list.find('li:contains(Delta)')).toExist()
|
||||
expect(list.find('li:first')).toHaveClass 'selected'
|
||||
expect(selectList.error).not.toBeVisible()
|
||||
|
||||
it "displays an error if there are no matches, removes error when there are matches", ->
|
||||
filterEditorView.getEditor().insertText('nothing will match this')
|
||||
window.advanceClock(selectList.inputThrottle)
|
||||
|
||||
expect(list.find('li').length).toBe 0
|
||||
expect(selectList.error).not.toBeHidden()
|
||||
|
||||
filterEditorView.getEditor().setText('la')
|
||||
window.advanceClock(selectList.inputThrottle)
|
||||
|
||||
expect(list.find('li').length).toBe 2
|
||||
expect(selectList.error).not.toBeVisible()
|
||||
|
||||
it "displays no elements until the array has been set on the list", ->
|
||||
selectList.items = null
|
||||
selectList.list.empty()
|
||||
filterEditorView.getEditor().insertText('la')
|
||||
window.advanceClock(selectList.inputThrottle)
|
||||
|
||||
expect(list.find('li').length).toBe 0
|
||||
expect(selectList.error).toBeHidden()
|
||||
selectList.setItems(items)
|
||||
expect(list.find('li').length).toBe 2
|
||||
|
||||
describe "when core:move-up / core:move-down are triggered on the filterEditorView", ->
|
||||
it "selects the previous / next item in the list, or wraps around to the other side", ->
|
||||
expect(list.find('li:first')).toHaveClass 'selected'
|
||||
|
||||
filterEditorView.trigger 'core:move-up'
|
||||
|
||||
expect(list.find('li:first')).not.toHaveClass 'selected'
|
||||
expect(list.find('li:last')).toHaveClass 'selected'
|
||||
|
||||
filterEditorView.trigger 'core:move-down'
|
||||
|
||||
expect(list.find('li:first')).toHaveClass 'selected'
|
||||
expect(list.find('li:last')).not.toHaveClass 'selected'
|
||||
|
||||
filterEditorView.trigger 'core:move-down'
|
||||
|
||||
expect(list.find('li:eq(0)')).not.toHaveClass 'selected'
|
||||
expect(list.find('li:eq(1)')).toHaveClass 'selected'
|
||||
|
||||
filterEditorView.trigger 'core:move-down'
|
||||
|
||||
expect(list.find('li:eq(1)')).not.toHaveClass 'selected'
|
||||
expect(list.find('li:eq(2)')).toHaveClass 'selected'
|
||||
|
||||
filterEditorView.trigger 'core:move-up'
|
||||
|
||||
expect(list.find('li:eq(2)')).not.toHaveClass 'selected'
|
||||
expect(list.find('li:eq(1)')).toHaveClass 'selected'
|
||||
|
||||
it "scrolls to keep the selected item in view", ->
|
||||
selectList.attachToDom()
|
||||
itemHeight = list.find('li').outerHeight()
|
||||
list.height(itemHeight * 2)
|
||||
|
||||
filterEditorView.trigger 'core:move-down'
|
||||
filterEditorView.trigger 'core:move-down'
|
||||
expect(list.scrollBottom()).toBe itemHeight * 3
|
||||
|
||||
filterEditorView.trigger 'core:move-down'
|
||||
expect(list.scrollBottom()).toBe itemHeight * 4
|
||||
|
||||
filterEditorView.trigger 'core:move-up'
|
||||
filterEditorView.trigger 'core:move-up'
|
||||
expect(list.scrollTop()).toBe itemHeight
|
||||
|
||||
describe "the core:confirm event", ->
|
||||
describe "when there is an item selected (because the list in not empty)", ->
|
||||
it "triggers the selected hook with the selected array element", ->
|
||||
filterEditorView.trigger 'core:move-down'
|
||||
filterEditorView.trigger 'core:move-down'
|
||||
filterEditorView.trigger 'core:confirm'
|
||||
expect(selectList.confirmed).toHaveBeenCalledWith(items[2])
|
||||
|
||||
describe "when there is no item selected (because the list is empty)", ->
|
||||
beforeEach ->
|
||||
selectList.attachToDom()
|
||||
|
||||
it "does not trigger the confirmed hook", ->
|
||||
filterEditorView.getEditor().insertText("i will never match anything")
|
||||
window.advanceClock(selectList.inputThrottle)
|
||||
|
||||
expect(list.find('li')).not.toExist()
|
||||
filterEditorView.trigger 'core:confirm'
|
||||
expect(selectList.confirmed).not.toHaveBeenCalled()
|
||||
|
||||
it "does trigger the cancelled hook", ->
|
||||
filterEditorView.getEditor().insertText("i will never match anything")
|
||||
window.advanceClock(selectList.inputThrottle)
|
||||
|
||||
expect(list.find('li')).not.toExist()
|
||||
filterEditorView.trigger 'core:confirm'
|
||||
expect(selectList.cancelled).toHaveBeenCalled()
|
||||
|
||||
describe "when a list item is clicked", ->
|
||||
it "selects the item on mousedown and confirms it on mouseup", ->
|
||||
item = list.find('li:eq(1)')
|
||||
|
||||
item.mousedown()
|
||||
expect(item).toHaveClass 'selected'
|
||||
item.mouseup()
|
||||
|
||||
expect(selectList.confirmed).toHaveBeenCalledWith(items[1])
|
||||
|
||||
describe "the core:cancel event", ->
|
||||
it "triggers the cancelled hook and detaches and empties the select list", ->
|
||||
spyOn(selectList, 'detach')
|
||||
filterEditorView.trigger 'core:cancel'
|
||||
expect(selectList.cancelled).toHaveBeenCalled()
|
||||
expect(selectList.detach).toHaveBeenCalled()
|
||||
expect(selectList.list).toBeEmpty()
|
||||
|
||||
describe "when the mini editor loses focus", ->
|
||||
it "triggers the cancelled hook and detaches the select list", ->
|
||||
spyOn(selectList, 'detach')
|
||||
filterEditorView.trigger 'blur'
|
||||
expect(selectList.cancelled).toHaveBeenCalled()
|
||||
expect(selectList.detach).toHaveBeenCalled()
|
||||
|
||||
describe "the core:move-to-top event", ->
|
||||
it "scrolls to the top, selects the first element, and does not bubble the event", ->
|
||||
selectList.attachToDom()
|
||||
moveToTopHandler = jasmine.createSpy("moveToTopHandler")
|
||||
selectList.parent().on 'core:move-to-top', moveToTopHandler
|
||||
|
||||
selectList.trigger 'core:move-down'
|
||||
expect(list.find('li:eq(1)')).toHaveClass 'selected'
|
||||
selectList.trigger 'core:move-to-top'
|
||||
expect(list.find('li:first')).toHaveClass 'selected'
|
||||
expect(moveToTopHandler).not.toHaveBeenCalled()
|
||||
|
||||
describe "the core:move-to-bottom event", ->
|
||||
it "scrolls to the bottom, selects the last element, and does not bubble the event", ->
|
||||
selectList.attachToDom()
|
||||
moveToBottomHandler = jasmine.createSpy("moveToBottomHandler")
|
||||
selectList.parent().on 'core:move-to-bottom', moveToBottomHandler
|
||||
|
||||
expect(list.find('li:first')).toHaveClass 'selected'
|
||||
selectList.trigger 'core:move-to-bottom'
|
||||
expect(list.find('li:last')).toHaveClass 'selected'
|
||||
expect(moveToBottomHandler).not.toHaveBeenCalled()
|
||||
@@ -56,6 +56,19 @@ describe "Selection", ->
|
||||
selection.selectToScreenPosition([0, 25])
|
||||
expect(selection.isReversed()).toBeFalsy()
|
||||
|
||||
describe ".selectLine(row)", ->
|
||||
describe "when passed a row", ->
|
||||
it "selects the specified row", ->
|
||||
selection.setBufferRange([[2, 4], [3, 4]])
|
||||
selection.selectLine(5)
|
||||
expect(selection.getBufferRange()).toEqual [[5, 0], [6, 0]]
|
||||
|
||||
describe "when not passed a row", ->
|
||||
it "selects all rows spanned by the selection", ->
|
||||
selection.setBufferRange([[2, 4], [3, 4]])
|
||||
selection.selectLine()
|
||||
expect(selection.getBufferRange()).toEqual [[2, 0], [4, 0]]
|
||||
|
||||
describe "when only the selection's tail is moved (regression)", ->
|
||||
it "notifies ::onDidChangeRange observers", ->
|
||||
selection.setBufferRange([[2, 0], [2, 10]], reversed: true)
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
{View, $, $$} = require '../src/space-pen-extensions'
|
||||
|
||||
describe "SpacePen extensions", ->
|
||||
class TestView extends View
|
||||
@content: -> @div()
|
||||
|
||||
[view, parent] = []
|
||||
|
||||
beforeEach ->
|
||||
view = new TestView
|
||||
parent = $$ -> @div()
|
||||
parent.append(view)
|
||||
|
||||
describe "View.subscribe(eventEmitter, eventName, callback)", ->
|
||||
[emitter, eventHandler] = []
|
||||
|
||||
beforeEach ->
|
||||
eventHandler = jasmine.createSpy 'eventHandler'
|
||||
emitter = $$ -> @div()
|
||||
view.subscribe emitter, 'foo', eventHandler
|
||||
|
||||
it "subscribes to the given event emitter and unsubscribes when unsubscribe is called", ->
|
||||
emitter.trigger "foo"
|
||||
expect(eventHandler).toHaveBeenCalled()
|
||||
|
||||
describe "tooltips", ->
|
||||
describe "when the window is resized", ->
|
||||
it "hides the tooltips", ->
|
||||
class TooltipView extends View
|
||||
@content: ->
|
||||
@div()
|
||||
|
||||
view = new TooltipView()
|
||||
view.attachToDom()
|
||||
view.setTooltip('this is a tip')
|
||||
|
||||
view.tooltip('show')
|
||||
expect($(document.body).find('.tooltip')).toBeVisible()
|
||||
|
||||
$(window).trigger('resize')
|
||||
expect($(document.body).find('.tooltip')).not.toExist()
|
||||
@@ -10,16 +10,12 @@ fs = require 'fs-plus'
|
||||
Grim = require 'grim'
|
||||
KeymapManager = require '../src/keymap-extensions'
|
||||
|
||||
# FIXME: Remove jquery from this
|
||||
{$} = require '../src/space-pen-extensions'
|
||||
|
||||
Config = require '../src/config'
|
||||
{Point} = require 'text-buffer'
|
||||
Project = require '../src/project'
|
||||
Workspace = require '../src/workspace'
|
||||
ServiceHub = require 'service-hub'
|
||||
TextEditor = require '../src/text-editor'
|
||||
TextEditorView = require '../src/text-editor-view'
|
||||
TextEditorElement = require '../src/text-editor-element'
|
||||
TokenizedBuffer = require '../src/tokenized-buffer'
|
||||
TextEditorComponent = require '../src/text-editor-component'
|
||||
@@ -41,7 +37,9 @@ window.addEventListener 'core:close', -> window.close()
|
||||
window.addEventListener 'beforeunload', ->
|
||||
atom.storeWindowDimensions()
|
||||
atom.saveSync()
|
||||
$('html,body').css('overflow', 'auto')
|
||||
|
||||
document.querySelector('html').style.overflow = 'auto'
|
||||
document.body.style.overflow = 'auto'
|
||||
|
||||
# Allow document.title to be assigned in specs without screwing up spec window title
|
||||
documentTitle = null
|
||||
@@ -91,7 +89,6 @@ if specDirectory
|
||||
isCoreSpec = specDirectory is fs.realpathSync(__dirname)
|
||||
|
||||
beforeEach ->
|
||||
$.fx.off = true
|
||||
documentTitle = null
|
||||
projectPath = specProjectPath ? path.join(@specDirectory, 'fixtures')
|
||||
atom.packages.serviceHub = new ServiceHub
|
||||
@@ -102,7 +99,7 @@ beforeEach ->
|
||||
atom.styles.restoreSnapshot(styleElementsToRestore)
|
||||
atom.views.clearDocumentRequests()
|
||||
|
||||
atom.workspaceViewParentSelector = '#jasmine-content'
|
||||
atom.workspaceParentSelectorctor = '#jasmine-content'
|
||||
|
||||
window.resetTimeouts()
|
||||
spyOn(_._, "now").andCallFake -> window.now
|
||||
@@ -159,8 +156,6 @@ beforeEach ->
|
||||
spyOn(clipboard, 'writeText').andCallFake (text) -> clipboardContent = text
|
||||
spyOn(clipboard, 'readText').andCallFake -> clipboardContent
|
||||
|
||||
spyOn(atom.packages, 'uninstallAutocompletePlus')
|
||||
|
||||
addCustomMatchers(this)
|
||||
|
||||
afterEach ->
|
||||
@@ -171,7 +166,6 @@ afterEach ->
|
||||
|
||||
atom.workspace?.destroy()
|
||||
atom.workspace = null
|
||||
atom.__workspaceView = null
|
||||
delete atom.state.workspace
|
||||
|
||||
atom.project?.destroy()
|
||||
@@ -181,7 +175,7 @@ afterEach ->
|
||||
|
||||
delete atom.state.packageStates
|
||||
|
||||
$('#jasmine-content').empty() unless window.debugContent
|
||||
document.getElementById('jasmine-content').innerHTML = '' unless window.debugContent
|
||||
|
||||
jasmine.unspy(atom, 'saveSync')
|
||||
ensureNoPathSubscriptions()
|
||||
@@ -279,43 +273,6 @@ addCustomMatchers = (spec) ->
|
||||
@message = -> return "Expected element '#{element}' or its descendants#{notText} to show."
|
||||
element.style.display in ['block', 'inline-block', 'static', 'fixed']
|
||||
|
||||
window.keyIdentifierForKey = (key) ->
|
||||
if key.length > 1 # named key
|
||||
key
|
||||
else
|
||||
charCode = key.toUpperCase().charCodeAt(0)
|
||||
"U+00" + charCode.toString(16)
|
||||
|
||||
window.keydownEvent = (key, properties={}) ->
|
||||
originalEventProperties = {}
|
||||
originalEventProperties.ctrl = properties.ctrlKey
|
||||
originalEventProperties.alt = properties.altKey
|
||||
originalEventProperties.shift = properties.shiftKey
|
||||
originalEventProperties.cmd = properties.metaKey
|
||||
originalEventProperties.target = properties.target?[0] ? properties.target
|
||||
originalEventProperties.which = properties.which
|
||||
originalEvent = KeymapManager.buildKeydownEvent(key, originalEventProperties)
|
||||
properties = $.extend({originalEvent}, properties)
|
||||
$.Event("keydown", properties)
|
||||
|
||||
window.mouseEvent = (type, properties) ->
|
||||
if properties.point
|
||||
{point, editorView} = properties
|
||||
{top, left} = @pagePixelPositionForPoint(editorView, point)
|
||||
properties.pageX = left + 1
|
||||
properties.pageY = top + 1
|
||||
properties.originalEvent ?= {detail: 1}
|
||||
$.Event type, properties
|
||||
|
||||
window.clickEvent = (properties={}) ->
|
||||
window.mouseEvent("click", properties)
|
||||
|
||||
window.mousedownEvent = (properties={}) ->
|
||||
window.mouseEvent('mousedown', properties)
|
||||
|
||||
window.mousemoveEvent = (properties={}) ->
|
||||
window.mouseEvent('mousemove', properties)
|
||||
|
||||
window.waitsForPromise = (args...) ->
|
||||
if args.length > 1
|
||||
{shouldReject, timeout} = args[0]
|
||||
@@ -374,45 +331,3 @@ window.advanceClock = (delta=1) ->
|
||||
true
|
||||
|
||||
callback() for callback in callbacks
|
||||
|
||||
window.pagePixelPositionForPoint = (editorView, point) ->
|
||||
point = Point.fromObject point
|
||||
top = editorView.renderedLines.offset().top + point.row * editorView.lineHeight
|
||||
left = editorView.renderedLines.offset().left + point.column * editorView.charWidth - editorView.renderedLines.scrollLeft()
|
||||
{top, left}
|
||||
|
||||
window.tokensText = (tokens) ->
|
||||
_.pluck(tokens, 'value').join('')
|
||||
|
||||
window.setEditorWidthInChars = (editorView, widthInChars, charWidth=editorView.charWidth) ->
|
||||
editorView.width(charWidth * widthInChars + editorView.gutter.outerWidth())
|
||||
$(window).trigger 'resize' # update width of editor view's on-screen lines
|
||||
|
||||
window.setEditorHeightInLines = (editorView, heightInLines, lineHeight=editorView.lineHeight) ->
|
||||
editorView.height(editorView.getEditor().getLineHeightInPixels() * heightInLines)
|
||||
editorView.component?.measureDimensions()
|
||||
|
||||
$.fn.resultOfTrigger = (type) ->
|
||||
event = $.Event(type)
|
||||
this.trigger(event)
|
||||
event.result
|
||||
|
||||
$.fn.enableKeymap = ->
|
||||
@on 'keydown', (e) ->
|
||||
originalEvent = e.originalEvent ? e
|
||||
Object.defineProperty(originalEvent, 'target', get: -> e.target) unless originalEvent.target?
|
||||
atom.keymaps.handleKeyboardEvent(originalEvent)
|
||||
not e.originalEvent.defaultPrevented
|
||||
|
||||
$.fn.attachToDom = ->
|
||||
@appendTo($('#jasmine-content')) unless @isOnDom()
|
||||
|
||||
$.fn.simulateDomAttachment = ->
|
||||
$('<html>').append(this)
|
||||
|
||||
$.fn.textInput = (data) ->
|
||||
this.each ->
|
||||
event = document.createEvent('TextEvent')
|
||||
event.initTextEvent('textInput', true, true, window, data)
|
||||
event = $.event.fix(event)
|
||||
$(this).trigger(event)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -243,3 +243,61 @@ describe "TextEditorElement", ->
|
||||
expect(element.hasAttribute('mini')).toBe true
|
||||
element.getModel().setMini(false)
|
||||
expect(element.hasAttribute('mini')).toBe false
|
||||
|
||||
describe "events", ->
|
||||
element = null
|
||||
|
||||
beforeEach ->
|
||||
element = new TextEditorElement
|
||||
element.getModel().setText("lorem\nipsum\ndolor\nsit\namet")
|
||||
element.setUpdatedSynchronously(true)
|
||||
element.setHeight(20)
|
||||
element.setWidth(20)
|
||||
|
||||
describe "::onDidChangeScrollTop(callback)", ->
|
||||
it "triggers even when subscribing before attaching the element", ->
|
||||
positions = []
|
||||
subscription1 = element.onDidChangeScrollTop (p) -> positions.push(p)
|
||||
jasmine.attachToDOM(element)
|
||||
subscription2 = element.onDidChangeScrollTop (p) -> positions.push(p)
|
||||
|
||||
positions.length = 0
|
||||
element.setScrollTop(10)
|
||||
expect(positions).toEqual([10, 10])
|
||||
|
||||
element.remove()
|
||||
jasmine.attachToDOM(element)
|
||||
|
||||
positions.length = 0
|
||||
element.setScrollTop(20)
|
||||
expect(positions).toEqual([20, 20])
|
||||
|
||||
subscription1.dispose()
|
||||
|
||||
positions.length = 0
|
||||
element.setScrollTop(30)
|
||||
expect(positions).toEqual([30])
|
||||
|
||||
describe "::onDidChangeScrollLeft(callback)", ->
|
||||
it "triggers even when subscribing before attaching the element", ->
|
||||
positions = []
|
||||
subscription1 = element.onDidChangeScrollLeft (p) -> positions.push(p)
|
||||
jasmine.attachToDOM(element)
|
||||
subscription2 = element.onDidChangeScrollLeft (p) -> positions.push(p)
|
||||
|
||||
positions.length = 0
|
||||
element.setScrollLeft(10)
|
||||
expect(positions).toEqual([10, 10])
|
||||
|
||||
element.remove()
|
||||
jasmine.attachToDOM(element)
|
||||
|
||||
positions.length = 0
|
||||
element.setScrollLeft(20)
|
||||
expect(positions).toEqual([20, 20])
|
||||
|
||||
subscription1.dispose()
|
||||
|
||||
positions.length = 0
|
||||
element.setScrollLeft(30)
|
||||
expect(positions).toEqual([30])
|
||||
|
||||
@@ -4,6 +4,7 @@ TextBuffer = require 'text-buffer'
|
||||
{Point, Range} = TextBuffer
|
||||
TextEditor = require '../src/text-editor'
|
||||
TextEditorPresenter = require '../src/text-editor-presenter'
|
||||
FakeLinesYardstick = require './fake-lines-yardstick'
|
||||
|
||||
describe "TextEditorPresenter", ->
|
||||
# These `describe` and `it` blocks mirror the structure of the ::state object.
|
||||
@@ -40,7 +41,9 @@ describe "TextEditorPresenter", ->
|
||||
scrollTop: 0
|
||||
scrollLeft: 0
|
||||
|
||||
new TextEditorPresenter(params)
|
||||
presenter = new TextEditorPresenter(params)
|
||||
presenter.setLinesYardstick(new FakeLinesYardstick(editor, presenter))
|
||||
presenter
|
||||
|
||||
expectValues = (actual, expected) ->
|
||||
for key, value of expected
|
||||
@@ -99,6 +102,57 @@ describe "TextEditorPresenter", ->
|
||||
|
||||
expect(stateFn(presenter).tiles[12]).toBeUndefined()
|
||||
|
||||
it "includes state for tiles containing screen rows to measure", ->
|
||||
presenter = buildPresenter(explicitHeight: 6, scrollTop: 0, lineHeight: 1, tileSize: 2)
|
||||
presenter.setScreenRowsToMeasure([10, 12])
|
||||
|
||||
expect(stateFn(presenter).tiles[0]).toBeDefined()
|
||||
expect(stateFn(presenter).tiles[2]).toBeDefined()
|
||||
expect(stateFn(presenter).tiles[4]).toBeDefined()
|
||||
expect(stateFn(presenter).tiles[6]).toBeDefined()
|
||||
expect(stateFn(presenter).tiles[8]).toBeUndefined()
|
||||
expect(stateFn(presenter).tiles[10]).toBeDefined()
|
||||
expect(stateFn(presenter).tiles[12]).toBeDefined()
|
||||
|
||||
# clearing additional rows won't trigger a state update
|
||||
expectNoStateUpdate presenter, -> presenter.clearScreenRowsToMeasure()
|
||||
|
||||
expect(stateFn(presenter).tiles[0]).toBeDefined()
|
||||
expect(stateFn(presenter).tiles[2]).toBeDefined()
|
||||
expect(stateFn(presenter).tiles[4]).toBeDefined()
|
||||
expect(stateFn(presenter).tiles[6]).toBeDefined()
|
||||
expect(stateFn(presenter).tiles[8]).toBeUndefined()
|
||||
expect(stateFn(presenter).tiles[10]).toBeDefined()
|
||||
expect(stateFn(presenter).tiles[12]).toBeDefined()
|
||||
|
||||
# when another change triggers a state update we remove useless lines
|
||||
expectStateUpdate presenter, -> presenter.setScrollTop(1)
|
||||
|
||||
expect(stateFn(presenter).tiles[0]).toBeDefined()
|
||||
expect(stateFn(presenter).tiles[2]).toBeDefined()
|
||||
expect(stateFn(presenter).tiles[4]).toBeDefined()
|
||||
expect(stateFn(presenter).tiles[6]).toBeDefined()
|
||||
expect(stateFn(presenter).tiles[8]).toBeDefined()
|
||||
expect(stateFn(presenter).tiles[10]).toBeUndefined()
|
||||
expect(stateFn(presenter).tiles[12]).toBeUndefined()
|
||||
|
||||
it "excludes invalid tiles for screen rows to measure", ->
|
||||
presenter = buildPresenter(explicitHeight: 6, scrollTop: 0, lineHeight: 1, tileSize: 2)
|
||||
presenter.setScreenRowsToMeasure([20, 30]) # unexisting rows
|
||||
|
||||
expect(stateFn(presenter).tiles[0]).toBeDefined()
|
||||
expect(stateFn(presenter).tiles[2]).toBeDefined()
|
||||
expect(stateFn(presenter).tiles[4]).toBeDefined()
|
||||
expect(stateFn(presenter).tiles[6]).toBeDefined()
|
||||
expect(stateFn(presenter).tiles[8]).toBeUndefined()
|
||||
expect(stateFn(presenter).tiles[10]).toBeUndefined()
|
||||
expect(stateFn(presenter).tiles[12]).toBeUndefined()
|
||||
|
||||
presenter.setScreenRowsToMeasure([12])
|
||||
buffer.deleteRows(12, 13)
|
||||
|
||||
expect(stateFn(presenter).tiles[12]).toBeUndefined()
|
||||
|
||||
it "includes state for all tiles if no external ::explicitHeight is assigned", ->
|
||||
presenter = buildPresenter(explicitHeight: null, tileSize: 2)
|
||||
expect(stateFn(presenter).tiles[0]).toBeDefined()
|
||||
@@ -162,12 +216,13 @@ describe "TextEditorPresenter", ->
|
||||
expect(stateFn(presenter).tiles[6]).toBeDefined()
|
||||
expect(stateFn(presenter).tiles[8]).toBeUndefined()
|
||||
|
||||
expectStateUpdate presenter, -> presenter.setLineHeight(2)
|
||||
expectStateUpdate presenter, -> presenter.setLineHeight(4)
|
||||
|
||||
expect(stateFn(presenter).tiles[0]).toBeDefined()
|
||||
expect(stateFn(presenter).tiles[2]).toBeDefined()
|
||||
expect(stateFn(presenter).tiles[4]).toBeDefined()
|
||||
expect(stateFn(presenter).tiles[6]).toBeUndefined()
|
||||
expect(stateFn(presenter).tiles[4]).toBeUndefined()
|
||||
expect(stateFn(presenter).tiles[6]).toBeDefined()
|
||||
expect(stateFn(presenter).tiles[8]).toBeUndefined()
|
||||
|
||||
it "does not remove out-of-view tiles corresponding to ::mouseWheelScreenRow until ::stoppedScrollingDelay elapses", ->
|
||||
presenter = buildPresenter(explicitHeight: 6, scrollTop: 0, lineHeight: 1, tileSize: 2, stoppedScrollingDelay: 200)
|
||||
@@ -289,15 +344,7 @@ describe "TextEditorPresenter", ->
|
||||
expectStateUpdate presenter, -> presenter.setContentFrameWidth(10 * maxLineLength + 20)
|
||||
expect(presenter.getState().horizontalScrollbar.scrollWidth).toBe 10 * maxLineLength + 20
|
||||
|
||||
it "updates when the ::baseCharacterWidth changes", ->
|
||||
maxLineLength = editor.getMaxScreenLineLength()
|
||||
presenter = buildPresenter(contentFrameWidth: 50, baseCharacterWidth: 10)
|
||||
|
||||
expect(presenter.getState().horizontalScrollbar.scrollWidth).toBe 10 * maxLineLength + 1
|
||||
expectStateUpdate presenter, -> presenter.setBaseCharacterWidth(15)
|
||||
expect(presenter.getState().horizontalScrollbar.scrollWidth).toBe 15 * maxLineLength + 1
|
||||
|
||||
it "updates when the scoped character widths change", ->
|
||||
it "updates when character widths change", ->
|
||||
waitsForPromise -> atom.packages.activatePackage('language-javascript')
|
||||
|
||||
runs ->
|
||||
@@ -305,7 +352,9 @@ describe "TextEditorPresenter", ->
|
||||
presenter = buildPresenter(contentFrameWidth: 50, baseCharacterWidth: 10)
|
||||
|
||||
expect(presenter.getState().horizontalScrollbar.scrollWidth).toBe 10 * maxLineLength + 1
|
||||
expectStateUpdate presenter, -> presenter.setScopedCharacterWidth(['source.js', 'support.function.js'], 'p', 20)
|
||||
expectStateUpdate presenter, ->
|
||||
presenter.getLinesYardstick().setScopedCharacterWidth(['source.js', 'support.function.js'], 'p', 20)
|
||||
presenter.characterWidthsChanged()
|
||||
expect(presenter.getState().horizontalScrollbar.scrollWidth).toBe (10 * (maxLineLength - 2)) + (20 * 2) + 1 # 2 of the characters are 20px wide now instead of 10px wide
|
||||
|
||||
it "updates when ::softWrapped changes on the editor", ->
|
||||
@@ -357,6 +406,18 @@ describe "TextEditorPresenter", ->
|
||||
expectStateUpdate presenter, -> presenter.setScrollLeft(-300)
|
||||
expect(presenter.getState().horizontalScrollbar.scrollLeft).toBe 0
|
||||
|
||||
it "is always 0 when soft wrapping is enabled", ->
|
||||
presenter = buildPresenter(scrollLeft: 0, verticalScrollbarWidth: 0, contentFrameWidth: 85, baseCharacterWidth: 10)
|
||||
|
||||
editor.setSoftWrapped(false)
|
||||
presenter.setScrollLeft(Infinity)
|
||||
expect(presenter.getState().content.scrollLeft).toBeGreaterThan 0
|
||||
|
||||
editor.setSoftWrapped(true)
|
||||
expect(presenter.getState().content.scrollLeft).toBe 0
|
||||
presenter.setScrollLeft(10)
|
||||
expect(presenter.getState().content.scrollLeft).toBe 0
|
||||
|
||||
describe ".verticalScrollbar", ->
|
||||
describe ".visible", ->
|
||||
it "is true if the scrollHeight exceeds the computed client height", ->
|
||||
@@ -504,11 +565,11 @@ describe "TextEditorPresenter", ->
|
||||
expectValues presenter.getState().hiddenInput, {top: 0, left: 0}
|
||||
|
||||
expectStateUpdate presenter, -> editor.setCursorBufferPosition([11, 43])
|
||||
expectValues presenter.getState().hiddenInput, {top: 11 * 10 - editor.getScrollTop(), left: 43 * 10 - editor.getScrollLeft()}
|
||||
expectValues presenter.getState().hiddenInput, {top: 11 * 10 - presenter.getScrollTop(), left: 43 * 10 - presenter.getScrollLeft()}
|
||||
|
||||
newCursor = null
|
||||
expectStateUpdate presenter, -> newCursor = editor.addCursorAtBufferPosition([6, 10])
|
||||
expectValues presenter.getState().hiddenInput, {top: (6 * 10) - editor.getScrollTop(), left: (10 * 10) - editor.getScrollLeft()}
|
||||
expectValues presenter.getState().hiddenInput, {top: (6 * 10) - presenter.getScrollTop(), left: (10 * 10) - presenter.getScrollLeft()}
|
||||
|
||||
expectStateUpdate presenter, -> newCursor.destroy()
|
||||
expectValues presenter.getState().hiddenInput, {top: 50 - 10, left: 300 - 10}
|
||||
@@ -536,7 +597,9 @@ describe "TextEditorPresenter", ->
|
||||
expectStateUpdate presenter, -> presenter.setBaseCharacterWidth(15)
|
||||
expect(presenter.getState().hiddenInput.width).toBe 15
|
||||
|
||||
expectStateUpdate presenter, -> presenter.setScopedCharacterWidth(['source.js', 'storage.modifier.js'], 'r', 20)
|
||||
expectStateUpdate presenter, ->
|
||||
presenter.getLinesYardstick().setScopedCharacterWidth(['source.js', 'storage.modifier.js'], 'r', 20)
|
||||
presenter.characterWidthsChanged()
|
||||
expect(presenter.getState().hiddenInput.width).toBe 20
|
||||
|
||||
it "is 2px at the end of lines", ->
|
||||
@@ -554,6 +617,7 @@ describe "TextEditorPresenter", ->
|
||||
advanceClock(100)
|
||||
expect(presenter.getState().content.scrollingVertically).toBe true
|
||||
presenter.setScrollTop(10)
|
||||
presenter.getState() # commits scroll position
|
||||
advanceClock(100)
|
||||
expect(presenter.getState().content.scrollingVertically).toBe true
|
||||
expectStateUpdate presenter, -> advanceClock(100)
|
||||
@@ -623,15 +687,7 @@ describe "TextEditorPresenter", ->
|
||||
expectStateUpdate presenter, -> presenter.setContentFrameWidth(10 * maxLineLength + 20)
|
||||
expect(presenter.getState().content.scrollWidth).toBe 10 * maxLineLength + 20
|
||||
|
||||
it "updates when the ::baseCharacterWidth changes", ->
|
||||
maxLineLength = editor.getMaxScreenLineLength()
|
||||
presenter = buildPresenter(contentFrameWidth: 50, baseCharacterWidth: 10)
|
||||
|
||||
expect(presenter.getState().content.scrollWidth).toBe 10 * maxLineLength + 1
|
||||
expectStateUpdate presenter, -> presenter.setBaseCharacterWidth(15)
|
||||
expect(presenter.getState().content.scrollWidth).toBe 15 * maxLineLength + 1
|
||||
|
||||
it "updates when the scoped character widths change", ->
|
||||
it "updates when character widths change", ->
|
||||
waitsForPromise -> atom.packages.activatePackage('language-javascript')
|
||||
|
||||
runs ->
|
||||
@@ -639,7 +695,9 @@ describe "TextEditorPresenter", ->
|
||||
presenter = buildPresenter(contentFrameWidth: 50, baseCharacterWidth: 10)
|
||||
|
||||
expect(presenter.getState().content.scrollWidth).toBe 10 * maxLineLength + 1
|
||||
expectStateUpdate presenter, -> presenter.setScopedCharacterWidth(['source.js', 'support.function.js'], 'p', 20)
|
||||
expectStateUpdate presenter, ->
|
||||
presenter.getLinesYardstick().setScopedCharacterWidth(['source.js', 'support.function.js'], 'p', 20)
|
||||
presenter.characterWidthsChanged()
|
||||
expect(presenter.getState().content.scrollWidth).toBe (10 * (maxLineLength - 2)) + (20 * 2) + 1 # 2 of the characters are 20px wide now instead of 10px wide
|
||||
|
||||
it "updates when ::softWrapped changes on the editor", ->
|
||||
@@ -666,12 +724,55 @@ describe "TextEditorPresenter", ->
|
||||
expect(presenter.getState().content.scrollWidth).toBe 10 * editor.getMaxScreenLineLength() + 1
|
||||
|
||||
describe ".scrollTop", ->
|
||||
it "changes based on the scroll operation that was performed last", ->
|
||||
presenter = buildPresenter(scrollTop: 0, lineHeight: 10, explicitHeight: 20)
|
||||
expect(presenter.getState().content.scrollTop).toBe(0)
|
||||
|
||||
presenter.setScrollTop(20)
|
||||
editor.setCursorBufferPosition([5, 0])
|
||||
|
||||
expect(presenter.getState().content.scrollTop).toBe(50)
|
||||
|
||||
editor.setCursorBufferPosition([8, 0])
|
||||
presenter.setScrollTop(10)
|
||||
|
||||
expect(presenter.getState().content.scrollTop).toBe(10)
|
||||
|
||||
it "corresponds to the passed logical coordinates when building the presenter", ->
|
||||
presenter = buildPresenter(scrollRow: 4, lineHeight: 10, explicitHeight: 20)
|
||||
expect(presenter.getState().content.scrollTop).toBe(40)
|
||||
|
||||
it "tracks the value of ::scrollTop", ->
|
||||
presenter = buildPresenter(scrollTop: 10, lineHeight: 10, explicitHeight: 20)
|
||||
expect(presenter.getState().content.scrollTop).toBe 10
|
||||
expectStateUpdate presenter, -> presenter.setScrollTop(50)
|
||||
expect(presenter.getState().content.scrollTop).toBe 50
|
||||
|
||||
it "keeps the model up to date with the corresponding logical coordinates", ->
|
||||
presenter = buildPresenter(scrollTop: 0, explicitHeight: 20, horizontalScrollbarHeight: 10, lineHeight: 10)
|
||||
|
||||
expectStateUpdate presenter, -> presenter.setScrollTop(50)
|
||||
presenter.getState() # commits scroll position
|
||||
expect(editor.getScrollRow()).toBe(5)
|
||||
|
||||
expectStateUpdate presenter, -> presenter.setScrollTop(57)
|
||||
presenter.getState() # commits scroll position
|
||||
expect(editor.getScrollRow()).toBe(6)
|
||||
|
||||
it "reassigns the scrollTop if it exceeds the max possible value after lines are removed", ->
|
||||
presenter = buildPresenter(scrollTop: 80, lineHeight: 10, explicitHeight: 50, horizontalScrollbarHeight: 0)
|
||||
expect(presenter.getState().content.scrollTop).toBe(80)
|
||||
buffer.deleteRows(10, 9, 8)
|
||||
expect(presenter.getState().content.scrollTop).toBe(60)
|
||||
|
||||
it "is always rounded to the nearest integer", ->
|
||||
presenter = buildPresenter(scrollTop: 10, lineHeight: 10, explicitHeight: 20)
|
||||
expect(presenter.getState().content.scrollTop).toBe 10
|
||||
expectStateUpdate presenter, -> presenter.setScrollTop(11.4)
|
||||
expect(presenter.getState().content.scrollTop).toBe 11
|
||||
expectStateUpdate presenter, -> presenter.setScrollTop(12.6)
|
||||
expect(presenter.getState().content.scrollTop).toBe 13
|
||||
|
||||
it "scrolls down automatically when the model is changed", ->
|
||||
presenter = buildPresenter(scrollTop: 0, lineHeight: 10, explicitHeight: 20)
|
||||
|
||||
@@ -683,23 +784,40 @@ describe "TextEditorPresenter", ->
|
||||
expect(presenter.getState().content.scrollTop).toBe(10)
|
||||
|
||||
it "never exceeds the computed scroll height minus the computed client height", ->
|
||||
didChangeScrollTopSpy = jasmine.createSpy()
|
||||
presenter = buildPresenter(scrollTop: 10, lineHeight: 10, explicitHeight: 50, horizontalScrollbarHeight: 10)
|
||||
presenter.onDidChangeScrollTop(didChangeScrollTopSpy)
|
||||
|
||||
expectStateUpdate presenter, -> presenter.setScrollTop(100)
|
||||
expect(presenter.getState().content.scrollTop).toBe presenter.scrollHeight - presenter.clientHeight
|
||||
expect(presenter.getRealScrollTop()).toBe presenter.scrollHeight - presenter.clientHeight
|
||||
expect(didChangeScrollTopSpy).toHaveBeenCalledWith presenter.scrollHeight - presenter.clientHeight
|
||||
|
||||
didChangeScrollTopSpy.reset()
|
||||
expectStateUpdate presenter, -> presenter.setExplicitHeight(60)
|
||||
expect(presenter.getState().content.scrollTop).toBe presenter.scrollHeight - presenter.clientHeight
|
||||
expect(presenter.getRealScrollTop()).toBe presenter.scrollHeight - presenter.clientHeight
|
||||
expect(didChangeScrollTopSpy).toHaveBeenCalledWith presenter.scrollHeight - presenter.clientHeight
|
||||
|
||||
didChangeScrollTopSpy.reset()
|
||||
expectStateUpdate presenter, -> presenter.setHorizontalScrollbarHeight(5)
|
||||
expect(presenter.getState().content.scrollTop).toBe presenter.scrollHeight - presenter.clientHeight
|
||||
expect(presenter.getRealScrollTop()).toBe presenter.scrollHeight - presenter.clientHeight
|
||||
expect(didChangeScrollTopSpy).toHaveBeenCalledWith presenter.scrollHeight - presenter.clientHeight
|
||||
|
||||
didChangeScrollTopSpy.reset()
|
||||
expectStateUpdate presenter, -> editor.getBuffer().delete([[8, 0], [12, 0]])
|
||||
expect(presenter.getState().content.scrollTop).toBe presenter.scrollHeight - presenter.clientHeight
|
||||
expect(presenter.getRealScrollTop()).toBe presenter.scrollHeight - presenter.clientHeight
|
||||
expect(didChangeScrollTopSpy).toHaveBeenCalledWith presenter.scrollHeight - presenter.clientHeight
|
||||
|
||||
# Scroll top only gets smaller when needed as dimensions change, never bigger
|
||||
scrollTopBefore = presenter.getState().verticalScrollbar.scrollTop
|
||||
didChangeScrollTopSpy.reset()
|
||||
expectStateUpdate presenter, -> editor.getBuffer().insert([9, Infinity], '\n\n\n')
|
||||
expect(presenter.getState().content.scrollTop).toBe scrollTopBefore
|
||||
expect(presenter.getRealScrollTop()).toBe scrollTopBefore
|
||||
expect(didChangeScrollTopSpy).not.toHaveBeenCalled()
|
||||
|
||||
it "never goes negative", ->
|
||||
presenter = buildPresenter(scrollTop: 10, explicitHeight: 50, horizontalScrollbarHeight: 10)
|
||||
@@ -719,30 +837,84 @@ describe "TextEditorPresenter", ->
|
||||
expect(presenter.getState().content.scrollTop).toBe presenter.contentHeight - presenter.clientHeight
|
||||
|
||||
describe ".scrollLeft", ->
|
||||
it "changes based on the scroll operation that was performed last", ->
|
||||
presenter = buildPresenter(scrollLeft: 0, lineHeight: 10, baseCharacterWidth: 10, verticalScrollbarWidth: 10, contentFrameWidth: 10)
|
||||
expect(presenter.getState().content.scrollLeft).toBe(0)
|
||||
|
||||
presenter.setScrollLeft(20)
|
||||
editor.setCursorBufferPosition([0, 9])
|
||||
|
||||
expect(presenter.getState().content.scrollLeft).toBe(90)
|
||||
|
||||
editor.setCursorBufferPosition([0, 18])
|
||||
presenter.setScrollLeft(50)
|
||||
|
||||
expect(presenter.getState().content.scrollLeft).toBe(50)
|
||||
|
||||
it "corresponds to the passed logical coordinates when building the presenter", ->
|
||||
presenter = buildPresenter(scrollColumn: 3, lineHeight: 10, baseCharacterWidth: 10, verticalScrollbarWidth: 10, contentFrameWidth: 500)
|
||||
expect(presenter.getState().content.scrollLeft).toBe(30)
|
||||
|
||||
it "tracks the value of ::scrollLeft", ->
|
||||
presenter = buildPresenter(scrollLeft: 10, lineHeight: 10, baseCharacterWidth: 10, verticalScrollbarWidth: 10, contentFrameWidth: 500)
|
||||
expect(presenter.getState().content.scrollLeft).toBe 10
|
||||
expectStateUpdate presenter, -> presenter.setScrollLeft(50)
|
||||
expect(presenter.getState().content.scrollLeft).toBe 50
|
||||
|
||||
it "never exceeds the computed scrollWidth minus the computed clientWidth", ->
|
||||
it "keeps the model up to date with the corresponding logical coordinates", ->
|
||||
presenter = buildPresenter(scrollLeft: 0, lineHeight: 10, baseCharacterWidth: 10, verticalScrollbarWidth: 10, contentFrameWidth: 500)
|
||||
|
||||
expectStateUpdate presenter, -> presenter.setScrollLeft(50)
|
||||
presenter.getState() # commits scroll position
|
||||
expect(editor.getScrollColumn()).toBe(5)
|
||||
|
||||
expectStateUpdate presenter, -> presenter.setScrollLeft(57)
|
||||
presenter.getState() # commits scroll position
|
||||
expect(editor.getScrollColumn()).toBe(6)
|
||||
|
||||
it "is always rounded to the nearest integer", ->
|
||||
presenter = buildPresenter(scrollLeft: 10, lineHeight: 10, baseCharacterWidth: 10, verticalScrollbarWidth: 10, contentFrameWidth: 500)
|
||||
expect(presenter.getState().content.scrollLeft).toBe 10
|
||||
expectStateUpdate presenter, -> presenter.setScrollLeft(11.4)
|
||||
expect(presenter.getState().content.scrollLeft).toBe 11
|
||||
expectStateUpdate presenter, -> presenter.setScrollLeft(12.6)
|
||||
expect(presenter.getState().content.scrollLeft).toBe 13
|
||||
|
||||
it "never exceeds the computed scrollWidth minus the computed clientWidth", ->
|
||||
didChangeScrollLeftSpy = jasmine.createSpy()
|
||||
presenter = buildPresenter(scrollLeft: 10, lineHeight: 10, baseCharacterWidth: 10, verticalScrollbarWidth: 10, contentFrameWidth: 500)
|
||||
presenter.onDidChangeScrollLeft(didChangeScrollLeftSpy)
|
||||
|
||||
expectStateUpdate presenter, -> presenter.setScrollLeft(300)
|
||||
expect(presenter.getState().content.scrollLeft).toBe presenter.scrollWidth - presenter.clientWidth
|
||||
expect(presenter.getRealScrollLeft()).toBe presenter.scrollWidth - presenter.clientWidth
|
||||
expect(didChangeScrollLeftSpy).toHaveBeenCalledWith presenter.scrollWidth - presenter.clientWidth
|
||||
|
||||
didChangeScrollLeftSpy.reset()
|
||||
expectStateUpdate presenter, -> presenter.setContentFrameWidth(600)
|
||||
expect(presenter.getState().content.scrollLeft).toBe presenter.scrollWidth - presenter.clientWidth
|
||||
expect(presenter.getRealScrollLeft()).toBe presenter.scrollWidth - presenter.clientWidth
|
||||
expect(didChangeScrollLeftSpy).toHaveBeenCalledWith presenter.scrollWidth - presenter.clientWidth
|
||||
|
||||
didChangeScrollLeftSpy.reset()
|
||||
expectStateUpdate presenter, -> presenter.setVerticalScrollbarWidth(5)
|
||||
expect(presenter.getState().content.scrollLeft).toBe presenter.scrollWidth - presenter.clientWidth
|
||||
expect(presenter.getRealScrollLeft()).toBe presenter.scrollWidth - presenter.clientWidth
|
||||
expect(didChangeScrollLeftSpy).toHaveBeenCalledWith presenter.scrollWidth - presenter.clientWidth
|
||||
|
||||
didChangeScrollLeftSpy.reset()
|
||||
expectStateUpdate presenter, -> editor.getBuffer().delete([[6, 0], [6, Infinity]])
|
||||
expect(presenter.getState().content.scrollLeft).toBe presenter.scrollWidth - presenter.clientWidth
|
||||
expect(presenter.getRealScrollLeft()).toBe presenter.scrollWidth - presenter.clientWidth
|
||||
expect(didChangeScrollLeftSpy).toHaveBeenCalledWith presenter.scrollWidth - presenter.clientWidth
|
||||
|
||||
# Scroll top only gets smaller when needed as dimensions change, never bigger
|
||||
scrollLeftBefore = presenter.getState().content.scrollLeft
|
||||
didChangeScrollLeftSpy.reset()
|
||||
expectStateUpdate presenter, -> editor.getBuffer().insert([6, 0], new Array(100).join('x'))
|
||||
expect(presenter.getState().content.scrollLeft).toBe scrollLeftBefore
|
||||
expect(presenter.getRealScrollLeft()).toBe scrollLeftBefore
|
||||
expect(didChangeScrollLeftSpy).not.toHaveBeenCalled()
|
||||
|
||||
it "never goes negative", ->
|
||||
presenter = buildPresenter(scrollLeft: 10, verticalScrollbarWidth: 10, contentFrameWidth: 500)
|
||||
@@ -851,7 +1023,6 @@ describe "TextEditorPresenter", ->
|
||||
firstNonWhitespaceIndex: line3.firstNonWhitespaceIndex
|
||||
firstTrailingWhitespaceIndex: line3.firstTrailingWhitespaceIndex
|
||||
invisibles: line3.invisibles
|
||||
top: 0
|
||||
}
|
||||
|
||||
line4 = editor.tokenizedLineForScreenRow(4)
|
||||
@@ -863,7 +1034,6 @@ describe "TextEditorPresenter", ->
|
||||
firstNonWhitespaceIndex: line4.firstNonWhitespaceIndex
|
||||
firstTrailingWhitespaceIndex: line4.firstTrailingWhitespaceIndex
|
||||
invisibles: line4.invisibles
|
||||
top: 1
|
||||
}
|
||||
|
||||
line5 = editor.tokenizedLineForScreenRow(5)
|
||||
@@ -875,7 +1045,6 @@ describe "TextEditorPresenter", ->
|
||||
firstNonWhitespaceIndex: line5.firstNonWhitespaceIndex
|
||||
firstTrailingWhitespaceIndex: line5.firstTrailingWhitespaceIndex
|
||||
invisibles: line5.invisibles
|
||||
top: 2
|
||||
}
|
||||
|
||||
line6 = editor.tokenizedLineForScreenRow(6)
|
||||
@@ -887,7 +1056,6 @@ describe "TextEditorPresenter", ->
|
||||
firstNonWhitespaceIndex: line6.firstNonWhitespaceIndex
|
||||
firstTrailingWhitespaceIndex: line6.firstTrailingWhitespaceIndex
|
||||
invisibles: line6.invisibles
|
||||
top: 0
|
||||
}
|
||||
|
||||
line7 = editor.tokenizedLineForScreenRow(7)
|
||||
@@ -899,7 +1067,6 @@ describe "TextEditorPresenter", ->
|
||||
firstNonWhitespaceIndex: line7.firstNonWhitespaceIndex
|
||||
firstTrailingWhitespaceIndex: line7.firstTrailingWhitespaceIndex
|
||||
invisibles: line7.invisibles
|
||||
top: 1
|
||||
}
|
||||
|
||||
line8 = editor.tokenizedLineForScreenRow(8)
|
||||
@@ -911,7 +1078,6 @@ describe "TextEditorPresenter", ->
|
||||
firstNonWhitespaceIndex: line8.firstNonWhitespaceIndex
|
||||
firstTrailingWhitespaceIndex: line8.firstTrailingWhitespaceIndex
|
||||
invisibles: line8.invisibles
|
||||
top: 2
|
||||
}
|
||||
|
||||
expect(lineStateForScreenRow(presenter, 9)).toBeUndefined()
|
||||
@@ -1179,13 +1345,6 @@ describe "TextEditorPresenter", ->
|
||||
expect(stateForCursor(presenter, 3)).toEqual {top: 5, left: 12 * 10, width: 10, height: 5}
|
||||
expect(stateForCursor(presenter, 4)).toEqual {top: 8 * 5 - 20, left: 4 * 10, width: 10, height: 5}
|
||||
|
||||
it "updates when ::baseCharacterWidth changes", ->
|
||||
editor.setCursorBufferPosition([2, 4])
|
||||
presenter = buildPresenter(explicitHeight: 20, scrollTop: 20)
|
||||
|
||||
expectStateUpdate presenter, -> presenter.setBaseCharacterWidth(20)
|
||||
expect(stateForCursor(presenter, 0)).toEqual {top: 0, left: 4 * 20, width: 20, height: 10}
|
||||
|
||||
it "updates when scoped character widths change", ->
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-javascript')
|
||||
@@ -1194,10 +1353,14 @@ describe "TextEditorPresenter", ->
|
||||
editor.setCursorBufferPosition([1, 4])
|
||||
presenter = buildPresenter(explicitHeight: 20)
|
||||
|
||||
expectStateUpdate presenter, -> presenter.setScopedCharacterWidth(['source.js', 'storage.modifier.js'], 'v', 20)
|
||||
expectStateUpdate presenter, ->
|
||||
presenter.getLinesYardstick().setScopedCharacterWidth(['source.js', 'storage.modifier.js'], 'v', 20)
|
||||
presenter.characterWidthsChanged()
|
||||
expect(stateForCursor(presenter, 0)).toEqual {top: 1 * 10, left: (3 * 10) + 20, width: 10, height: 10}
|
||||
|
||||
expectStateUpdate presenter, -> presenter.setScopedCharacterWidth(['source.js', 'storage.modifier.js'], 'r', 20)
|
||||
expectStateUpdate presenter, ->
|
||||
presenter.getLinesYardstick().setScopedCharacterWidth(['source.js', 'storage.modifier.js'], 'r', 20)
|
||||
presenter.characterWidthsChanged()
|
||||
expect(stateForCursor(presenter, 0)).toEqual {top: 1 * 10, left: (3 * 10) + 20, width: 20, height: 10}
|
||||
|
||||
it "updates when cursors are added, moved, hidden, shown, or destroyed", ->
|
||||
@@ -1513,21 +1676,6 @@ describe "TextEditorPresenter", ->
|
||||
]
|
||||
}
|
||||
|
||||
it "updates when ::baseCharacterWidth changes", ->
|
||||
editor.setSelectedBufferRanges([
|
||||
[[2, 2], [2, 4]],
|
||||
])
|
||||
|
||||
presenter = buildPresenter(explicitHeight: 20, scrollTop: 0, tileSize: 2)
|
||||
|
||||
expectValues stateForSelectionInTile(presenter, 0, 2), {
|
||||
regions: [{top: 0, left: 2 * 10, width: 2 * 10, height: 10}]
|
||||
}
|
||||
expectStateUpdate presenter, -> presenter.setBaseCharacterWidth(20)
|
||||
expectValues stateForSelectionInTile(presenter, 0, 2), {
|
||||
regions: [{top: 0, left: 2 * 20, width: 2 * 20, height: 10}]
|
||||
}
|
||||
|
||||
it "updates when scoped character widths change", ->
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-javascript')
|
||||
@@ -1542,7 +1690,9 @@ describe "TextEditorPresenter", ->
|
||||
expectValues stateForSelectionInTile(presenter, 0, 2), {
|
||||
regions: [{top: 0, left: 4 * 10, width: 2 * 10, height: 10}]
|
||||
}
|
||||
expectStateUpdate presenter, -> presenter.setScopedCharacterWidth(['source.js', 'keyword.control.js'], 'i', 20)
|
||||
expectStateUpdate presenter, ->
|
||||
presenter.getLinesYardstick().setScopedCharacterWidth(['source.js', 'keyword.control.js'], 'i', 20)
|
||||
presenter.characterWidthsChanged()
|
||||
expectValues stateForSelectionInTile(presenter, 0, 2), {
|
||||
regions: [{top: 0, left: 4 * 10, width: 20 + 10, height: 10}]
|
||||
}
|
||||
@@ -1695,7 +1845,7 @@ describe "TextEditorPresenter", ->
|
||||
pixelPosition: {top: 3 * 10 - presenter.state.content.scrollTop, left: 13 * 10}
|
||||
}
|
||||
|
||||
it "updates when ::baseCharacterWidth changes", ->
|
||||
it "updates when character widths changes", ->
|
||||
scrollTop = 20
|
||||
marker = editor.markBufferPosition([2, 13], invalidate: 'touch')
|
||||
decoration = editor.decorateMarker(marker, {type: 'overlay', item})
|
||||
@@ -1821,7 +1971,8 @@ describe "TextEditorPresenter", ->
|
||||
|
||||
expectStateUpdate presenter, ->
|
||||
editor.insertNewline()
|
||||
editor.setScrollTop(scrollTop) # I'm fighting the editor
|
||||
presenter.setScrollTop(scrollTop) # I'm fighting the editor
|
||||
|
||||
expectValues stateForOverlay(presenter, decoration), {
|
||||
item: item
|
||||
pixelPosition: {top: 6 * 10 - scrollTop - itemHeight, left: gutterWidth}
|
||||
@@ -1923,6 +2074,24 @@ describe "TextEditorPresenter", ->
|
||||
}
|
||||
|
||||
describe ".height", ->
|
||||
it "updates model's rows per page when it changes", ->
|
||||
presenter = buildPresenter(explicitHeight: 50, lineHeightInPixels: 10, horizontalScrollbarHeight: 10)
|
||||
|
||||
presenter.getState() # trigger state update
|
||||
expect(editor.getRowsPerPage()).toBe(4)
|
||||
|
||||
presenter.setExplicitHeight(100)
|
||||
presenter.getState() # trigger state update
|
||||
expect(editor.getRowsPerPage()).toBe(9)
|
||||
|
||||
presenter.setHorizontalScrollbarHeight(0)
|
||||
presenter.getState() # trigger state update
|
||||
expect(editor.getRowsPerPage()).toBe(10)
|
||||
|
||||
presenter.setLineHeight(5)
|
||||
presenter.getState() # trigger state update
|
||||
expect(editor.getRowsPerPage()).toBe(20)
|
||||
|
||||
it "tracks the computed content height if ::autoHeight is true so the editor auto-expands vertically", ->
|
||||
presenter = buildPresenter(explicitHeight: null, autoHeight: true)
|
||||
expect(presenter.getState().height).toBe editor.getScreenLineCount() * 10
|
||||
@@ -2035,15 +2204,10 @@ describe "TextEditorPresenter", ->
|
||||
lineNumberStateForScreenRow = (presenter, screenRow) ->
|
||||
editor = presenter.model
|
||||
tileRow = presenter.tileForRow(screenRow)
|
||||
bufferRow = editor.bufferRowForScreenRow(screenRow)
|
||||
wrapCount = screenRow - editor.screenRowForBufferRow(bufferRow)
|
||||
if wrapCount > 0
|
||||
key = bufferRow + '-' + wrapCount
|
||||
else
|
||||
key = bufferRow
|
||||
line = editor.tokenizedLineForScreenRow(screenRow)
|
||||
|
||||
gutterState = getLineNumberGutterState(presenter)
|
||||
gutterState.content.tiles[tileRow]?.lineNumbers[key]
|
||||
gutterState.content.tiles[tileRow]?.lineNumbers[line?.id]
|
||||
|
||||
tiledContentContract (presenter) -> getLineNumberGutterState(presenter).content
|
||||
|
||||
@@ -2055,12 +2219,12 @@ describe "TextEditorPresenter", ->
|
||||
presenter = buildPresenter(explicitHeight: 25, scrollTop: 30, lineHeight: 10, tileSize: 2)
|
||||
|
||||
expect(lineNumberStateForScreenRow(presenter, 1)).toBeUndefined()
|
||||
expectValues lineNumberStateForScreenRow(presenter, 2), {screenRow: 2, bufferRow: 2, softWrapped: false, top: 0 * 10}
|
||||
expectValues lineNumberStateForScreenRow(presenter, 3), {screenRow: 3, bufferRow: 3, softWrapped: false, top: 1 * 10}
|
||||
expectValues lineNumberStateForScreenRow(presenter, 4), {screenRow: 4, bufferRow: 3, softWrapped: true, top: 0 * 10}
|
||||
expectValues lineNumberStateForScreenRow(presenter, 5), {screenRow: 5, bufferRow: 4, softWrapped: false, top: 1 * 10}
|
||||
expectValues lineNumberStateForScreenRow(presenter, 6), {screenRow: 6, bufferRow: 7, softWrapped: false, top: 0 * 10}
|
||||
expectValues lineNumberStateForScreenRow(presenter, 7), {screenRow: 7, bufferRow: 8, softWrapped: false, top: 1 * 10}
|
||||
expectValues lineNumberStateForScreenRow(presenter, 2), {screenRow: 2, bufferRow: 2, softWrapped: false}
|
||||
expectValues lineNumberStateForScreenRow(presenter, 3), {screenRow: 3, bufferRow: 3, softWrapped: false}
|
||||
expectValues lineNumberStateForScreenRow(presenter, 4), {screenRow: 4, bufferRow: 3, softWrapped: true}
|
||||
expectValues lineNumberStateForScreenRow(presenter, 5), {screenRow: 5, bufferRow: 4, softWrapped: false}
|
||||
expectValues lineNumberStateForScreenRow(presenter, 6), {screenRow: 6, bufferRow: 7, softWrapped: false}
|
||||
expectValues lineNumberStateForScreenRow(presenter, 7), {screenRow: 7, bufferRow: 8, softWrapped: false}
|
||||
expect(lineNumberStateForScreenRow(presenter, 8)).toBeUndefined()
|
||||
|
||||
it "updates when the editor's content changes", ->
|
||||
@@ -2103,6 +2267,12 @@ describe "TextEditorPresenter", ->
|
||||
expectValues lineNumberStateForScreenRow(presenter, 6), {screenRow: 6, bufferRow: 3, softWrapped: true}
|
||||
expectValues lineNumberStateForScreenRow(presenter, 7), {screenRow: 7, bufferRow: 4, softWrapped: false}
|
||||
|
||||
presenter.setContentFrameWidth(500)
|
||||
|
||||
expectValues lineNumberStateForScreenRow(presenter, 5), {screenRow: 5, bufferRow: 4, softWrapped: false}
|
||||
expectValues lineNumberStateForScreenRow(presenter, 6), {screenRow: 6, bufferRow: 5, softWrapped: false}
|
||||
expectValues lineNumberStateForScreenRow(presenter, 7), {screenRow: 7, bufferRow: 6, softWrapped: false}
|
||||
|
||||
describe ".decorationClasses", ->
|
||||
it "adds decoration classes to the relevant line number state objects, both initially and when decorations change", ->
|
||||
marker1 = editor.markBufferRange([[4, 0], [6, 2]], invalidate: 'touch', maintainHistory: true)
|
||||
|
||||
@@ -31,7 +31,7 @@ describe "TextEditor", ->
|
||||
|
||||
runs ->
|
||||
fs.mkdirSync(pathToOpen)
|
||||
expect(editor1.testSerialization()).toBeUndefined()
|
||||
expect(TextEditor.deserialize(editor1.serialize())).toBeUndefined()
|
||||
|
||||
it "restores selections and folds based on markers in the buffer", ->
|
||||
editor.setSelectedBufferRange([[1, 2], [3, 4]])
|
||||
@@ -39,7 +39,7 @@ describe "TextEditor", ->
|
||||
editor.foldBufferRow(4)
|
||||
expect(editor.isFoldedAtBufferRow(4)).toBeTruthy()
|
||||
|
||||
editor2 = editor.testSerialization()
|
||||
editor2 = TextEditor.deserialize(editor.serialize())
|
||||
|
||||
expect(editor2.id).toBe editor.id
|
||||
expect(editor2.getBuffer().getPath()).toBe editor.getBuffer().getPath()
|
||||
@@ -52,7 +52,7 @@ describe "TextEditor", ->
|
||||
atom.config.set('editor.showInvisibles', true)
|
||||
previousInvisibles = editor.tokenizedLineForScreenRow(0).invisibles
|
||||
|
||||
editor2 = editor.testSerialization()
|
||||
editor2 = TextEditor.deserialize(editor.serialize())
|
||||
|
||||
expect(previousInvisibles).toBeDefined()
|
||||
expect(editor2.displayBuffer.tokenizedLineForScreenRow(0).invisibles).toEqual previousInvisibles
|
||||
@@ -909,118 +909,6 @@ describe "TextEditor", ->
|
||||
cursor2 = editor.addCursorAtBufferPosition([1, 4])
|
||||
expect(cursor2.marker).toBe cursor1.marker
|
||||
|
||||
describe "autoscroll", ->
|
||||
beforeEach ->
|
||||
editor.setVerticalScrollMargin(2)
|
||||
editor.setHorizontalScrollMargin(2)
|
||||
editor.setLineHeightInPixels(10)
|
||||
editor.setDefaultCharWidth(10)
|
||||
editor.setHorizontalScrollbarHeight(0)
|
||||
editor.setHeight(5.5 * 10)
|
||||
editor.setWidth(5.5 * 10)
|
||||
|
||||
it "scrolls down when the last cursor gets closer than ::verticalScrollMargin to the bottom of the editor", ->
|
||||
expect(editor.getScrollTop()).toBe 0
|
||||
expect(editor.getScrollBottom()).toBe 5.5 * 10
|
||||
|
||||
editor.setCursorScreenPosition([2, 0])
|
||||
expect(editor.getScrollBottom()).toBe 5.5 * 10
|
||||
|
||||
editor.moveDown()
|
||||
expect(editor.getScrollBottom()).toBe 6 * 10
|
||||
|
||||
editor.moveDown()
|
||||
expect(editor.getScrollBottom()).toBe 7 * 10
|
||||
|
||||
it "scrolls up when the last cursor gets closer than ::verticalScrollMargin to the top of the editor", ->
|
||||
editor.setCursorScreenPosition([11, 0])
|
||||
editor.setScrollBottom(editor.getScrollHeight())
|
||||
|
||||
editor.moveUp()
|
||||
expect(editor.getScrollBottom()).toBe editor.getScrollHeight()
|
||||
|
||||
editor.moveUp()
|
||||
expect(editor.getScrollTop()).toBe 7 * 10
|
||||
|
||||
editor.moveUp()
|
||||
expect(editor.getScrollTop()).toBe 6 * 10
|
||||
|
||||
it "scrolls right when the last cursor gets closer than ::horizontalScrollMargin to the right of the editor", ->
|
||||
expect(editor.getScrollLeft()).toBe 0
|
||||
expect(editor.getScrollRight()).toBe 5.5 * 10
|
||||
|
||||
editor.setCursorScreenPosition([0, 2])
|
||||
expect(editor.getScrollRight()).toBe 5.5 * 10
|
||||
|
||||
editor.moveRight()
|
||||
expect(editor.getScrollRight()).toBe 6 * 10
|
||||
|
||||
editor.moveRight()
|
||||
expect(editor.getScrollRight()).toBe 7 * 10
|
||||
|
||||
it "scrolls left when the last cursor gets closer than ::horizontalScrollMargin to the left of the editor", ->
|
||||
editor.setScrollRight(editor.getScrollWidth())
|
||||
expect(editor.getScrollRight()).toBe editor.getScrollWidth()
|
||||
editor.setCursorScreenPosition([6, 62], autoscroll: false)
|
||||
|
||||
editor.moveLeft()
|
||||
expect(editor.getScrollLeft()).toBe 59 * 10
|
||||
|
||||
editor.moveLeft()
|
||||
expect(editor.getScrollLeft()).toBe 58 * 10
|
||||
|
||||
it "scrolls down when inserting lines makes the document longer than the editor's height", ->
|
||||
editor.setCursorScreenPosition([13, Infinity])
|
||||
editor.insertNewline()
|
||||
expect(editor.getScrollBottom()).toBe 14 * 10
|
||||
editor.insertNewline()
|
||||
expect(editor.getScrollBottom()).toBe 15 * 10
|
||||
|
||||
it "autoscrolls to the cursor when it moves due to undo", ->
|
||||
editor.insertText('abc')
|
||||
editor.setScrollTop(Infinity)
|
||||
editor.undo()
|
||||
expect(editor.getScrollTop()).toBe 0
|
||||
|
||||
it "doesn't scroll when the cursor moves into the visible area", ->
|
||||
editor.setCursorBufferPosition([0, 0])
|
||||
editor.setScrollTop(40)
|
||||
expect(editor.getVisibleRowRange()).toEqual([4, 9])
|
||||
editor.setCursorBufferPosition([6, 0])
|
||||
expect(editor.getScrollTop()).toBe 40
|
||||
|
||||
it "honors the autoscroll option on cursor and selection manipulation methods", ->
|
||||
expect(editor.getScrollTop()).toBe 0
|
||||
editor.addCursorAtScreenPosition([11, 11], autoscroll: false)
|
||||
expect(editor.getScrollTop()).toBe 0
|
||||
editor.addCursorAtBufferPosition([11, 11], autoscroll: false)
|
||||
expect(editor.getScrollTop()).toBe 0
|
||||
editor.setCursorScreenPosition([11, 11], autoscroll: false)
|
||||
expect(editor.getScrollTop()).toBe 0
|
||||
editor.setCursorBufferPosition([11, 11], autoscroll: false)
|
||||
expect(editor.getScrollTop()).toBe 0
|
||||
editor.addSelectionForBufferRange([[11, 11], [11, 11]], autoscroll: false)
|
||||
expect(editor.getScrollTop()).toBe 0
|
||||
editor.addSelectionForScreenRange([[11, 11], [11, 12]], autoscroll: false)
|
||||
expect(editor.getScrollTop()).toBe 0
|
||||
editor.setSelectedBufferRange([[11, 0], [11, 1]], autoscroll: false)
|
||||
expect(editor.getScrollTop()).toBe 0
|
||||
editor.setSelectedScreenRange([[11, 0], [11, 6]], autoscroll: false)
|
||||
expect(editor.getScrollTop()).toBe 0
|
||||
editor.clearSelections(autoscroll: false)
|
||||
expect(editor.getScrollTop()).toBe 0
|
||||
|
||||
editor.addSelectionForScreenRange([[0, 0], [0, 4]])
|
||||
|
||||
editor.getCursors()[0].setScreenPosition([11, 11], autoscroll: true)
|
||||
expect(editor.getScrollTop()).toBeGreaterThan 0
|
||||
editor.getCursors()[0].setBufferPosition([0, 0], autoscroll: true)
|
||||
expect(editor.getScrollTop()).toBe 0
|
||||
editor.getSelections()[0].setScreenRange([[11, 0], [11, 4]], autoscroll: true)
|
||||
expect(editor.getScrollTop()).toBeGreaterThan 0
|
||||
editor.getSelections()[0].setBufferRange([[0, 0], [0, 4]], autoscroll: true)
|
||||
expect(editor.getScrollTop()).toBe 0
|
||||
|
||||
describe '.logCursorScope()', ->
|
||||
beforeEach ->
|
||||
spyOn(atom.notifications, 'addInfo')
|
||||
@@ -1284,7 +1172,7 @@ describe "TextEditor", ->
|
||||
expect(selection2.isReversed()).toBeFalsy()
|
||||
|
||||
describe ".selectLinesContainingCursors()", ->
|
||||
it "selects the entire line (including newlines) at given row", ->
|
||||
it "selects to the entire line (including newlines) at given row", ->
|
||||
editor.setCursorScreenPosition([1, 2])
|
||||
editor.selectLinesContainingCursors()
|
||||
expect(editor.getSelectedBufferRange()).toEqual [[1, 0], [2, 0]]
|
||||
@@ -1299,19 +1187,12 @@ describe "TextEditor", ->
|
||||
editor.selectLinesContainingCursors()
|
||||
expect(editor.getSelectedBufferRange()).toEqual [[0, 0], [2, 0]]
|
||||
|
||||
it "autoscrolls to the selection", ->
|
||||
editor.setLineHeightInPixels(10)
|
||||
editor.setDefaultCharWidth(10)
|
||||
editor.setHeight(50)
|
||||
editor.setWidth(50)
|
||||
editor.setHorizontalScrollbarHeight(0)
|
||||
editor.setCursorScreenPosition([5, 6])
|
||||
|
||||
editor.scrollToTop()
|
||||
expect(editor.getScrollTop()).toBe 0
|
||||
|
||||
editor.selectLinesContainingCursors()
|
||||
expect(editor.getScrollBottom()).toBe (7 + editor.getVerticalScrollMargin()) * 10
|
||||
describe "when the selection spans multiple row", ->
|
||||
it "selects from the beginning of the first line to the last line", ->
|
||||
selection = editor.getLastSelection()
|
||||
selection.setBufferRange [[1, 10], [3, 20]]
|
||||
editor.selectLinesContainingCursors()
|
||||
expect(editor.getSelectedBufferRange()).toEqual [[1, 0], [4, 0]]
|
||||
|
||||
describe ".selectToBeginningOfWord()", ->
|
||||
it "selects text from cusor position to beginning of word", ->
|
||||
@@ -1565,30 +1446,6 @@ describe "TextEditor", ->
|
||||
expect(selection1).toBe selection
|
||||
expect(selection1.getScreenRange()).toEqual [[2, 2], [3, 4]]
|
||||
|
||||
describe ".setSelectedBufferRange(range)", ->
|
||||
it "autoscrolls the selection if it is last unless the 'autoscroll' option is false", ->
|
||||
editor.setVerticalScrollMargin(2)
|
||||
editor.setHorizontalScrollMargin(2)
|
||||
editor.setLineHeightInPixels(10)
|
||||
editor.setDefaultCharWidth(10)
|
||||
editor.setHeight(70)
|
||||
editor.setWidth(100)
|
||||
editor.setHorizontalScrollbarHeight(0)
|
||||
|
||||
expect(editor.getScrollTop()).toBe 0
|
||||
|
||||
editor.setSelectedBufferRange([[5, 6], [6, 8]])
|
||||
expect(editor.getScrollBottom()).toBe (7 + editor.getVerticalScrollMargin()) * 10
|
||||
expect(editor.getScrollRight()).toBe (8 + editor.getHorizontalScrollMargin()) * 10
|
||||
|
||||
editor.setSelectedBufferRange([[0, 0], [0, 0]])
|
||||
expect(editor.getScrollTop()).toBe 0
|
||||
expect(editor.getScrollLeft()).toBe 0
|
||||
|
||||
editor.setSelectedBufferRange([[6, 6], [6, 8]])
|
||||
expect(editor.getScrollBottom()).toBe (7 + editor.getVerticalScrollMargin()) * 10
|
||||
expect(editor.getScrollRight()).toBe (8 + editor.getHorizontalScrollMargin()) * 10
|
||||
|
||||
describe ".selectMarker(marker)", ->
|
||||
describe "if the marker is valid", ->
|
||||
it "selects the marker's range and returns the selected range", ->
|
||||
@@ -1608,17 +1465,6 @@ describe "TextEditor", ->
|
||||
editor.addSelectionForBufferRange([[3, 4], [5, 6]])
|
||||
expect(editor.getSelectedBufferRanges()).toEqual [[[0, 0], [0, 0]], [[3, 4], [5, 6]]]
|
||||
|
||||
it "autoscrolls to the added selection if needed", ->
|
||||
editor.setVerticalScrollMargin(2)
|
||||
editor.setHorizontalScrollMargin(2)
|
||||
editor.setLineHeightInPixels(10)
|
||||
editor.setDefaultCharWidth(10)
|
||||
editor.setHeight(80)
|
||||
editor.setWidth(100)
|
||||
editor.addSelectionForBufferRange([[8, 10], [8, 15]])
|
||||
expect(editor.getScrollBottom()).toBe (9 * 10) + (2 * 10)
|
||||
expect(editor.getScrollRight()).toBe (15 * 10) + (2 * 10)
|
||||
|
||||
describe ".addSelectionBelow()", ->
|
||||
describe "when the selection is non-empty", ->
|
||||
it "selects the same region of the line below current selections if possible", ->
|
||||
@@ -1883,7 +1729,7 @@ describe "TextEditor", ->
|
||||
expect(editor.getSelectedBufferRanges()).toEqual [[[0, 0], [0, 3]]]
|
||||
|
||||
describe ".consolidateSelections()", ->
|
||||
it "destroys all selections but the most recent, returning true if any selections were destroyed", ->
|
||||
it "destroys all selections but the least recent, returning true if any selections were destroyed", ->
|
||||
editor.setSelectedBufferRange([[3, 16], [3, 21]])
|
||||
selection1 = editor.getLastSelection()
|
||||
selection2 = editor.addSelectionForBufferRange([[3, 25], [3, 34]])
|
||||
@@ -1891,10 +1737,10 @@ describe "TextEditor", ->
|
||||
|
||||
expect(editor.getSelections()).toEqual [selection1, selection2, selection3]
|
||||
expect(editor.consolidateSelections()).toBeTruthy()
|
||||
expect(editor.getSelections()).toEqual [selection3]
|
||||
expect(selection3.isEmpty()).toBeFalsy()
|
||||
expect(editor.getSelections()).toEqual [selection1]
|
||||
expect(selection1.isEmpty()).toBeFalsy()
|
||||
expect(editor.consolidateSelections()).toBeFalsy()
|
||||
expect(editor.getSelections()).toEqual [selection3]
|
||||
expect(editor.getSelections()).toEqual [selection1]
|
||||
|
||||
describe "when the cursor is moved while there is a selection", ->
|
||||
makeSelection = -> selection.setBufferRange [[1, 2], [1, 5]]
|
||||
@@ -2267,16 +2113,6 @@ describe "TextEditor", ->
|
||||
expect(cursor1.getBufferPosition()).toEqual [1, 5]
|
||||
expect(cursor2.getBufferPosition()).toEqual [2, 7]
|
||||
|
||||
it "autoscrolls to the last cursor", ->
|
||||
editor.setCursorScreenPosition([1, 2])
|
||||
editor.addCursorAtScreenPosition([10, 4])
|
||||
editor.setLineHeightInPixels(10)
|
||||
editor.setHeight(50)
|
||||
|
||||
expect(editor.getScrollTop()).toBe 0
|
||||
editor.insertText('a')
|
||||
expect(editor.getScrollTop()).toBe 80
|
||||
|
||||
describe "when there are multiple non-empty selections", ->
|
||||
describe "when the selections are on the same line", ->
|
||||
it "replaces each selection range with the inserted characters", ->
|
||||
@@ -3190,6 +3026,32 @@ describe "TextEditor", ->
|
||||
expect(buffer.lineForRow(3)).toBe ' var pivot = item'
|
||||
expect(atom.clipboard.read()).toBe ' <= 1) ret\ns.shift(), current, left = [], right = [];'
|
||||
|
||||
describe ".cutToEndOfBufferLine()", ->
|
||||
beforeEach ->
|
||||
editor.setSoftWrapped(true)
|
||||
editor.setEditorWidthInChars(10)
|
||||
|
||||
describe "when nothing is selected", ->
|
||||
it "cuts up to the end of the buffer line", ->
|
||||
editor.setCursorBufferPosition([2, 20])
|
||||
editor.addCursorAtBufferPosition([3, 20])
|
||||
|
||||
editor.cutToEndOfBufferLine()
|
||||
|
||||
expect(buffer.lineForRow(2)).toBe ' if (items.length'
|
||||
expect(buffer.lineForRow(3)).toBe ' var pivot = item'
|
||||
expect(atom.clipboard.read()).toBe ' <= 1) return items;\ns.shift(), current, left = [], right = [];'
|
||||
|
||||
describe "when text is selected", ->
|
||||
it "only cuts the selected text, not to the end of the buffer line", ->
|
||||
editor.setSelectedBufferRanges([[[2, 20], [2, 30]], [[3, 20], [3, 20]]])
|
||||
|
||||
editor.cutToEndOfBufferLine()
|
||||
|
||||
expect(buffer.lineForRow(2)).toBe ' if (items.lengthurn items;'
|
||||
expect(buffer.lineForRow(3)).toBe ' var pivot = item'
|
||||
expect(atom.clipboard.read()).toBe ' <= 1) ret\ns.shift(), current, left = [], right = [];'
|
||||
|
||||
describe ".copySelectedText()", ->
|
||||
it "copies selected text onto the clipboard", ->
|
||||
editor.setSelectedBufferRanges([[[0, 4], [0, 13]], [[1, 6], [1, 10]], [[2, 8], [2, 13]]])
|
||||
@@ -4120,19 +3982,6 @@ describe "TextEditor", ->
|
||||
runs ->
|
||||
expect(editor.softTabs).toBe false
|
||||
|
||||
it "uses hard tabs in Makefile files", ->
|
||||
# FIXME remove once this is handled by a scoped setting in the
|
||||
# language-make package
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-make')
|
||||
|
||||
waitsForPromise ->
|
||||
atom.project.open('Makefile').then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
expect(editor.softTabs).toBe false
|
||||
|
||||
describe "when editor.tabType is 'hard'", ->
|
||||
beforeEach ->
|
||||
atom.config.set('editor.tabType', 'hard')
|
||||
@@ -4812,30 +4661,10 @@ describe "TextEditor", ->
|
||||
editor.normalizeTabsInBufferRange([[0, 0], [Infinity, Infinity]])
|
||||
expect(editor.getText()).toBe ' '
|
||||
|
||||
describe ".scrollToCursorPosition()", ->
|
||||
it "scrolls the last cursor into view, centering around the cursor if possible and the 'center' option isn't false", ->
|
||||
editor.setCursorScreenPosition([8, 8])
|
||||
editor.setLineHeightInPixels(10)
|
||||
editor.setDefaultCharWidth(10)
|
||||
editor.setHeight(60)
|
||||
editor.setWidth(130)
|
||||
editor.setHorizontalScrollbarHeight(0)
|
||||
expect(editor.getScrollTop()).toBe 0
|
||||
expect(editor.getScrollLeft()).toBe 0
|
||||
|
||||
editor.scrollToCursorPosition()
|
||||
expect(editor.getScrollTop()).toBe (8.5 * 10) - 30
|
||||
expect(editor.getScrollBottom()).toBe (8.5 * 10) + 30
|
||||
expect(editor.getScrollRight()).toBe (9 + editor.getHorizontalScrollMargin()) * 10
|
||||
|
||||
editor.setScrollTop(0)
|
||||
editor.scrollToCursorPosition(center: false)
|
||||
expect(editor.getScrollBottom()).toBe (9 + editor.getVerticalScrollMargin()) * 10
|
||||
|
||||
describe ".pageUp/Down()", ->
|
||||
it "moves the cursor down one page length", ->
|
||||
editor.setLineHeightInPixels(10)
|
||||
editor.setHeight(50)
|
||||
editor.setRowsPerPage(5)
|
||||
|
||||
expect(editor.getCursorBufferPosition().row).toBe 0
|
||||
|
||||
editor.pageDown()
|
||||
@@ -4852,9 +4681,8 @@ describe "TextEditor", ->
|
||||
|
||||
describe ".selectPageUp/Down()", ->
|
||||
it "selects one screen height of text up or down", ->
|
||||
editor.setLineHeightInPixels(10)
|
||||
editor.setHeight(50)
|
||||
expect(editor.getScrollHeight()).toBe 130
|
||||
editor.setRowsPerPage(5)
|
||||
|
||||
expect(editor.getCursorBufferPosition().row).toBe 0
|
||||
|
||||
editor.selectPageDown()
|
||||
@@ -5244,18 +5072,6 @@ describe "TextEditor", ->
|
||||
beforeEach ->
|
||||
marker = editor.markBufferRange([[1, 0], [1, 0]])
|
||||
|
||||
it "casts 'gutter' type to 'line-number' unless a gutter name is specified.", ->
|
||||
jasmine.snapshotDeprecations()
|
||||
|
||||
lineNumberDecoration = editor.decorateMarker(marker, {type: 'gutter'})
|
||||
customGutterDecoration = editor.decorateMarker(marker, {type: 'gutter', gutterName: 'custom'})
|
||||
expect(lineNumberDecoration.getProperties().type).toBe 'line-number'
|
||||
expect(lineNumberDecoration.getProperties().gutterName).toBe 'line-number'
|
||||
expect(customGutterDecoration.getProperties().type).toBe 'gutter'
|
||||
expect(customGutterDecoration.getProperties().gutterName).toBe 'custom'
|
||||
|
||||
jasmine.restoreDeprecationsSnapshot()
|
||||
|
||||
it 'reflects an added decoration when one of its custom gutters is decorated.', ->
|
||||
gutter = editor.addGutter {'name': 'custom-gutter'}
|
||||
decoration = gutter.decorateMarker marker, {class: 'custom-class'}
|
||||
|
||||
@@ -1,22 +1,18 @@
|
||||
path = require 'path'
|
||||
|
||||
{$, $$} = require '../src/space-pen-extensions'
|
||||
fs = require 'fs-plus'
|
||||
temp = require 'temp'
|
||||
|
||||
ThemeManager = require '../src/theme-manager'
|
||||
Package = require '../src/package'
|
||||
|
||||
describe "ThemeManager", ->
|
||||
themeManager = null
|
||||
describe "atom.themes", ->
|
||||
resourcePath = atom.getLoadSettings().resourcePath
|
||||
configDirPath = atom.getConfigDirPath()
|
||||
|
||||
beforeEach ->
|
||||
themeManager = new ThemeManager({packageManager: atom.packages, resourcePath, configDirPath})
|
||||
spyOn(console, 'warn')
|
||||
|
||||
afterEach ->
|
||||
themeManager.deactivateThemes()
|
||||
atom.themes.deactivateThemes()
|
||||
|
||||
describe "theme getters and setters", ->
|
||||
beforeEach ->
|
||||
@@ -26,19 +22,20 @@ describe "ThemeManager", ->
|
||||
afterEach ->
|
||||
jasmine.restoreDeprecationsSnapshot()
|
||||
|
||||
it 'getLoadedThemes get all the loaded themes', ->
|
||||
themes = themeManager.getLoadedThemes()
|
||||
expect(themes.length).toBeGreaterThan(2)
|
||||
describe 'getLoadedThemes', ->
|
||||
it 'gets all the loaded themes', ->
|
||||
themes = atom.themes.getLoadedThemes()
|
||||
expect(themes.length).toBeGreaterThan(2)
|
||||
|
||||
it 'getActiveThemes get all the active themes', ->
|
||||
waitsForPromise ->
|
||||
themeManager.activateThemes()
|
||||
describe "getActiveThemes", ->
|
||||
it 'gets all the active themes', ->
|
||||
waitsForPromise -> atom.themes.activateThemes()
|
||||
|
||||
runs ->
|
||||
names = atom.config.get('core.themes')
|
||||
expect(names.length).toBeGreaterThan(0)
|
||||
themes = themeManager.getActiveThemes()
|
||||
expect(themes).toHaveLength(names.length)
|
||||
runs ->
|
||||
names = atom.config.get('core.themes')
|
||||
expect(names.length).toBeGreaterThan(0)
|
||||
themes = atom.themes.getActiveThemes()
|
||||
expect(themes).toHaveLength(names.length)
|
||||
|
||||
describe "when the core.themes config value contains invalid entry", ->
|
||||
it "ignores theme", ->
|
||||
@@ -54,13 +51,13 @@ describe "ThemeManager", ->
|
||||
'atom-dark-ui'
|
||||
]
|
||||
|
||||
expect(themeManager.getEnabledThemeNames()).toEqual ['atom-dark-ui', 'atom-light-ui']
|
||||
expect(atom.themes.getEnabledThemeNames()).toEqual ['atom-dark-ui', 'atom-light-ui']
|
||||
|
||||
describe "::getImportPaths()", ->
|
||||
it "returns the theme directories before the themes are loaded", ->
|
||||
atom.config.set('core.themes', ['theme-with-index-less', 'atom-dark-ui', 'atom-light-ui'])
|
||||
|
||||
paths = themeManager.getImportPaths()
|
||||
paths = atom.themes.getImportPaths()
|
||||
|
||||
# syntax theme is not a dir at this time, so only two.
|
||||
expect(paths.length).toBe 2
|
||||
@@ -69,45 +66,45 @@ describe "ThemeManager", ->
|
||||
|
||||
it "ignores themes that cannot be resolved to a directory", ->
|
||||
atom.config.set('core.themes', ['definitely-not-a-theme'])
|
||||
expect(-> themeManager.getImportPaths()).not.toThrow()
|
||||
expect(-> atom.themes.getImportPaths()).not.toThrow()
|
||||
|
||||
describe "when the core.themes config value changes", ->
|
||||
it "add/removes stylesheets to reflect the new config value", ->
|
||||
themeManager.onDidChangeActiveThemes didChangeActiveThemesHandler = jasmine.createSpy()
|
||||
atom.themes.onDidChangeActiveThemes didChangeActiveThemesHandler = jasmine.createSpy()
|
||||
spyOn(atom.styles, 'getUserStyleSheetPath').andCallFake -> null
|
||||
|
||||
waitsForPromise ->
|
||||
themeManager.activateThemes()
|
||||
atom.themes.activateThemes()
|
||||
|
||||
runs ->
|
||||
didChangeActiveThemesHandler.reset()
|
||||
atom.config.set('core.themes', [])
|
||||
|
||||
waitsFor ->
|
||||
waitsFor 'a', ->
|
||||
didChangeActiveThemesHandler.callCount is 1
|
||||
|
||||
runs ->
|
||||
didChangeActiveThemesHandler.reset()
|
||||
expect($('style.theme')).toHaveLength 0
|
||||
expect(document.querySelectorAll('style.theme')).toHaveLength 0
|
||||
atom.config.set('core.themes', ['atom-dark-ui'])
|
||||
|
||||
waitsFor ->
|
||||
waitsFor 'b', ->
|
||||
didChangeActiveThemesHandler.callCount is 1
|
||||
|
||||
runs ->
|
||||
didChangeActiveThemesHandler.reset()
|
||||
expect($('style[priority=1]')).toHaveLength 2
|
||||
expect($('style[priority=1]:eq(0)').attr('source-path')).toMatch /atom-dark-ui/
|
||||
expect(document.querySelectorAll('style[priority="1"]')).toHaveLength 2
|
||||
expect(document.querySelector('style[priority="1"]').getAttribute('source-path')).toMatch /atom-dark-ui/
|
||||
atom.config.set('core.themes', ['atom-light-ui', 'atom-dark-ui'])
|
||||
|
||||
waitsFor ->
|
||||
waitsFor 'c', ->
|
||||
didChangeActiveThemesHandler.callCount is 1
|
||||
|
||||
runs ->
|
||||
didChangeActiveThemesHandler.reset()
|
||||
expect($('style[priority=1]')).toHaveLength 2
|
||||
expect($('style[priority=1]:eq(0)').attr('source-path')).toMatch /atom-dark-ui/
|
||||
expect($('style[priority=1]:eq(1)').attr('source-path')).toMatch /atom-light-ui/
|
||||
expect(document.querySelectorAll('style[priority="1"]')).toHaveLength 2
|
||||
expect(document.querySelectorAll('style[priority="1"]')[0].getAttribute('source-path')).toMatch /atom-dark-ui/
|
||||
expect(document.querySelectorAll('style[priority="1"]')[1].getAttribute('source-path')).toMatch /atom-light-ui/
|
||||
atom.config.set('core.themes', [])
|
||||
|
||||
waitsFor ->
|
||||
@@ -115,7 +112,7 @@ describe "ThemeManager", ->
|
||||
|
||||
runs ->
|
||||
didChangeActiveThemesHandler.reset()
|
||||
expect($('style[priority=1]')).toHaveLength 2
|
||||
expect(document.querySelectorAll('style[priority="1"]')).toHaveLength 2
|
||||
# atom-dark-ui has an directory path, the syntax one doesn't
|
||||
atom.config.set('core.themes', ['theme-with-index-less', 'atom-dark-ui'])
|
||||
|
||||
@@ -123,23 +120,23 @@ describe "ThemeManager", ->
|
||||
didChangeActiveThemesHandler.callCount is 1
|
||||
|
||||
runs ->
|
||||
expect($('style[priority=1]')).toHaveLength 2
|
||||
importPaths = themeManager.getImportPaths()
|
||||
expect(document.querySelectorAll('style[priority="1"]')).toHaveLength 2
|
||||
importPaths = atom.themes.getImportPaths()
|
||||
expect(importPaths.length).toBe 1
|
||||
expect(importPaths[0]).toContain 'atom-dark-ui'
|
||||
|
||||
it 'adds theme-* classes to the workspace for each active theme', ->
|
||||
atom.config.set('core.themes', ['atom-dark-ui', 'atom-dark-syntax'])
|
||||
workspaceElement = atom.views.getView(atom.workspace)
|
||||
themeManager.onDidChangeActiveThemes didChangeActiveThemesHandler = jasmine.createSpy()
|
||||
atom.themes.onDidChangeActiveThemes didChangeActiveThemesHandler = jasmine.createSpy()
|
||||
|
||||
waitsForPromise ->
|
||||
themeManager.activateThemes()
|
||||
atom.themes.activateThemes()
|
||||
|
||||
runs ->
|
||||
expect(workspaceElement).toHaveClass 'theme-atom-dark-ui'
|
||||
|
||||
themeManager.onDidChangeActiveThemes didChangeActiveThemesHandler = jasmine.createSpy()
|
||||
atom.themes.onDidChangeActiveThemes didChangeActiveThemesHandler = jasmine.createSpy()
|
||||
atom.config.set('core.themes', ['theme-with-ui-variables', 'theme-with-syntax-variables'])
|
||||
|
||||
waitsFor ->
|
||||
@@ -154,8 +151,8 @@ describe "ThemeManager", ->
|
||||
|
||||
describe "when a theme fails to load", ->
|
||||
it "logs a warning", ->
|
||||
spyOn(console, 'warn')
|
||||
atom.packages.activatePackage('a-theme-that-will-not-be-found')
|
||||
console.warn.reset()
|
||||
atom.packages.activatePackage('a-theme-that-will-not-be-found').then((->), (->))
|
||||
expect(console.warn.callCount).toBe 1
|
||||
expect(console.warn.argsForCall[0][0]).toContain "Could not resolve 'a-theme-that-will-not-be-found'"
|
||||
|
||||
@@ -168,41 +165,37 @@ describe "ThemeManager", ->
|
||||
|
||||
it "synchronously loads css at the given path and installs a style tag for it in the head", ->
|
||||
atom.styles.onDidAddStyleElement styleElementAddedHandler = jasmine.createSpy("styleElementAddedHandler")
|
||||
themeManager.onDidChangeStylesheets stylesheetsChangedHandler = jasmine.createSpy("stylesheetsChangedHandler")
|
||||
themeManager.onDidAddStylesheet stylesheetAddedHandler = jasmine.createSpy("stylesheetAddedHandler")
|
||||
|
||||
cssPath = atom.project.getDirectories()[0]?.resolve('css.css')
|
||||
lengthBefore = $('head style').length
|
||||
lengthBefore = document.querySelectorAll('head style').length
|
||||
|
||||
themeManager.requireStylesheet(cssPath)
|
||||
expect($('head style').length).toBe lengthBefore + 1
|
||||
atom.themes.requireStylesheet(cssPath)
|
||||
expect(document.querySelectorAll('head style').length).toBe lengthBefore + 1
|
||||
|
||||
expect(styleElementAddedHandler).toHaveBeenCalled()
|
||||
expect(stylesheetAddedHandler).toHaveBeenCalled()
|
||||
expect(stylesheetsChangedHandler).toHaveBeenCalled()
|
||||
|
||||
element = $('head style[source-path*="css.css"]')
|
||||
expect(element.attr('source-path')).toBe themeManager.stringToId(cssPath)
|
||||
expect(element.text()).toBe fs.readFileSync(cssPath, 'utf8')
|
||||
expect(element[0].sheet).toBe stylesheetAddedHandler.argsForCall[0][0]
|
||||
element = document.querySelector('head style[source-path*="css.css"]')
|
||||
expect(element.getAttribute('source-path')).toBe atom.themes.stringToId(cssPath)
|
||||
expect(element.textContent).toBe fs.readFileSync(cssPath, 'utf8')
|
||||
|
||||
# doesn't append twice
|
||||
styleElementAddedHandler.reset()
|
||||
themeManager.requireStylesheet(cssPath)
|
||||
expect($('head style').length).toBe lengthBefore + 1
|
||||
atom.themes.requireStylesheet(cssPath)
|
||||
expect(document.querySelectorAll('head style').length).toBe lengthBefore + 1
|
||||
expect(styleElementAddedHandler).not.toHaveBeenCalled()
|
||||
|
||||
$('head style[id*="css.css"]').remove()
|
||||
for styleElement in document.querySelectorAll('head style[id*="css.css"]')
|
||||
styleElement.remove()
|
||||
|
||||
it "synchronously loads and parses less files at the given path and installs a style tag for it in the head", ->
|
||||
lessPath = atom.project.getDirectories()[0]?.resolve('sample.less')
|
||||
lengthBefore = $('head style').length
|
||||
themeManager.requireStylesheet(lessPath)
|
||||
expect($('head style').length).toBe lengthBefore + 1
|
||||
lengthBefore = document.querySelectorAll('head style').length
|
||||
atom.themes.requireStylesheet(lessPath)
|
||||
expect(document.querySelectorAll('head style').length).toBe lengthBefore + 1
|
||||
|
||||
element = $('head style[source-path*="sample.less"]')
|
||||
expect(element.attr('source-path')).toBe themeManager.stringToId(lessPath)
|
||||
expect(element.text()).toBe """
|
||||
element = document.querySelector('head style[source-path*="sample.less"]')
|
||||
expect(element.getAttribute('source-path')).toBe atom.themes.stringToId(lessPath)
|
||||
expect(element.textContent).toBe """
|
||||
#header {
|
||||
color: #4d926f;
|
||||
}
|
||||
@@ -213,41 +206,35 @@ describe "ThemeManager", ->
|
||||
"""
|
||||
|
||||
# doesn't append twice
|
||||
themeManager.requireStylesheet(lessPath)
|
||||
expect($('head style').length).toBe lengthBefore + 1
|
||||
$('head style[id*="sample.less"]').remove()
|
||||
atom.themes.requireStylesheet(lessPath)
|
||||
expect(document.querySelectorAll('head style').length).toBe lengthBefore + 1
|
||||
for styleElement in document.querySelectorAll('head style[id*="sample.less"]')
|
||||
styleElement.remove()
|
||||
|
||||
it "supports requiring css and less stylesheets without an explicit extension", ->
|
||||
themeManager.requireStylesheet path.join(__dirname, 'fixtures', 'css')
|
||||
expect($('head style[source-path*="css.css"]').attr('source-path')).toBe themeManager.stringToId(atom.project.getDirectories()[0]?.resolve('css.css'))
|
||||
themeManager.requireStylesheet path.join(__dirname, 'fixtures', 'sample')
|
||||
expect($('head style[source-path*="sample.less"]').attr('source-path')).toBe themeManager.stringToId(atom.project.getDirectories()[0]?.resolve('sample.less'))
|
||||
atom.themes.requireStylesheet path.join(__dirname, 'fixtures', 'css')
|
||||
expect(document.querySelector('head style[source-path*="css.css"]').getAttribute('source-path')).toBe atom.themes.stringToId(atom.project.getDirectories()[0]?.resolve('css.css'))
|
||||
atom.themes.requireStylesheet path.join(__dirname, 'fixtures', 'sample')
|
||||
expect(document.querySelector('head style[source-path*="sample.less"]').getAttribute('source-path')).toBe atom.themes.stringToId(atom.project.getDirectories()[0]?.resolve('sample.less'))
|
||||
|
||||
$('head style[id*="css.css"]').remove()
|
||||
$('head style[id*="sample.less"]').remove()
|
||||
document.querySelector('head style[source-path*="css.css"]').remove()
|
||||
document.querySelector('head style[source-path*="sample.less"]').remove()
|
||||
|
||||
it "returns a disposable allowing styles applied by the given path to be removed", ->
|
||||
cssPath = require.resolve('./fixtures/css.css')
|
||||
|
||||
expect($(document.body).css('font-weight')).not.toBe("bold")
|
||||
disposable = themeManager.requireStylesheet(cssPath)
|
||||
expect($(document.body).css('font-weight')).toBe("bold")
|
||||
expect(getComputedStyle(document.body).fontWeight).not.toBe("bold")
|
||||
disposable = atom.themes.requireStylesheet(cssPath)
|
||||
expect(getComputedStyle(document.body).fontWeight).toBe("bold")
|
||||
|
||||
atom.styles.onDidRemoveStyleElement styleElementRemovedHandler = jasmine.createSpy("styleElementRemovedHandler")
|
||||
themeManager.onDidRemoveStylesheet stylesheetRemovedHandler = jasmine.createSpy("stylesheetRemovedHandler")
|
||||
themeManager.onDidChangeStylesheets stylesheetsChangedHandler = jasmine.createSpy("stylesheetsChangedHandler")
|
||||
|
||||
disposable.dispose()
|
||||
|
||||
expect($(document.body).css('font-weight')).not.toBe("bold")
|
||||
expect(getComputedStyle(document.body).fontWeight).not.toBe("bold")
|
||||
|
||||
expect(styleElementRemovedHandler).toHaveBeenCalled()
|
||||
expect(stylesheetRemovedHandler).toHaveBeenCalled()
|
||||
stylesheet = stylesheetRemovedHandler.argsForCall[0][0]
|
||||
expect(stylesheet instanceof CSSStyleSheet).toBe true
|
||||
expect(stylesheet.cssRules[0].selectorText).toBe 'body'
|
||||
|
||||
expect(stylesheetsChangedHandler).toHaveBeenCalled()
|
||||
|
||||
describe "base style sheet loading", ->
|
||||
workspaceElement = null
|
||||
@@ -257,10 +244,10 @@ describe "ThemeManager", ->
|
||||
workspaceElement.appendChild document.createElement('atom-text-editor')
|
||||
|
||||
waitsForPromise ->
|
||||
themeManager.activateThemes()
|
||||
atom.themes.activateThemes()
|
||||
|
||||
it "loads the correct values from the theme's ui-variables file", ->
|
||||
themeManager.onDidChangeActiveThemes didChangeActiveThemesHandler = jasmine.createSpy()
|
||||
atom.themes.onDidChangeActiveThemes didChangeActiveThemesHandler = jasmine.createSpy()
|
||||
atom.config.set('core.themes', ['theme-with-ui-variables', 'theme-with-syntax-variables'])
|
||||
|
||||
waitsFor ->
|
||||
@@ -271,13 +258,13 @@ describe "ThemeManager", ->
|
||||
expect(getComputedStyle(workspaceElement)["background-color"]).toBe "rgb(0, 0, 255)"
|
||||
|
||||
# from within the theme itself
|
||||
expect($("atom-text-editor").css("padding-top")).toBe "150px"
|
||||
expect($("atom-text-editor").css("padding-right")).toBe "150px"
|
||||
expect($("atom-text-editor").css("padding-bottom")).toBe "150px"
|
||||
expect(getComputedStyle(document.querySelector("atom-text-editor")).paddingTop).toBe "150px"
|
||||
expect(getComputedStyle(document.querySelector("atom-text-editor")).paddingRight).toBe "150px"
|
||||
expect(getComputedStyle(document.querySelector("atom-text-editor")).paddingBottom).toBe "150px"
|
||||
|
||||
describe "when there is a theme with incomplete variables", ->
|
||||
it "loads the correct values from the fallback ui-variables", ->
|
||||
themeManager.onDidChangeActiveThemes didChangeActiveThemesHandler = jasmine.createSpy()
|
||||
atom.themes.onDidChangeActiveThemes didChangeActiveThemesHandler = jasmine.createSpy()
|
||||
atom.config.set('core.themes', ['theme-with-incomplete-ui-variables', 'theme-with-syntax-variables'])
|
||||
|
||||
waitsFor ->
|
||||
@@ -288,7 +275,7 @@ describe "ThemeManager", ->
|
||||
expect(getComputedStyle(workspaceElement)["background-color"]).toBe "rgb(0, 0, 255)"
|
||||
|
||||
# from within the theme itself
|
||||
expect($("atom-text-editor").css("background-color")).toBe "rgb(0, 152, 255)"
|
||||
expect(getComputedStyle(document.querySelector("atom-text-editor")).backgroundColor).toBe "rgb(0, 152, 255)"
|
||||
|
||||
describe "user stylesheet", ->
|
||||
userStylesheetPath = null
|
||||
@@ -306,67 +293,52 @@ describe "ThemeManager", ->
|
||||
|
||||
it "reloads it", ->
|
||||
[styleElementAddedHandler, styleElementRemovedHandler] = []
|
||||
[stylesheetRemovedHandler, stylesheetAddedHandler, stylesheetsChangedHandler] = []
|
||||
|
||||
waitsForPromise ->
|
||||
themeManager.activateThemes()
|
||||
atom.themes.activateThemes()
|
||||
|
||||
runs ->
|
||||
atom.styles.onDidRemoveStyleElement styleElementRemovedHandler = jasmine.createSpy("styleElementRemovedHandler")
|
||||
atom.styles.onDidAddStyleElement styleElementAddedHandler = jasmine.createSpy("styleElementAddedHandler")
|
||||
|
||||
themeManager.onDidChangeStylesheets stylesheetsChangedHandler = jasmine.createSpy("stylesheetsChangedHandler")
|
||||
themeManager.onDidRemoveStylesheet stylesheetRemovedHandler = jasmine.createSpy("stylesheetRemovedHandler")
|
||||
themeManager.onDidAddStylesheet stylesheetAddedHandler = jasmine.createSpy("stylesheetAddedHandler")
|
||||
spyOn(themeManager, 'loadUserStylesheet').andCallThrough()
|
||||
spyOn(atom.themes, 'loadUserStylesheet').andCallThrough()
|
||||
|
||||
expect($(document.body).css('border-style')).toBe 'dotted'
|
||||
expect(getComputedStyle(document.body).borderStyle).toBe 'dotted'
|
||||
fs.writeFileSync(userStylesheetPath, 'body {border-style: dashed}')
|
||||
|
||||
waitsFor ->
|
||||
themeManager.loadUserStylesheet.callCount is 1
|
||||
atom.themes.loadUserStylesheet.callCount is 1
|
||||
|
||||
runs ->
|
||||
expect($(document.body).css('border-style')).toBe 'dashed'
|
||||
expect(getComputedStyle(document.body).borderStyle).toBe 'dashed'
|
||||
|
||||
expect(styleElementRemovedHandler).toHaveBeenCalled()
|
||||
expect(styleElementRemovedHandler.argsForCall[0][0].textContent).toContain 'dotted'
|
||||
expect(stylesheetRemovedHandler).toHaveBeenCalled()
|
||||
expect(stylesheetRemovedHandler.argsForCall[0][0].cssRules[0].style.border).toBe 'dotted'
|
||||
|
||||
expect(styleElementAddedHandler).toHaveBeenCalled()
|
||||
expect(styleElementAddedHandler.argsForCall[0][0].textContent).toContain 'dashed'
|
||||
expect(stylesheetAddedHandler).toHaveBeenCalled()
|
||||
expect(stylesheetAddedHandler.argsForCall[0][0].cssRules[0].style.border).toBe 'dashed'
|
||||
|
||||
expect(stylesheetsChangedHandler).toHaveBeenCalled()
|
||||
|
||||
styleElementRemovedHandler.reset()
|
||||
stylesheetRemovedHandler.reset()
|
||||
stylesheetsChangedHandler.reset()
|
||||
fs.removeSync(userStylesheetPath)
|
||||
|
||||
waitsFor ->
|
||||
themeManager.loadUserStylesheet.callCount is 2
|
||||
atom.themes.loadUserStylesheet.callCount is 2
|
||||
|
||||
runs ->
|
||||
expect(styleElementRemovedHandler).toHaveBeenCalled()
|
||||
expect(styleElementRemovedHandler.argsForCall[0][0].textContent).toContain 'dashed'
|
||||
expect(stylesheetRemovedHandler).toHaveBeenCalled()
|
||||
expect(stylesheetRemovedHandler.argsForCall[0][0].cssRules[0].style.border).toBe 'dashed'
|
||||
expect($(document.body).css('border-style')).toBe 'none'
|
||||
expect(stylesheetsChangedHandler).toHaveBeenCalled()
|
||||
expect(getComputedStyle(document.body).borderStyle).toBe 'none'
|
||||
|
||||
describe "when there is an error reading the stylesheet", ->
|
||||
addErrorHandler = null
|
||||
beforeEach ->
|
||||
themeManager.loadUserStylesheet()
|
||||
spyOn(themeManager.lessCache, 'cssForFile').andCallFake ->
|
||||
atom.themes.loadUserStylesheet()
|
||||
spyOn(atom.themes.lessCache, 'cssForFile').andCallFake ->
|
||||
throw new Error('EACCES permission denied "styles.less"')
|
||||
atom.notifications.onDidAddNotification addErrorHandler = jasmine.createSpy()
|
||||
|
||||
it "creates an error notification and does not add the stylesheet", ->
|
||||
themeManager.loadUserStylesheet()
|
||||
atom.themes.loadUserStylesheet()
|
||||
expect(addErrorHandler).toHaveBeenCalled()
|
||||
note = addErrorHandler.mostRecentCall.args[0]
|
||||
expect(note.getType()).toBe 'error'
|
||||
@@ -380,11 +352,11 @@ describe "ThemeManager", ->
|
||||
spyOn(File::, 'on').andCallFake (event) ->
|
||||
if event.indexOf('contents-changed') > -1
|
||||
throw new Error('Unable to watch path')
|
||||
spyOn(themeManager, 'loadStylesheet').andReturn ''
|
||||
spyOn(atom.themes, 'loadStylesheet').andReturn ''
|
||||
atom.notifications.onDidAddNotification addErrorHandler = jasmine.createSpy()
|
||||
|
||||
it "creates an error notification", ->
|
||||
themeManager.loadUserStylesheet()
|
||||
atom.themes.loadUserStylesheet()
|
||||
expect(addErrorHandler).toHaveBeenCalled()
|
||||
note = addErrorHandler.mostRecentCall.args[0]
|
||||
expect(note.getType()).toBe 'error'
|
||||
@@ -393,38 +365,35 @@ describe "ThemeManager", ->
|
||||
it "adds a notification when a theme's stylesheet is invalid", ->
|
||||
addErrorHandler = jasmine.createSpy()
|
||||
atom.notifications.onDidAddNotification(addErrorHandler)
|
||||
expect(-> atom.packages.activatePackage('theme-with-invalid-styles')).not.toThrow()
|
||||
expect(-> atom.packages.activatePackage('theme-with-invalid-styles').then((->), (->))).not.toThrow()
|
||||
expect(addErrorHandler.callCount).toBe 2
|
||||
expect(addErrorHandler.argsForCall[1][0].message).toContain("Failed to activate the theme-with-invalid-styles theme")
|
||||
|
||||
describe "when a non-existent theme is present in the config", ->
|
||||
beforeEach ->
|
||||
spyOn(console, 'warn')
|
||||
console.warn.reset()
|
||||
atom.config.set('core.themes', ['non-existent-dark-ui', 'non-existent-dark-syntax'])
|
||||
|
||||
waitsForPromise ->
|
||||
themeManager.activateThemes()
|
||||
atom.themes.activateThemes()
|
||||
|
||||
it 'uses the default dark UI and syntax themes and logs a warning', ->
|
||||
activeThemeNames = themeManager.getActiveThemeNames()
|
||||
activeThemeNames = atom.themes.getActiveThemeNames()
|
||||
expect(console.warn.callCount).toBe 2
|
||||
expect(activeThemeNames.length).toBe(2)
|
||||
expect(activeThemeNames).toContain('atom-dark-ui')
|
||||
expect(activeThemeNames).toContain('atom-dark-syntax')
|
||||
|
||||
describe "when in safe mode", ->
|
||||
beforeEach ->
|
||||
themeManager = new ThemeManager({packageManager: atom.packages, resourcePath, configDirPath, safeMode: true})
|
||||
|
||||
describe 'when the enabled UI and syntax themes are bundled with Atom', ->
|
||||
beforeEach ->
|
||||
atom.config.set('core.themes', ['atom-light-ui', 'atom-dark-syntax'])
|
||||
|
||||
waitsForPromise ->
|
||||
themeManager.activateThemes()
|
||||
atom.themes.activateThemes()
|
||||
|
||||
it 'uses the enabled themes', ->
|
||||
activeThemeNames = themeManager.getActiveThemeNames()
|
||||
activeThemeNames = atom.themes.getActiveThemeNames()
|
||||
expect(activeThemeNames.length).toBe(2)
|
||||
expect(activeThemeNames).toContain('atom-light-ui')
|
||||
expect(activeThemeNames).toContain('atom-dark-syntax')
|
||||
@@ -434,10 +403,10 @@ describe "ThemeManager", ->
|
||||
atom.config.set('core.themes', ['installed-dark-ui', 'installed-dark-syntax'])
|
||||
|
||||
waitsForPromise ->
|
||||
themeManager.activateThemes()
|
||||
atom.themes.activateThemes()
|
||||
|
||||
it 'uses the default dark UI and syntax themes', ->
|
||||
activeThemeNames = themeManager.getActiveThemeNames()
|
||||
activeThemeNames = atom.themes.getActiveThemeNames()
|
||||
expect(activeThemeNames.length).toBe(2)
|
||||
expect(activeThemeNames).toContain('atom-dark-ui')
|
||||
expect(activeThemeNames).toContain('atom-dark-syntax')
|
||||
@@ -447,10 +416,10 @@ describe "ThemeManager", ->
|
||||
atom.config.set('core.themes', ['installed-dark-ui', 'atom-light-syntax'])
|
||||
|
||||
waitsForPromise ->
|
||||
themeManager.activateThemes()
|
||||
atom.themes.activateThemes()
|
||||
|
||||
it 'uses the default dark UI theme', ->
|
||||
activeThemeNames = themeManager.getActiveThemeNames()
|
||||
activeThemeNames = atom.themes.getActiveThemeNames()
|
||||
expect(activeThemeNames.length).toBe(2)
|
||||
expect(activeThemeNames).toContain('atom-dark-ui')
|
||||
expect(activeThemeNames).toContain('atom-light-syntax')
|
||||
@@ -460,10 +429,10 @@ describe "ThemeManager", ->
|
||||
atom.config.set('core.themes', ['atom-light-ui', 'installed-dark-syntax'])
|
||||
|
||||
waitsForPromise ->
|
||||
themeManager.activateThemes()
|
||||
atom.themes.activateThemes()
|
||||
|
||||
it 'uses the default dark syntax theme', ->
|
||||
activeThemeNames = themeManager.getActiveThemeNames()
|
||||
activeThemeNames = atom.themes.getActiveThemeNames()
|
||||
expect(activeThemeNames.length).toBe(2)
|
||||
expect(activeThemeNames).toContain('atom-light-ui')
|
||||
expect(activeThemeNames).toContain('atom-dark-syntax')
|
||||
|
||||
35
spec/token-iterator-spec.coffee
Normal file
35
spec/token-iterator-spec.coffee
Normal file
@@ -0,0 +1,35 @@
|
||||
TextBuffer = require 'text-buffer'
|
||||
TokenizedBuffer = require '../src/tokenized-buffer'
|
||||
|
||||
describe "TokenIterator", ->
|
||||
it "correctly terminates scopes at the beginning of the line (regression)", ->
|
||||
grammar = atom.grammars.createGrammar('test', {
|
||||
'scopeName': 'text.broken'
|
||||
'name': 'Broken grammar'
|
||||
'patterns': [
|
||||
{
|
||||
'begin': 'start'
|
||||
'end': '(?=end)'
|
||||
'name': 'blue.broken'
|
||||
}
|
||||
{
|
||||
'match': '.'
|
||||
'name': 'yellow.broken'
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
buffer = new TextBuffer(text: """
|
||||
start x
|
||||
end x
|
||||
x
|
||||
""")
|
||||
tokenizedBuffer = new TokenizedBuffer({buffer})
|
||||
tokenizedBuffer.setGrammar(grammar)
|
||||
|
||||
tokenIterator = tokenizedBuffer.tokenizedLineForRow(1).getTokenIterator()
|
||||
tokenIterator.next()
|
||||
|
||||
expect(tokenIterator.getBufferStart()).toBe 0
|
||||
expect(tokenIterator.getScopeEnds()).toEqual []
|
||||
expect(tokenIterator.getScopeStarts()).toEqual ['text.broken', 'yellow.broken']
|
||||
@@ -1,5 +1,4 @@
|
||||
TooltipManager = require '../src/tooltip-manager'
|
||||
{$} = require '../src/space-pen-extensions'
|
||||
_ = require "underscore-plus"
|
||||
|
||||
describe "TooltipManager", ->
|
||||
@@ -15,18 +14,51 @@ describe "TooltipManager", ->
|
||||
jasmine.attachToDOM(element)
|
||||
|
||||
hover = (element, fn) ->
|
||||
$(element).trigger 'mouseenter'
|
||||
element.dispatchEvent(new CustomEvent('mouseenter', bubbles: false))
|
||||
element.dispatchEvent(new CustomEvent('mouseover', bubbles: true))
|
||||
advanceClock(manager.defaults.delay.show)
|
||||
fn()
|
||||
$(element).trigger 'mouseleave'
|
||||
element.dispatchEvent(new CustomEvent('mouseleave', bubbles: false))
|
||||
element.dispatchEvent(new CustomEvent('mouseout', bubbles: true))
|
||||
advanceClock(manager.defaults.delay.hide)
|
||||
|
||||
describe "::add(target, options)", ->
|
||||
describe "when the target is an element", ->
|
||||
it "creates a tooltip based on the given options when hovering over the target element", ->
|
||||
manager.add element, title: "Title"
|
||||
hover element, ->
|
||||
expect(document.body.querySelector(".tooltip")).toHaveText("Title")
|
||||
it "creates a tooltip based on the given options when hovering over the target element", ->
|
||||
manager.add element, title: "Title"
|
||||
hover element, ->
|
||||
expect(document.body.querySelector(".tooltip")).toHaveText("Title")
|
||||
|
||||
it "allows jQuery elements to be passed as the target", ->
|
||||
element2 = document.createElement('div')
|
||||
jasmine.attachToDOM(element2)
|
||||
|
||||
fakeJqueryWrapper = [element, element2]
|
||||
fakeJqueryWrapper.jquery = 'any-version'
|
||||
disposable = manager.add fakeJqueryWrapper, title: "Title"
|
||||
|
||||
hover element, -> expect(document.body.querySelector(".tooltip")).toHaveText("Title")
|
||||
expect(document.body.querySelector(".tooltip")).toBeNull()
|
||||
hover element2, -> expect(document.body.querySelector(".tooltip")).toHaveText("Title")
|
||||
expect(document.body.querySelector(".tooltip")).toBeNull()
|
||||
|
||||
disposable.dispose()
|
||||
|
||||
hover element, -> expect(document.body.querySelector(".tooltip")).toBeNull()
|
||||
hover element2, -> expect(document.body.querySelector(".tooltip")).toBeNull()
|
||||
|
||||
describe "when a selector is specified", ->
|
||||
it "creates a tooltip when hovering over a descendant of the target that matches the selector", ->
|
||||
child = document.createElement('div')
|
||||
child.classList.add('bar')
|
||||
grandchild = document.createElement('div')
|
||||
element.appendChild(child)
|
||||
child.appendChild(grandchild)
|
||||
|
||||
manager.add element, selector: '.bar', title: 'Bar'
|
||||
|
||||
hover grandchild, ->
|
||||
expect(document.body.querySelector('.tooltip')).toHaveText('Bar')
|
||||
expect(document.body.querySelector('.tooltip')).toBeNull()
|
||||
|
||||
describe "when a keyBindingCommand is specified", ->
|
||||
describe "when a title is specified", ->
|
||||
@@ -81,3 +113,11 @@ describe "TooltipManager", ->
|
||||
|
||||
hover element, ->
|
||||
expect(document.body.querySelector(".tooltip")).toBeNull()
|
||||
|
||||
describe "when the window is resized", ->
|
||||
it "hides the tooltips", ->
|
||||
manager.add element, title: "Title"
|
||||
hover element, ->
|
||||
expect(document.body.querySelector(".tooltip")).toBeDefined()
|
||||
window.dispatchEvent(new CustomEvent('resize'))
|
||||
expect(document.body.querySelector(".tooltip")).toBeNull()
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
ViewRegistry = require '../src/view-registry'
|
||||
{View} = require '../src/space-pen-extensions'
|
||||
|
||||
describe "ViewRegistry", ->
|
||||
registry = null
|
||||
@@ -16,16 +15,6 @@ describe "ViewRegistry", ->
|
||||
node = document.createElement('div')
|
||||
expect(registry.getView(node)).toBe node
|
||||
|
||||
describe "when passed a SpacePen view", ->
|
||||
it "returns the root node of the view with a .spacePenView property pointing at the SpacePen view", ->
|
||||
class TestView extends View
|
||||
@content: -> @div "Hello"
|
||||
|
||||
view = new TestView
|
||||
node = registry.getView(view)
|
||||
expect(node.textContent).toBe "Hello"
|
||||
expect(node.spacePenView).toBe view
|
||||
|
||||
describe "when passed an object with an element property", ->
|
||||
it "returns the element property if it's an instance of HTMLElement", ->
|
||||
class TestComponent
|
||||
@@ -59,30 +48,8 @@ describe "ViewRegistry", ->
|
||||
expect(view2.model).toBe subclassModel
|
||||
|
||||
describe "when no view provider is registered for the object's constructor", ->
|
||||
describe "when the object has a .getViewClass() method", ->
|
||||
it "builds an instance of the view class with the model, then returns its root node with a __spacePenView property pointing at the view", ->
|
||||
class TestView extends View
|
||||
@content: (model) -> @div model.name
|
||||
initialize: (@model) ->
|
||||
|
||||
class TestModel
|
||||
constructor: (@name) ->
|
||||
getViewClass: -> TestView
|
||||
|
||||
model = new TestModel("hello")
|
||||
node = registry.getView(model)
|
||||
|
||||
expect(node.textContent).toBe "hello"
|
||||
view = node.spacePenView
|
||||
expect(view instanceof TestView).toBe true
|
||||
expect(view.model).toBe model
|
||||
|
||||
# returns the same DOM node for repeated calls
|
||||
expect(registry.getView(model)).toBe node
|
||||
|
||||
describe "when the object has no .getViewClass() method", ->
|
||||
it "throws an exception", ->
|
||||
expect(-> registry.getView(new Object)).toThrow()
|
||||
it "throws an exception", ->
|
||||
expect(-> registry.getView(new Object)).toThrow()
|
||||
|
||||
describe "::addViewProvider(providerSpec)", ->
|
||||
it "returns a disposable that can be used to remove the provider", ->
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{$, $$} = require '../src/space-pen-extensions'
|
||||
KeymapManager = require 'atom-keymap'
|
||||
path = require 'path'
|
||||
fs = require 'fs-plus'
|
||||
temp = require 'temp'
|
||||
@@ -16,45 +16,41 @@ describe "Window", ->
|
||||
loadSettings.initialPath = initialPath
|
||||
loadSettings
|
||||
atom.project.destroy()
|
||||
windowEventHandler = new WindowEventHandler()
|
||||
atom.deserializeEditorWindow()
|
||||
atom.windowEventHandler.unsubscribe()
|
||||
windowEventHandler = new WindowEventHandler
|
||||
projectPath = atom.project.getPaths()[0]
|
||||
|
||||
afterEach ->
|
||||
windowEventHandler.unsubscribe()
|
||||
$(window).off 'beforeunload'
|
||||
|
||||
describe "when the window is loaded", ->
|
||||
it "doesn't have .is-blurred on the body tag", ->
|
||||
expect($("body")).not.toHaveClass("is-blurred")
|
||||
expect(document.body.className).not.toMatch("is-blurred")
|
||||
|
||||
describe "when the window is blurred", ->
|
||||
beforeEach ->
|
||||
$(window).triggerHandler 'blur'
|
||||
window.dispatchEvent(new CustomEvent('blur'))
|
||||
|
||||
afterEach ->
|
||||
$('body').removeClass('is-blurred')
|
||||
document.body.classList.remove('is-blurred')
|
||||
|
||||
it "adds the .is-blurred class on the body", ->
|
||||
expect($("body")).toHaveClass("is-blurred")
|
||||
expect(document.body.className).toMatch("is-blurred")
|
||||
|
||||
describe "when the window is focused again", ->
|
||||
it "removes the .is-blurred class from the body", ->
|
||||
$(window).triggerHandler 'focus'
|
||||
expect($("body")).not.toHaveClass("is-blurred")
|
||||
window.dispatchEvent(new CustomEvent('focus'))
|
||||
expect(document.body.className).not.toMatch("is-blurred")
|
||||
|
||||
describe "window:close event", ->
|
||||
it "closes the window", ->
|
||||
spyOn(atom, 'close')
|
||||
$(window).trigger 'window:close'
|
||||
window.dispatchEvent(new CustomEvent('window:close'))
|
||||
expect(atom.close).toHaveBeenCalled()
|
||||
|
||||
describe "beforeunload event", ->
|
||||
[beforeUnloadEvent] = []
|
||||
|
||||
beforeEach ->
|
||||
jasmine.unspy(TextEditor.prototype, "shouldPromptToSave")
|
||||
beforeUnloadEvent = $.Event(new Event('beforeunload'))
|
||||
|
||||
describe "when pane items are modified", ->
|
||||
it "prompts user to save and calls atom.workspace.confirmClose", ->
|
||||
@@ -67,7 +63,7 @@ describe "Window", ->
|
||||
|
||||
runs ->
|
||||
editor.insertText("I look different, I feel different.")
|
||||
$(window).trigger(beforeUnloadEvent)
|
||||
window.dispatchEvent(new CustomEvent('beforeunload'))
|
||||
expect(atom.workspace.confirmClose).toHaveBeenCalled()
|
||||
expect(atom.confirm).toHaveBeenCalled()
|
||||
|
||||
@@ -80,7 +76,7 @@ describe "Window", ->
|
||||
|
||||
runs ->
|
||||
editor.insertText("I look different, I feel different.")
|
||||
$(window).trigger(beforeUnloadEvent)
|
||||
window.dispatchEvent(new CustomEvent('beforeunload'))
|
||||
expect(atom.confirm).toHaveBeenCalled()
|
||||
|
||||
it "prompts user to save and handler returns false if dialog is canceled", ->
|
||||
@@ -91,11 +87,12 @@ describe "Window", ->
|
||||
|
||||
runs ->
|
||||
editor.insertText("I look different, I feel different.")
|
||||
$(window).trigger(beforeUnloadEvent)
|
||||
window.dispatchEvent(new CustomEvent('beforeunload'))
|
||||
expect(atom.confirm).toHaveBeenCalled()
|
||||
|
||||
describe "when the same path is modified in multiple panes", ->
|
||||
it "prompts to save the item", ->
|
||||
return
|
||||
editor = null
|
||||
filePath = path.join(temp.mkdirSync('atom-file'), 'file.txt')
|
||||
fs.writeFileSync(filePath, 'hello')
|
||||
@@ -108,146 +105,125 @@ describe "Window", ->
|
||||
runs ->
|
||||
atom.workspace.getActivePane().splitRight(copyActiveItem: true)
|
||||
editor.setText('world')
|
||||
$(window).trigger(beforeUnloadEvent)
|
||||
window.dispatchEvent(new CustomEvent('beforeunload'))
|
||||
expect(atom.workspace.confirmClose).toHaveBeenCalled()
|
||||
expect(atom.confirm.callCount).toBe 1
|
||||
expect(fs.readFileSync(filePath, 'utf8')).toBe 'world'
|
||||
|
||||
describe ".unloadEditorWindow()", ->
|
||||
it "saves the serialized state of the window so it can be deserialized after reload", ->
|
||||
workspaceState = atom.workspace.serialize()
|
||||
syntaxState = atom.grammars.serialize()
|
||||
projectState = atom.project.serialize()
|
||||
|
||||
atom.unloadEditorWindow()
|
||||
|
||||
expect(atom.state.workspace).toEqual workspaceState
|
||||
expect(atom.state.grammars).toEqual syntaxState
|
||||
expect(atom.state.project).toEqual projectState
|
||||
expect(atom.saveSync).toHaveBeenCalled()
|
||||
|
||||
describe ".removeEditorWindow()", ->
|
||||
it "unsubscribes from all buffers", ->
|
||||
waitsForPromise ->
|
||||
atom.workspace.open("sample.js")
|
||||
|
||||
runs ->
|
||||
buffer = atom.workspace.getActivePaneItem().buffer
|
||||
pane = atom.workspace.getActivePane()
|
||||
pane.splitRight(copyActiveItem: true)
|
||||
expect(atom.workspace.getTextEditors().length).toBe 2
|
||||
|
||||
atom.removeEditorWindow()
|
||||
|
||||
expect(buffer.getSubscriptionCount()).toBe 0
|
||||
|
||||
describe "when a link is clicked", ->
|
||||
it "opens the http/https links in an external application", ->
|
||||
shell = require 'shell'
|
||||
spyOn(shell, 'openExternal')
|
||||
|
||||
$("<a href='http://github.com'>the website</a>").appendTo(document.body).click().remove()
|
||||
link = document.createElement('a')
|
||||
linkChild = document.createElement('span')
|
||||
link.appendChild(linkChild)
|
||||
link.href = 'http://github.com'
|
||||
jasmine.attachToDOM(link)
|
||||
fakeEvent = {target: linkChild, currentTarget: link, preventDefault: (->)}
|
||||
|
||||
windowEventHandler.handleLinkClick(fakeEvent)
|
||||
expect(shell.openExternal).toHaveBeenCalled()
|
||||
expect(shell.openExternal.argsForCall[0][0]).toBe "http://github.com"
|
||||
|
||||
shell.openExternal.reset()
|
||||
$("<a href='https://github.com'>the website</a>").appendTo(document.body).click().remove()
|
||||
|
||||
link.href = 'https://github.com'
|
||||
windowEventHandler.handleLinkClick(fakeEvent)
|
||||
expect(shell.openExternal).toHaveBeenCalled()
|
||||
expect(shell.openExternal.argsForCall[0][0]).toBe "https://github.com"
|
||||
|
||||
shell.openExternal.reset()
|
||||
$("<a href=''>the website</a>").appendTo(document.body).click().remove()
|
||||
|
||||
link.href = ''
|
||||
windowEventHandler.handleLinkClick(fakeEvent)
|
||||
expect(shell.openExternal).not.toHaveBeenCalled()
|
||||
|
||||
shell.openExternal.reset()
|
||||
$("<a href='#scroll-me'>link</a>").appendTo(document.body).click().remove()
|
||||
|
||||
link.href = '#scroll-me'
|
||||
windowEventHandler.handleLinkClick(fakeEvent)
|
||||
expect(shell.openExternal).not.toHaveBeenCalled()
|
||||
|
||||
describe "when a form is submitted", ->
|
||||
it "prevents the default so that the window's URL isn't changed", ->
|
||||
submitSpy = jasmine.createSpy('submit')
|
||||
$(document).on('submit', 'form', submitSpy)
|
||||
$("<form>foo</form>").appendTo(document.body).submit().remove()
|
||||
expect(submitSpy.callCount).toBe 1
|
||||
expect(submitSpy.argsForCall[0][0].isDefaultPrevented()).toBe true
|
||||
form = document.createElement('form')
|
||||
jasmine.attachToDOM(form)
|
||||
|
||||
defaultPrevented = false
|
||||
event = new CustomEvent('submit', bubbles: true)
|
||||
event.preventDefault = -> defaultPrevented = true
|
||||
form.dispatchEvent(event)
|
||||
expect(defaultPrevented).toBe(true)
|
||||
|
||||
describe "core:focus-next and core:focus-previous", ->
|
||||
describe "when there is no currently focused element", ->
|
||||
it "focuses the element with the lowest/highest tabindex", ->
|
||||
elements = $$ ->
|
||||
@div =>
|
||||
@button tabindex: 2
|
||||
@input tabindex: 1
|
||||
wrapperDiv = document.createElement('div')
|
||||
wrapperDiv.innerHTML = """
|
||||
<div>
|
||||
<button tabindex="2"></button>
|
||||
<input tabindex="1">
|
||||
</div>
|
||||
"""
|
||||
elements = wrapperDiv.firstChild
|
||||
jasmine.attachToDOM(elements)
|
||||
|
||||
elements.attachToDom()
|
||||
elements.dispatchEvent(new CustomEvent("core:focus-next", bubbles: true))
|
||||
expect(document.activeElement.tabIndex).toBe 1
|
||||
|
||||
elements.trigger "core:focus-next"
|
||||
expect(elements.find("[tabindex=1]:focus")).toExist()
|
||||
|
||||
$(":focus").blur()
|
||||
|
||||
elements.trigger "core:focus-previous"
|
||||
expect(elements.find("[tabindex=2]:focus")).toExist()
|
||||
document.body.focus()
|
||||
elements.dispatchEvent(new CustomEvent("core:focus-previous", bubbles: true))
|
||||
expect(document.activeElement.tabIndex).toBe 2
|
||||
|
||||
describe "when a tabindex is set on the currently focused element", ->
|
||||
it "focuses the element with the next highest tabindex", ->
|
||||
elements = $$ ->
|
||||
@div =>
|
||||
@input tabindex: 1
|
||||
@button tabindex: 2
|
||||
@button tabindex: 5
|
||||
@input tabindex: -1
|
||||
@input tabindex: 3
|
||||
@button tabindex: 7
|
||||
it "focuses the element with the next highest/lowest tabindex, skipping disabled elements", ->
|
||||
wrapperDiv = document.createElement('div')
|
||||
wrapperDiv.innerHTML = """
|
||||
<div>
|
||||
<input tabindex="1">
|
||||
<button tabindex="2"></button>
|
||||
<button tabindex="5"></button>
|
||||
<input tabindex="-1">
|
||||
<input tabindex="3">
|
||||
<button tabindex="7"></button>
|
||||
<input tabindex="9" disabled>
|
||||
</div>
|
||||
"""
|
||||
elements = wrapperDiv.firstChild
|
||||
jasmine.attachToDOM(elements)
|
||||
|
||||
elements.attachToDom()
|
||||
elements.find("[tabindex=1]").focus()
|
||||
elements.querySelector('[tabindex="1"]').focus()
|
||||
|
||||
elements.trigger "core:focus-next"
|
||||
expect(elements.find("[tabindex=2]:focus")).toExist()
|
||||
elements.dispatchEvent(new CustomEvent("core:focus-next", bubbles: true))
|
||||
expect(document.activeElement.tabIndex).toBe 2
|
||||
|
||||
elements.trigger "core:focus-next"
|
||||
expect(elements.find("[tabindex=3]:focus")).toExist()
|
||||
elements.dispatchEvent(new CustomEvent("core:focus-next", bubbles: true))
|
||||
expect(document.activeElement.tabIndex).toBe 3
|
||||
|
||||
elements.focus().trigger "core:focus-next"
|
||||
expect(elements.find("[tabindex=5]:focus")).toExist()
|
||||
elements.dispatchEvent(new CustomEvent("core:focus-next", bubbles: true))
|
||||
expect(document.activeElement.tabIndex).toBe 5
|
||||
|
||||
elements.focus().trigger "core:focus-next"
|
||||
expect(elements.find("[tabindex=7]:focus")).toExist()
|
||||
elements.dispatchEvent(new CustomEvent("core:focus-next", bubbles: true))
|
||||
expect(document.activeElement.tabIndex).toBe 7
|
||||
|
||||
elements.focus().trigger "core:focus-next"
|
||||
expect(elements.find("[tabindex=1]:focus")).toExist()
|
||||
elements.dispatchEvent(new CustomEvent("core:focus-next", bubbles: true))
|
||||
expect(document.activeElement.tabIndex).toBe 1
|
||||
|
||||
elements.trigger "core:focus-previous"
|
||||
expect(elements.find("[tabindex=7]:focus")).toExist()
|
||||
elements.dispatchEvent(new CustomEvent("core:focus-previous", bubbles: true))
|
||||
expect(document.activeElement.tabIndex).toBe 7
|
||||
|
||||
elements.trigger "core:focus-previous"
|
||||
expect(elements.find("[tabindex=5]:focus")).toExist()
|
||||
elements.dispatchEvent(new CustomEvent("core:focus-previous", bubbles: true))
|
||||
expect(document.activeElement.tabIndex).toBe 5
|
||||
|
||||
elements.focus().trigger "core:focus-previous"
|
||||
expect(elements.find("[tabindex=3]:focus")).toExist()
|
||||
elements.dispatchEvent(new CustomEvent("core:focus-previous", bubbles: true))
|
||||
expect(document.activeElement.tabIndex).toBe 3
|
||||
|
||||
elements.focus().trigger "core:focus-previous"
|
||||
expect(elements.find("[tabindex=2]:focus")).toExist()
|
||||
elements.dispatchEvent(new CustomEvent("core:focus-previous", bubbles: true))
|
||||
expect(document.activeElement.tabIndex).toBe 2
|
||||
|
||||
elements.focus().trigger "core:focus-previous"
|
||||
expect(elements.find("[tabindex=1]:focus")).toExist()
|
||||
elements.dispatchEvent(new CustomEvent("core:focus-previous", bubbles: true))
|
||||
expect(document.activeElement.tabIndex).toBe 1
|
||||
|
||||
it "skips disabled elements", ->
|
||||
elements = $$ ->
|
||||
@div =>
|
||||
@input tabindex: 1
|
||||
@button tabindex: 2, disabled: 'disabled'
|
||||
@input tabindex: 3
|
||||
|
||||
elements.attachToDom()
|
||||
elements.find("[tabindex=1]").focus()
|
||||
|
||||
elements.trigger "core:focus-next"
|
||||
expect(elements.find("[tabindex=3]:focus")).toExist()
|
||||
|
||||
elements.trigger "core:focus-previous"
|
||||
expect(elements.find("[tabindex=1]:focus")).toExist()
|
||||
elements.dispatchEvent(new CustomEvent("core:focus-previous", bubbles: true))
|
||||
expect(document.activeElement.tabIndex).toBe 7
|
||||
|
||||
describe "the window:open-locations event", ->
|
||||
beforeEach ->
|
||||
@@ -304,3 +280,16 @@ describe "Window", ->
|
||||
|
||||
runs ->
|
||||
expect(atom.project.getPaths()[0]).toBe pathToOpen
|
||||
|
||||
describe "when keydown events occur on the document", ->
|
||||
it "dispatches the event via the KeymapManager and CommandRegistry", ->
|
||||
dispatchedCommands = []
|
||||
atom.commands.onWillDispatch (command) -> dispatchedCommands.push(command)
|
||||
atom.commands.add '*', 'foo-command': ->
|
||||
atom.keymaps.add 'source-name', '*': {'x': 'foo-command'}
|
||||
|
||||
event = KeymapManager.buildKeydownEvent('x', target: document.createElement('div'))
|
||||
document.dispatchEvent(event)
|
||||
|
||||
expect(dispatchedCommands.length).toBe 1
|
||||
expect(dispatchedCommands[0].type).toBe 'foo-command'
|
||||
@@ -3,13 +3,90 @@ path = require 'path'
|
||||
temp = require('temp').track()
|
||||
|
||||
describe "WorkspaceElement", ->
|
||||
workspaceElement = null
|
||||
describe "when the workspace element is focused", ->
|
||||
it "transfers focus to the active pane", ->
|
||||
workspaceElement = atom.views.getView(atom.workspace)
|
||||
jasmine.attachToDOM(workspaceElement)
|
||||
activePaneElement = atom.views.getView(atom.workspace.getActivePane())
|
||||
document.body.focus()
|
||||
expect(document.activeElement).not.toBe(activePaneElement)
|
||||
workspaceElement.focus()
|
||||
expect(document.activeElement).toBe(activePaneElement)
|
||||
|
||||
beforeEach ->
|
||||
workspaceElement = atom.views.getView(atom.workspace)
|
||||
describe "the scrollbar visibility class", ->
|
||||
it "has a class based on the style of the scrollbar", ->
|
||||
observeCallback = null
|
||||
scrollbarStyle = require 'scrollbar-style'
|
||||
spyOn(scrollbarStyle, 'observePreferredScrollbarStyle').andCallFake (cb) -> observeCallback = cb
|
||||
workspaceElement = atom.views.getView(atom.workspace)
|
||||
|
||||
observeCallback('legacy')
|
||||
expect(workspaceElement.className).toMatch 'scrollbars-visible-always'
|
||||
|
||||
observeCallback('overlay')
|
||||
expect(workspaceElement).toHaveClass 'scrollbars-visible-when-scrolling'
|
||||
|
||||
describe "editor font styling", ->
|
||||
[editor, editorElement] = []
|
||||
|
||||
beforeEach ->
|
||||
waitsForPromise -> atom.workspace.open('sample.js')
|
||||
|
||||
runs ->
|
||||
workspaceElement = atom.views.getView(atom.workspace)
|
||||
jasmine.attachToDOM(workspaceElement)
|
||||
editor = atom.workspace.getActiveTextEditor()
|
||||
editorElement = atom.views.getView(editor)
|
||||
|
||||
it "updates the font-size based on the 'editor.fontSize' config value", ->
|
||||
initialCharWidth = editor.getDefaultCharWidth()
|
||||
expect(getComputedStyle(editorElement).fontSize).toBe atom.config.get('editor.fontSize') + 'px'
|
||||
atom.config.set('editor.fontSize', atom.config.get('editor.fontSize') + 5)
|
||||
expect(getComputedStyle(editorElement).fontSize).toBe atom.config.get('editor.fontSize') + 'px'
|
||||
expect(editor.getDefaultCharWidth()).toBeGreaterThan initialCharWidth
|
||||
|
||||
it "updates the font-family based on the 'editor.fontFamily' config value", ->
|
||||
initialCharWidth = editor.getDefaultCharWidth()
|
||||
expect(getComputedStyle(editorElement).fontFamily).toBe atom.config.get('editor.fontFamily')
|
||||
atom.config.set('editor.fontFamily', 'sans-serif')
|
||||
expect(getComputedStyle(editorElement).fontFamily).toBe atom.config.get('editor.fontFamily')
|
||||
expect(editor.getDefaultCharWidth()).not.toBe initialCharWidth
|
||||
|
||||
it "updates the line-height based on the 'editor.lineHeight' config value", ->
|
||||
initialLineHeight = editor.getLineHeightInPixels()
|
||||
atom.config.set('editor.lineHeight', '30px')
|
||||
expect(getComputedStyle(editorElement).lineHeight).toBe atom.config.get('editor.lineHeight')
|
||||
expect(editor.getLineHeightInPixels()).not.toBe initialLineHeight
|
||||
|
||||
describe 'panel containers', ->
|
||||
it 'inserts panel container elements in the correct places in the DOM', ->
|
||||
workspaceElement = atom.views.getView(atom.workspace)
|
||||
|
||||
leftContainer = workspaceElement.querySelector('atom-panel-container.left')
|
||||
rightContainer = workspaceElement.querySelector('atom-panel-container.right')
|
||||
expect(leftContainer.nextSibling).toBe workspaceElement.verticalAxis
|
||||
expect(rightContainer.previousSibling).toBe workspaceElement.verticalAxis
|
||||
|
||||
topContainer = workspaceElement.querySelector('atom-panel-container.top')
|
||||
bottomContainer = workspaceElement.querySelector('atom-panel-container.bottom')
|
||||
expect(topContainer.nextSibling).toBe workspaceElement.paneContainer
|
||||
expect(bottomContainer.previousSibling).toBe workspaceElement.paneContainer
|
||||
|
||||
modalContainer = workspaceElement.querySelector('atom-panel-container.modal')
|
||||
expect(modalContainer.parentNode).toBe workspaceElement
|
||||
|
||||
describe "the 'window:toggle-invisibles' command", ->
|
||||
it "shows/hides invisibles in all open and future editors", ->
|
||||
workspaceElement = atom.views.getView(atom.workspace)
|
||||
expect(atom.config.get('editor.showInvisibles')).toBe false
|
||||
atom.commands.dispatch(workspaceElement, 'window:toggle-invisibles')
|
||||
expect(atom.config.get('editor.showInvisibles')).toBe true
|
||||
atom.commands.dispatch(workspaceElement, 'window:toggle-invisibles')
|
||||
expect(atom.config.get('editor.showInvisibles')).toBe false
|
||||
|
||||
describe "the 'window:run-package-specs' command", ->
|
||||
it "runs the package specs for the active item's project path, or the first project path", ->
|
||||
workspaceElement = atom.views.getView(atom.workspace)
|
||||
spyOn(ipc, 'send')
|
||||
|
||||
# No project paths. Don't try to run specs.
|
||||
|
||||
@@ -2,12 +2,10 @@ path = require 'path'
|
||||
temp = require 'temp'
|
||||
Workspace = require '../src/workspace'
|
||||
Pane = require '../src/pane'
|
||||
{View} = require '../src/space-pen-extensions'
|
||||
platform = require './spec-helper-platform'
|
||||
_ = require 'underscore-plus'
|
||||
fstream = require 'fstream'
|
||||
fs = require 'fs-plus'
|
||||
Grim = require 'grim'
|
||||
|
||||
describe "Workspace", ->
|
||||
workspace = null
|
||||
@@ -17,6 +15,64 @@ describe "Workspace", ->
|
||||
atom.workspace = workspace = new Workspace
|
||||
waits(1)
|
||||
|
||||
describe "serialization", ->
|
||||
simulateReload = ->
|
||||
workspaceState = atom.workspace.serialize()
|
||||
projectState = atom.project.serialize()
|
||||
atom.workspace.destroy()
|
||||
atom.project.destroy()
|
||||
atom.project = atom.deserializers.deserialize(projectState)
|
||||
atom.workspace = Workspace.deserialize(workspaceState)
|
||||
|
||||
describe "when the workspace contains text editors", ->
|
||||
it "constructs the view with the same panes", ->
|
||||
pane1 = atom.workspace.getActivePane()
|
||||
pane2 = pane1.splitRight(copyActiveItem: true)
|
||||
pane3 = pane2.splitRight(copyActiveItem: true)
|
||||
pane4 = null
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('b').then (editor) ->
|
||||
pane2.activateItem(editor.copy())
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('../sample.js').then (editor) ->
|
||||
pane3.activateItem(editor)
|
||||
|
||||
runs ->
|
||||
pane3.activeItem.setCursorScreenPosition([2, 4])
|
||||
pane4 = pane2.splitDown()
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('../sample.txt').then (editor) ->
|
||||
pane4.activateItem(editor)
|
||||
|
||||
runs ->
|
||||
pane4.getActiveItem().setCursorScreenPosition([0, 2])
|
||||
pane2.activate()
|
||||
|
||||
simulateReload()
|
||||
|
||||
expect(atom.workspace.getTextEditors().length).toBe 4
|
||||
[editor1, editor2, editor3, editor4] = atom.workspace.getTextEditors()
|
||||
|
||||
expect(editor1.getPath()).toBe atom.project.getDirectories()[0]?.resolve('b')
|
||||
expect(editor2.getPath()).toBe atom.project.getDirectories()[0]?.resolve('../sample.txt')
|
||||
expect(editor2.getCursorScreenPosition()).toEqual [0, 2]
|
||||
expect(editor3.getPath()).toBe atom.project.getDirectories()[0]?.resolve('b')
|
||||
expect(editor4.getPath()).toBe atom.project.getDirectories()[0]?.resolve('../sample.js')
|
||||
expect(editor4.getCursorScreenPosition()).toEqual [2, 4]
|
||||
|
||||
expect(atom.workspace.getActiveTextEditor().getPath()).toBe editor3.getPath()
|
||||
expect(document.title).toBe "#{path.basename(editor3.getPath())} - #{atom.project.getPaths()[0]} - Atom"
|
||||
|
||||
describe "where there are no open panes or editors", ->
|
||||
it "constructs the view with no open editors", ->
|
||||
atom.workspace.getActivePane().destroy()
|
||||
expect(atom.workspace.getTextEditors().length).toBe 0
|
||||
simulateReload()
|
||||
expect(atom.workspace.getTextEditors().length).toBe 0
|
||||
|
||||
describe "::open(uri, options)", ->
|
||||
openEvents = null
|
||||
|
||||
@@ -326,21 +382,6 @@ describe "Workspace", ->
|
||||
runs ->
|
||||
expect(newEditorHandler.argsForCall[0][0].textEditor).toBe editor
|
||||
|
||||
it "records a deprecation warning on the appropriate package if the item has a ::getUri method instead of ::getURI", ->
|
||||
jasmine.snapshotDeprecations()
|
||||
|
||||
waitsForPromise -> atom.packages.activatePackage('package-with-deprecated-pane-item-method')
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open("test")
|
||||
|
||||
runs ->
|
||||
deprecations = Grim.getDeprecations()
|
||||
expect(deprecations.length).toBe 1
|
||||
expect(deprecations[0].message).toBe "Pane item with class `TestItem` should implement `::getURI` instead of `::getUri`."
|
||||
expect(deprecations[0].getStacks()[0].metadata.packageName).toBe "package-with-deprecated-pane-item-method"
|
||||
jasmine.restoreDeprecationsSnapshot()
|
||||
|
||||
describe "when there is an error opening the file", ->
|
||||
notificationSpy = null
|
||||
beforeEach ->
|
||||
@@ -625,7 +666,7 @@ describe "Workspace", ->
|
||||
|
||||
it "updates the title to contain the project's path", ->
|
||||
document.title = null
|
||||
workspace2 = atom.workspace.testSerialization()
|
||||
workspace2 = Workspace.deserialize(atom.workspace.serialize())
|
||||
item = atom.workspace.getActivePaneItem()
|
||||
expect(document.title).toBe "#{item.getTitle()} - #{atom.project.getPaths()[0]} - Atom"
|
||||
workspace2.destroy()
|
||||
@@ -1065,11 +1106,16 @@ describe "Workspace", ->
|
||||
|
||||
it "can be cancelled when the object returned by scan() has its cancel() method invoked", ->
|
||||
thenable = atom.workspace.scan /aaaa/, ->
|
||||
expect(fakeSearch.cancelled).toBe(undefined)
|
||||
thenable.cancel()
|
||||
expect(fakeSearch.cancelled).toBe(true)
|
||||
|
||||
resultOfPromiseSearch = null
|
||||
|
||||
waitsFor 'fakeSearch to be defined', -> fakeSearch?
|
||||
|
||||
runs ->
|
||||
expect(fakeSearch.cancelled).toBe(undefined)
|
||||
thenable.cancel()
|
||||
expect(fakeSearch.cancelled).toBe(true)
|
||||
|
||||
|
||||
waitsForPromise ->
|
||||
thenable.then (promiseResult) -> resultOfPromiseSearch = promiseResult
|
||||
|
||||
@@ -1077,15 +1123,28 @@ describe "Workspace", ->
|
||||
expect(resultOfPromiseSearch).toBe('cancelled')
|
||||
|
||||
it "will have the side-effect of failing the overall search if it fails", ->
|
||||
cancelableSearch = atom.workspace.scan /aaaa/, ->
|
||||
fakeSearch.hoistedReject()
|
||||
# This provider's search should be cancelled when the first provider fails
|
||||
fakeSearch2 = null
|
||||
atom.packages.serviceHub.provide('atom.directory-searcher', '0.1.0', {
|
||||
canSearchDirectory: (directory) -> directory.getPath() is dir2
|
||||
search: (directory, regex, options) -> fakeSearch2 = new FakeSearch(options)
|
||||
})
|
||||
|
||||
didReject = false
|
||||
promise = cancelableSearch = atom.workspace.scan /aaaa/, ->
|
||||
waitsFor 'fakeSearch to be defined', -> fakeSearch?
|
||||
|
||||
runs ->
|
||||
fakeSearch.hoistedReject()
|
||||
|
||||
waitsForPromise ->
|
||||
cancelableSearch.catch -> didReject = true
|
||||
|
||||
waitsFor (done) -> promise.then(null, done)
|
||||
|
||||
runs ->
|
||||
expect(didReject).toBe(true)
|
||||
expect(fakeSearch2.cancelled).toBe true # Cancels other ongoing searches
|
||||
|
||||
describe "::replace(regex, replacementText, paths, iterator)", ->
|
||||
[filePath, commentFilePath, sampleContent, sampleCommentContent] = []
|
||||
@@ -1267,3 +1326,32 @@ describe "Workspace", ->
|
||||
|
||||
save = -> atom.workspace.saveActivePaneItem()
|
||||
expect(save).toThrow()
|
||||
|
||||
describe "::destroyActivePaneItemOrEmptyPane", ->
|
||||
beforeEach ->
|
||||
waitsForPromise -> atom.workspace.open()
|
||||
|
||||
it "closes the active pane item until all that remains is a single empty pane", ->
|
||||
atom.config.set('core.destroyEmptyPanes', false)
|
||||
|
||||
pane1 = atom.workspace.getActivePane()
|
||||
pane2 = pane1.splitRight(copyActiveItem: true)
|
||||
|
||||
expect(atom.workspace.getPanes().length).toBe 2
|
||||
expect(pane2.getItems().length).toBe 1
|
||||
atom.workspace.destroyActivePaneItemOrEmptyPane()
|
||||
|
||||
expect(atom.workspace.getPanes().length).toBe 2
|
||||
expect(pane2.getItems().length).toBe 0
|
||||
|
||||
atom.workspace.destroyActivePaneItemOrEmptyPane()
|
||||
|
||||
expect(atom.workspace.getPanes().length).toBe 1
|
||||
expect(pane1.getItems().length).toBe 1
|
||||
|
||||
atom.workspace.destroyActivePaneItemOrEmptyPane()
|
||||
expect(atom.workspace.getPanes().length).toBe 1
|
||||
expect(pane1.getItems().length).toBe 0
|
||||
|
||||
atom.workspace.destroyActivePaneItemOrEmptyPane()
|
||||
expect(atom.workspace.getPanes().length).toBe 1
|
||||
|
||||
@@ -1,301 +0,0 @@
|
||||
{$, $$, View} = require '../src/space-pen-extensions'
|
||||
Q = require 'q'
|
||||
path = require 'path'
|
||||
temp = require 'temp'
|
||||
TextEditorView = require '../src/text-editor-view'
|
||||
PaneView = require '../src/pane-view'
|
||||
Workspace = require '../src/workspace'
|
||||
|
||||
describe "WorkspaceView", ->
|
||||
pathToOpen = null
|
||||
|
||||
beforeEach ->
|
||||
jasmine.snapshotDeprecations()
|
||||
|
||||
atom.project.setPaths([atom.project.getDirectories()[0]?.resolve('dir')])
|
||||
pathToOpen = atom.project.getDirectories()[0]?.resolve('a')
|
||||
atom.workspace = new Workspace
|
||||
atom.workspaceView = atom.views.getView(atom.workspace).__spacePenView
|
||||
atom.workspaceView.enableKeymap()
|
||||
atom.workspaceView.focus()
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open(pathToOpen)
|
||||
|
||||
afterEach ->
|
||||
jasmine.restoreDeprecationsSnapshot()
|
||||
|
||||
describe "@deserialize()", ->
|
||||
viewState = null
|
||||
|
||||
simulateReload = ->
|
||||
workspaceState = atom.workspace.serialize()
|
||||
projectState = atom.project.serialize()
|
||||
atom.workspaceView.remove()
|
||||
atom.project = atom.deserializers.deserialize(projectState)
|
||||
atom.workspace = Workspace.deserialize(workspaceState)
|
||||
atom.workspaceView = atom.views.getView(atom.workspace).__spacePenView
|
||||
atom.workspaceView.attachToDom()
|
||||
|
||||
describe "when the serialized WorkspaceView has an unsaved buffer", ->
|
||||
it "constructs the view with the same panes", ->
|
||||
atom.workspaceView.attachToDom()
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open()
|
||||
|
||||
runs ->
|
||||
editorView1 = atom.workspaceView.getActiveView()
|
||||
buffer = editorView1.getEditor().getBuffer()
|
||||
editorView1.getPaneView().getModel().splitRight(copyActiveItem: true)
|
||||
expect(atom.workspaceView.getActivePaneView()).toBe atom.workspaceView.getPaneViews()[1]
|
||||
|
||||
simulateReload()
|
||||
|
||||
expect(atom.workspaceView.getEditorViews().length).toBe 2
|
||||
expect(atom.workspaceView.getActivePaneView()).toBe atom.workspaceView.getPaneViews()[1]
|
||||
expect(document.title).toBe "untitled - #{atom.project.getPaths()[0]} - Atom"
|
||||
|
||||
describe "when there are open editors", ->
|
||||
it "constructs the view with the same panes", ->
|
||||
atom.workspaceView.attachToDom()
|
||||
pane1 = atom.workspaceView.getActivePaneView()
|
||||
pane2 = pane1.splitRight()
|
||||
pane3 = pane2.splitRight()
|
||||
pane4 = null
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('b').then (editor) ->
|
||||
pane2.activateItem(editor.copy())
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('../sample.js').then (editor) ->
|
||||
pane3.activateItem(editor)
|
||||
|
||||
runs ->
|
||||
pane3.activeItem.setCursorScreenPosition([2, 4])
|
||||
pane4 = pane2.splitDown()
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('../sample.txt').then (editor) ->
|
||||
pane4.activateItem(editor)
|
||||
|
||||
runs ->
|
||||
pane4.activeItem.setCursorScreenPosition([0, 2])
|
||||
pane2.focus()
|
||||
|
||||
simulateReload()
|
||||
|
||||
expect(atom.workspaceView.getEditorViews().length).toBe 4
|
||||
editorView1 = atom.workspaceView.panes.find('atom-pane-axis.horizontal > atom-pane atom-text-editor:eq(0)').view()
|
||||
editorView3 = atom.workspaceView.panes.find('atom-pane-axis.horizontal > atom-pane atom-text-editor:eq(1)').view()
|
||||
editorView2 = atom.workspaceView.panes.find('atom-pane-axis.horizontal > atom-pane-axis.vertical > atom-pane atom-text-editor:eq(0)').view()
|
||||
editorView4 = atom.workspaceView.panes.find('atom-pane-axis.horizontal > atom-pane-axis.vertical > atom-pane atom-text-editor:eq(1)').view()
|
||||
|
||||
expect(editorView1.getEditor().getPath()).toBe atom.project.getDirectories()[0]?.resolve('a')
|
||||
expect(editorView2.getEditor().getPath()).toBe atom.project.getDirectories()[0]?.resolve('b')
|
||||
expect(editorView3.getEditor().getPath()).toBe atom.project.getDirectories()[0]?.resolve('../sample.js')
|
||||
expect(editorView3.getEditor().getCursorScreenPosition()).toEqual [2, 4]
|
||||
expect(editorView4.getEditor().getPath()).toBe atom.project.getDirectories()[0]?.resolve('../sample.txt')
|
||||
expect(editorView4.getEditor().getCursorScreenPosition()).toEqual [0, 2]
|
||||
|
||||
# ensure adjust pane dimensions is called
|
||||
expect(editorView1.width()).toBeGreaterThan 0
|
||||
expect(editorView2.width()).toBeGreaterThan 0
|
||||
expect(editorView3.width()).toBeGreaterThan 0
|
||||
expect(editorView4.width()).toBeGreaterThan 0
|
||||
|
||||
# ensure correct editorView is focused again
|
||||
expect(editorView2).toHaveFocus()
|
||||
expect(editorView1).not.toHaveFocus()
|
||||
expect(editorView3).not.toHaveFocus()
|
||||
expect(editorView4).not.toHaveFocus()
|
||||
|
||||
expect(document.title).toBe "#{path.basename(editorView2.getEditor().getPath())} - #{atom.project.getPaths()[0]} - Atom"
|
||||
|
||||
describe "where there are no open editors", ->
|
||||
it "constructs the view with no open editors", ->
|
||||
atom.workspaceView.getActivePaneView().remove()
|
||||
expect(atom.workspaceView.getEditorViews().length).toBe 0
|
||||
simulateReload()
|
||||
expect(atom.workspaceView.getEditorViews().length).toBe 0
|
||||
|
||||
describe "focus", ->
|
||||
beforeEach ->
|
||||
atom.workspaceView.attachToDom()
|
||||
|
||||
it "hands off focus to the active pane", ->
|
||||
activePane = atom.workspaceView.getActivePaneView()
|
||||
$('body').focus()
|
||||
expect(activePane).not.toHaveFocus()
|
||||
atom.workspaceView.focus()
|
||||
expect(activePane).toHaveFocus()
|
||||
|
||||
describe "keymap wiring", ->
|
||||
describe "when a keydown event is triggered in the WorkspaceView", ->
|
||||
it "triggers matching keybindings for that event", ->
|
||||
commandHandler = jasmine.createSpy('commandHandler')
|
||||
atom.workspaceView.on('foo-command', commandHandler)
|
||||
atom.keymaps.add('name', '*': {'x': 'foo-command'})
|
||||
event = keydownEvent 'x', target: atom.workspaceView[0]
|
||||
|
||||
atom.workspaceView.trigger(event)
|
||||
expect(commandHandler).toHaveBeenCalled()
|
||||
|
||||
describe "window:toggle-invisibles event", ->
|
||||
it "shows/hides invisibles in all open and future editors", ->
|
||||
atom.workspaceView.height(200)
|
||||
atom.workspaceView.attachToDom()
|
||||
rightEditorView = atom.workspaceView.getActiveView()
|
||||
rightEditorView.getEditor().setText("\t \n")
|
||||
rightEditorView.getPaneView().getModel().splitLeft(copyActiveItem: true)
|
||||
leftEditorView = atom.workspaceView.getActiveView()
|
||||
expect(rightEditorView.find(".line:first").text()).toBe " "
|
||||
expect(leftEditorView.find(".line:first").text()).toBe " "
|
||||
|
||||
{space, tab, eol} = atom.config.get('editor.invisibles')
|
||||
withInvisiblesShowing = "#{tab} #{space}#{space}#{eol}"
|
||||
|
||||
atom.workspaceView.trigger "window:toggle-invisibles"
|
||||
expect(rightEditorView.find(".line:first").text()).toBe withInvisiblesShowing
|
||||
expect(leftEditorView.find(".line:first").text()).toBe withInvisiblesShowing
|
||||
|
||||
leftEditorView.getPaneView().getModel().splitDown(copyActiveItem: true)
|
||||
lowerLeftEditorView = atom.workspaceView.getActiveView()
|
||||
expect(lowerLeftEditorView.find(".line:first").text()).toBe withInvisiblesShowing
|
||||
|
||||
atom.workspaceView.trigger "window:toggle-invisibles"
|
||||
expect(rightEditorView.find(".line:first").text()).toBe " "
|
||||
expect(leftEditorView.find(".line:first").text()).toBe " "
|
||||
|
||||
rightEditorView.getPaneView().getModel().splitDown(copyActiveItem: true)
|
||||
lowerRightEditorView = atom.workspaceView.getActiveView()
|
||||
expect(lowerRightEditorView.find(".line:first").text()).toBe " "
|
||||
|
||||
describe ".eachEditorView(callback)", ->
|
||||
beforeEach ->
|
||||
atom.workspaceView.attachToDom()
|
||||
|
||||
it "invokes the callback for existing editor", ->
|
||||
count = 0
|
||||
callbackEditor = null
|
||||
callback = (editor) ->
|
||||
callbackEditor = editor
|
||||
count++
|
||||
atom.workspaceView.eachEditorView(callback)
|
||||
expect(count).toBe 1
|
||||
expect(callbackEditor).toBe atom.workspaceView.getActiveView()
|
||||
|
||||
it "invokes the callback for new editor", ->
|
||||
count = 0
|
||||
callbackEditor = null
|
||||
callback = (editor) ->
|
||||
callbackEditor = editor
|
||||
count++
|
||||
|
||||
atom.workspaceView.eachEditorView(callback)
|
||||
count = 0
|
||||
callbackEditor = null
|
||||
atom.workspaceView.getActiveView().getPaneView().getModel().splitRight(copyActiveItem: true)
|
||||
expect(count).toBe 1
|
||||
expect(callbackEditor).toBe atom.workspaceView.getActiveView()
|
||||
|
||||
it "does not invoke the callback for mini editors", ->
|
||||
editorViewCreatedHandler = jasmine.createSpy('editorViewCreatedHandler')
|
||||
atom.workspaceView.eachEditorView(editorViewCreatedHandler)
|
||||
editorViewCreatedHandler.reset()
|
||||
miniEditor = new TextEditorView(mini: true)
|
||||
atom.workspaceView.append(miniEditor)
|
||||
expect(editorViewCreatedHandler).not.toHaveBeenCalled()
|
||||
|
||||
it "returns a subscription that can be disabled", ->
|
||||
count = 0
|
||||
callback = (editor) -> count++
|
||||
|
||||
subscription = atom.workspaceView.eachEditorView(callback)
|
||||
expect(count).toBe 1
|
||||
atom.workspaceView.getActiveView().getPaneView().getModel().splitRight(copyActiveItem: true)
|
||||
expect(count).toBe 2
|
||||
subscription.off()
|
||||
atom.workspaceView.getActiveView().getPaneView().getModel().splitRight(copyActiveItem: true)
|
||||
expect(count).toBe 2
|
||||
|
||||
describe "core:close", ->
|
||||
it "closes the active pane item until all that remains is a single empty pane", ->
|
||||
atom.config.set('core.destroyEmptyPanes', true)
|
||||
|
||||
paneView1 = atom.workspaceView.getActivePaneView()
|
||||
editorView = atom.workspaceView.getActiveView()
|
||||
editorView.getPaneView().getModel().splitRight(copyActiveItem: true)
|
||||
paneView2 = atom.workspaceView.getActivePaneView()
|
||||
|
||||
expect(paneView1).not.toBe paneView2
|
||||
expect(atom.workspaceView.getPaneViews()).toHaveLength 2
|
||||
atom.workspaceView.trigger('core:close')
|
||||
|
||||
expect(atom.workspaceView.getActivePaneView().getItems()).toHaveLength 1
|
||||
expect(atom.workspaceView.getPaneViews()).toHaveLength 1
|
||||
atom.workspaceView.trigger('core:close')
|
||||
|
||||
expect(atom.workspaceView.getActivePaneView().getItems()).toHaveLength 0
|
||||
expect(atom.workspaceView.getPaneViews()).toHaveLength 1
|
||||
|
||||
describe "the scrollbar visibility class", ->
|
||||
it "has a class based on the style of the scrollbar", ->
|
||||
style = 'legacy'
|
||||
scrollbarStyle = require 'scrollbar-style'
|
||||
spyOn(scrollbarStyle, 'getPreferredScrollbarStyle').andCallFake -> style
|
||||
|
||||
atom.workspaceView.element.observeScrollbarStyle()
|
||||
expect(atom.workspaceView).toHaveClass 'scrollbars-visible-always'
|
||||
|
||||
style = 'overlay'
|
||||
atom.workspaceView.element.observeScrollbarStyle()
|
||||
expect(atom.workspaceView).toHaveClass 'scrollbars-visible-when-scrolling'
|
||||
|
||||
describe "editor font styling", ->
|
||||
[editorNode, editor] = []
|
||||
|
||||
beforeEach ->
|
||||
atom.workspaceView.attachToDom()
|
||||
editorNode = atom.workspaceView.find('atom-text-editor')[0]
|
||||
editor = atom.workspaceView.find('atom-text-editor').view().getEditor()
|
||||
|
||||
it "updates the font-size based on the 'editor.fontSize' config value", ->
|
||||
initialCharWidth = editor.getDefaultCharWidth()
|
||||
expect(getComputedStyle(editorNode).fontSize).toBe atom.config.get('editor.fontSize') + 'px'
|
||||
atom.config.set('editor.fontSize', atom.config.get('editor.fontSize') + 5)
|
||||
expect(getComputedStyle(editorNode).fontSize).toBe atom.config.get('editor.fontSize') + 'px'
|
||||
expect(editor.getDefaultCharWidth()).toBeGreaterThan initialCharWidth
|
||||
|
||||
it "updates the font-family based on the 'editor.fontFamily' config value", ->
|
||||
initialCharWidth = editor.getDefaultCharWidth()
|
||||
expect(getComputedStyle(editorNode).fontFamily).toBe atom.config.get('editor.fontFamily')
|
||||
atom.config.set('editor.fontFamily', 'sans-serif')
|
||||
expect(getComputedStyle(editorNode).fontFamily).toBe atom.config.get('editor.fontFamily')
|
||||
expect(editor.getDefaultCharWidth()).not.toBe initialCharWidth
|
||||
|
||||
it "updates the line-height based on the 'editor.lineHeight' config value", ->
|
||||
initialLineHeight = editor.getLineHeightInPixels()
|
||||
atom.config.set('editor.lineHeight', '30px')
|
||||
expect(getComputedStyle(editorNode).lineHeight).toBe atom.config.get('editor.lineHeight')
|
||||
expect(editor.getLineHeightInPixels()).not.toBe initialLineHeight
|
||||
|
||||
describe 'panel containers', ->
|
||||
workspaceElement = null
|
||||
beforeEach ->
|
||||
workspaceElement = atom.views.getView(atom.workspace)
|
||||
|
||||
it 'inserts panel container elements in the correct places in the DOM', ->
|
||||
leftContainer = workspaceElement.querySelector('atom-panel-container.left')
|
||||
rightContainer = workspaceElement.querySelector('atom-panel-container.right')
|
||||
expect(leftContainer.nextSibling).toBe workspaceElement.verticalAxis
|
||||
expect(rightContainer.previousSibling).toBe workspaceElement.verticalAxis
|
||||
|
||||
topContainer = workspaceElement.querySelector('atom-panel-container.top')
|
||||
bottomContainer = workspaceElement.querySelector('atom-panel-container.bottom')
|
||||
expect(topContainer.nextSibling).toBe workspaceElement.paneContainer
|
||||
expect(bottomContainer.previousSibling).toBe workspaceElement.paneContainer
|
||||
|
||||
modalContainer = workspaceElement.querySelector('atom-panel-container.modal')
|
||||
expect(modalContainer.parentNode).toBe workspaceElement
|
||||
Reference in New Issue
Block a user