mirror of
https://github.com/atom/atom.git
synced 2026-02-15 09:05:58 -05:00
Merge branch 'master' into ns-use-display-layers
# Conflicts: # package.json # src/display-buffer.coffee # src/text-editor.coffee # src/tokenized-buffer.coffee
This commit is contained in:
@@ -19,7 +19,9 @@ exports.afterEach = (fn) ->
|
||||
|
||||
waitsForPromise = (fn) ->
|
||||
promise = fn()
|
||||
waitsFor 'spec promise to resolve', 30000, (done) ->
|
||||
# This timeout is 3 minutes. We need to bump it back down once we fix backgrounding
|
||||
# of the renderer process on CI. See https://github.com/atom/electron/issues/4317
|
||||
waitsFor 'spec promise to resolve', 3 * 60 * 1000, (done) ->
|
||||
promise.then(
|
||||
done,
|
||||
(error) ->
|
||||
|
||||
@@ -152,6 +152,8 @@ describe "AtomEnvironment", ->
|
||||
atom.enablePersistence = false
|
||||
|
||||
it "selects the state based on the current project paths", ->
|
||||
jasmine.useRealClock()
|
||||
|
||||
[dir1, dir2] = [temp.mkdirSync("dir1-"), temp.mkdirSync("dir2-")]
|
||||
|
||||
loadSettings = _.extend atom.getLoadSettings(),
|
||||
@@ -159,20 +161,55 @@ describe "AtomEnvironment", ->
|
||||
windowState: null
|
||||
|
||||
spyOn(atom, 'getLoadSettings').andCallFake -> loadSettings
|
||||
spyOn(atom.getStorageFolder(), 'getPath').andReturn(temp.mkdirSync("storage-dir-"))
|
||||
spyOn(atom, 'serialize').andReturn({stuff: 'cool'})
|
||||
|
||||
atom.state.stuff = "cool"
|
||||
atom.project.setPaths([dir1, dir2])
|
||||
atom.saveStateSync()
|
||||
# State persistence will fail if other Atom instances are running
|
||||
waitsForPromise ->
|
||||
atom.stateStore.connect().then (isConnected) ->
|
||||
expect(isConnected).toBe true
|
||||
|
||||
atom.state = {}
|
||||
atom.loadStateSync()
|
||||
expect(atom.state.stuff).toBeUndefined()
|
||||
waitsForPromise ->
|
||||
atom.saveState().then ->
|
||||
atom.loadState().then (state) ->
|
||||
expect(state).toBeNull()
|
||||
|
||||
loadSettings.initialPaths = [dir2, dir1]
|
||||
atom.state = {}
|
||||
atom.loadStateSync()
|
||||
expect(atom.state.stuff).toBe("cool")
|
||||
waitsForPromise ->
|
||||
loadSettings.initialPaths = [dir2, dir1]
|
||||
atom.loadState().then (state) ->
|
||||
expect(state).toEqual({stuff: 'cool'})
|
||||
|
||||
it "saves state on keydown, mousedown, and when the editor window unloads", ->
|
||||
spyOn(atom, 'saveState')
|
||||
|
||||
keydown = new KeyboardEvent('keydown')
|
||||
atom.document.dispatchEvent(keydown)
|
||||
advanceClock atom.saveStateDebounceInterval
|
||||
expect(atom.saveState).toHaveBeenCalledWith({isUnloading: false})
|
||||
expect(atom.saveState).not.toHaveBeenCalledWith({isUnloading: true})
|
||||
|
||||
atom.saveState.reset()
|
||||
mousedown = new MouseEvent('mousedown')
|
||||
atom.document.dispatchEvent(mousedown)
|
||||
advanceClock atom.saveStateDebounceInterval
|
||||
expect(atom.saveState).toHaveBeenCalledWith({isUnloading: false})
|
||||
expect(atom.saveState).not.toHaveBeenCalledWith({isUnloading: true})
|
||||
|
||||
atom.saveState.reset()
|
||||
atom.unloadEditorWindow()
|
||||
mousedown = new MouseEvent('mousedown')
|
||||
atom.document.dispatchEvent(mousedown)
|
||||
advanceClock atom.saveStateDebounceInterval
|
||||
expect(atom.saveState).toHaveBeenCalledWith({isUnloading: true})
|
||||
expect(atom.saveState).not.toHaveBeenCalledWith({isUnloading: false})
|
||||
|
||||
it "serializes the project state with all the options supplied in saveState", ->
|
||||
spyOn(atom.project, 'serialize').andReturn({foo: 42})
|
||||
|
||||
waitsForPromise -> atom.saveState({anyOption: 'any option'})
|
||||
runs ->
|
||||
expect(atom.project.serialize.calls.length).toBe(1)
|
||||
expect(atom.project.serialize.mostRecentCall.args[0]).toEqual({anyOption: 'any option'})
|
||||
|
||||
describe "openInitialEmptyEditorIfNecessary", ->
|
||||
describe "when there are no paths set", ->
|
||||
@@ -230,23 +267,6 @@ describe "AtomEnvironment", ->
|
||||
|
||||
atomEnvironment.destroy()
|
||||
|
||||
it "saves the serialized state of the window so it can be deserialized after reload", ->
|
||||
atomEnvironment = new AtomEnvironment({applicationDelegate: atom.applicationDelegate, window, document})
|
||||
spyOn(atomEnvironment, 'saveStateSync')
|
||||
|
||||
workspaceState = atomEnvironment.workspace.serialize()
|
||||
grammarsState = {grammarOverridesByPath: atomEnvironment.grammars.grammarOverridesByPath}
|
||||
projectState = atomEnvironment.project.serialize()
|
||||
|
||||
atomEnvironment.unloadEditorWindow()
|
||||
|
||||
expect(atomEnvironment.state.workspace).toEqual workspaceState
|
||||
expect(atomEnvironment.state.grammars).toEqual grammarsState
|
||||
expect(atomEnvironment.state.project).toEqual projectState
|
||||
expect(atomEnvironment.saveStateSync).toHaveBeenCalled()
|
||||
|
||||
atomEnvironment.destroy()
|
||||
|
||||
describe "::destroy()", ->
|
||||
it "does not throw exceptions when unsubscribing from ipc events (regression)", ->
|
||||
configDirPath = temp.mkdirSync()
|
||||
@@ -258,6 +278,7 @@ describe "AtomEnvironment", ->
|
||||
}
|
||||
atomEnvironment = new AtomEnvironment({applicationDelegate: atom.applicationDelegate, window, document: fakeDocument})
|
||||
spyOn(atomEnvironment.packages, 'getAvailablePackagePaths').andReturn []
|
||||
spyOn(atomEnvironment, 'displayWindow').andReturn Promise.resolve()
|
||||
atomEnvironment.startEditorWindow()
|
||||
atomEnvironment.unloadEditorWindow()
|
||||
atomEnvironment.destroy()
|
||||
@@ -273,6 +294,14 @@ describe "AtomEnvironment", ->
|
||||
atom.openLocations([{pathToOpen}])
|
||||
expect(atom.project.getPaths()[0]).toBe __dirname
|
||||
|
||||
describe "then a second path is opened with forceAddToWindow", ->
|
||||
it "adds the second path to the project's paths", ->
|
||||
firstPathToOpen = __dirname
|
||||
secondPathToOpen = path.resolve(__dirname, './fixtures')
|
||||
atom.openLocations([{pathToOpen: firstPathToOpen}])
|
||||
atom.openLocations([{pathToOpen: secondPathToOpen, forceAddToWindow: true}])
|
||||
expect(atom.project.getPaths()).toEqual([firstPathToOpen, secondPathToOpen])
|
||||
|
||||
describe "when the opened path does not exist but its parent directory does", ->
|
||||
it "adds the parent directory to the project paths", ->
|
||||
pathToOpen = path.join(__dirname, 'this-path-does-not-exist.txt')
|
||||
@@ -318,3 +347,18 @@ describe "AtomEnvironment", ->
|
||||
runs ->
|
||||
{releaseVersion} = updateAvailableHandler.mostRecentCall.args[0]
|
||||
expect(releaseVersion).toBe 'version'
|
||||
|
||||
describe "::getReleaseChannel()", ->
|
||||
[version] = []
|
||||
beforeEach ->
|
||||
spyOn(atom, 'getVersion').andCallFake -> version
|
||||
|
||||
it "returns the correct channel based on the version number", ->
|
||||
version = '1.5.6'
|
||||
expect(atom.getReleaseChannel()).toBe 'stable'
|
||||
|
||||
version = '1.5.0-beta10'
|
||||
expect(atom.getReleaseChannel()).toBe 'beta'
|
||||
|
||||
version = '1.7.0-dev-5340c91'
|
||||
expect(atom.getReleaseChannel()).toBe 'dev'
|
||||
|
||||
@@ -172,7 +172,7 @@ class AtomReporter
|
||||
listen document, 'click', '.stack-trace', (event) ->
|
||||
event.currentTarget.classList.toggle('expanded')
|
||||
|
||||
@reloadButton.addEventListener('click', -> require('ipc').send('call-window-method', 'restart'))
|
||||
@reloadButton.addEventListener('click', -> require('electron').ipcRenderer.send('call-window-method', 'restart'))
|
||||
|
||||
updateSpecCounts: ->
|
||||
if @skippedCount
|
||||
|
||||
115
spec/auto-update-manager-spec.js
Normal file
115
spec/auto-update-manager-spec.js
Normal file
@@ -0,0 +1,115 @@
|
||||
'use babel'
|
||||
|
||||
import AutoUpdateManager from '../src/auto-update-manager'
|
||||
import {remote} from 'electron'
|
||||
const electronAutoUpdater = remote.require('electron').autoUpdater
|
||||
|
||||
describe('AutoUpdateManager (renderer)', () => {
|
||||
let autoUpdateManager
|
||||
|
||||
beforeEach(() => {
|
||||
autoUpdateManager = new AutoUpdateManager({
|
||||
applicationDelegate: atom.applicationDelegate
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
autoUpdateManager.destroy()
|
||||
})
|
||||
|
||||
describe('::onDidBeginCheckingForUpdate', () => {
|
||||
it('subscribes to "did-begin-checking-for-update" event', () => {
|
||||
const spy = jasmine.createSpy('spy')
|
||||
autoUpdateManager.onDidBeginCheckingForUpdate(spy)
|
||||
electronAutoUpdater.emit('checking-for-update')
|
||||
waitsFor(() => {
|
||||
return spy.callCount === 1
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('::onDidBeginDownloadingUpdate', () => {
|
||||
it('subscribes to "did-begin-downloading-update" event', () => {
|
||||
const spy = jasmine.createSpy('spy')
|
||||
autoUpdateManager.onDidBeginDownloadingUpdate(spy)
|
||||
electronAutoUpdater.emit('update-available')
|
||||
waitsFor(() => {
|
||||
return spy.callCount === 1
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('::onDidCompleteDownloadingUpdate', () => {
|
||||
it('subscribes to "did-complete-downloading-update" event', () => {
|
||||
const spy = jasmine.createSpy('spy')
|
||||
autoUpdateManager.onDidCompleteDownloadingUpdate(spy)
|
||||
electronAutoUpdater.emit('update-downloaded', null, null, '1.2.3')
|
||||
waitsFor(() => {
|
||||
return spy.callCount === 1
|
||||
})
|
||||
runs(() => {
|
||||
expect(spy.mostRecentCall.args[0].releaseVersion).toBe('1.2.3')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('::onUpdateNotAvailable', () => {
|
||||
it('subscribes to "update-not-available" event', () => {
|
||||
const spy = jasmine.createSpy('spy')
|
||||
autoUpdateManager.onUpdateNotAvailable(spy)
|
||||
electronAutoUpdater.emit('update-not-available')
|
||||
waitsFor(() => {
|
||||
return spy.callCount === 1
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('::platformSupportsUpdates', () => {
|
||||
let state, releaseChannel
|
||||
it('returns true on OS X and Windows when in stable', () => {
|
||||
spyOn(autoUpdateManager, 'getState').andCallFake(() => state)
|
||||
spyOn(atom, 'getReleaseChannel').andCallFake(() => releaseChannel)
|
||||
|
||||
state = 'idle'
|
||||
releaseChannel = 'stable'
|
||||
expect(autoUpdateManager.platformSupportsUpdates()).toBe(true)
|
||||
|
||||
state = 'idle'
|
||||
releaseChannel = 'dev'
|
||||
expect(autoUpdateManager.platformSupportsUpdates()).toBe(false)
|
||||
|
||||
state = 'unsupported'
|
||||
releaseChannel = 'stable'
|
||||
expect(autoUpdateManager.platformSupportsUpdates()).toBe(false)
|
||||
|
||||
state = 'unsupported'
|
||||
releaseChannel = 'dev'
|
||||
expect(autoUpdateManager.platformSupportsUpdates()).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('::destroy', () => {
|
||||
it('unsubscribes from all events', () => {
|
||||
const spy = jasmine.createSpy('spy')
|
||||
const doneIndicator = jasmine.createSpy('spy')
|
||||
atom.applicationDelegate.onUpdateNotAvailable(doneIndicator)
|
||||
autoUpdateManager.onDidBeginCheckingForUpdate(spy)
|
||||
autoUpdateManager.onDidBeginDownloadingUpdate(spy)
|
||||
autoUpdateManager.onDidCompleteDownloadingUpdate(spy)
|
||||
autoUpdateManager.onUpdateNotAvailable(spy)
|
||||
autoUpdateManager.destroy()
|
||||
electronAutoUpdater.emit('checking-for-update')
|
||||
electronAutoUpdater.emit('update-available')
|
||||
electronAutoUpdater.emit('update-downloaded', null, null, '1.2.3')
|
||||
electronAutoUpdater.emit('update-not-available')
|
||||
|
||||
waitsFor(() => {
|
||||
return doneIndicator.callCount === 1
|
||||
})
|
||||
|
||||
runs(() => {
|
||||
expect(spy.callCount).toBe(0)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -15,7 +15,6 @@ describe "Babel transpiler support", ->
|
||||
CompileCache.setCacheDirectory(temp.mkdirSync('compile-cache'))
|
||||
for cacheKey in Object.keys(require.cache)
|
||||
if cacheKey.startsWith(path.join(__dirname, 'fixtures', 'babel'))
|
||||
console.log('deleting', cacheKey)
|
||||
delete require.cache[cacheKey]
|
||||
|
||||
afterEach ->
|
||||
|
||||
@@ -1621,6 +1621,16 @@ describe "Config", ->
|
||||
expect(color.toHexString()).toBe '#ff0000'
|
||||
expect(color.toRGBAString()).toBe 'rgba(255, 0, 0, 1)'
|
||||
|
||||
color.red = 11
|
||||
color.green = 11
|
||||
color.blue = 124
|
||||
color.alpha = 1
|
||||
atom.config.set('foo.bar.aColor', color)
|
||||
|
||||
color = atom.config.get('foo.bar.aColor')
|
||||
expect(color.toHexString()).toBe '#0b0b7c'
|
||||
expect(color.toRGBAString()).toBe 'rgba(11, 11, 124, 1)'
|
||||
|
||||
it 'coerces various types to a color object', ->
|
||||
atom.config.set('foo.bar.aColor', 'red')
|
||||
expect(atom.config.get('foo.bar.aColor')).toEqual {red: 255, green: 0, blue: 0, alpha: 1}
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
module.exports = function (state) {
|
||||
return {
|
||||
wasDeserializedBy: 'Deserializer1',
|
||||
state: state
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
module.exports = function (state) {
|
||||
return {
|
||||
wasDeserializedBy: 'Deserializer2',
|
||||
state: state
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,17 @@
|
||||
module.exports = {
|
||||
activate: function() {}
|
||||
activate () {},
|
||||
|
||||
deserializeMethod1 (state) {
|
||||
return {
|
||||
wasDeserializedBy: 'deserializeMethod1',
|
||||
state: state
|
||||
}
|
||||
},
|
||||
|
||||
deserializeMethod2 (state) {
|
||||
return {
|
||||
wasDeserializedBy: 'deserializeMethod2',
|
||||
state: state
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"version": "1.0.0",
|
||||
"main": "./index",
|
||||
"deserializers": {
|
||||
"Deserializer1": "./deserializer-1.js",
|
||||
"Deserializer2": "./deserializer-2.js"
|
||||
"Deserializer1": "deserializeMethod1",
|
||||
"Deserializer2": "deserializeMethod2"
|
||||
}
|
||||
}
|
||||
|
||||
8
spec/fixtures/packages/package-with-prefixed-and-suffixed-repo-url/package.json
vendored
Normal file
8
spec/fixtures/packages/package-with-prefixed-and-suffixed-repo-url/package.json
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "package-with-a-git-prefixed-git-repo-url",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/example/repo.git"
|
||||
},
|
||||
"_id": "this is here to simulate the URL being already normalized by npm. we still need to stript git+ from the beginning and .git from the end."
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
module.exports = function (state) {
|
||||
return {state: state}
|
||||
}
|
||||
@@ -1,3 +1,25 @@
|
||||
'use strict'
|
||||
|
||||
module.exports = {
|
||||
activate: function() {}
|
||||
activate () {},
|
||||
|
||||
theDeserializerMethod (state) {
|
||||
return {state: state}
|
||||
},
|
||||
|
||||
viewProviderMethod1 (model) {
|
||||
if (model.worksWithViewProvider1) {
|
||||
let element = document.createElement('div')
|
||||
element.dataset['createdBy'] = 'view-provider-1'
|
||||
return element
|
||||
}
|
||||
},
|
||||
|
||||
viewProviderMethod2 (model) {
|
||||
if (model.worksWithViewProvider2) {
|
||||
let element = document.createElement('div')
|
||||
element.dataset['createdBy'] = 'view-provider-2'
|
||||
return element
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
"main": "./index",
|
||||
"version": "1.0.0",
|
||||
"deserializers": {
|
||||
"DeserializerFromPackageWithViewProviders": "./deserializer"
|
||||
"DeserializerFromPackageWithViewProviders": "theDeserializerMethod"
|
||||
},
|
||||
"viewProviders": [
|
||||
"./view-provider-1",
|
||||
"./view-provider-2"
|
||||
"viewProviderMethod1",
|
||||
"viewProviderMethod2"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
module.exports = function (model) {
|
||||
if (model.worksWithViewProvider1) {
|
||||
let element = document.createElement('div')
|
||||
element.dataset['createdBy'] = 'view-provider-1'
|
||||
return element
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
module.exports = function (model) {
|
||||
if (model.worksWithViewProvider2) {
|
||||
let element = document.createElement('div')
|
||||
element.dataset['createdBy'] = 'view-provider-2'
|
||||
return element
|
||||
}
|
||||
}
|
||||
13
spec/fixtures/sample-with-comments.js
vendored
13
spec/fixtures/sample-with-comments.js
vendored
@@ -9,12 +9,23 @@ var quicksort = function () {
|
||||
// Wowza
|
||||
if (items.length <= 1) return items;
|
||||
var pivot = items.shift(), current, left = [], right = [];
|
||||
/*
|
||||
This is a multiline comment block with
|
||||
an empty line inside of it.
|
||||
|
||||
Awesome.
|
||||
*/
|
||||
while(items.length > 0) {
|
||||
current = items.shift();
|
||||
current < pivot ? left.push(current) : right.push(current);
|
||||
}
|
||||
// This is a collection of
|
||||
// single line comments
|
||||
|
||||
// ...with an empty line
|
||||
// among it, geez!
|
||||
return sort(left).concat(pivot).concat(sort(right));
|
||||
};
|
||||
// this is a single-line comment
|
||||
return sort(Array.apply(this, arguments));
|
||||
};
|
||||
};
|
||||
|
||||
@@ -338,10 +338,10 @@ describe('GitRepositoryAsync', () => {
|
||||
})
|
||||
|
||||
describe('.refreshStatus()', () => {
|
||||
let newPath, modifiedPath, cleanPath
|
||||
let newPath, modifiedPath, cleanPath, workingDirectory
|
||||
|
||||
beforeEach(() => {
|
||||
const workingDirectory = copyRepository()
|
||||
workingDirectory = copyRepository()
|
||||
repo = GitRepositoryAsync.open(workingDirectory)
|
||||
modifiedPath = path.join(workingDirectory, 'file.txt')
|
||||
newPath = path.join(workingDirectory, 'untracked.txt')
|
||||
@@ -362,7 +362,7 @@ describe('GitRepositoryAsync', () => {
|
||||
|
||||
describe('in a repository with submodules', () => {
|
||||
beforeEach(() => {
|
||||
const workingDirectory = copySubmoduleRepository()
|
||||
workingDirectory = copySubmoduleRepository()
|
||||
repo = GitRepositoryAsync.open(workingDirectory)
|
||||
modifiedPath = path.join(workingDirectory, 'jstips', 'README.md')
|
||||
newPath = path.join(workingDirectory, 'You-Dont-Need-jQuery', 'untracked.txt')
|
||||
@@ -380,6 +380,86 @@ describe('GitRepositoryAsync', () => {
|
||||
expect(repo.isStatusModified(await repo.getCachedPathStatus(modifiedPath))).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
it('caches the proper statuses when a subdir is open', async () => {
|
||||
const subDir = path.join(workingDirectory, 'dir')
|
||||
fs.mkdirSync(subDir)
|
||||
|
||||
const filePath = path.join(subDir, 'b.txt')
|
||||
fs.writeFileSync(filePath, '')
|
||||
|
||||
atom.project.setPaths([subDir])
|
||||
|
||||
await atom.workspace.open('b.txt')
|
||||
|
||||
const repo = atom.project.getRepositories()[0].async
|
||||
|
||||
await repo.refreshStatus()
|
||||
|
||||
const status = await repo.getCachedPathStatus(filePath)
|
||||
expect(repo.isStatusModified(status)).toBe(false)
|
||||
expect(repo.isStatusNew(status)).toBe(false)
|
||||
})
|
||||
|
||||
it('caches the proper statuses when multiple project are open', async () => {
|
||||
const otherWorkingDirectory = copyRepository()
|
||||
|
||||
atom.project.setPaths([workingDirectory, otherWorkingDirectory])
|
||||
|
||||
await atom.workspace.open('b.txt')
|
||||
|
||||
const repo = atom.project.getRepositories()[0].async
|
||||
|
||||
await repo.refreshStatus()
|
||||
|
||||
const subDir = path.join(workingDirectory, 'dir')
|
||||
fs.mkdirSync(subDir)
|
||||
|
||||
const filePath = path.join(subDir, 'b.txt')
|
||||
fs.writeFileSync(filePath, 'some content!')
|
||||
|
||||
const status = await repo.getCachedPathStatus(filePath)
|
||||
expect(repo.isStatusModified(status)).toBe(true)
|
||||
expect(repo.isStatusNew(status)).toBe(false)
|
||||
})
|
||||
|
||||
it('emits did-change-statuses if the status changes', async () => {
|
||||
const someNewPath = path.join(workingDirectory, 'MyNewJSFramework.md')
|
||||
fs.writeFileSync(someNewPath, '')
|
||||
|
||||
const statusHandler = jasmine.createSpy('statusHandler')
|
||||
repo.onDidChangeStatuses(statusHandler)
|
||||
|
||||
await repo.refreshStatus()
|
||||
|
||||
waitsFor('the onDidChangeStatuses handler to be called', () => statusHandler.callCount > 0)
|
||||
})
|
||||
|
||||
it('emits did-change-statuses if the branch changes', async () => {
|
||||
const statusHandler = jasmine.createSpy('statusHandler')
|
||||
repo.onDidChangeStatuses(statusHandler)
|
||||
|
||||
repo._refreshBranch = jasmine.createSpy('_refreshBranch').andCallFake(() => {
|
||||
return Promise.resolve(true)
|
||||
})
|
||||
|
||||
await repo.refreshStatus()
|
||||
|
||||
waitsFor('the onDidChangeStatuses handler to be called', () => statusHandler.callCount > 0)
|
||||
})
|
||||
|
||||
it('emits did-change-statuses if the ahead/behind changes', async () => {
|
||||
const statusHandler = jasmine.createSpy('statusHandler')
|
||||
repo.onDidChangeStatuses(statusHandler)
|
||||
|
||||
repo._refreshAheadBehindCount = jasmine.createSpy('_refreshAheadBehindCount').andCallFake(() => {
|
||||
return Promise.resolve(true)
|
||||
})
|
||||
|
||||
await repo.refreshStatus()
|
||||
|
||||
waitsFor('the onDidChangeStatuses handler to be called', () => statusHandler.callCount > 0)
|
||||
})
|
||||
})
|
||||
|
||||
describe('.isProjectAtRoot()', () => {
|
||||
@@ -499,7 +579,7 @@ describe('GitRepositoryAsync', () => {
|
||||
await atom.workspace.open('file.txt')
|
||||
|
||||
project2 = new Project({notificationManager: atom.notifications, packageManager: atom.packages, confirm: atom.confirm})
|
||||
project2.deserialize(atom.project.serialize(), atom.deserializers)
|
||||
project2.deserialize(atom.project.serialize({isUnloading: true}))
|
||||
|
||||
const repo = project2.getRepositories()[0].async
|
||||
waitsForPromise(() => repo.refreshStatus())
|
||||
@@ -548,6 +628,14 @@ describe('GitRepositoryAsync', () => {
|
||||
const relativizedPath = repo.relativize(`${workdir}/a/b.txt`, workdir)
|
||||
expect(relativizedPath).toBe('a/b.txt')
|
||||
})
|
||||
|
||||
it('preserves file case', () => {
|
||||
repo.isCaseInsensitive = true
|
||||
|
||||
const workdir = '/tmp/foo/bar/baz/'
|
||||
const relativizedPath = repo.relativize(`${workdir}a/README.txt`, workdir)
|
||||
expect(relativizedPath).toBe('a/README.txt')
|
||||
})
|
||||
})
|
||||
|
||||
describe('.getShortHead(path)', () => {
|
||||
@@ -626,7 +714,7 @@ describe('GitRepositoryAsync', () => {
|
||||
repo = GitRepositoryAsync.open(workingDirectory)
|
||||
})
|
||||
|
||||
it('returns 0, 0 for a branch with no upstream', async () => {
|
||||
it('returns 1, 0 for a branch which is ahead by 1', async () => {
|
||||
await repo.refreshStatus()
|
||||
|
||||
const {ahead, behind} = await repo.getCachedUpstreamAheadBehindCount('You-Dont-Need-jQuery')
|
||||
|
||||
@@ -205,7 +205,7 @@ describe "GitRepository", ->
|
||||
expect(repo.isStatusModified(repo.getDirectoryStatus(directoryPath))).toBe true
|
||||
|
||||
describe ".refreshStatus()", ->
|
||||
[newPath, modifiedPath, cleanPath, originalModifiedPathText] = []
|
||||
[newPath, modifiedPath, cleanPath, originalModifiedPathText, workingDirectory] = []
|
||||
|
||||
beforeEach ->
|
||||
workingDirectory = copyRepository()
|
||||
@@ -231,6 +231,64 @@ describe "GitRepository", ->
|
||||
expect(repo.isStatusNew(repo.getCachedPathStatus(newPath))).toBeTruthy()
|
||||
expect(repo.isStatusModified(repo.getCachedPathStatus(modifiedPath))).toBeTruthy()
|
||||
|
||||
it 'caches the proper statuses when a subdir is open', ->
|
||||
subDir = path.join(workingDirectory, 'dir')
|
||||
fs.mkdirSync(subDir)
|
||||
|
||||
filePath = path.join(subDir, 'b.txt')
|
||||
fs.writeFileSync(filePath, '')
|
||||
|
||||
atom.project.setPaths([subDir])
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('b.txt')
|
||||
|
||||
statusHandler = null
|
||||
runs ->
|
||||
repo = atom.project.getRepositories()[0]
|
||||
|
||||
statusHandler = jasmine.createSpy('statusHandler')
|
||||
repo.onDidChangeStatuses statusHandler
|
||||
repo.refreshStatus()
|
||||
|
||||
waitsFor ->
|
||||
statusHandler.callCount > 0
|
||||
|
||||
runs ->
|
||||
status = repo.getCachedPathStatus(filePath)
|
||||
expect(repo.isStatusModified(status)).toBe false
|
||||
expect(repo.isStatusNew(status)).toBe false
|
||||
|
||||
it 'caches the proper statuses when multiple project are open', ->
|
||||
otherWorkingDirectory = copyRepository()
|
||||
|
||||
atom.project.setPaths([workingDirectory, otherWorkingDirectory])
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('b.txt')
|
||||
|
||||
statusHandler = null
|
||||
runs ->
|
||||
repo = atom.project.getRepositories()[0]
|
||||
|
||||
statusHandler = jasmine.createSpy('statusHandler')
|
||||
repo.onDidChangeStatuses statusHandler
|
||||
repo.refreshStatus()
|
||||
|
||||
waitsFor ->
|
||||
statusHandler.callCount > 0
|
||||
|
||||
runs ->
|
||||
subDir = path.join(workingDirectory, 'dir')
|
||||
fs.mkdirSync(subDir)
|
||||
|
||||
filePath = path.join(subDir, 'b.txt')
|
||||
fs.writeFileSync(filePath, '')
|
||||
|
||||
status = repo.getCachedPathStatus(filePath)
|
||||
expect(repo.isStatusModified(status)).toBe true
|
||||
expect(repo.isStatusNew(status)).toBe false
|
||||
|
||||
describe "buffer events", ->
|
||||
[editor] = []
|
||||
|
||||
@@ -289,7 +347,7 @@ describe "GitRepository", ->
|
||||
|
||||
runs ->
|
||||
project2 = new Project({notificationManager: atom.notifications, packageManager: atom.packages, confirm: atom.confirm})
|
||||
project2.deserialize(atom.project.serialize(), atom.deserializers)
|
||||
project2.deserialize(atom.project.serialize({isUnloading: false}))
|
||||
buffer = project2.getBuffers()[0]
|
||||
|
||||
waitsFor ->
|
||||
|
||||
@@ -15,6 +15,8 @@ ChromedriverPort = 9515
|
||||
ChromedriverURLBase = "/wd/hub"
|
||||
ChromedriverStatusURL = "http://localhost:#{ChromedriverPort}#{ChromedriverURLBase}/status"
|
||||
|
||||
userDataDir = temp.mkdirSync('atom-user-data-dir')
|
||||
|
||||
chromeDriverUp = (done) ->
|
||||
checkStatus = ->
|
||||
http
|
||||
@@ -48,7 +50,7 @@ buildAtomClient = (args, env) ->
|
||||
"atom-env=#{map(env, (value, key) -> "#{key}=#{value}").join(" ")}"
|
||||
"dev"
|
||||
"safe"
|
||||
"user-data-dir=#{temp.mkdirSync('atom-user-data-dir')}"
|
||||
"user-data-dir=#{userDataDir}"
|
||||
"socket-path=#{SocketPath}"
|
||||
])
|
||||
|
||||
@@ -124,7 +126,7 @@ buildAtomClient = (args, env) ->
|
||||
|
||||
.addCommand "simulateQuit", (done) ->
|
||||
@execute -> atom.unloadEditorWindow()
|
||||
.execute -> require("remote").require("app").emit("before-quit")
|
||||
.execute -> require("electron").remote.app.emit("before-quit")
|
||||
.call(done)
|
||||
|
||||
module.exports = (args, env, fn) ->
|
||||
|
||||
@@ -28,13 +28,12 @@ describe "Starting Atom", ->
|
||||
it "opens the parent directory and creates an empty text editor", ->
|
||||
runAtom [path.join(tempDirPath, "new-file")], {ATOM_HOME: atomHome}, (client) ->
|
||||
client
|
||||
.waitForPaneItemCount(1, 1000)
|
||||
|
||||
.treeViewRootDirectories()
|
||||
.then ({value}) -> expect(value).toEqual([tempDirPath])
|
||||
|
||||
.waitForExist("atom-text-editor", 5000)
|
||||
.then (exists) -> expect(exists).toBe true
|
||||
.waitForPaneItemCount(1, 1000)
|
||||
.click("atom-text-editor")
|
||||
.keys("Hello!")
|
||||
.execute -> atom.workspace.getActiveTextEditor().getText()
|
||||
@@ -124,6 +123,34 @@ describe "Starting Atom", ->
|
||||
.waitForPaneItemCount(0, 1000)
|
||||
.treeViewRootDirectories()
|
||||
.then ({value}) -> expect(value).toEqual([otherTempDirPath])
|
||||
describe "when using the -a, --add option", ->
|
||||
it "reuses that window and add the folder to project paths", ->
|
||||
fourthTempDir = temp.mkdirSync("a-fourth-dir")
|
||||
fourthTempFilePath = path.join(fourthTempDir, "a-file")
|
||||
fs.writeFileSync(fourthTempFilePath, "4 - This file was already here.")
|
||||
|
||||
fifthTempDir = temp.mkdirSync("a-fifth-dir")
|
||||
fifthTempFilePath = path.join(fifthTempDir, "a-file")
|
||||
fs.writeFileSync(fifthTempFilePath, "5 - This file was already here.")
|
||||
|
||||
runAtom [path.join(tempDirPath, "new-file")], {ATOM_HOME: atomHome}, (client) ->
|
||||
client
|
||||
.waitForPaneItemCount(1, 5000)
|
||||
|
||||
# Opening another file reuses the same window and add parent dir to
|
||||
# project paths.
|
||||
.startAnotherAtom(['-a', fourthTempFilePath], ATOM_HOME: atomHome)
|
||||
.waitForPaneItemCount(2, 5000)
|
||||
.waitForWindowCount(1, 1000)
|
||||
.treeViewRootDirectories()
|
||||
.then ({value}) -> expect(value).toEqual([tempDirPath, fourthTempDir])
|
||||
.execute -> atom.workspace.getActiveTextEditor().getText()
|
||||
.then ({value: text}) -> expect(text).toBe "4 - This file was already here."
|
||||
|
||||
# Opening another directory resuses the same window and add the folder to project paths.
|
||||
.startAnotherAtom(['--add', fifthTempDir], ATOM_HOME: atomHome)
|
||||
.treeViewRootDirectories()
|
||||
.then ({value}) -> expect(value).toEqual([tempDirPath, fourthTempDir, fifthTempDir])
|
||||
|
||||
it "opens the new window offset from the other window", ->
|
||||
runAtom [path.join(tempDirPath, "new-file")], {ATOM_HOME: atomHome}, (client) ->
|
||||
@@ -153,6 +180,8 @@ describe "Starting Atom", ->
|
||||
.waitForPaneItemCount(0, 3000)
|
||||
.execute -> atom.workspace.open()
|
||||
.waitForPaneItemCount(1, 3000)
|
||||
.keys("Hello!")
|
||||
.waitUntil((-> Promise.resolve(false)), 1100)
|
||||
|
||||
runAtom [tempDirPath], {ATOM_HOME: atomHome}, (client) ->
|
||||
client
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
_ = require 'underscore-plus'
|
||||
fs = require 'fs-plus'
|
||||
path = require 'path'
|
||||
ipc = require 'ipc'
|
||||
{ipcRenderer} = require 'electron'
|
||||
|
||||
module.exports = ({logFile, headless, testPaths, buildAtomEnvironment}) ->
|
||||
window[key] = value for key, value of require '../vendor/jasmine'
|
||||
@@ -88,7 +88,7 @@ buildTerminalReporter = (logFile, resolveWithExitCode) ->
|
||||
if logStream?
|
||||
fs.writeSync(logStream, str)
|
||||
else
|
||||
ipc.send 'write-to-stderr', str
|
||||
ipcRenderer.send 'write-to-stderr', str
|
||||
|
||||
{TerminalReporter} = require 'jasmine-tagged'
|
||||
new TerminalReporter
|
||||
|
||||
@@ -430,7 +430,7 @@ describe "LanguageMode", ->
|
||||
languageMode.foldAll()
|
||||
|
||||
fold1 = editor.tokenizedLineForScreenRow(0).fold
|
||||
expect([fold1.getStartRow(), fold1.getEndRow()]).toEqual [0, 19]
|
||||
expect([fold1.getStartRow(), fold1.getEndRow()]).toEqual [0, 30]
|
||||
fold1.destroy()
|
||||
|
||||
fold2 = editor.tokenizedLineForScreenRow(1).fold
|
||||
@@ -441,6 +441,14 @@ describe "LanguageMode", ->
|
||||
fold4 = editor.tokenizedLineForScreenRow(3).fold
|
||||
expect([fold4.getStartRow(), fold4.getEndRow()]).toEqual [6, 8]
|
||||
|
||||
fold5 = editor.tokenizedLineForScreenRow(6).fold
|
||||
expect([fold5.getStartRow(), fold5.getEndRow()]).toEqual [11, 16]
|
||||
fold5.destroy()
|
||||
|
||||
fold6 = editor.tokenizedLineForScreenRow(13).fold
|
||||
expect([fold6.getStartRow(), fold6.getEndRow()]).toEqual [21, 22]
|
||||
fold6.destroy()
|
||||
|
||||
describe ".foldAllAtIndentLevel()", ->
|
||||
it "folds every foldable range at a given indentLevel", ->
|
||||
languageMode.foldAllAtIndentLevel(2)
|
||||
@@ -450,14 +458,26 @@ describe "LanguageMode", ->
|
||||
fold1.destroy()
|
||||
|
||||
fold2 = editor.tokenizedLineForScreenRow(11).fold
|
||||
expect([fold2.getStartRow(), fold2.getEndRow()]).toEqual [11, 14]
|
||||
expect([fold2.getStartRow(), fold2.getEndRow()]).toEqual [11, 16]
|
||||
fold2.destroy()
|
||||
|
||||
fold3 = editor.tokenizedLineForScreenRow(17).fold
|
||||
expect([fold3.getStartRow(), fold3.getEndRow()]).toEqual [17, 20]
|
||||
fold3.destroy()
|
||||
|
||||
fold4 = editor.tokenizedLineForScreenRow(21).fold
|
||||
expect([fold4.getStartRow(), fold4.getEndRow()]).toEqual [21, 22]
|
||||
fold4.destroy()
|
||||
|
||||
fold5 = editor.tokenizedLineForScreenRow(24).fold
|
||||
expect([fold5.getStartRow(), fold5.getEndRow()]).toEqual [24, 25]
|
||||
fold5.destroy()
|
||||
|
||||
it "does not fold anything but the indentLevel", ->
|
||||
languageMode.foldAllAtIndentLevel(0)
|
||||
|
||||
fold1 = editor.tokenizedLineForScreenRow(0).fold
|
||||
expect([fold1.getStartRow(), fold1.getEndRow()]).toEqual [0, 19]
|
||||
expect([fold1.getStartRow(), fold1.getEndRow()]).toEqual [0, 30]
|
||||
fold1.destroy()
|
||||
|
||||
fold2 = editor.tokenizedLineForScreenRow(5).fold
|
||||
@@ -467,7 +487,13 @@ describe "LanguageMode", ->
|
||||
it "returns true if the line starts a multi-line comment", ->
|
||||
expect(languageMode.isFoldableAtBufferRow(1)).toBe true
|
||||
expect(languageMode.isFoldableAtBufferRow(6)).toBe true
|
||||
expect(languageMode.isFoldableAtBufferRow(17)).toBe false
|
||||
expect(languageMode.isFoldableAtBufferRow(8)).toBe false
|
||||
expect(languageMode.isFoldableAtBufferRow(11)).toBe true
|
||||
expect(languageMode.isFoldableAtBufferRow(15)).toBe false
|
||||
expect(languageMode.isFoldableAtBufferRow(17)).toBe true
|
||||
expect(languageMode.isFoldableAtBufferRow(21)).toBe true
|
||||
expect(languageMode.isFoldableAtBufferRow(24)).toBe true
|
||||
expect(languageMode.isFoldableAtBufferRow(28)).toBe false
|
||||
|
||||
it "does not return true for a line in the middle of a comment that's followed by an indented line", ->
|
||||
expect(languageMode.isFoldableAtBufferRow(7)).toBe false
|
||||
|
||||
@@ -55,12 +55,17 @@ describe "PackageManager", ->
|
||||
it "normalizes short repository urls in package.json", ->
|
||||
{metadata} = atom.packages.loadPackage("package-with-short-url-package-json")
|
||||
expect(metadata.repository.type).toBe "git"
|
||||
expect(metadata.repository.url).toBe "https://github.com/example/repo.git"
|
||||
expect(metadata.repository.url).toBe "https://github.com/example/repo"
|
||||
|
||||
{metadata} = atom.packages.loadPackage("package-with-invalid-url-package-json")
|
||||
expect(metadata.repository.type).toBe "git"
|
||||
expect(metadata.repository.url).toBe "foo"
|
||||
|
||||
it "trims git+ from the beginning and .git from the end of repository URLs, even if npm already normalized them ", ->
|
||||
{metadata} = atom.packages.loadPackage("package-with-prefixed-and-suffixed-repo-url")
|
||||
expect(metadata.repository.type).toBe "git"
|
||||
expect(metadata.repository.url).toBe "https://github.com/example/repo"
|
||||
|
||||
it "returns null if the package is not found in any package directory", ->
|
||||
spyOn(console, 'warn')
|
||||
expect(atom.packages.loadPackage("this-package-cannot-be-found")).toBeNull()
|
||||
@@ -88,18 +93,16 @@ describe "PackageManager", ->
|
||||
|
||||
state1 = {deserializer: 'Deserializer1', a: 'b'}
|
||||
expect(atom.deserializers.deserialize(state1)).toEqual {
|
||||
wasDeserializedBy: 'Deserializer1'
|
||||
wasDeserializedBy: 'deserializeMethod1'
|
||||
state: state1
|
||||
}
|
||||
|
||||
state2 = {deserializer: 'Deserializer2', c: 'd'}
|
||||
expect(atom.deserializers.deserialize(state2)).toEqual {
|
||||
wasDeserializedBy: 'Deserializer2'
|
||||
wasDeserializedBy: 'deserializeMethod2'
|
||||
state: state2
|
||||
}
|
||||
|
||||
expect(pack.mainModule).toBeNull()
|
||||
|
||||
describe "when there are view providers specified in the package's package.json", ->
|
||||
model1 = {worksWithViewProvider1: true}
|
||||
model2 = {worksWithViewProvider2: true}
|
||||
@@ -448,16 +451,15 @@ describe "PackageManager", ->
|
||||
pack = null
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage("package-with-serialization").then (p) -> pack = p
|
||||
|
||||
runs ->
|
||||
expect(pack.mainModule.someNumber).not.toBe 77
|
||||
pack.mainModule.someNumber = 77
|
||||
atom.packages.deactivatePackage("package-with-serialization")
|
||||
spyOn(pack.mainModule, 'activate').andCallThrough()
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage("package-with-serialization")
|
||||
runs ->
|
||||
expect(pack.mainModule.activate).toHaveBeenCalledWith({someNumber: 77})
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage("package-with-serialization")
|
||||
runs ->
|
||||
expect(pack.mainModule.activate).toHaveBeenCalledWith({someNumber: 77})
|
||||
|
||||
it "invokes ::onDidActivatePackage listeners with the activated package", ->
|
||||
activatedPackage = null
|
||||
@@ -821,6 +823,34 @@ describe "PackageManager", ->
|
||||
expect(atom.packages.isPackageActive("package-with-missing-provided-services")).toBe true
|
||||
expect(addErrorHandler.callCount).toBe 0
|
||||
|
||||
describe "::serialize", ->
|
||||
it "does not serialize packages that threw an error during activation", ->
|
||||
spyOn(console, 'warn')
|
||||
badPack = null
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage("package-that-throws-on-activate").then (p) -> badPack = p
|
||||
|
||||
runs ->
|
||||
spyOn(badPack.mainModule, 'serialize').andCallThrough()
|
||||
|
||||
atom.packages.serialize()
|
||||
expect(badPack.mainModule.serialize).not.toHaveBeenCalled()
|
||||
|
||||
it "absorbs exceptions that are thrown by the package module's serialize method", ->
|
||||
spyOn(console, 'error')
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('package-with-serialize-error')
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('package-with-serialization')
|
||||
|
||||
runs ->
|
||||
atom.packages.serialize()
|
||||
expect(atom.packages.packageStates['package-with-serialize-error']).toBeUndefined()
|
||||
expect(atom.packages.packageStates['package-with-serialization']).toEqual someNumber: 1
|
||||
expect(console.error).toHaveBeenCalled()
|
||||
|
||||
describe "::deactivatePackage(id)", ->
|
||||
afterEach ->
|
||||
atom.packages.unloadPackages()
|
||||
@@ -852,33 +882,6 @@ describe "PackageManager", ->
|
||||
expect(badPack.mainModule.deactivate).not.toHaveBeenCalled()
|
||||
expect(atom.packages.isPackageActive("package-that-throws-on-activate")).toBeFalsy()
|
||||
|
||||
it "does not serialize packages that have not been activated called on their main module", ->
|
||||
spyOn(console, 'warn')
|
||||
badPack = null
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage("package-that-throws-on-activate").then (p) -> badPack = p
|
||||
|
||||
runs ->
|
||||
spyOn(badPack.mainModule, 'serialize').andCallThrough()
|
||||
|
||||
atom.packages.deactivatePackage("package-that-throws-on-activate")
|
||||
expect(badPack.mainModule.serialize).not.toHaveBeenCalled()
|
||||
|
||||
it "absorbs exceptions that are thrown by the package module's serialize method", ->
|
||||
spyOn(console, 'error')
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('package-with-serialize-error')
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('package-with-serialization')
|
||||
|
||||
runs ->
|
||||
atom.packages.deactivatePackages()
|
||||
expect(atom.packages.packageStates['package-with-serialize-error']).toBeUndefined()
|
||||
expect(atom.packages.packageStates['package-with-serialization']).toEqual someNumber: 1
|
||||
expect(console.error).toHaveBeenCalled()
|
||||
|
||||
it "absorbs exceptions that are thrown by the package module's deactivate method", ->
|
||||
spyOn(console, 'error')
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{extend} = require 'underscore-plus'
|
||||
{Emitter} = require 'event-kit'
|
||||
Grim = require 'grim'
|
||||
Pane = require '../src/pane'
|
||||
PaneAxis = require '../src/pane-axis'
|
||||
PaneContainer = require '../src/pane-container'
|
||||
@@ -18,8 +19,8 @@ describe "Pane", ->
|
||||
onDidDestroy: (fn) -> @emitter.on('did-destroy', fn)
|
||||
destroy: -> @destroyed = true; @emitter.emit('did-destroy')
|
||||
isDestroyed: -> @destroyed
|
||||
isPending: -> @pending
|
||||
pending: false
|
||||
onDidTerminatePendingState: (callback) -> @emitter.on 'terminate-pending-state', callback
|
||||
terminatePendingState: -> @emitter.emit 'terminate-pending-state'
|
||||
|
||||
beforeEach ->
|
||||
confirm = spyOn(atom.applicationDelegate, 'confirm')
|
||||
@@ -92,7 +93,7 @@ describe "Pane", ->
|
||||
pane = new Pane(paneParams(items: [new Item("A"), new Item("B")]))
|
||||
[item1, item2] = pane.getItems()
|
||||
item3 = new Item("C")
|
||||
pane.addItem(item3, 1)
|
||||
pane.addItem(item3, index: 1)
|
||||
expect(pane.getItems()).toEqual [item1, item3, item2]
|
||||
|
||||
it "adds the item after the active item if no index is provided", ->
|
||||
@@ -115,7 +116,7 @@ describe "Pane", ->
|
||||
pane.onDidAddItem (event) -> events.push(event)
|
||||
|
||||
item = new Item("C")
|
||||
pane.addItem(item, 1)
|
||||
pane.addItem(item, index: 1)
|
||||
expect(events).toEqual [{item, index: 1, moved: false}]
|
||||
|
||||
it "throws an exception if the item is already present on a pane", ->
|
||||
@@ -132,15 +133,56 @@ describe "Pane", ->
|
||||
expect(-> pane.addItem('foo')).toThrow()
|
||||
expect(-> pane.addItem(1)).toThrow()
|
||||
|
||||
it "destroys any existing pending item if the new item is pending", ->
|
||||
it "destroys any existing pending item", ->
|
||||
pane = new Pane(paneParams(items: []))
|
||||
itemA = new Item("A")
|
||||
itemB = new Item("B")
|
||||
itemA.pending = true
|
||||
itemB.pending = true
|
||||
pane.addItem(itemA)
|
||||
itemC = new Item("C")
|
||||
pane.addItem(itemA, pending: false)
|
||||
pane.addItem(itemB, pending: true)
|
||||
pane.addItem(itemC, pending: false)
|
||||
expect(itemB.isDestroyed()).toBe true
|
||||
|
||||
it "adds the new item before destroying any existing pending item", ->
|
||||
eventOrder = []
|
||||
|
||||
pane = new Pane(paneParams(items: []))
|
||||
itemA = new Item("A")
|
||||
itemB = new Item("B")
|
||||
pane.addItem(itemA, pending: true)
|
||||
|
||||
pane.onDidAddItem ({item}) ->
|
||||
eventOrder.push("add") if item is itemB
|
||||
|
||||
pane.onDidRemoveItem ({item}) ->
|
||||
eventOrder.push("remove") if item is itemA
|
||||
|
||||
pane.addItem(itemB)
|
||||
expect(itemA.isDestroyed()).toBe true
|
||||
|
||||
waitsFor ->
|
||||
eventOrder.length is 2
|
||||
|
||||
runs ->
|
||||
expect(eventOrder).toEqual ["add", "remove"]
|
||||
|
||||
describe "when using the old API of ::addItem(item, index)", ->
|
||||
beforeEach ->
|
||||
spyOn Grim, "deprecate"
|
||||
|
||||
it "supports the older public API", ->
|
||||
pane = new Pane(paneParams(items: []))
|
||||
itemA = new Item("A")
|
||||
itemB = new Item("B")
|
||||
itemC = new Item("C")
|
||||
pane.addItem(itemA, 0)
|
||||
pane.addItem(itemB, 0)
|
||||
pane.addItem(itemC, 0)
|
||||
expect(pane.getItems()).toEqual [itemC, itemB, itemA]
|
||||
|
||||
it "shows a deprecation warning", ->
|
||||
pane = new Pane(paneParams(items: []))
|
||||
pane.addItem(new Item(), 2)
|
||||
expect(Grim.deprecate).toHaveBeenCalledWith "Pane::addItem(item, 2) is deprecated in favor of Pane::addItem(item, {index: 2})"
|
||||
|
||||
describe "::activateItem(item)", ->
|
||||
pane = null
|
||||
@@ -172,21 +214,83 @@ describe "Pane", ->
|
||||
beforeEach ->
|
||||
itemC = new Item("C")
|
||||
itemD = new Item("D")
|
||||
itemC.pending = true
|
||||
itemD.pending = true
|
||||
|
||||
it "replaces the active item if it is pending", ->
|
||||
pane.activateItem(itemC)
|
||||
pane.activateItem(itemC, pending: true)
|
||||
expect(pane.getItems().map (item) -> item.name).toEqual ['A', 'C', 'B']
|
||||
pane.activateItem(itemD)
|
||||
pane.activateItem(itemD, pending: true)
|
||||
expect(pane.getItems().map (item) -> item.name).toEqual ['A', 'D', 'B']
|
||||
|
||||
it "adds the item after the active item if it is not pending", ->
|
||||
pane.activateItem(itemC)
|
||||
pane.activateItem(itemC, pending: true)
|
||||
pane.activateItemAtIndex(2)
|
||||
pane.activateItem(itemD)
|
||||
pane.activateItem(itemD, pending: true)
|
||||
expect(pane.getItems().map (item) -> item.name).toEqual ['A', 'B', 'D']
|
||||
|
||||
describe "::setPendingItem", ->
|
||||
pane = null
|
||||
|
||||
beforeEach ->
|
||||
pane = atom.workspace.getActivePane()
|
||||
|
||||
it "changes the pending item", ->
|
||||
expect(pane.getPendingItem()).toBeNull()
|
||||
pane.setPendingItem("fake item")
|
||||
expect(pane.getPendingItem()).toEqual "fake item"
|
||||
|
||||
describe "::onItemDidTerminatePendingState callback", ->
|
||||
pane = null
|
||||
callbackCalled = false
|
||||
|
||||
beforeEach ->
|
||||
pane = atom.workspace.getActivePane()
|
||||
callbackCalled = false
|
||||
|
||||
it "is called when the pending item changes", ->
|
||||
pane.setPendingItem("fake item one")
|
||||
pane.onItemDidTerminatePendingState (item) ->
|
||||
callbackCalled = true
|
||||
expect(item).toEqual "fake item one"
|
||||
pane.setPendingItem("fake item two")
|
||||
expect(callbackCalled).toBeTruthy()
|
||||
|
||||
it "has access to the new pending item via ::getPendingItem", ->
|
||||
pane.setPendingItem("fake item one")
|
||||
pane.onItemDidTerminatePendingState (item) ->
|
||||
callbackCalled = true
|
||||
expect(pane.getPendingItem()).toEqual "fake item two"
|
||||
pane.setPendingItem("fake item two")
|
||||
expect(callbackCalled).toBeTruthy()
|
||||
|
||||
describe "::activateNextRecentlyUsedItem() and ::activatePreviousRecentlyUsedItem()", ->
|
||||
it "sets the active item to the next/previous item in the itemStack, looping around at either end", ->
|
||||
pane = new Pane(paneParams(items: [new Item("A"), new Item("B"), new Item("C"), new Item("D"), new Item("E")]))
|
||||
[item1, item2, item3, item4, item5] = pane.getItems()
|
||||
pane.itemStack = [item3, item1, item2, item5, item4]
|
||||
|
||||
pane.activateItem(item4)
|
||||
expect(pane.getActiveItem()).toBe item4
|
||||
pane.activateNextRecentlyUsedItem()
|
||||
expect(pane.getActiveItem()).toBe item5
|
||||
pane.activateNextRecentlyUsedItem()
|
||||
expect(pane.getActiveItem()).toBe item2
|
||||
pane.activatePreviousRecentlyUsedItem()
|
||||
expect(pane.getActiveItem()).toBe item5
|
||||
pane.activatePreviousRecentlyUsedItem()
|
||||
expect(pane.getActiveItem()).toBe item4
|
||||
pane.activatePreviousRecentlyUsedItem()
|
||||
expect(pane.getActiveItem()).toBe item3
|
||||
pane.activatePreviousRecentlyUsedItem()
|
||||
expect(pane.getActiveItem()).toBe item1
|
||||
pane.activateNextRecentlyUsedItem()
|
||||
expect(pane.getActiveItem()).toBe item3
|
||||
pane.activateNextRecentlyUsedItem()
|
||||
expect(pane.getActiveItem()).toBe item4
|
||||
pane.activateNextRecentlyUsedItem()
|
||||
pane.moveActiveItemToTopOfStack()
|
||||
expect(pane.getActiveItem()).toBe item5
|
||||
expect(pane.itemStack[4]).toBe item5
|
||||
|
||||
describe "::activateNextItem() and ::activatePreviousItem()", ->
|
||||
it "sets the active item to the next/previous item, looping around at either end", ->
|
||||
pane = new Pane(paneParams(items: [new Item("A"), new Item("B"), new Item("C")]))
|
||||
@@ -253,7 +357,7 @@ describe "Pane", ->
|
||||
pane = new Pane(paneParams(items: [new Item("A"), new Item("B"), new Item("C")]))
|
||||
[item1, item2, item3] = pane.getItems()
|
||||
|
||||
it "removes the item from the items list and destroyes it", ->
|
||||
it "removes the item from the items list and destroys it", ->
|
||||
expect(pane.getActiveItem()).toBe item1
|
||||
pane.destroyItem(item2)
|
||||
expect(item2 in pane.getItems()).toBe false
|
||||
@@ -264,6 +368,23 @@ describe "Pane", ->
|
||||
expect(item1 in pane.getItems()).toBe false
|
||||
expect(item1.isDestroyed()).toBe true
|
||||
|
||||
it "removes the item from the itemStack", ->
|
||||
pane.itemStack = [item2, item3, item1]
|
||||
|
||||
pane.activateItem(item1)
|
||||
expect(pane.getActiveItem()).toBe item1
|
||||
pane.destroyItem(item3)
|
||||
expect(pane.itemStack).toEqual [item2, item1]
|
||||
expect(pane.getActiveItem()).toBe item1
|
||||
|
||||
pane.destroyItem(item1)
|
||||
expect(pane.itemStack).toEqual [item2]
|
||||
expect(pane.getActiveItem()).toBe item2
|
||||
|
||||
pane.destroyItem(item2)
|
||||
expect(pane.itemStack).toEqual []
|
||||
expect(pane.getActiveItem()).toBeUndefined()
|
||||
|
||||
it "invokes ::onWillDestroyItem() observers before destroying the item", ->
|
||||
events = []
|
||||
pane.onWillDestroyItem (event) ->
|
||||
@@ -605,6 +726,23 @@ describe "Pane", ->
|
||||
expect(pane2.isDestroyed()).toBe true
|
||||
expect(item4.isDestroyed()).toBe false
|
||||
|
||||
describe "when the item being moved is pending", ->
|
||||
it "is made permanent in the new pane", ->
|
||||
item6 = new Item("F")
|
||||
pane1.addItem(item6, pending: true)
|
||||
expect(pane1.getPendingItem()).toEqual item6
|
||||
pane1.moveItemToPane(item6, pane2, 0)
|
||||
expect(pane2.getPendingItem()).not.toEqual item6
|
||||
|
||||
describe "when the target pane has a pending item", ->
|
||||
it "does not destroy the pending item", ->
|
||||
item6 = new Item("F")
|
||||
pane1.addItem(item6, pending: true)
|
||||
expect(pane1.getPendingItem()).toEqual item6
|
||||
pane2.moveItemToPane(item5, pane1, 0)
|
||||
expect(pane1.getPendingItem()).toEqual item6
|
||||
|
||||
|
||||
describe "split methods", ->
|
||||
[pane1, item1, container] = []
|
||||
|
||||
@@ -806,6 +944,67 @@ describe "Pane", ->
|
||||
pane2.destroy()
|
||||
expect(container.root).toBe pane1
|
||||
|
||||
describe "pending state", ->
|
||||
editor1 = null
|
||||
pane = null
|
||||
eventCount = null
|
||||
|
||||
beforeEach ->
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('sample.txt', pending: true).then (o) ->
|
||||
editor1 = o
|
||||
pane = atom.workspace.getActivePane()
|
||||
|
||||
runs ->
|
||||
eventCount = 0
|
||||
editor1.onDidTerminatePendingState -> eventCount++
|
||||
|
||||
it "does not open file in pending state by default", ->
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('sample.js').then (o) ->
|
||||
editor1 = o
|
||||
pane = atom.workspace.getActivePane()
|
||||
|
||||
runs ->
|
||||
expect(pane.getPendingItem()).toBeNull()
|
||||
|
||||
it "opens file in pending state if 'pending' option is true", ->
|
||||
expect(pane.getPendingItem()).toEqual editor1
|
||||
|
||||
it "terminates pending state if ::terminatePendingState is invoked", ->
|
||||
editor1.terminatePendingState()
|
||||
|
||||
expect(pane.getPendingItem()).toBeNull()
|
||||
expect(eventCount).toBe 1
|
||||
|
||||
it "terminates pending state when buffer is changed", ->
|
||||
editor1.insertText('I\'ll be back!')
|
||||
advanceClock(editor1.getBuffer().stoppedChangingDelay)
|
||||
|
||||
expect(pane.getPendingItem()).toBeNull()
|
||||
expect(eventCount).toBe 1
|
||||
|
||||
it "only calls terminate handler once when text is modified twice", ->
|
||||
editor1.insertText('Some text')
|
||||
advanceClock(editor1.getBuffer().stoppedChangingDelay)
|
||||
|
||||
editor1.save()
|
||||
|
||||
editor1.insertText('More text')
|
||||
advanceClock(editor1.getBuffer().stoppedChangingDelay)
|
||||
|
||||
expect(pane.getPendingItem()).toBeNull()
|
||||
expect(eventCount).toBe 1
|
||||
|
||||
it "only calls clearPendingItem if there is a pending item to clear", ->
|
||||
spyOn(pane, "clearPendingItem").andCallThrough()
|
||||
|
||||
editor1.terminatePendingState()
|
||||
editor1.terminatePendingState()
|
||||
|
||||
expect(pane.getPendingItem()).toBeNull()
|
||||
expect(pane.clearPendingItem.callCount).toBe 1
|
||||
|
||||
describe "serialization", ->
|
||||
pane = null
|
||||
|
||||
@@ -837,3 +1036,30 @@ describe "Pane", ->
|
||||
pane.focus()
|
||||
newPane = Pane.deserialize(pane.serialize(), atom)
|
||||
expect(newPane.focused).toBe true
|
||||
|
||||
it "can serialize and deserialize the order of the items in the itemStack", ->
|
||||
[item1, item2, item3] = pane.getItems()
|
||||
pane.itemStack = [item3, item1, item2]
|
||||
newPane = Pane.deserialize(pane.serialize(), atom)
|
||||
expect(newPane.itemStack).toEqual pane.itemStack
|
||||
expect(newPane.itemStack[2]).toEqual item2
|
||||
|
||||
it "builds the itemStack if the itemStack is not serialized", ->
|
||||
[item1, item2, item3] = pane.getItems()
|
||||
newPane = Pane.deserialize(pane.serialize(), atom)
|
||||
expect(newPane.getItems()).toEqual newPane.itemStack
|
||||
|
||||
it "rebuilds the itemStack if items.length does not match itemStack.length", ->
|
||||
[item1, item2, item3] = pane.getItems()
|
||||
pane.itemStack = [item2, item3]
|
||||
newPane = Pane.deserialize(pane.serialize(), atom)
|
||||
expect(newPane.getItems()).toEqual newPane.itemStack
|
||||
|
||||
it "does not serialize the reference to the items in the itemStack for pane items that will not be serialized", ->
|
||||
[item1, item2, item3] = pane.getItems()
|
||||
pane.itemStack = [item2, item1, item3]
|
||||
unserializable = {}
|
||||
pane.activateItem(unserializable)
|
||||
|
||||
newPane = Pane.deserialize(pane.serialize(), atom)
|
||||
expect(newPane.itemStack).toEqual [item2, item1, item3]
|
||||
|
||||
@@ -21,6 +21,14 @@ describe "Project", ->
|
||||
afterEach ->
|
||||
deserializedProject?.destroy()
|
||||
|
||||
it "does not deserialize paths to non directories", ->
|
||||
deserializedProject = new Project({notificationManager: atom.notifications, packageManager: atom.packages, confirm: atom.confirm})
|
||||
state = atom.project.serialize()
|
||||
state.paths.push('/directory/that/does/not/exist')
|
||||
state.paths.push(path.join(__dirname, 'fixtures', 'sample.js'))
|
||||
deserializedProject.deserialize(state, atom.deserializers)
|
||||
expect(deserializedProject.getPaths()).toEqual(atom.project.getPaths())
|
||||
|
||||
it "does not include unretained buffers in the serialized state", ->
|
||||
waitsForPromise ->
|
||||
atom.project.bufferForPath('a')
|
||||
@@ -29,7 +37,7 @@ describe "Project", ->
|
||||
expect(atom.project.getBuffers().length).toBe 1
|
||||
|
||||
deserializedProject = new Project({notificationManager: atom.notifications, packageManager: atom.packages, confirm: atom.confirm})
|
||||
deserializedProject.deserialize(atom.project.serialize(), atom.deserializers)
|
||||
deserializedProject.deserialize(atom.project.serialize({isUnloading: false}))
|
||||
expect(deserializedProject.getBuffers().length).toBe 0
|
||||
|
||||
it "listens for destroyed events on deserialized buffers and removes them when they are destroyed", ->
|
||||
@@ -39,7 +47,7 @@ describe "Project", ->
|
||||
runs ->
|
||||
expect(atom.project.getBuffers().length).toBe 1
|
||||
deserializedProject = new Project({notificationManager: atom.notifications, packageManager: atom.packages, confirm: atom.confirm})
|
||||
deserializedProject.deserialize(atom.project.serialize(), atom.deserializers)
|
||||
deserializedProject.deserialize(atom.project.serialize({isUnloading: false}))
|
||||
|
||||
expect(deserializedProject.getBuffers().length).toBe 1
|
||||
deserializedProject.getBuffers()[0].destroy()
|
||||
@@ -56,7 +64,7 @@ describe "Project", ->
|
||||
expect(atom.project.getBuffers().length).toBe 1
|
||||
fs.mkdirSync(pathToOpen)
|
||||
deserializedProject = new Project({notificationManager: atom.notifications, packageManager: atom.packages, confirm: atom.confirm})
|
||||
deserializedProject.deserialize(atom.project.serialize(), atom.deserializers)
|
||||
deserializedProject.deserialize(atom.project.serialize({isUnloading: false}))
|
||||
expect(deserializedProject.getBuffers().length).toBe 0
|
||||
|
||||
it "does not deserialize buffers when their path is inaccessible", ->
|
||||
@@ -70,9 +78,26 @@ describe "Project", ->
|
||||
expect(atom.project.getBuffers().length).toBe 1
|
||||
fs.chmodSync(pathToOpen, '000')
|
||||
deserializedProject = new Project({notificationManager: atom.notifications, packageManager: atom.packages, confirm: atom.confirm})
|
||||
deserializedProject.deserialize(atom.project.serialize(), atom.deserializers)
|
||||
deserializedProject.deserialize(atom.project.serialize({isUnloading: false}))
|
||||
expect(deserializedProject.getBuffers().length).toBe 0
|
||||
|
||||
it "serializes marker layers only if Atom is quitting", ->
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('a')
|
||||
|
||||
runs ->
|
||||
bufferA = atom.project.getBuffers()[0]
|
||||
layerA = bufferA.addMarkerLayer(maintainHistory: true)
|
||||
markerA = layerA.markPosition([0, 3])
|
||||
|
||||
notQuittingProject = new Project({notificationManager: atom.notifications, packageManager: atom.packages, confirm: atom.confirm})
|
||||
notQuittingProject.deserialize(atom.project.serialize({isUnloading: false}))
|
||||
expect(notQuittingProject.getBuffers()[0].getMarkerLayer(layerA.id)?.getMarker(markerA.id)).toBeUndefined()
|
||||
|
||||
quittingProject = new Project({notificationManager: atom.notifications, packageManager: atom.packages, confirm: atom.confirm})
|
||||
quittingProject.deserialize(atom.project.serialize({isUnloading: true}))
|
||||
expect(quittingProject.getBuffers()[0].getMarkerLayer(layerA.id)?.getMarker(markerA.id)).not.toBeUndefined()
|
||||
|
||||
describe "when an editor is saved and the project has no path", ->
|
||||
it "sets the project's path to the saved file's parent directory", ->
|
||||
tempFile = temp.openSync().path
|
||||
|
||||
@@ -83,3 +83,11 @@ describe "Selection", ->
|
||||
selection.setBufferRange([[2, 0], [2, 10]])
|
||||
selection.destroy()
|
||||
expect(selection.marker.isDestroyed()).toBeTruthy()
|
||||
|
||||
describe ".insertText(text, options)", ->
|
||||
it "allows pasting white space only lines when autoIndent is enabled", ->
|
||||
selection.setBufferRange [[0, 0], [0, 0]]
|
||||
selection.insertText(" \n \n\n", autoIndent: true)
|
||||
expect(buffer.lineForRow(0)).toBe " "
|
||||
expect(buffer.lineForRow(1)).toBe " "
|
||||
expect(buffer.lineForRow(2)).toBe ""
|
||||
|
||||
61
spec/state-store-spec.js
Normal file
61
spec/state-store-spec.js
Normal file
@@ -0,0 +1,61 @@
|
||||
/** @babel */
|
||||
import {it, fit, ffit, fffit, beforeEach, afterEach} from './async-spec-helpers'
|
||||
|
||||
const StateStore = require('../src/state-store.js')
|
||||
|
||||
describe("StateStore", () => {
|
||||
let databaseName = `test-database-${Date.now()}`
|
||||
let version = 1
|
||||
|
||||
it("can save and load states", () => {
|
||||
const store = new StateStore(databaseName, version)
|
||||
return store.save('key', {foo:'bar'})
|
||||
.then(() => store.load('key'))
|
||||
.then((state) => {
|
||||
expect(state).toEqual({foo:'bar'})
|
||||
})
|
||||
})
|
||||
|
||||
it("resolves with null when a non-existent key is loaded", () => {
|
||||
const store = new StateStore(databaseName, version)
|
||||
return store.load('no-such-key').then((value) => {
|
||||
expect(value).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
it("can clear the state object store", () => {
|
||||
const store = new StateStore(databaseName, version)
|
||||
return store.save('key', {foo:'bar'})
|
||||
.then(() => store.count())
|
||||
.then((count) =>
|
||||
expect(count).toBe(1)
|
||||
)
|
||||
.then(() => store.clear())
|
||||
.then(() => store.count())
|
||||
.then((count) => {
|
||||
expect(count).toBe(0)
|
||||
})
|
||||
})
|
||||
|
||||
describe("when there is an error reading from the database", () => {
|
||||
it("rejects the promise returned by load", () => {
|
||||
const store = new StateStore(databaseName, version)
|
||||
|
||||
const fakeErrorEvent = {target: {errorCode: "Something bad happened"}}
|
||||
|
||||
spyOn(IDBObjectStore.prototype, 'get').andCallFake((key) => {
|
||||
let request = {}
|
||||
process.nextTick(() => request.onerror(fakeErrorEvent))
|
||||
return request
|
||||
})
|
||||
|
||||
return store.load('nonexistentKey')
|
||||
.then(() => {
|
||||
throw new Error("Promise should have been rejected")
|
||||
})
|
||||
.catch((event) => {
|
||||
expect(event).toBe(fakeErrorEvent)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,6 +1,6 @@
|
||||
/** @babel */
|
||||
|
||||
import {it, ffit, fffit, beforeEach, afterEach} from './async-spec-helpers'
|
||||
import {it, fit, ffit, fffit, beforeEach, afterEach} from './async-spec-helpers'
|
||||
import TextEditorElement from '../src/text-editor-element'
|
||||
import _, {extend, flatten, last, toArray} from 'underscore-plus'
|
||||
|
||||
@@ -2151,10 +2151,11 @@ describe('TextEditorComponent', function () {
|
||||
item.style.height = itemHeight + 'px'
|
||||
wrapperNode.style.width = windowWidth + 'px'
|
||||
wrapperNode.style.height = windowHeight + 'px'
|
||||
atom.setWindowDimensions({
|
||||
await atom.setWindowDimensions({
|
||||
width: windowWidth,
|
||||
height: windowHeight
|
||||
})
|
||||
|
||||
component.measureDimensions()
|
||||
component.measureWindowSize()
|
||||
await nextViewUpdatePromise()
|
||||
@@ -4834,7 +4835,7 @@ describe('TextEditorComponent', function () {
|
||||
|
||||
it('pastes the previously selected text at the clicked location', async function () {
|
||||
let clipboardWrittenTo = false
|
||||
spyOn(require('ipc'), 'send').andCallFake(function (eventName, selectedText) {
|
||||
spyOn(require('electron').ipcRenderer, 'send').andCallFake(function (eventName, selectedText) {
|
||||
if (eventName === 'write-text-to-selection-clipboard') {
|
||||
require('../src/safe-clipboard').writeText(selectedText, 'selection')
|
||||
clipboardWrittenTo = true
|
||||
|
||||
@@ -91,11 +91,13 @@ describe "TextEditorPresenter", ->
|
||||
expectNoStateUpdate = (presenter, fn) -> expectStateUpdatedToBe(false, presenter, fn)
|
||||
|
||||
waitsForStateToUpdate = (presenter, fn) ->
|
||||
waitsFor "presenter state to update", 1000, (done) ->
|
||||
fn?()
|
||||
line = new Error().stack.split('\n')[2].split(':')[1]
|
||||
|
||||
waitsFor "presenter state to update at line #{line}", 1000, (done) ->
|
||||
disposable = presenter.onDidUpdateState ->
|
||||
disposable.dispose()
|
||||
process.nextTick(done)
|
||||
fn?()
|
||||
|
||||
tiledContentContract = (stateFn) ->
|
||||
it "contains states for tiles that are visible on screen", ->
|
||||
@@ -633,16 +635,28 @@ describe "TextEditorPresenter", ->
|
||||
expectStateUpdate presenter, -> presenter.setExplicitHeight(500)
|
||||
expect(getState(presenter).verticalScrollbar.scrollHeight).toBe 500
|
||||
|
||||
it "adds the computed clientHeight to the computed scrollHeight if editor.scrollPastEnd is true", ->
|
||||
presenter = buildPresenter(scrollTop: 10, explicitHeight: 50, horizontalScrollbarHeight: 10)
|
||||
expectStateUpdate presenter, -> presenter.setScrollTop(300)
|
||||
expect(getState(presenter).verticalScrollbar.scrollHeight).toBe presenter.contentHeight
|
||||
describe "scrollPastEnd", ->
|
||||
it "adds the computed clientHeight to the computed scrollHeight if editor.scrollPastEnd is true", ->
|
||||
presenter = buildPresenter(scrollTop: 10, explicitHeight: 50, horizontalScrollbarHeight: 10)
|
||||
expectStateUpdate presenter, -> presenter.setScrollTop(300)
|
||||
expect(getState(presenter).verticalScrollbar.scrollHeight).toBe presenter.contentHeight
|
||||
|
||||
expectStateUpdate presenter, -> atom.config.set("editor.scrollPastEnd", true)
|
||||
expect(getState(presenter).verticalScrollbar.scrollHeight).toBe presenter.contentHeight + presenter.clientHeight - (presenter.lineHeight * 3)
|
||||
expectStateUpdate presenter, -> atom.config.set("editor.scrollPastEnd", true)
|
||||
expect(getState(presenter).verticalScrollbar.scrollHeight).toBe presenter.contentHeight + presenter.clientHeight - (presenter.lineHeight * 3)
|
||||
|
||||
expectStateUpdate presenter, -> atom.config.set("editor.scrollPastEnd", false)
|
||||
expect(getState(presenter).verticalScrollbar.scrollHeight).toBe presenter.contentHeight
|
||||
expectStateUpdate presenter, -> atom.config.set("editor.scrollPastEnd", false)
|
||||
expect(getState(presenter).verticalScrollbar.scrollHeight).toBe presenter.contentHeight
|
||||
|
||||
it "doesn't add the computed clientHeight to the computed scrollHeight if editor.scrollPastEnd is true but the presenter is created with scrollPastEnd as false", ->
|
||||
presenter = buildPresenter(scrollTop: 10, explicitHeight: 50, horizontalScrollbarHeight: 10, scrollPastEnd: false)
|
||||
expectStateUpdate presenter, -> presenter.setScrollTop(300)
|
||||
expect(getState(presenter).verticalScrollbar.scrollHeight).toBe presenter.contentHeight
|
||||
|
||||
expectStateUpdate presenter, -> atom.config.set("editor.scrollPastEnd", true)
|
||||
expect(getState(presenter).verticalScrollbar.scrollHeight).toBe presenter.contentHeight
|
||||
|
||||
expectStateUpdate presenter, -> atom.config.set("editor.scrollPastEnd", false)
|
||||
expect(getState(presenter).verticalScrollbar.scrollHeight).toBe presenter.contentHeight
|
||||
|
||||
describe ".scrollTop", ->
|
||||
it "tracks the value of ::scrollTop", ->
|
||||
@@ -1336,9 +1350,11 @@ describe "TextEditorPresenter", ->
|
||||
presenter = buildPresenter()
|
||||
blockDecoration2 = addBlockDecorationBeforeScreenRow(3)
|
||||
blockDecoration3 = addBlockDecorationBeforeScreenRow(7)
|
||||
blockDecoration4 = addBlockDecorationAfterScreenRow(7)
|
||||
blockDecoration4 = null
|
||||
|
||||
waitsForStateToUpdate presenter, ->
|
||||
blockDecoration4 = addBlockDecorationAfterScreenRow(7)
|
||||
|
||||
waitsForStateToUpdate presenter
|
||||
runs ->
|
||||
expect(lineStateForScreenRow(presenter, 0).precedingBlockDecorations).toEqual([blockDecoration1])
|
||||
expect(lineStateForScreenRow(presenter, 0).followingBlockDecorations).toEqual([])
|
||||
@@ -1472,9 +1488,9 @@ describe "TextEditorPresenter", ->
|
||||
decoration1 = editor.decorateMarker(marker1, type: 'line', class: 'a')
|
||||
presenter = buildPresenter()
|
||||
marker2 = editor.addMarkerLayer(maintainHistory: true).markBufferRange([[4, 0], [6, 2]], invalidate: 'touch')
|
||||
decoration2 = editor.decorateMarker(marker2, type: 'line', class: 'b')
|
||||
decoration2 = null
|
||||
|
||||
waitsForStateToUpdate presenter
|
||||
waitsForStateToUpdate presenter, -> decoration2 = editor.decorateMarker(marker2, type: 'line', class: 'b')
|
||||
runs ->
|
||||
expect(lineStateForScreenRow(presenter, 3).decorationClasses).toBeNull()
|
||||
expect(lineStateForScreenRow(presenter, 4).decorationClasses).toEqual ['a', 'b']
|
||||
@@ -2150,31 +2166,40 @@ describe "TextEditorPresenter", ->
|
||||
}
|
||||
|
||||
# becoming empty
|
||||
waitsForStateToUpdate presenter, -> editor.getSelections()[1].clear(autoscroll: false)
|
||||
runs ->
|
||||
editor.getSelections()[1].clear(autoscroll: false)
|
||||
waitsForStateToUpdate presenter
|
||||
runs ->
|
||||
expectUndefinedStateForSelection(presenter, 1)
|
||||
|
||||
# becoming non-empty
|
||||
waitsForStateToUpdate presenter, -> editor.getSelections()[1].setBufferRange([[2, 4], [2, 6]], autoscroll: false)
|
||||
runs ->
|
||||
editor.getSelections()[1].setBufferRange([[2, 4], [2, 6]], autoscroll: false)
|
||||
waitsForStateToUpdate presenter
|
||||
runs ->
|
||||
expectValues stateForSelectionInTile(presenter, 1, 2), {
|
||||
regions: [{top: 0, left: 4 * 10, width: 2 * 10, height: 10}]
|
||||
}
|
||||
|
||||
# moving out of view
|
||||
waitsForStateToUpdate presenter, -> editor.getSelections()[1].setBufferRange([[3, 4], [3, 6]], autoscroll: false)
|
||||
runs ->
|
||||
editor.getSelections()[1].setBufferRange([[3, 4], [3, 6]], autoscroll: false)
|
||||
waitsForStateToUpdate presenter
|
||||
runs ->
|
||||
expectUndefinedStateForSelection(presenter, 1)
|
||||
|
||||
# adding
|
||||
waitsForStateToUpdate presenter, -> editor.addSelectionForBufferRange([[1, 4], [1, 6]], autoscroll: false)
|
||||
runs -> editor.addSelectionForBufferRange([[1, 4], [1, 6]], autoscroll: false)
|
||||
waitsForStateToUpdate presenter
|
||||
runs ->
|
||||
expectValues stateForSelectionInTile(presenter, 2, 0), {
|
||||
regions: [{top: 10, left: 4 * 10, width: 2 * 10, height: 10}]
|
||||
}
|
||||
|
||||
# moving added selection
|
||||
waitsForStateToUpdate presenter, -> editor.getSelections()[2].setBufferRange([[1, 4], [1, 8]], autoscroll: false)
|
||||
runs ->
|
||||
editor.getSelections()[2].setBufferRange([[1, 4], [1, 8]], autoscroll: false)
|
||||
waitsForStateToUpdate presenter
|
||||
|
||||
destroyedSelection = null
|
||||
runs ->
|
||||
@@ -2208,8 +2233,9 @@ describe "TextEditorPresenter", ->
|
||||
presenter = buildPresenter(explicitHeight: 30, scrollTop: 20, tileSize: 2)
|
||||
|
||||
marker = editor.markBufferPosition([2, 2])
|
||||
highlight = editor.decorateMarker(marker, type: 'highlight', class: 'a')
|
||||
highlight = null
|
||||
waitsForStateToUpdate presenter, ->
|
||||
highlight = editor.decorateMarker(marker, type: 'highlight', class: 'a')
|
||||
marker.setBufferRange([[2, 2], [5, 2]])
|
||||
highlight.flash('b', 500)
|
||||
runs ->
|
||||
@@ -2969,9 +2995,8 @@ describe "TextEditorPresenter", ->
|
||||
presenter.setBlockDecorationDimensions(blockDecoration4, 0, 35)
|
||||
presenter.setBlockDecorationDimensions(blockDecoration4, 0, 40)
|
||||
presenter.setBlockDecorationDimensions(blockDecoration5, 0, 50)
|
||||
presenter.setBlockDecorationDimensions(blockDecoration6, 0, 60)
|
||||
|
||||
waitsForStateToUpdate presenter
|
||||
waitsForStateToUpdate presenter, -> presenter.setBlockDecorationDimensions(blockDecoration6, 0, 60)
|
||||
runs ->
|
||||
expect(lineNumberStateForScreenRow(presenter, 0).blockDecorationsHeight).toBe(10)
|
||||
expect(lineNumberStateForScreenRow(presenter, 1).blockDecorationsHeight).toBe(0)
|
||||
@@ -3460,9 +3485,9 @@ describe "TextEditorPresenter", ->
|
||||
gutterName: 'test-gutter-2'
|
||||
class: 'test-class'
|
||||
marker4 = editor.markBufferRange([[0, 0], [1, 0]])
|
||||
decoration4 = editor.decorateMarker(marker4, decorationParams)
|
||||
decoration4 = null
|
||||
|
||||
waitsForStateToUpdate presenter
|
||||
waitsForStateToUpdate presenter, -> decoration4 = editor.decorateMarker(marker4, decorationParams)
|
||||
|
||||
runs ->
|
||||
expectStateUpdate presenter, -> editor.addGutter({name: 'test-gutter-2'})
|
||||
|
||||
38
spec/text-editor-registry-spec.coffee
Normal file
38
spec/text-editor-registry-spec.coffee
Normal file
@@ -0,0 +1,38 @@
|
||||
TextEditorRegistry = require '../src/text-editor-registry'
|
||||
|
||||
describe "TextEditorRegistry", ->
|
||||
[registry, editor] = []
|
||||
|
||||
beforeEach ->
|
||||
registry = new TextEditorRegistry
|
||||
|
||||
describe "when a TextEditor is added", ->
|
||||
it "gets added to the list of registered editors", ->
|
||||
editor = {}
|
||||
registry.add(editor)
|
||||
expect(registry.editors.size).toBe 1
|
||||
expect(registry.editors.has(editor)).toBe(true)
|
||||
|
||||
it "returns a Disposable that can unregister the editor", ->
|
||||
editor = {}
|
||||
disposable = registry.add(editor)
|
||||
expect(registry.editors.size).toBe 1
|
||||
disposable.dispose()
|
||||
expect(registry.editors.size).toBe 0
|
||||
|
||||
describe "when the registry is observed", ->
|
||||
it "calls the callback for current and future editors until unsubscribed", ->
|
||||
[editor1, editor2, editor3] = [{}, {}, {}]
|
||||
|
||||
registry.add(editor1)
|
||||
subscription = registry.observe spy = jasmine.createSpy()
|
||||
expect(spy.calls.length).toBe 1
|
||||
|
||||
registry.add(editor2)
|
||||
expect(spy.calls.length).toBe 2
|
||||
expect(spy.argsForCall[0][0]).toBe editor1
|
||||
expect(spy.argsForCall[1][0]).toBe editor2
|
||||
|
||||
subscription.dispose()
|
||||
registry.add(editor3)
|
||||
expect(spy.calls.length).toBe 2
|
||||
@@ -55,16 +55,6 @@ describe "TextEditor", ->
|
||||
|
||||
expect(editor.tokenizedLineForScreenRow(0).invisibles.eol).toBe '?'
|
||||
|
||||
it "restores pending tabs in pending state", ->
|
||||
expect(editor.isPending()).toBe false
|
||||
editor2 = TextEditor.deserialize(editor.serialize(), atom)
|
||||
expect(editor2.isPending()).toBe false
|
||||
|
||||
pendingEditor = atom.workspace.buildTextEditor(pending: true)
|
||||
expect(pendingEditor.isPending()).toBe true
|
||||
editor3 = TextEditor.deserialize(pendingEditor.serialize(), atom)
|
||||
expect(editor3.isPending()).toBe true
|
||||
|
||||
describe "when the editor is constructed with the largeFileMode option set to true", ->
|
||||
it "loads the editor but doesn't tokenize", ->
|
||||
editor = null
|
||||
@@ -139,6 +129,15 @@ describe "TextEditor", ->
|
||||
expect(editor2.getSoftTabs()).toBe true
|
||||
expect(editor2.getEncoding()).toBe 'macroman'
|
||||
|
||||
atom.config.set('editor.tabLength', -1)
|
||||
expect(editor2.getTabLength()).toBe 1
|
||||
atom.config.set('editor.tabLength', 2)
|
||||
expect(editor2.getTabLength()).toBe 2
|
||||
atom.config.set('editor.tabLength', 17)
|
||||
expect(editor2.getTabLength()).toBe 17
|
||||
atom.config.set('editor.tabLength', 128)
|
||||
expect(editor2.getTabLength()).toBe 128
|
||||
|
||||
it "uses scoped `core.fileEncoding` values", ->
|
||||
editor1 = null
|
||||
editor2 = null
|
||||
@@ -2133,20 +2132,31 @@ describe "TextEditor", ->
|
||||
editor.splitSelectionsIntoLines()
|
||||
expect(editor.getSelectedBufferRanges()).toEqual [[[0, 0], [0, 3]]]
|
||||
|
||||
describe ".consolidateSelections()", ->
|
||||
it "destroys all selections but the least recent, returning true if any selections were destroyed", ->
|
||||
editor.setSelectedBufferRange([[3, 16], [3, 21]])
|
||||
selection1 = editor.getLastSelection()
|
||||
describe "::consolidateSelections()", ->
|
||||
makeMultipleSelections = ->
|
||||
selection.setBufferRange [[3, 16], [3, 21]]
|
||||
selection2 = editor.addSelectionForBufferRange([[3, 25], [3, 34]])
|
||||
selection3 = editor.addSelectionForBufferRange([[8, 4], [8, 10]])
|
||||
selection4 = editor.addSelectionForBufferRange([[1, 6], [1, 10]])
|
||||
expect(editor.getSelections()).toEqual [selection, selection2, selection3, selection4]
|
||||
[selection, selection2, selection3, selection4]
|
||||
|
||||
it "destroys all selections but the oldest selection and autoscrolls to it, returning true if any selections were destroyed", ->
|
||||
[selection1] = makeMultipleSelections()
|
||||
|
||||
autoscrollEvents = []
|
||||
editor.onDidRequestAutoscroll (event) -> autoscrollEvents.push(event)
|
||||
|
||||
expect(editor.getSelections()).toEqual [selection1, selection2, selection3]
|
||||
expect(editor.consolidateSelections()).toBeTruthy()
|
||||
expect(editor.getSelections()).toEqual [selection1]
|
||||
expect(selection1.isEmpty()).toBeFalsy()
|
||||
expect(editor.consolidateSelections()).toBeFalsy()
|
||||
expect(editor.getSelections()).toEqual [selection1]
|
||||
|
||||
expect(autoscrollEvents).toEqual([
|
||||
{screenRange: selection1.getScreenRange(), options: {center: true, reversed: false}}
|
||||
])
|
||||
|
||||
describe "when the cursor is moved while there is a selection", ->
|
||||
makeSelection = -> selection.setBufferRange [[1, 2], [1, 5]]
|
||||
|
||||
@@ -5819,52 +5829,29 @@ describe "TextEditor", ->
|
||||
rangeIsReversed: false
|
||||
}
|
||||
|
||||
describe "pending state", ->
|
||||
editor1 = null
|
||||
eventCount = null
|
||||
|
||||
describe "when the editor is constructed with the showInvisibles option set to false", ->
|
||||
beforeEach ->
|
||||
atom.workspace.destroyActivePane()
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('sample.txt', pending: true).then (o) -> editor1 = o
|
||||
atom.workspace.open('sample.js', showInvisibles: false).then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
eventCount = 0
|
||||
editor1.onDidTerminatePendingState -> eventCount++
|
||||
it "ignores invisibles even if editor.showInvisibles is true", ->
|
||||
atom.config.set('editor.showInvisibles', true)
|
||||
invisibles = editor.tokenizedLineForScreenRow(0).invisibles
|
||||
expect(invisibles).toBe(null)
|
||||
|
||||
it "does not open file in pending state by default", ->
|
||||
expect(editor.isPending()).toBe false
|
||||
describe "when the editor is constructed with the grammar option set", ->
|
||||
beforeEach ->
|
||||
atom.workspace.destroyActivePane()
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-coffee-script')
|
||||
|
||||
it "opens file in pending state if 'pending' option is true", ->
|
||||
expect(editor1.isPending()).toBe true
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('sample.js', grammar: atom.grammars.grammarForScopeName('source.coffee')).then (o) -> editor = o
|
||||
|
||||
it "terminates pending state if ::terminatePendingState is invoked", ->
|
||||
editor1.terminatePendingState()
|
||||
it "sets the grammar", ->
|
||||
expect(editor.getGrammar().name).toBe 'CoffeeScript'
|
||||
|
||||
expect(editor1.isPending()).toBe false
|
||||
expect(eventCount).toBe 1
|
||||
|
||||
it "terminates pending state when buffer is changed", ->
|
||||
editor1.insertText('I\'ll be back!')
|
||||
advanceClock(editor1.getBuffer().stoppedChangingDelay)
|
||||
|
||||
expect(editor1.isPending()).toBe false
|
||||
expect(eventCount).toBe 1
|
||||
|
||||
it "only calls terminate handler once when text is modified twice", ->
|
||||
editor1.insertText('Some text')
|
||||
advanceClock(editor1.getBuffer().stoppedChangingDelay)
|
||||
|
||||
editor1.save()
|
||||
|
||||
editor1.insertText('More text')
|
||||
advanceClock(editor1.getBuffer().stoppedChangingDelay)
|
||||
|
||||
expect(editor1.isPending()).toBe false
|
||||
expect(eventCount).toBe 1
|
||||
|
||||
it "only calls terminate handler once when terminatePendingState is called twice", ->
|
||||
editor1.terminatePendingState()
|
||||
editor1.terminatePendingState()
|
||||
|
||||
expect(editor1.isPending()).toBe false
|
||||
expect(eventCount).toBe 1
|
||||
describe "::getElement", ->
|
||||
it "returns an element", ->
|
||||
expect(editor.getElement() instanceof HTMLElement).toBe(true)
|
||||
|
||||
@@ -202,8 +202,7 @@ describe "TokenizedBuffer", ->
|
||||
expect(tokenizedBuffer.firstInvalidRow()).toBe 3
|
||||
|
||||
advanceClock()
|
||||
# we discover that row 2 starts a foldable region when line 3 gets tokenized
|
||||
expect(changeHandler).toHaveBeenCalledWith(start: 2, end: 7, delta: 0)
|
||||
expect(changeHandler).toHaveBeenCalledWith(start: 3, end: 7, delta: 0)
|
||||
expect(tokenizedBuffer.firstInvalidRow()).toBe 8
|
||||
|
||||
describe "when there is a buffer change surrounding an invalid row", ->
|
||||
@@ -253,7 +252,7 @@ describe "TokenizedBuffer", ->
|
||||
expect(changeHandler).toHaveBeenCalled()
|
||||
[event] = changeHandler.argsForCall[0]
|
||||
delete event.bufferChange
|
||||
expect(event).toEqual(start: 1, end: 2, delta: 0)
|
||||
expect(event).toEqual(start: 2, end: 2, delta: 0)
|
||||
changeHandler.reset()
|
||||
|
||||
advanceClock()
|
||||
@@ -263,8 +262,7 @@ describe "TokenizedBuffer", ->
|
||||
expect(changeHandler).toHaveBeenCalled()
|
||||
[event] = changeHandler.argsForCall[0]
|
||||
delete event.bufferChange
|
||||
# we discover that row 2 starts a foldable region when line 3 gets tokenized
|
||||
expect(event).toEqual(start: 2, end: 5, delta: 0)
|
||||
expect(event).toEqual(start: 3, end: 5, delta: 0)
|
||||
|
||||
it "resumes highlighting with the state of the previous line", ->
|
||||
buffer.insert([0, 0], '/*')
|
||||
@@ -292,7 +290,7 @@ describe "TokenizedBuffer", ->
|
||||
expect(changeHandler).toHaveBeenCalled()
|
||||
[event] = changeHandler.argsForCall[0]
|
||||
delete event.bufferChange
|
||||
expect(event).toEqual(start: 0, end: 3, delta: -2) # starts at 0 because foldable on row 0 becomes false
|
||||
expect(event).toEqual(start: 1, end: 3, delta: -2)
|
||||
|
||||
describe "when the change invalidates the tokenization of subsequent lines", ->
|
||||
it "schedules the invalidated lines to be tokenized in the background", ->
|
||||
@@ -305,7 +303,7 @@ describe "TokenizedBuffer", ->
|
||||
expect(changeHandler).toHaveBeenCalled()
|
||||
[event] = changeHandler.argsForCall[0]
|
||||
delete event.bufferChange
|
||||
expect(event).toEqual(start: 1, end: 3, delta: -1)
|
||||
expect(event).toEqual(start: 2, end: 3, delta: -1)
|
||||
changeHandler.reset()
|
||||
|
||||
advanceClock()
|
||||
@@ -314,8 +312,7 @@ describe "TokenizedBuffer", ->
|
||||
expect(changeHandler).toHaveBeenCalled()
|
||||
[event] = changeHandler.argsForCall[0]
|
||||
delete event.bufferChange
|
||||
# we discover that row 2 starts a foldable region when line 3 gets tokenized
|
||||
expect(event).toEqual(start: 2, end: 4, delta: 0)
|
||||
expect(event).toEqual(start: 3, end: 4, delta: 0)
|
||||
|
||||
describe "when lines are both updated and inserted", ->
|
||||
it "updates tokens to reflect the change", ->
|
||||
@@ -339,7 +336,7 @@ describe "TokenizedBuffer", ->
|
||||
expect(changeHandler).toHaveBeenCalled()
|
||||
[event] = changeHandler.argsForCall[0]
|
||||
delete event.bufferChange
|
||||
expect(event).toEqual(start: 0, end: 2, delta: 2) # starts at 0 because .foldable becomes false on row 0
|
||||
expect(event).toEqual(start: 1, end: 2, delta: 2)
|
||||
|
||||
describe "when the change invalidates the tokenization of subsequent lines", ->
|
||||
it "schedules the invalidated lines to be tokenized in the background", ->
|
||||
@@ -350,7 +347,7 @@ describe "TokenizedBuffer", ->
|
||||
expect(changeHandler).toHaveBeenCalled()
|
||||
[event] = changeHandler.argsForCall[0]
|
||||
delete event.bufferChange
|
||||
expect(event).toEqual(start: 1, end: 2, delta: 2)
|
||||
expect(event).toEqual(start: 2, end: 2, delta: 2)
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[0].scopes).toEqual ['source.js', 'comment.block.js', 'punctuation.definition.comment.js']
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(3).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(4).tokens[0].scopes).toEqual ['source.js', 'comment.block.js']
|
||||
@@ -894,7 +891,7 @@ describe "TokenizedBuffer", ->
|
||||
buffer.setTextInRange([[7, 0], [8, 65]], ' ok')
|
||||
|
||||
delete changeHandler.argsForCall[0][0].bufferChange
|
||||
expect(changeHandler).toHaveBeenCalledWith(start: 4, end: 10, delta: -1) # starts at row 4 because it became foldable
|
||||
expect(changeHandler).toHaveBeenCalledWith(start: 5, end: 10, delta: -1)
|
||||
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(5).indentLevel).toBe 2
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(6).indentLevel).toBe 2
|
||||
@@ -903,7 +900,7 @@ describe "TokenizedBuffer", ->
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(9).indentLevel).toBe 2
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(10).indentLevel).toBe 2 # }
|
||||
|
||||
describe ".foldable on tokenized lines", ->
|
||||
describe "::isFoldableAtRow(row)", ->
|
||||
changes = null
|
||||
|
||||
beforeEach ->
|
||||
@@ -915,74 +912,66 @@ describe "TokenizedBuffer", ->
|
||||
buffer, config: atom.config, grammarRegistry: atom.grammars, packageManager: atom.packages, assert: atom.assert
|
||||
})
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
tokenizedBuffer.onDidChange (change) ->
|
||||
delete change.bufferChange
|
||||
changes.push(change)
|
||||
|
||||
it "sets .foldable to true on the first line of multi-line comments", ->
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).foldable).toBe true
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(1).foldable).toBe false
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).foldable).toBe false
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(3).foldable).toBe true # because of indent
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(13).foldable).toBe true
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(14).foldable).toBe false
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(15).foldable).toBe false
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(16).foldable).toBe false
|
||||
it "includes the first line of multi-line comments", ->
|
||||
expect(tokenizedBuffer.isFoldableAtRow(0)).toBe true
|
||||
expect(tokenizedBuffer.isFoldableAtRow(1)).toBe false
|
||||
expect(tokenizedBuffer.isFoldableAtRow(2)).toBe false
|
||||
expect(tokenizedBuffer.isFoldableAtRow(3)).toBe true # because of indent
|
||||
expect(tokenizedBuffer.isFoldableAtRow(13)).toBe true
|
||||
expect(tokenizedBuffer.isFoldableAtRow(14)).toBe false
|
||||
expect(tokenizedBuffer.isFoldableAtRow(15)).toBe false
|
||||
expect(tokenizedBuffer.isFoldableAtRow(16)).toBe false
|
||||
|
||||
buffer.insert([0, Infinity], '\n')
|
||||
expect(changes).toEqual [{start: 0, end: 1, delta: 1}]
|
||||
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).foldable).toBe false
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(1).foldable).toBe false
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).foldable).toBe true
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(3).foldable).toBe false
|
||||
expect(tokenizedBuffer.isFoldableAtRow(0)).toBe false
|
||||
expect(tokenizedBuffer.isFoldableAtRow(1)).toBe false
|
||||
expect(tokenizedBuffer.isFoldableAtRow(2)).toBe true
|
||||
expect(tokenizedBuffer.isFoldableAtRow(3)).toBe false
|
||||
|
||||
changes = []
|
||||
buffer.undo()
|
||||
expect(changes).toEqual [{start: 0, end: 2, delta: -1}]
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).foldable).toBe true
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(1).foldable).toBe false
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).foldable).toBe false
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(3).foldable).toBe true # because of indent
|
||||
|
||||
it "sets .foldable to true on non-comment lines that precede an increase in indentation", ->
|
||||
expect(tokenizedBuffer.isFoldableAtRow(0)).toBe true
|
||||
expect(tokenizedBuffer.isFoldableAtRow(1)).toBe false
|
||||
expect(tokenizedBuffer.isFoldableAtRow(2)).toBe false
|
||||
expect(tokenizedBuffer.isFoldableAtRow(3)).toBe true # because of indent
|
||||
|
||||
it "includes non-comment lines that precede an increase in indentation", ->
|
||||
buffer.insert([2, 0], ' ') # commented lines preceding an indent aren't foldable
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(1).foldable).toBe false
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).foldable).toBe false
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(3).foldable).toBe true
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(4).foldable).toBe true
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(5).foldable).toBe false
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(6).foldable).toBe false
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(7).foldable).toBe true
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(8).foldable).toBe false
|
||||
|
||||
changes = []
|
||||
expect(tokenizedBuffer.isFoldableAtRow(1)).toBe false
|
||||
expect(tokenizedBuffer.isFoldableAtRow(2)).toBe false
|
||||
expect(tokenizedBuffer.isFoldableAtRow(3)).toBe true
|
||||
expect(tokenizedBuffer.isFoldableAtRow(4)).toBe true
|
||||
expect(tokenizedBuffer.isFoldableAtRow(5)).toBe false
|
||||
expect(tokenizedBuffer.isFoldableAtRow(6)).toBe false
|
||||
expect(tokenizedBuffer.isFoldableAtRow(7)).toBe true
|
||||
expect(tokenizedBuffer.isFoldableAtRow(8)).toBe false
|
||||
|
||||
buffer.insert([7, 0], ' ')
|
||||
expect(changes).toEqual [{start: 6, end: 7, delta: 0}]
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(6).foldable).toBe true
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(7).foldable).toBe false
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(8).foldable).toBe false
|
||||
|
||||
changes = []
|
||||
expect(tokenizedBuffer.isFoldableAtRow(6)).toBe true
|
||||
expect(tokenizedBuffer.isFoldableAtRow(7)).toBe false
|
||||
expect(tokenizedBuffer.isFoldableAtRow(8)).toBe false
|
||||
|
||||
buffer.undo()
|
||||
expect(changes).toEqual [{start: 6, end: 7, delta: 0}]
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(6).foldable).toBe false
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(7).foldable).toBe true
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(8).foldable).toBe false
|
||||
|
||||
changes = []
|
||||
expect(tokenizedBuffer.isFoldableAtRow(6)).toBe false
|
||||
expect(tokenizedBuffer.isFoldableAtRow(7)).toBe true
|
||||
expect(tokenizedBuffer.isFoldableAtRow(8)).toBe false
|
||||
|
||||
buffer.insert([7, 0], " \n x\n")
|
||||
expect(changes).toEqual [{start: 6, end: 7, delta: 2}]
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(6).foldable).toBe true
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(7).foldable).toBe false
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(8).foldable).toBe false
|
||||
|
||||
changes = []
|
||||
expect(tokenizedBuffer.isFoldableAtRow(6)).toBe true
|
||||
expect(tokenizedBuffer.isFoldableAtRow(7)).toBe false
|
||||
expect(tokenizedBuffer.isFoldableAtRow(8)).toBe false
|
||||
|
||||
buffer.insert([9, 0], " ")
|
||||
expect(changes).toEqual [{start: 9, end: 9, delta: 0}]
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(6).foldable).toBe true
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(7).foldable).toBe false
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(8).foldable).toBe false
|
||||
|
||||
expect(tokenizedBuffer.isFoldableAtRow(6)).toBe true
|
||||
expect(tokenizedBuffer.isFoldableAtRow(7)).toBe false
|
||||
expect(tokenizedBuffer.isFoldableAtRow(8)).toBe false
|
||||
|
||||
describe "when the buffer is configured with the null grammar", ->
|
||||
it "uses the placeholder tokens and does not actually tokenize using the grammar", ->
|
||||
|
||||
@@ -28,6 +28,12 @@ describe "TooltipManager", ->
|
||||
hover element, ->
|
||||
expect(document.body.querySelector(".tooltip")).toHaveText("Title")
|
||||
|
||||
it "creates a tooltip immediately if the trigger type is manual", ->
|
||||
disposable = manager.add element, title: "Title", trigger: "manual"
|
||||
expect(document.body.querySelector(".tooltip")).toHaveText("Title")
|
||||
disposable.dispose()
|
||||
expect(document.body.querySelector(".tooltip")).toBeNull()
|
||||
|
||||
it "allows jQuery elements to be passed as the target", ->
|
||||
element2 = document.createElement('div')
|
||||
jasmine.attachToDOM(element2)
|
||||
|
||||
@@ -23,6 +23,15 @@ describe "ViewRegistry", ->
|
||||
component = new TestComponent
|
||||
expect(registry.getView(component)).toBe component.element
|
||||
|
||||
describe "when passed an object with a getElement function", ->
|
||||
it "returns the return value of getElement if it's an instance of HTMLElement", ->
|
||||
class TestComponent
|
||||
getElement: ->
|
||||
@myElement ?= document.createElement('div')
|
||||
|
||||
component = new TestComponent
|
||||
expect(registry.getView(component)).toBe component.myElement
|
||||
|
||||
describe "when passed a model object", ->
|
||||
describe "when a view provider is registered matching the object's constructor", ->
|
||||
it "constructs a view element and assigns the model on it", ->
|
||||
|
||||
@@ -4,7 +4,7 @@ fs = require 'fs-plus'
|
||||
temp = require 'temp'
|
||||
TextEditor = require '../src/text-editor'
|
||||
WindowEventHandler = require '../src/window-event-handler'
|
||||
ipc = require 'ipc'
|
||||
{ipcRenderer} = require 'electron'
|
||||
|
||||
describe "WindowEventHandler", ->
|
||||
[projectPath, windowEventHandler] = []
|
||||
@@ -53,7 +53,7 @@ describe "WindowEventHandler", ->
|
||||
describe "beforeunload event", ->
|
||||
beforeEach ->
|
||||
jasmine.unspy(TextEditor.prototype, "shouldPromptToSave")
|
||||
spyOn(ipc, 'send')
|
||||
spyOn(ipcRenderer, 'send')
|
||||
|
||||
describe "when pane items are modified", ->
|
||||
editor = null
|
||||
@@ -65,13 +65,13 @@ describe "WindowEventHandler", ->
|
||||
spyOn(atom.workspace, 'confirmClose').andReturn(true)
|
||||
window.dispatchEvent(new CustomEvent('beforeunload'))
|
||||
expect(atom.workspace.confirmClose).toHaveBeenCalled()
|
||||
expect(ipc.send).not.toHaveBeenCalledWith('did-cancel-window-unload')
|
||||
expect(ipcRenderer.send).not.toHaveBeenCalledWith('did-cancel-window-unload')
|
||||
|
||||
it "cancels the unload if the user selects cancel", ->
|
||||
spyOn(atom.workspace, 'confirmClose').andReturn(false)
|
||||
window.dispatchEvent(new CustomEvent('beforeunload'))
|
||||
expect(atom.workspace.confirmClose).toHaveBeenCalled()
|
||||
expect(ipc.send).toHaveBeenCalledWith('did-cancel-window-unload')
|
||||
expect(ipcRenderer.send).toHaveBeenCalledWith('did-cancel-window-unload')
|
||||
|
||||
describe "when a link is clicked", ->
|
||||
it "opens the http/https links in an external application", ->
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
ipc = require 'ipc'
|
||||
{ipcRenderer} = require 'electron'
|
||||
path = require 'path'
|
||||
temp = require('temp').track()
|
||||
|
||||
@@ -127,35 +127,35 @@ describe "WorkspaceElement", ->
|
||||
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')
|
||||
spyOn(ipcRenderer, 'send')
|
||||
|
||||
# No project paths. Don't try to run specs.
|
||||
atom.commands.dispatch(workspaceElement, "window:run-package-specs")
|
||||
expect(ipc.send).not.toHaveBeenCalledWith("run-package-specs")
|
||||
expect(ipcRenderer.send).not.toHaveBeenCalledWith("run-package-specs")
|
||||
|
||||
projectPaths = [temp.mkdirSync("dir1-"), temp.mkdirSync("dir2-")]
|
||||
atom.project.setPaths(projectPaths)
|
||||
|
||||
# No active item. Use first project directory.
|
||||
atom.commands.dispatch(workspaceElement, "window:run-package-specs")
|
||||
expect(ipc.send).toHaveBeenCalledWith("run-package-specs", path.join(projectPaths[0], "spec"))
|
||||
ipc.send.reset()
|
||||
expect(ipcRenderer.send).toHaveBeenCalledWith("run-package-specs", path.join(projectPaths[0], "spec"))
|
||||
ipcRenderer.send.reset()
|
||||
|
||||
# Active item doesn't implement ::getPath(). Use first project directory.
|
||||
item = document.createElement("div")
|
||||
atom.workspace.getActivePane().activateItem(item)
|
||||
atom.commands.dispatch(workspaceElement, "window:run-package-specs")
|
||||
expect(ipc.send).toHaveBeenCalledWith("run-package-specs", path.join(projectPaths[0], "spec"))
|
||||
ipc.send.reset()
|
||||
expect(ipcRenderer.send).toHaveBeenCalledWith("run-package-specs", path.join(projectPaths[0], "spec"))
|
||||
ipcRenderer.send.reset()
|
||||
|
||||
# Active item has no path. Use first project directory.
|
||||
item.getPath = -> null
|
||||
atom.commands.dispatch(workspaceElement, "window:run-package-specs")
|
||||
expect(ipc.send).toHaveBeenCalledWith("run-package-specs", path.join(projectPaths[0], "spec"))
|
||||
ipc.send.reset()
|
||||
expect(ipcRenderer.send).toHaveBeenCalledWith("run-package-specs", path.join(projectPaths[0], "spec"))
|
||||
ipcRenderer.send.reset()
|
||||
|
||||
# Active item has path. Use project path for item path.
|
||||
item.getPath = -> path.join(projectPaths[1], "a-file.txt")
|
||||
atom.commands.dispatch(workspaceElement, "window:run-package-specs")
|
||||
expect(ipc.send).toHaveBeenCalledWith("run-package-specs", path.join(projectPaths[1], "spec"))
|
||||
ipc.send.reset()
|
||||
expect(ipcRenderer.send).toHaveBeenCalledWith("run-package-specs", path.join(projectPaths[1], "spec"))
|
||||
ipcRenderer.send.reset()
|
||||
|
||||
@@ -22,11 +22,11 @@ describe "Workspace", ->
|
||||
describe "serialization", ->
|
||||
simulateReload = ->
|
||||
workspaceState = atom.workspace.serialize()
|
||||
projectState = atom.project.serialize()
|
||||
projectState = atom.project.serialize({isUnloading: true})
|
||||
atom.workspace.destroy()
|
||||
atom.project.destroy()
|
||||
atom.project = new Project({notificationManager: atom.notifications, packageManager: atom.packages, confirm: atom.confirm.bind(atom)})
|
||||
atom.project.deserialize(projectState, atom.deserializers)
|
||||
atom.project.deserialize(projectState)
|
||||
atom.workspace = new Workspace({
|
||||
config: atom.config, project: atom.project, packageManager: atom.packages,
|
||||
grammarRegistry: atom.grammars, deserializerManager: atom.deserializers,
|
||||
@@ -585,6 +585,72 @@ describe "Workspace", ->
|
||||
open = -> workspace.open('file1', workspace.getActivePane())
|
||||
expect(open).toThrow()
|
||||
|
||||
describe "when the file is already open in pending state", ->
|
||||
it "should terminate the pending state", ->
|
||||
editor = null
|
||||
pane = null
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('sample.js', pending: true).then (o) ->
|
||||
editor = o
|
||||
pane = atom.workspace.getActivePane()
|
||||
|
||||
runs ->
|
||||
expect(pane.getPendingItem()).toEqual editor
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('sample.js')
|
||||
|
||||
runs ->
|
||||
expect(pane.getPendingItem()).toBeNull()
|
||||
|
||||
describe "when opening will switch from a pending tab to a permanent tab", ->
|
||||
it "keeps the pending tab open", ->
|
||||
editor1 = null
|
||||
editor2 = null
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('sample.txt').then (o) ->
|
||||
editor1 = o
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('sample2.txt', pending: true).then (o) ->
|
||||
editor2 = o
|
||||
|
||||
runs ->
|
||||
pane = atom.workspace.getActivePane()
|
||||
pane.activateItem(editor1)
|
||||
expect(pane.getItems().length).toBe 2
|
||||
expect(pane.getItems()).toEqual [editor1, editor2]
|
||||
|
||||
describe "when replacing a pending item which is the last item in a second pane", ->
|
||||
it "does not destory the pane even if core.destroyEmptyPanes is on", ->
|
||||
atom.config.set('core.destroyEmptyPanes', true)
|
||||
editor1 = null
|
||||
editor2 = null
|
||||
leftPane = atom.workspace.getActivePane()
|
||||
rightPane = null
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('sample.js', pending: true, split: 'right').then (o) ->
|
||||
editor1 = o
|
||||
rightPane = atom.workspace.getActivePane()
|
||||
spyOn rightPane, "destroyed"
|
||||
|
||||
runs ->
|
||||
expect(leftPane).not.toBe rightPane
|
||||
expect(atom.workspace.getActivePane()).toBe rightPane
|
||||
expect(atom.workspace.getActivePane().getItems().length).toBe 1
|
||||
expect(rightPane.getPendingItem()).toBe editor1
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('sample.txt', pending: true).then (o) ->
|
||||
editor2 = o
|
||||
|
||||
runs ->
|
||||
expect(rightPane.getPendingItem()).toBe editor2
|
||||
expect(rightPane.destroyed.callCount).toBe 0
|
||||
|
||||
describe "::reopenItem()", ->
|
||||
it "opens the uri associated with the last closed pane that isn't currently open", ->
|
||||
pane = workspace.getActivePane()
|
||||
@@ -1532,3 +1598,15 @@ describe "Workspace", ->
|
||||
|
||||
atom.workspace.closeActivePaneItemOrEmptyPaneOrWindow()
|
||||
expect(atom.close).toHaveBeenCalled()
|
||||
|
||||
describe "when the core.allowPendingPaneItems option is falsey", ->
|
||||
it "does not open item with `pending: true` option as pending", ->
|
||||
pane = null
|
||||
atom.config.set('core.allowPendingPaneItems', false)
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('sample.js', pending: true).then ->
|
||||
pane = atom.workspace.getActivePane()
|
||||
|
||||
runs ->
|
||||
expect(pane.getPendingItem()).toBeFalsy()
|
||||
|
||||
Reference in New Issue
Block a user