Merge pull request #18896 from rafeca/add-linting-to-specs

Add linter to spec/ folder
This commit is contained in:
Rafael Oleza
2019-02-28 20:39:33 +01:00
committed by GitHub
58 changed files with 12555 additions and 5688 deletions

View File

@@ -6,29 +6,46 @@ const path = require('path')
const CONFIG = require('../config')
module.exports = function () {
module.exports = async function () {
const globPathsToLint = [
path.join(CONFIG.repositoryRootPath, 'exports', '**', '*.js'),
path.join(CONFIG.repositoryRootPath, 'packages', '**', '*.js'),
path.join(CONFIG.repositoryRootPath, 'script', '**', '*.js'),
path.join(CONFIG.repositoryRootPath, 'spec', '**', '*.js'),
path.join(CONFIG.repositoryRootPath, 'src', '**', '*.js'),
path.join(CONFIG.repositoryRootPath, 'static', '*.js')
]
return expandGlobPaths(globPathsToLint).then((paths) => {
return new Promise((resolve, reject) => {
standard.lintFiles(paths, (error, lintOutput) => {
if (error) {
reject(error)
} else {
const errors = []
for (let result of lintOutput.results) {
for (let message of result.messages) {
errors.push({path: result.filePath, lineNumber: message.line, message: message.message, rule: message.ruleId})
}
const globPathsToIgnore = [
path.join(CONFIG.repositoryRootPath, 'spec', 'fixtures', '**', '*.js')
]
const [includePaths, excludePaths] = await Promise.all([
expandGlobPaths(globPathsToLint),
expandGlobPaths(globPathsToIgnore)
])
const paths = includePaths.filter(
myPath => !excludePaths.includes(myPath)
)
return new Promise((resolve, reject) => {
standard.lintFiles(paths, (error, lintOutput) => {
if (error) {
reject(error)
} else {
const errors = []
for (let result of lintOutput.results) {
for (let message of result.messages) {
errors.push({
path: result.filePath,
lineNumber: message.line,
message: message.message,
rule: message.ruleId
})
}
resolve(errors)
}
})
resolve(errors)
}
})
})
}

View File

@@ -1,14 +1,14 @@
/** @babel */
import {it, fit, ffit, fffit, beforeEach, afterEach} from './async-spec-helpers'
import { it } from './async-spec-helpers'
import ApplicationDelegate from '../src/application-delegate'
describe('ApplicationDelegate', function () {
describe('set/getTemporaryWindowState', function () {
it('can serialize object trees containing redundant child object references', async function () {
const applicationDelegate = new ApplicationDelegate()
const childObject = {c: 1}
const sentObject = {a: childObject, b: childObject}
const childObject = { c: 1 }
const sentObject = { a: childObject, b: childObject }
await applicationDelegate.setTemporaryWindowState(sentObject)
const receivedObject = await applicationDelegate.getTemporaryWindowState()

View File

@@ -32,7 +32,10 @@ function afterEach (fn) {
}
})
async function conditionPromise (condition, description = 'anonymous condition') {
async function conditionPromise (
condition,
description = 'anonymous condition'
) {
const startTime = Date.now()
while (true) {

View File

@@ -1,5 +1,9 @@
const {it, fit, ffit, beforeEach, afterEach, conditionPromise} = require('./async-spec-helpers')
const _ = require('underscore-plus')
const {
it,
beforeEach,
afterEach,
conditionPromise
} = require('./async-spec-helpers')
const fs = require('fs')
const path = require('path')
const temp = require('temp').track()
@@ -15,26 +19,26 @@ describe('AtomEnvironment', () => {
describe('window sizing methods', () => {
describe('::getPosition and ::setPosition', () => {
let originalPosition = null
beforeEach(() => originalPosition = atom.getPosition())
beforeEach(() => (originalPosition = atom.getPosition()))
afterEach(() => atom.setPosition(originalPosition.x, originalPosition.y))
it('sets the position of the window, and can retrieve the position just set', () => {
atom.setPosition(22, 45)
expect(atom.getPosition()).toEqual({x: 22, y: 45})
expect(atom.getPosition()).toEqual({ x: 22, y: 45 })
})
})
describe('::getSize and ::setSize', () => {
let originalSize = null
beforeEach(() => originalSize = atom.getSize())
beforeEach(() => (originalSize = atom.getSize()))
afterEach(() => atom.setSize(originalSize.width, originalSize.height))
it('sets the size of the window, and can retrieve the size just set', async () => {
const newWidth = originalSize.width - 12
const newHeight = originalSize.height - 23
await atom.setSize(newWidth, newHeight)
expect(atom.getSize()).toEqual({width: newWidth, height: newHeight})
expect(atom.getSize()).toEqual({ width: newWidth, height: newHeight })
})
})
})
@@ -67,9 +71,9 @@ describe('AtomEnvironment', () => {
it('will open the dev tools when an error is triggered', async () => {
try {
a + 1
a + 1 // eslint-disable-line no-undef
} catch (e) {
window.onerror.call(window, e.toString(), 'abc', 2, 3, e)
window.onerror(e.toString(), 'abc', 2, 3, e)
}
await devToolsPromise
@@ -88,10 +92,10 @@ describe('AtomEnvironment', () => {
let error = null
atom.onWillThrowError(willThrowSpy)
try {
a + 1
a + 1 // eslint-disable-line no-undef
} catch (e) {
error = e
window.onerror.call(window, e.toString(), 'abc', 2, 3, e)
window.onerror(e.toString(), 'abc', 2, 3, e)
}
delete willThrowSpy.mostRecentCall.args[0].preventDefault
@@ -109,9 +113,9 @@ describe('AtomEnvironment', () => {
atom.onWillThrowError(willThrowSpy)
try {
a + 1
a + 1 // eslint-disable-line no-undef
} catch (e) {
window.onerror.call(window, e.toString(), 'abc', 2, 3, e)
window.onerror(e.toString(), 'abc', 2, 3, e)
}
expect(willThrowSpy).toHaveBeenCalled()
@@ -122,16 +126,16 @@ describe('AtomEnvironment', () => {
describe('::onDidThrowError', () => {
let didThrowSpy = null
beforeEach(() => didThrowSpy = jasmine.createSpy())
beforeEach(() => (didThrowSpy = jasmine.createSpy()))
it('is called when there is an error', () => {
let error = null
atom.onDidThrowError(didThrowSpy)
try {
a + 1
a + 1 // eslint-disable-line no-undef
} catch (e) {
error = e
window.onerror.call(window, e.toString(), 'abc', 2, 3, e)
window.onerror(e.toString(), 'abc', 2, 3, e)
}
expect(didThrowSpy).toHaveBeenCalledWith({
message: error.toString(),
@@ -165,22 +169,24 @@ describe('AtomEnvironment', () => {
describe('if passed a callback function', () => {
it("calls the callback with the assertion failure's error object", () => {
let error = null
atom.assert(false, 'a == b', e => error = e)
atom.assert(false, 'a == b', e => (error = e))
expect(error).toBe(errors[0])
})
})
describe('if passed metadata', () => {
it("assigns the metadata on the assertion failure's error object", () => {
atom.assert(false, 'a == b', {foo: 'bar'})
expect(errors[0].metadata).toEqual({foo: 'bar'})
atom.assert(false, 'a == b', { foo: 'bar' })
expect(errors[0].metadata).toEqual({ foo: 'bar' })
})
})
describe('when Atom has been built from source', () => {
it('throws an error', () => {
atom.isReleasedVersion.andReturn(false)
expect(() => atom.assert(false, 'testing')).toThrow('Assertion failed: testing')
expect(() => atom.assert(false, 'testing')).toThrow(
'Assertion failed: testing'
)
})
})
})
@@ -195,9 +201,9 @@ describe('AtomEnvironment', () => {
})
describe('saving and loading', () => {
beforeEach(() => atom.enablePersistence = true)
beforeEach(() => (atom.enablePersistence = true))
afterEach(() => atom.enablePersistence = false)
afterEach(() => (atom.enablePersistence = false))
it('selects the state based on the current project paths', async () => {
jasmine.useRealClock()
@@ -210,7 +216,7 @@ describe('AtomEnvironment', () => {
})
spyOn(atom, 'getLoadSettings').andCallFake(() => loadSettings)
spyOn(atom, 'serialize').andReturn({stuff: 'cool'})
spyOn(atom, 'serialize').andReturn({ stuff: 'cool' })
atom.project.setPaths([dir1, dir2])
@@ -221,7 +227,7 @@ describe('AtomEnvironment', () => {
expect(await atom.loadState()).toBeFalsy()
loadSettings.initialPaths = [dir2, dir1]
expect(await atom.loadState()).toEqual({stuff: 'cool'})
expect(await atom.loadState()).toEqual({ stuff: 'cool' })
})
it('saves state when the CPU is idle after a keydown or mousedown event', () => {
@@ -231,7 +237,9 @@ describe('AtomEnvironment', () => {
const idleCallbacks = []
atomEnv.initialize({
window: {
requestIdleCallback (callback) { idleCallbacks.push(callback) },
requestIdleCallback (callback) {
idleCallbacks.push(callback)
},
addEventListener () {},
removeEventListener () {}
},
@@ -244,16 +252,16 @@ describe('AtomEnvironment', () => {
atomEnv.document.dispatchEvent(keydown)
advanceClock(atomEnv.saveStateDebounceInterval)
idleCallbacks.shift()()
expect(atomEnv.saveState).toHaveBeenCalledWith({isUnloading: false})
expect(atomEnv.saveState).not.toHaveBeenCalledWith({isUnloading: true})
expect(atomEnv.saveState).toHaveBeenCalledWith({ isUnloading: false })
expect(atomEnv.saveState).not.toHaveBeenCalledWith({ isUnloading: true })
atomEnv.saveState.reset()
const mousedown = new MouseEvent('mousedown')
atomEnv.document.dispatchEvent(mousedown)
advanceClock(atomEnv.saveStateDebounceInterval)
idleCallbacks.shift()()
expect(atomEnv.saveState).toHaveBeenCalledWith({isUnloading: false})
expect(atomEnv.saveState).not.toHaveBeenCalledWith({isUnloading: true})
expect(atomEnv.saveState).toHaveBeenCalledWith({ isUnloading: false })
expect(atomEnv.saveState).not.toHaveBeenCalledWith({ isUnloading: true })
atomEnv.destroy()
})
@@ -265,7 +273,9 @@ describe('AtomEnvironment', () => {
const idleCallbacks = []
atomEnv.initialize({
window: {
requestIdleCallback (callback) { idleCallbacks.push(callback) },
requestIdleCallback (callback) {
idleCallbacks.push(callback)
},
addEventListener () {},
removeEventListener () {}
},
@@ -278,7 +288,7 @@ describe('AtomEnvironment', () => {
atomEnv.document.dispatchEvent(mousedown)
expect(atomEnv.saveState).not.toHaveBeenCalled()
await atomEnv.prepareToUnloadEditorWindow()
expect(atomEnv.saveState).toHaveBeenCalledWith({isUnloading: true})
expect(atomEnv.saveState).toHaveBeenCalledWith({ isUnloading: true })
advanceClock(atomEnv.saveStateDebounceInterval)
idleCallbacks.shift()()
@@ -294,11 +304,13 @@ describe('AtomEnvironment', () => {
})
it('serializes the project state with all the options supplied in saveState', async () => {
spyOn(atom.project, 'serialize').andReturn({foo: 42})
spyOn(atom.project, 'serialize').andReturn({ foo: 42 })
await atom.saveState({anyOption: 'any option'})
await atom.saveState({ anyOption: 'any option' })
expect(atom.project.serialize.calls.length).toBe(1)
expect(atom.project.serialize.mostRecentCall.args[0]).toEqual({anyOption: 'any option'})
expect(atom.project.serialize.mostRecentCall.args[0]).toEqual({
anyOption: 'any option'
})
})
it('serializes the text editor registry', async () => {
@@ -309,20 +321,22 @@ describe('AtomEnvironment', () => {
const atom2 = new AtomEnvironment({
applicationDelegate: atom.applicationDelegate,
window: document.createElement('div'),
document: Object.assign(
document.createElement('div'),
{
body: document.createElement('div'),
head: document.createElement('div')
}
)
document: Object.assign(document.createElement('div'), {
body: document.createElement('div'),
head: document.createElement('div')
})
})
atom2.initialize({document, window})
atom2.initialize({ document, window })
await atom2.deserialize(atom.serialize())
await atom2.packages.activatePackage('language-text')
const editor2 = atom2.workspace.getActiveTextEditor()
expect(editor2.getBuffer().getLanguageMode().getLanguageId()).toBe('text.plain')
expect(
editor2
.getBuffer()
.getLanguageMode()
.getLanguageId()
).toBe('text.plain')
atom2.destroy()
})
@@ -335,10 +349,13 @@ describe('AtomEnvironment', () => {
})
spyOn(atom.notifications, 'addError')
await atom.deserialize({project: 'should work'})
expect(atom.notifications.addError).toHaveBeenCalledWith('Unable to open project directory', {
description: 'Project directory `/foo` is no longer on disk.'
})
await atom.deserialize({ project: 'should work' })
expect(atom.notifications.addError).toHaveBeenCalledWith(
'Unable to open project directory',
{
description: 'Project directory `/foo` is no longer on disk.'
}
)
})
it('accumulates and reports two errors with one notification', async () => {
@@ -349,10 +366,14 @@ describe('AtomEnvironment', () => {
})
spyOn(atom.notifications, 'addError')
await atom.deserialize({project: 'should work'})
expect(atom.notifications.addError).toHaveBeenCalledWith('Unable to open 2 project directories', {
description: 'Project directories `/foo` and `/wat` are no longer on disk.'
})
await atom.deserialize({ project: 'should work' })
expect(atom.notifications.addError).toHaveBeenCalledWith(
'Unable to open 2 project directories',
{
description:
'Project directories `/foo` and `/wat` are no longer on disk.'
}
)
})
it('accumulates and reports three+ errors with one notification', async () => {
@@ -363,17 +384,23 @@ describe('AtomEnvironment', () => {
})
spyOn(atom.notifications, 'addError')
await atom.deserialize({project: 'should work'})
expect(atom.notifications.addError).toHaveBeenCalledWith('Unable to open 4 project directories', {
description: 'Project directories `/foo`, `/wat`, `/stuff`, and `/things` are no longer on disk.'
})
await atom.deserialize({ project: 'should work' })
expect(atom.notifications.addError).toHaveBeenCalledWith(
'Unable to open 4 project directories',
{
description:
'Project directories `/foo`, `/wat`, `/stuff`, and `/things` are no longer on disk.'
}
)
})
})
})
describe('openInitialEmptyEditorIfNecessary', () => {
describe('when there are no paths set', () => {
beforeEach(() => spyOn(atom, 'getLoadSettings').andReturn({initialPaths: []}))
beforeEach(() =>
spyOn(atom, 'getLoadSettings').andReturn({ initialPaths: [] })
)
it('opens an empty buffer', () => {
spyOn(atom.workspace, 'open')
@@ -396,7 +423,9 @@ describe('AtomEnvironment', () => {
describe('when the project has a path', () => {
beforeEach(() => {
spyOn(atom, 'getLoadSettings').andReturn({initialPaths: ['something']})
spyOn(atom, 'getLoadSettings').andReturn({
initialPaths: ['something']
})
spyOn(atom.workspace, 'open')
})
@@ -410,7 +439,6 @@ describe('AtomEnvironment', () => {
describe('adding a project folder', () => {
it('does nothing if the user dismisses the file picker', () => {
const initialPaths = atom.project.getPaths()
const tempDirectory = temp.mkdirSync('a-new-directory')
spyOn(atom, 'pickFolder').andCallFake(callback => callback(null))
atom.addProjectFolder()
expect(atom.project.getPaths()).toEqual(initialPaths)
@@ -423,9 +451,11 @@ describe('AtomEnvironment', () => {
})
it('adds the selected folder to the project', async () => {
const initialPaths = atom.project.setPaths([])
atom.project.setPaths([])
const tempDirectory = temp.mkdirSync('a-new-directory')
spyOn(atom, 'pickFolder').andCallFake(callback => callback([tempDirectory]))
spyOn(atom, 'pickFolder').andCallFake(callback =>
callback([tempDirectory])
)
await atom.addProjectFolder()
expect(atom.project.getPaths()).toEqual([tempDirectory])
expect(atom.attemptRestoreProjectStateForPaths).not.toHaveBeenCalled()
@@ -437,7 +467,9 @@ describe('AtomEnvironment', () => {
beforeEach(() => {
spyOn(atom, 'getStateKey').andCallFake(dirs => dirs.join(':'))
spyOn(atom, 'loadState').andCallFake(async (key) => key === __dirname ? state : null)
spyOn(atom, 'loadState').andCallFake(async key =>
key === __dirname ? state : null
)
spyOn(atom, 'attemptRestoreProjectStateForPaths')
spyOn(atom, 'pickFolder').andCallFake(callback => callback([__dirname]))
atom.project.setPaths([])
@@ -446,7 +478,10 @@ describe('AtomEnvironment', () => {
describe('when there are no project folders', () => {
it('attempts to restore the project state', async () => {
await atom.addProjectFolder()
expect(atom.attemptRestoreProjectStateForPaths).toHaveBeenCalledWith(state, [__dirname])
expect(atom.attemptRestoreProjectStateForPaths).toHaveBeenCalledWith(
state,
[__dirname]
)
expect(atom.project.getPaths()).toEqual([])
})
})
@@ -481,7 +516,9 @@ describe('AtomEnvironment', () => {
fs.writeFileSync(filePath2, 'def')
fs.writeFileSync(filePath3, 'ghi')
const env1 = new AtomEnvironment({applicationDelegate: atom.applicationDelegate})
const env1 = new AtomEnvironment({
applicationDelegate: atom.applicationDelegate
})
env1.project.setPaths([projectPath])
await env1.workspace.open(filePath1)
await env1.workspace.open(filePath2)
@@ -489,8 +526,14 @@ describe('AtomEnvironment', () => {
const env1State = env1.serialize()
env1.destroy()
const env2 = new AtomEnvironment({applicationDelegate: atom.applicationDelegate})
await env2.attemptRestoreProjectStateForPaths(env1State, [projectPath], [filePath2])
const env2 = new AtomEnvironment({
applicationDelegate: atom.applicationDelegate
})
await env2.attemptRestoreProjectStateForPaths(
env1State,
[projectPath],
[filePath2]
)
const restoredURIs = env2.workspace.getPaneItems().map(p => p.getURI())
expect(restoredURIs).toEqual([filePath1, filePath2, filePath3])
env2.destroy()
@@ -500,12 +543,18 @@ describe('AtomEnvironment', () => {
it("doesn't prompt the user to restore state", () => {
const dock = atom.workspace.getLeftDock()
dock.getActivePane().addItem({
getTitle () { return 'title' },
getTitle () {
return 'title'
},
element: document.createElement('div')
})
const state = {}
spyOn(atom, 'confirm')
atom.attemptRestoreProjectStateForPaths(state, [__dirname], [__filename])
atom.attemptRestoreProjectStateForPaths(
state,
[__dirname],
[__filename]
)
expect(atom.confirm).not.toHaveBeenCalled()
})
})
@@ -527,7 +576,11 @@ describe('AtomEnvironment', () => {
spyOn(atom.project, 'addPath')
spyOn(atom.workspace, 'open')
const state = Symbol()
atom.attemptRestoreProjectStateForPaths(state, [__dirname], [__filename])
atom.attemptRestoreProjectStateForPaths(
state,
[__dirname],
[__filename]
)
expect(atom.confirm).toHaveBeenCalled()
})
})
@@ -539,7 +592,11 @@ describe('AtomEnvironment', () => {
spyOn(atom.workspace, 'open')
const state = Symbol()
atom.attemptRestoreProjectStateForPaths(state, [__dirname], [__filename])
atom.attemptRestoreProjectStateForPaths(
state,
[__dirname],
[__filename]
)
expect(atom.confirm).toHaveBeenCalled()
await conditionPromise(() => atom.project.addPath.callCount === 1)
@@ -554,7 +611,11 @@ describe('AtomEnvironment', () => {
spyOn(atom, 'open')
const state = Symbol()
atom.attemptRestoreProjectStateForPaths(state, [__dirname], [__filename])
atom.attemptRestoreProjectStateForPaths(
state,
[__dirname],
[__filename]
)
expect(atom.confirm).toHaveBeenCalled()
await conditionPromise(() => atom.open.callCount === 1)
expect(atom.open).toHaveBeenCalledWith({
@@ -571,8 +632,16 @@ describe('AtomEnvironment', () => {
it('saves the BlobStore so it can be loaded after reload', () => {
const configDirPath = temp.mkdirSync('atom-spec-environment')
const fakeBlobStore = jasmine.createSpyObj('blob store', ['save'])
const atomEnvironment = new AtomEnvironment({applicationDelegate: atom.applicationDelegate, enablePersistence: true})
atomEnvironment.initialize({configDirPath, blobStore: fakeBlobStore, window, document})
const atomEnvironment = new AtomEnvironment({
applicationDelegate: atom.applicationDelegate,
enablePersistence: true
})
atomEnvironment.initialize({
configDirPath,
blobStore: fakeBlobStore,
window,
document
})
atomEnvironment.unloadEditorWindow()
@@ -584,16 +653,19 @@ describe('AtomEnvironment', () => {
describe('::destroy()', () => {
it('does not throw exceptions when unsubscribing from ipc events (regression)', async () => {
const configDirPath = temp.mkdirSync('atom-spec-environment')
const fakeDocument = {
addEventListener () {},
removeEventListener () {},
head: document.createElement('head'),
body: document.createElement('body')
}
const atomEnvironment = new AtomEnvironment({applicationDelegate: atom.applicationDelegate})
atomEnvironment.initialize({window, document: fakeDocument})
spyOn(atomEnvironment.packages, 'loadPackages').andReturn(Promise.resolve())
const atomEnvironment = new AtomEnvironment({
applicationDelegate: atom.applicationDelegate
})
atomEnvironment.initialize({ window, document: fakeDocument })
spyOn(atomEnvironment.packages, 'loadPackages').andReturn(
Promise.resolve()
)
spyOn(atomEnvironment.packages, 'activate').andReturn(Promise.resolve())
spyOn(atomEnvironment, 'displayWindow').andReturn(Promise.resolve())
await atomEnvironment.startEditorWindow()
@@ -606,17 +678,21 @@ describe('AtomEnvironment', () => {
let atomEnvironment, envLoaded, spy
beforeEach(() => {
let resolve = null
const promise = new Promise((r) => { resolve = r })
let resolvePromise = null
const promise = new Promise(resolve => {
resolvePromise = resolve
})
envLoaded = () => {
resolve()
resolvePromise()
return promise
}
atomEnvironment = new AtomEnvironment({
applicationDelegate: atom.applicationDelegate,
updateProcessEnv () { return promise }
updateProcessEnv () {
return promise
}
})
atomEnvironment.initialize({window, document})
atomEnvironment.initialize({ window, document })
spy = jasmine.createSpy()
})
@@ -650,23 +726,30 @@ describe('AtomEnvironment', () => {
describe('when the opened path exists', () => {
it('opens a file', async () => {
const pathToOpen = __filename
await atom.openLocations([{pathToOpen}])
await atom.openLocations([{ pathToOpen }])
expect(atom.project.getPaths()).toEqual([])
})
it('opens a directory as a project folder', async () => {
const pathToOpen = __dirname
await atom.openLocations([{pathToOpen}])
expect(atom.workspace.getTextEditors().map(e => e.getPath())).toEqual([])
await atom.openLocations([{ pathToOpen }])
expect(atom.workspace.getTextEditors().map(e => e.getPath())).toEqual(
[]
)
expect(atom.project.getPaths()).toEqual([pathToOpen])
})
})
describe('when the opened path does not exist', () => {
it('opens it as a new file', async () => {
const pathToOpen = path.join(__dirname, 'this-path-does-not-exist.txt')
await atom.openLocations([{pathToOpen}])
expect(atom.workspace.getTextEditors().map(e => e.getPath())).toEqual([pathToOpen])
const pathToOpen = path.join(
__dirname,
'this-path-does-not-exist.txt'
)
await atom.openLocations([{ pathToOpen }])
expect(atom.workspace.getTextEditors().map(e => e.getPath())).toEqual(
[pathToOpen]
)
expect(atom.project.getPaths()).toEqual([])
})
@@ -678,9 +761,9 @@ describe('AtomEnvironment', () => {
const existingDir = path.join(__dirname, 'fixtures')
await atom.openLocations([
{pathToOpen: nonExistent, mustBeDirectory: true},
{pathToOpen: existingFile, mustBeDirectory: true},
{pathToOpen: existingDir, mustBeDirectory: true}
{ pathToOpen: nonExistent, mustBeDirectory: true },
{ pathToOpen: existingFile, mustBeDirectory: true },
{ pathToOpen: existingDir, mustBeDirectory: true }
])
expect(atom.workspace.getTextEditors()).toEqual([])
@@ -688,7 +771,9 @@ describe('AtomEnvironment', () => {
expect(atom.notifications.addWarning).toHaveBeenCalledWith(
'Unable to open project folders',
{description: `The directories \`${nonExistent}\` and \`${existingFile}\` do not exist.`}
{
description: `The directories \`${nonExistent}\` and \`${existingFile}\` do not exist.`
}
)
})
})
@@ -697,15 +782,23 @@ describe('AtomEnvironment', () => {
let serviceDisposable
beforeEach(() => {
serviceDisposable = atom.packages.serviceHub.provide('atom.directory-provider', '0.1.0', {
directoryForURISync (uri) {
if (uri.startsWith('remote://')) {
return { getPath() { return uri } }
} else {
return null
serviceDisposable = atom.packages.serviceHub.provide(
'atom.directory-provider',
'0.1.0',
{
directoryForURISync (uri) {
if (uri.startsWith('remote://')) {
return {
getPath () {
return uri
}
}
} else {
return null
}
}
}
})
)
waitsFor(() => atom.project.directoryProviders.length > 0)
})
@@ -717,7 +810,7 @@ describe('AtomEnvironment', () => {
it("adds it to the project's paths as is", async () => {
const pathToOpen = 'remote://server:7644/some/dir/path'
spyOn(atom.project, 'addPath')
await atom.openLocations([{pathToOpen}])
await atom.openLocations([{ pathToOpen }])
expect(atom.project.addPath).toHaveBeenCalledWith(pathToOpen)
})
})
@@ -729,7 +822,11 @@ describe('AtomEnvironment', () => {
beforeEach(() => {
spyOn(atom, 'getStateKey').andCallFake(dirs => dirs.join(':'))
spyOn(atom, 'loadState').andCallFake(function (key) {
if (key === __dirname) { return Promise.resolve(state) } else { return Promise.resolve(null) }
if (key === __dirname) {
return Promise.resolve(state)
} else {
return Promise.resolve(null)
}
})
spyOn(atom, 'attemptRestoreProjectStateForPaths')
})
@@ -737,8 +834,12 @@ describe('AtomEnvironment', () => {
describe('when there are no project folders', () => {
it('attempts to restore the project state', async () => {
const pathToOpen = __dirname
await atom.openLocations([{pathToOpen}])
expect(atom.attemptRestoreProjectStateForPaths).toHaveBeenCalledWith(state, [pathToOpen], [])
await atom.openLocations([{ pathToOpen }])
expect(atom.attemptRestoreProjectStateForPaths).toHaveBeenCalledWith(
state,
[pathToOpen],
[]
)
expect(atom.project.getPaths()).toEqual([])
})
@@ -755,17 +856,28 @@ describe('AtomEnvironment', () => {
})
await atom.openLocations([
{pathToOpen: existingDir},
{pathToOpen: missingDir, mustBeDirectory: true}
{ pathToOpen: existingDir },
{ pathToOpen: missingDir, mustBeDirectory: true }
])
expect(atom.attemptRestoreProjectStateForPaths).toHaveBeenCalledWith(state, [existingDir], [])
expect(atom.attemptRestoreProjectStateForPaths).toHaveBeenCalledWith(
state,
[existingDir],
[]
)
expect(atom.project.getPaths(), [existingDir])
})
it('opens the specified files', async () => {
await atom.openLocations([{pathToOpen: __dirname}, {pathToOpen: __filename}])
expect(atom.attemptRestoreProjectStateForPaths).toHaveBeenCalledWith(state, [__dirname], [__filename])
await atom.openLocations([
{ pathToOpen: __dirname },
{ pathToOpen: __filename }
])
expect(atom.attemptRestoreProjectStateForPaths).toHaveBeenCalledWith(
state,
[__dirname],
[__filename]
)
expect(atom.project.getPaths()).toEqual([])
})
})
@@ -775,7 +887,7 @@ describe('AtomEnvironment', () => {
it('does not attempt to restore the project state, instead adding the project paths', async () => {
const pathToOpen = path.join(__dirname, 'fixtures')
await atom.openLocations([{pathToOpen, forceAddToWindow: true}])
await atom.openLocations([{ pathToOpen, forceAddToWindow: true }])
expect(atom.attemptRestoreProjectStateForPaths).not.toHaveBeenCalled()
expect(atom.project.getPaths()).toEqual([__dirname, pathToOpen])
})
@@ -783,8 +895,10 @@ describe('AtomEnvironment', () => {
it('opens the specified files', async () => {
const pathToOpen = path.join(__dirname, 'fixtures')
const fileToOpen = path.join(pathToOpen, 'michelle-is-awesome.txt')
await atom.openLocations([{pathToOpen}, {pathToOpen: fileToOpen}])
expect(atom.attemptRestoreProjectStateForPaths).not.toHaveBeenCalledWith(state, [pathToOpen], [fileToOpen])
await atom.openLocations([{ pathToOpen }, { pathToOpen: fileToOpen }])
expect(
atom.attemptRestoreProjectStateForPaths
).not.toHaveBeenCalledWith(state, [pathToOpen], [fileToOpen])
expect(atom.project.getPaths()).toEqual([__dirname, pathToOpen])
})
})

View File

@@ -1,14 +1,18 @@
/** @babel */
import {it, fit, ffit, fffit, beforeEach, afterEach} from './async-spec-helpers'
import {app} from 'remote'
import { it, beforeEach, afterEach } from './async-spec-helpers'
import { app } from 'remote'
import atomPaths from '../src/atom-paths'
import fs from 'fs-plus'
import path from 'path'
const temp = require('temp').track()
describe("AtomPaths", () => {
const portableAtomHomePath = path.join(atomPaths.getAppDirectory(), '..', '.atom')
describe('AtomPaths', () => {
const portableAtomHomePath = path.join(
atomPaths.getAppDirectory(),
'..',
'.atom'
)
afterEach(() => {
atomPaths.setAtomHome(app.getPath('home'))
@@ -18,8 +22,9 @@ describe("AtomPaths", () => {
describe('when a portable .atom folder exists', () => {
beforeEach(() => {
delete process.env.ATOM_HOME
if (!fs.existsSync(portableAtomHomePath))
if (!fs.existsSync(portableAtomHomePath)) {
fs.mkdirSync(portableAtomHomePath)
}
})
afterEach(() => {
@@ -69,6 +74,7 @@ describe("AtomPaths", () => {
})
describe('setUserData', () => {
let tempAtomConfigPath = null
let tempAtomHomePath = null
let electronUserDataPath = null
let defaultElectronUserDataPath = null

View File

@@ -1,5 +1,5 @@
const AutoUpdateManager = require('../src/auto-update-manager')
const {remote} = require('electron')
const { remote } = require('electron')
const electronAutoUpdater = remote.require('electron').autoUpdater
describe('AutoUpdateManager (renderer)', () => {
@@ -8,7 +8,9 @@ describe('AutoUpdateManager (renderer)', () => {
let autoUpdateManager
beforeEach(() => {
autoUpdateManager = new AutoUpdateManager({applicationDelegate: atom.applicationDelegate})
autoUpdateManager = new AutoUpdateManager({
applicationDelegate: atom.applicationDelegate
})
autoUpdateManager.initialize()
})
@@ -69,14 +71,16 @@ describe('AutoUpdateManager (renderer)', () => {
autoUpdateManager.onUpdateError(spy)
electronAutoUpdater.emit('error', {}, 'an error message')
waitsFor(() => spy.callCount === 1)
runs(() => expect(autoUpdateManager.getErrorMessage()).toBe('an error message'))
runs(() =>
expect(autoUpdateManager.getErrorMessage()).toBe('an error message')
)
})
})
describe('::platformSupportsUpdates', () => {
let state, releaseChannel
it('returns true on macOS and Windows when in stable', () => {
spyOn(autoUpdateManager, 'getState').andCallFake(() => state)
spyOn(autoUpdateManager, 'getState').andCallFake(() => state)
spyOn(atom, 'getReleaseChannel').andCallFake(() => releaseChannel)
state = 'idle'

View File

@@ -1,7 +1,11 @@
const path = require('path')
const fs = require('fs-plus')
const temp = require('temp').track()
const {it, fit, ffit, fffit, beforeEach, afterEach} = require('./async-spec-helpers');
const {
it,
beforeEach,
afterEach
} = require('./async-spec-helpers')
const CommandInstaller = require('../src/command-installer')
describe('CommandInstaller on #darwin', () => {
@@ -12,14 +16,25 @@ describe('CommandInstaller on #darwin', () => {
resourcesPath = temp.mkdirSync('atom-app')
atomBinPath = path.join(resourcesPath, 'app', 'atom.sh')
apmBinPath = path.join(resourcesPath, 'app', 'apm', 'node_modules', '.bin', 'apm')
apmBinPath = path.join(
resourcesPath,
'app',
'apm',
'node_modules',
'.bin',
'apm'
)
fs.writeFileSync(atomBinPath, '')
fs.writeFileSync(apmBinPath, '')
fs.chmodSync(atomBinPath, '755')
fs.chmodSync(apmBinPath, '755')
spyOn(CommandInstaller.prototype, 'getResourcesDirectory').andReturn(resourcesPath)
spyOn(CommandInstaller.prototype, 'getInstallDirectory').andReturn(installationPath)
spyOn(CommandInstaller.prototype, 'getResourcesDirectory').andReturn(
resourcesPath
)
spyOn(CommandInstaller.prototype, 'getInstallDirectory').andReturn(
installationPath
)
})
afterEach(() => {
@@ -32,7 +47,9 @@ describe('CommandInstaller on #darwin', () => {
const appDelegate = jasmine.createSpyObj('appDelegate', ['confirm'])
installer = new CommandInstaller(appDelegate)
installer.initialize('2.0.2')
spyOn(installer, 'installAtomCommand').andCallFake((__, callback) => callback(new Error('an error')))
spyOn(installer, 'installAtomCommand').andCallFake((__, callback) =>
callback(new Error('an error'))
)
installer.installShellCommandsInteractively()
@@ -43,7 +60,9 @@ describe('CommandInstaller on #darwin', () => {
appDelegate.confirm.reset()
installer.installAtomCommand.andCallFake((__, callback) => callback())
spyOn(installer, 'installApmCommand').andCallFake((__, callback) => callback(new Error('another error')))
spyOn(installer, 'installApmCommand').andCallFake((__, callback) =>
callback(new Error('another error'))
)
installer.installShellCommandsInteractively()
@@ -57,8 +76,12 @@ describe('CommandInstaller on #darwin', () => {
const appDelegate = jasmine.createSpyObj('appDelegate', ['confirm'])
installer = new CommandInstaller(appDelegate)
installer.initialize('2.0.2')
spyOn(installer, 'installAtomCommand').andCallFake((__, callback) => callback(undefined, 'atom'))
spyOn(installer, 'installApmCommand').andCallFake((__, callback) => callback(undefined, 'apm'))
spyOn(installer, 'installAtomCommand').andCallFake((__, callback) =>
callback(undefined, 'atom')
)
spyOn(installer, 'installApmCommand').andCallFake((__, callback) =>
callback(undefined, 'apm')
)
installer.installShellCommandsInteractively()
@@ -81,9 +104,13 @@ describe('CommandInstaller on #darwin', () => {
waitsFor(done => {
installer.installAtomCommand(false, error => {
expect(error).toBeNull()
expect(fs.realpathSync(installedAtomPath)).toBe(fs.realpathSync(atomBinPath))
expect(fs.realpathSync(installedAtomPath)).toBe(
fs.realpathSync(atomBinPath)
)
expect(fs.isExecutableSync(installedAtomPath)).toBe(true)
expect(fs.isFileSync(path.join(installationPath, 'atom-beta'))).toBe(false)
expect(fs.isFileSync(path.join(installationPath, 'atom-beta'))).toBe(
false
)
done()
})
})
@@ -96,9 +123,13 @@ describe('CommandInstaller on #darwin', () => {
waitsFor(done => {
installer.installApmCommand(false, error => {
expect(error).toBeNull()
expect(fs.realpathSync(installedApmPath)).toBe(fs.realpathSync(apmBinPath))
expect(fs.realpathSync(installedApmPath)).toBe(
fs.realpathSync(apmBinPath)
)
expect(fs.isExecutableSync(installedApmPath)).toBeTruthy()
expect(fs.isFileSync(path.join(installationPath, 'apm-beta'))).toBe(false)
expect(fs.isFileSync(path.join(installationPath, 'apm-beta'))).toBe(
false
)
done()
})
})
@@ -118,7 +149,9 @@ describe('CommandInstaller on #darwin', () => {
waitsFor(done => {
installer.installAtomCommand(false, error => {
expect(error).toBeNull()
expect(fs.realpathSync(installedAtomPath)).toBe(fs.realpathSync(atomBinPath))
expect(fs.realpathSync(installedAtomPath)).toBe(
fs.realpathSync(atomBinPath)
)
expect(fs.isExecutableSync(installedAtomPath)).toBe(true)
expect(fs.isFileSync(path.join(installationPath, 'atom'))).toBe(false)
done()
@@ -133,7 +166,9 @@ describe('CommandInstaller on #darwin', () => {
waitsFor(done => {
installer.installApmCommand(false, error => {
expect(error).toBeNull()
expect(fs.realpathSync(installedApmPath)).toBe(fs.realpathSync(apmBinPath))
expect(fs.realpathSync(installedApmPath)).toBe(
fs.realpathSync(apmBinPath)
)
expect(fs.isExecutableSync(installedApmPath)).toBeTruthy()
expect(fs.isFileSync(path.join(installationPath, 'apm'))).toBe(false)
done()
@@ -155,7 +190,9 @@ describe('CommandInstaller on #darwin', () => {
waitsFor(done => {
installer.installAtomCommand(false, error => {
expect(error).toBeNull()
expect(fs.realpathSync(installedAtomPath)).toBe(fs.realpathSync(atomBinPath))
expect(fs.realpathSync(installedAtomPath)).toBe(
fs.realpathSync(atomBinPath)
)
expect(fs.isExecutableSync(installedAtomPath)).toBe(true)
expect(fs.isFileSync(path.join(installationPath, 'atom'))).toBe(false)
done()
@@ -170,9 +207,13 @@ describe('CommandInstaller on #darwin', () => {
waitsFor(done => {
installer.installApmCommand(false, error => {
expect(error).toBeNull()
expect(fs.realpathSync(installedApmPath)).toBe(fs.realpathSync(apmBinPath))
expect(fs.realpathSync(installedApmPath)).toBe(
fs.realpathSync(apmBinPath)
)
expect(fs.isExecutableSync(installedApmPath)).toBeTruthy()
expect(fs.isFileSync(path.join(installationPath, 'nightly'))).toBe(false)
expect(fs.isFileSync(path.join(installationPath, 'nightly'))).toBe(
false
)
done()
})
})

View File

@@ -1,289 +1,307 @@
const CommandRegistry = require('../src/command-registry');
const _ = require('underscore-plus');
const {it, fit, ffit, fffit, beforeEach, afterEach} = require('./async-spec-helpers');
const CommandRegistry = require('../src/command-registry')
const _ = require('underscore-plus')
const { it, beforeEach, afterEach } = require('./async-spec-helpers')
describe("CommandRegistry", () => {
let registry, parent, child, grandchild;
describe('CommandRegistry', () => {
let registry, parent, child, grandchild
beforeEach(() => {
parent = document.createElement("div");
child = document.createElement("div");
grandchild = document.createElement("div");
parent.classList.add('parent');
child.classList.add('child');
grandchild.classList.add('grandchild');
child.appendChild(grandchild);
parent.appendChild(child);
document.querySelector('#jasmine-content').appendChild(parent);
parent = document.createElement('div')
child = document.createElement('div')
grandchild = document.createElement('div')
parent.classList.add('parent')
child.classList.add('child')
grandchild.classList.add('grandchild')
child.appendChild(grandchild)
parent.appendChild(child)
document.querySelector('#jasmine-content').appendChild(parent)
registry = new CommandRegistry;
registry.attach(parent);
});
registry = new CommandRegistry()
registry.attach(parent)
})
afterEach(() => registry.destroy());
afterEach(() => registry.destroy())
describe("when a command event is dispatched on an element", () => {
it("invokes callbacks with selectors matching the target", () => {
let called = false;
describe('when a command event is dispatched on an element', () => {
it('invokes callbacks with selectors matching the target', () => {
let called = false
registry.add('.grandchild', 'command', function (event) {
expect(this).toBe(grandchild);
expect(event.type).toBe('command');
expect(event.eventPhase).toBe(Event.BUBBLING_PHASE);
expect(event.target).toBe(grandchild);
expect(event.currentTarget).toBe(grandchild);
called = true;
});
expect(this).toBe(grandchild)
expect(event.type).toBe('command')
expect(event.eventPhase).toBe(Event.BUBBLING_PHASE)
expect(event.target).toBe(grandchild)
expect(event.currentTarget).toBe(grandchild)
called = true
})
grandchild.dispatchEvent(new CustomEvent('command', {bubbles: true}));
expect(called).toBe(true);
});
grandchild.dispatchEvent(new CustomEvent('command', { bubbles: true }))
expect(called).toBe(true)
})
it("invokes callbacks with selectors matching ancestors of the target", () => {
const calls = [];
it('invokes callbacks with selectors matching ancestors of the target', () => {
const calls = []
registry.add('.child', 'command', function (event) {
expect(this).toBe(child);
expect(event.target).toBe(grandchild);
expect(event.currentTarget).toBe(child);
calls.push('child');
});
expect(this).toBe(child)
expect(event.target).toBe(grandchild)
expect(event.currentTarget).toBe(child)
calls.push('child')
})
registry.add('.parent', 'command', function (event) {
expect(this).toBe(parent);
expect(event.target).toBe(grandchild);
expect(event.currentTarget).toBe(parent);
calls.push('parent');
});
registry.add('.parent', 'command', function (event) {
expect(this).toBe(parent)
expect(event.target).toBe(grandchild)
expect(event.currentTarget).toBe(parent)
calls.push('parent')
})
grandchild.dispatchEvent(new CustomEvent('command', {bubbles: true}));
expect(calls).toEqual(['child', 'parent']);
});
grandchild.dispatchEvent(new CustomEvent('command', { bubbles: true }))
expect(calls).toEqual(['child', 'parent'])
})
it("invokes inline listeners prior to listeners applied via selectors", () => {
const calls = [];
registry.add('.grandchild', 'command', () => calls.push('grandchild'));
registry.add(child, 'command', () => calls.push('child-inline'));
registry.add('.child', 'command', () => calls.push('child'));
registry.add('.parent', 'command', () => calls.push('parent'));
it('invokes inline listeners prior to listeners applied via selectors', () => {
const calls = []
registry.add('.grandchild', 'command', () => calls.push('grandchild'))
registry.add(child, 'command', () => calls.push('child-inline'))
registry.add('.child', 'command', () => calls.push('child'))
registry.add('.parent', 'command', () => calls.push('parent'))
grandchild.dispatchEvent(new CustomEvent('command', {bubbles: true}));
expect(calls).toEqual(['grandchild', 'child-inline', 'child', 'parent']);
});
grandchild.dispatchEvent(new CustomEvent('command', { bubbles: true }))
expect(calls).toEqual(['grandchild', 'child-inline', 'child', 'parent'])
})
it("orders multiple matching listeners for an element by selector specificity", () => {
child.classList.add('foo', 'bar');
const calls = [];
it('orders multiple matching listeners for an element by selector specificity', () => {
child.classList.add('foo', 'bar')
const calls = []
registry.add('.foo.bar', 'command', () => calls.push('.foo.bar'));
registry.add('.foo', 'command', () => calls.push('.foo'));
registry.add('.bar', 'command', () => calls.push('.bar')); // specificity ties favor commands added later, like CSS
registry.add('.foo.bar', 'command', () => calls.push('.foo.bar'))
registry.add('.foo', 'command', () => calls.push('.foo'))
registry.add('.bar', 'command', () => calls.push('.bar')) // specificity ties favor commands added later, like CSS
grandchild.dispatchEvent(new CustomEvent('command', {bubbles: true}));
expect(calls).toEqual(['.foo.bar', '.bar', '.foo']);
});
grandchild.dispatchEvent(new CustomEvent('command', { bubbles: true }))
expect(calls).toEqual(['.foo.bar', '.bar', '.foo'])
})
it("orders inline listeners by reverse registration order", () => {
const calls = [];
registry.add(child, 'command', () => calls.push('child1'));
registry.add(child, 'command', () => calls.push('child2'));
child.dispatchEvent(new CustomEvent('command', {bubbles: true}));
expect(calls).toEqual(['child2', 'child1']);
});
it('orders inline listeners by reverse registration order', () => {
const calls = []
registry.add(child, 'command', () => calls.push('child1'))
registry.add(child, 'command', () => calls.push('child2'))
child.dispatchEvent(new CustomEvent('command', { bubbles: true }))
expect(calls).toEqual(['child2', 'child1'])
})
it("stops bubbling through ancestors when .stopPropagation() is called on the event", () => {
const calls = [];
it('stops bubbling through ancestors when .stopPropagation() is called on the event', () => {
const calls = []
registry.add('.parent', 'command', () => calls.push('parent'));
registry.add('.child', 'command', () => calls.push('child-2'));
registry.add('.child', 'command', (event) => {
calls.push('child-1');
event.stopPropagation();
});
registry.add('.parent', 'command', () => calls.push('parent'))
registry.add('.child', 'command', () => calls.push('child-2'))
registry.add('.child', 'command', event => {
calls.push('child-1')
event.stopPropagation()
})
const dispatchedEvent = new CustomEvent('command', {bubbles: true});
spyOn(dispatchedEvent, 'stopPropagation');
grandchild.dispatchEvent(dispatchedEvent);
expect(calls).toEqual(['child-1', 'child-2']);
expect(dispatchedEvent.stopPropagation).toHaveBeenCalled();
});
const dispatchedEvent = new CustomEvent('command', { bubbles: true })
spyOn(dispatchedEvent, 'stopPropagation')
grandchild.dispatchEvent(dispatchedEvent)
expect(calls).toEqual(['child-1', 'child-2'])
expect(dispatchedEvent.stopPropagation).toHaveBeenCalled()
})
it("stops invoking callbacks when .stopImmediatePropagation() is called on the event", () => {
const calls = [];
it('stops invoking callbacks when .stopImmediatePropagation() is called on the event', () => {
const calls = []
registry.add('.parent', 'command', () => calls.push('parent'));
registry.add('.child', 'command', () => calls.push('child-2'));
registry.add('.child', 'command', (event) => {
calls.push('child-1');
event.stopImmediatePropagation();
});
registry.add('.parent', 'command', () => calls.push('parent'))
registry.add('.child', 'command', () => calls.push('child-2'))
registry.add('.child', 'command', event => {
calls.push('child-1')
event.stopImmediatePropagation()
})
const dispatchedEvent = new CustomEvent('command', {bubbles: true});
spyOn(dispatchedEvent, 'stopImmediatePropagation');
grandchild.dispatchEvent(dispatchedEvent);
expect(calls).toEqual(['child-1']);
expect(dispatchedEvent.stopImmediatePropagation).toHaveBeenCalled();
});
const dispatchedEvent = new CustomEvent('command', { bubbles: true })
spyOn(dispatchedEvent, 'stopImmediatePropagation')
grandchild.dispatchEvent(dispatchedEvent)
expect(calls).toEqual(['child-1'])
expect(dispatchedEvent.stopImmediatePropagation).toHaveBeenCalled()
})
it("forwards .preventDefault() calls from the synthetic event to the original", () => {
registry.add('.child', 'command', event => event.preventDefault());
it('forwards .preventDefault() calls from the synthetic event to the original', () => {
registry.add('.child', 'command', event => event.preventDefault())
const dispatchedEvent = new CustomEvent('command', {bubbles: true});
spyOn(dispatchedEvent, 'preventDefault');
grandchild.dispatchEvent(dispatchedEvent);
expect(dispatchedEvent.preventDefault).toHaveBeenCalled();
});
const dispatchedEvent = new CustomEvent('command', { bubbles: true })
spyOn(dispatchedEvent, 'preventDefault')
grandchild.dispatchEvent(dispatchedEvent)
expect(dispatchedEvent.preventDefault).toHaveBeenCalled()
})
it("forwards .abortKeyBinding() calls from the synthetic event to the original", () => {
registry.add('.child', 'command', event => event.abortKeyBinding());
it('forwards .abortKeyBinding() calls from the synthetic event to the original', () => {
registry.add('.child', 'command', event => event.abortKeyBinding())
const dispatchedEvent = new CustomEvent('command', {bubbles: true});
dispatchedEvent.abortKeyBinding = jasmine.createSpy('abortKeyBinding');
grandchild.dispatchEvent(dispatchedEvent);
expect(dispatchedEvent.abortKeyBinding).toHaveBeenCalled();
});
const dispatchedEvent = new CustomEvent('command', { bubbles: true })
dispatchedEvent.abortKeyBinding = jasmine.createSpy('abortKeyBinding')
grandchild.dispatchEvent(dispatchedEvent)
expect(dispatchedEvent.abortKeyBinding).toHaveBeenCalled()
})
it("copies non-standard properties from the original event to the synthetic event", () => {
let syntheticEvent = null;
registry.add('.child', 'command', event => syntheticEvent = event);
it('copies non-standard properties from the original event to the synthetic event', () => {
let syntheticEvent = null
registry.add('.child', 'command', event => (syntheticEvent = event))
const dispatchedEvent = new CustomEvent('command', {bubbles: true});
dispatchedEvent.nonStandardProperty = 'testing';
grandchild.dispatchEvent(dispatchedEvent);
expect(syntheticEvent.nonStandardProperty).toBe('testing');
});
const dispatchedEvent = new CustomEvent('command', { bubbles: true })
dispatchedEvent.nonStandardProperty = 'testing'
grandchild.dispatchEvent(dispatchedEvent)
expect(syntheticEvent.nonStandardProperty).toBe('testing')
})
it("allows listeners to be removed via a disposable returned by ::add", () => {
let calls = [];
it('allows listeners to be removed via a disposable returned by ::add', () => {
let calls = []
const disposable1 = registry.add('.parent', 'command', () => calls.push('parent'));
const disposable2 = registry.add('.child', 'command', () => calls.push('child'));
const disposable1 = registry.add('.parent', 'command', () =>
calls.push('parent')
)
const disposable2 = registry.add('.child', 'command', () =>
calls.push('child')
)
disposable1.dispose();
grandchild.dispatchEvent(new CustomEvent('command', {bubbles: true}));
expect(calls).toEqual(['child']);
disposable1.dispose()
grandchild.dispatchEvent(new CustomEvent('command', { bubbles: true }))
expect(calls).toEqual(['child'])
calls = [];
disposable2.dispose();
grandchild.dispatchEvent(new CustomEvent('command', {bubbles: true}));
expect(calls).toEqual([]);
});
calls = []
disposable2.dispose()
grandchild.dispatchEvent(new CustomEvent('command', { bubbles: true }))
expect(calls).toEqual([])
})
it("allows multiple commands to be registered under one selector when called with an object", () => {
let calls = [];
it('allows multiple commands to be registered under one selector when called with an object', () => {
let calls = []
const disposable = registry.add('.child', {
'command-1'() {
calls.push('command-1');
'command-1' () {
calls.push('command-1')
},
'command-2'() {
calls.push('command-2');
'command-2' () {
calls.push('command-2')
}
});
})
grandchild.dispatchEvent(new CustomEvent('command-1', {bubbles: true}));
grandchild.dispatchEvent(new CustomEvent('command-2', {bubbles: true}));
grandchild.dispatchEvent(new CustomEvent('command-1', { bubbles: true }))
grandchild.dispatchEvent(new CustomEvent('command-2', { bubbles: true }))
expect(calls).toEqual(['command-1', 'command-2']);
expect(calls).toEqual(['command-1', 'command-2'])
calls = [];
disposable.dispose();
grandchild.dispatchEvent(new CustomEvent('command-1', {bubbles: true}));
grandchild.dispatchEvent(new CustomEvent('command-2', {bubbles: true}));
expect(calls).toEqual([]);
});
calls = []
disposable.dispose()
grandchild.dispatchEvent(new CustomEvent('command-1', { bubbles: true }))
grandchild.dispatchEvent(new CustomEvent('command-2', { bubbles: true }))
expect(calls).toEqual([])
})
it("invokes callbacks registered with ::onWillDispatch and ::onDidDispatch", () => {
const sequence = [];
it('invokes callbacks registered with ::onWillDispatch and ::onDidDispatch', () => {
const sequence = []
registry.onDidDispatch(event => sequence.push(['onDidDispatch', event]));
registry.onDidDispatch(event => sequence.push(['onDidDispatch', event]))
registry.add('.grandchild', 'command', event => sequence.push(['listener', event]));
registry.add('.grandchild', 'command', event =>
sequence.push(['listener', event])
)
registry.onWillDispatch(event => sequence.push(['onWillDispatch', event]));
registry.onWillDispatch(event => sequence.push(['onWillDispatch', event]))
grandchild.dispatchEvent(new CustomEvent('command', {bubbles: true}));
grandchild.dispatchEvent(new CustomEvent('command', { bubbles: true }))
expect(sequence[0][0]).toBe('onWillDispatch');
expect(sequence[1][0]).toBe('listener');
expect(sequence[2][0]).toBe('onDidDispatch');
expect(sequence[0][0]).toBe('onWillDispatch')
expect(sequence[1][0]).toBe('listener')
expect(sequence[2][0]).toBe('onDidDispatch')
expect(sequence[0][1] === sequence[1][1] && sequence[1][1] === sequence[2][1]).toBe(true);
expect(sequence[0][1].constructor).toBe(CustomEvent);
expect(sequence[0][1].target).toBe(grandchild);
});
});
expect(
sequence[0][1] === sequence[1][1] && sequence[1][1] === sequence[2][1]
).toBe(true)
expect(sequence[0][1].constructor).toBe(CustomEvent)
expect(sequence[0][1].target).toBe(grandchild)
})
})
describe("::add(selector, commandName, callback)", () => {
it("throws an error when called with an invalid selector", () => {
const badSelector = '<>';
let addError = null;
describe('::add(selector, commandName, callback)', () => {
it('throws an error when called with an invalid selector', () => {
const badSelector = '<>'
let addError = null
try {
registry.add(badSelector, 'foo:bar', () => {});
registry.add(badSelector, 'foo:bar', () => {})
} catch (error) {
addError = error;
addError = error
}
expect(addError.message).toContain(badSelector);
});
expect(addError.message).toContain(badSelector)
})
it("throws an error when called with a null callback and selector target", () => {
const badCallback = null;
it('throws an error when called with a null callback and selector target', () => {
const badCallback = null
expect(() => {
registry.add('.selector', 'foo:bar', badCallback);
}).toThrow(new Error('Cannot register a command with a null listener.'));
});
registry.add('.selector', 'foo:bar', badCallback)
}).toThrow(new Error('Cannot register a command with a null listener.'))
})
it("throws an error when called with a null callback and object target", () => {
const badCallback = null;
it('throws an error when called with a null callback and object target', () => {
const badCallback = null
expect(() => {
registry.add(document.body, 'foo:bar', badCallback);
}).toThrow(new Error('Cannot register a command with a null listener.'));
});
registry.add(document.body, 'foo:bar', badCallback)
}).toThrow(new Error('Cannot register a command with a null listener.'))
})
it("throws an error when called with an object listener without a didDispatch method", () => {
it('throws an error when called with an object listener without a didDispatch method', () => {
const badListener = {
title: 'a listener without a didDispatch callback',
description: 'this should throw an error'
};
}
expect(() => {
registry.add(document.body, 'foo:bar', badListener);
}).toThrow(new Error('Listener must be a callback function or an object with a didDispatch method.'));
});
});
registry.add(document.body, 'foo:bar', badListener)
}).toThrow(
new Error(
'Listener must be a callback function or an object with a didDispatch method.'
)
)
})
})
describe("::findCommands({target})", () => {
it("returns command descriptors that can be invoked on the target or its ancestors", () => {
registry.add('.parent', 'namespace:command-1', () => {});
registry.add('.child', 'namespace:command-2', () => {});
registry.add('.grandchild', 'namespace:command-3', () => {});
registry.add('.grandchild.no-match', 'namespace:command-4', () => {});
describe('::findCommands({target})', () => {
it('returns command descriptors that can be invoked on the target or its ancestors', () => {
registry.add('.parent', 'namespace:command-1', () => {})
registry.add('.child', 'namespace:command-2', () => {})
registry.add('.grandchild', 'namespace:command-3', () => {})
registry.add('.grandchild.no-match', 'namespace:command-4', () => {})
registry.add(grandchild, 'namespace:inline-command-1', () => {});
registry.add(child, 'namespace:inline-command-2', () => {});
registry.add(grandchild, 'namespace:inline-command-1', () => {})
registry.add(child, 'namespace:inline-command-2', () => {})
const commands = registry.findCommands({target: grandchild});
const nonJqueryCommands = _.reject(commands, cmd => cmd.jQuery);
const commands = registry.findCommands({ target: grandchild })
const nonJqueryCommands = _.reject(commands, cmd => cmd.jQuery)
expect(nonJqueryCommands).toEqual([
{name: 'namespace:inline-command-1', displayName: 'Namespace: Inline Command 1'},
{name: 'namespace:command-3', displayName: 'Namespace: Command 3'},
{name: 'namespace:inline-command-2', displayName: 'Namespace: Inline Command 2'},
{name: 'namespace:command-2', displayName: 'Namespace: Command 2'},
{name: 'namespace:command-1', displayName: 'Namespace: Command 1'}
]);
});
{
name: 'namespace:inline-command-1',
displayName: 'Namespace: Inline Command 1'
},
{ name: 'namespace:command-3', displayName: 'Namespace: Command 3' },
{
name: 'namespace:inline-command-2',
displayName: 'Namespace: Inline Command 2'
},
{ name: 'namespace:command-2', displayName: 'Namespace: Command 2' },
{ name: 'namespace:command-1', displayName: 'Namespace: Command 1' }
])
})
it("returns command descriptors with arbitrary metadata if set in a listener object", () => {
registry.add('.grandchild', 'namespace:command-1', () => {});
it('returns command descriptors with arbitrary metadata if set in a listener object', () => {
registry.add('.grandchild', 'namespace:command-1', () => {})
registry.add('.grandchild', 'namespace:command-2', {
displayName: 'Custom Command 2',
metadata: {
some: 'other',
object: 'data'
},
didDispatch() {}
});
didDispatch () {}
})
registry.add('.grandchild', 'namespace:command-3', {
name: 'some:other:incorrect:commandname',
displayName: 'Custom Command 3',
@@ -291,10 +309,10 @@ describe("CommandRegistry", () => {
some: 'other',
object: 'data'
},
didDispatch() {}
});
didDispatch () {}
})
const commands = registry.findCommands({target: grandchild});
const commands = registry.findCommands({ target: grandchild })
expect(commands).toEqual([
{
displayName: 'Namespace: Command 1',
@@ -303,143 +321,163 @@ describe("CommandRegistry", () => {
{
displayName: 'Custom Command 2',
metadata: {
some : 'other',
object : 'data'
some: 'other',
object: 'data'
},
name: 'namespace:command-2'
},
{
displayName: 'Custom Command 3',
metadata: {
some : 'other',
object : 'data'
some: 'other',
object: 'data'
},
name: 'namespace:command-3'
}
]);
});
])
})
it("returns command descriptors with arbitrary metadata if set on a listener function", () => {
it('returns command descriptors with arbitrary metadata if set on a listener function', () => {
function listener () {}
listener.displayName = 'Custom Command 2'
listener.metadata = {
some: 'other',
object: 'data'
};
}
registry.add('.grandchild', 'namespace:command-2', listener);
const commands = registry.findCommands({target: grandchild});
registry.add('.grandchild', 'namespace:command-2', listener)
const commands = registry.findCommands({ target: grandchild })
expect(commands).toEqual([
{
displayName : 'Custom Command 2',
displayName: 'Custom Command 2',
metadata: {
some: 'other',
object: 'data'
},
name: 'namespace:command-2'
}
]);
});
});
describe("::dispatch(target, commandName)", () => {
it("simulates invocation of the given command ", () => {
let called = false;
registry.add('.grandchild', 'command', function (event) {
expect(this).toBe(grandchild);
expect(event.type).toBe('command');
expect(event.eventPhase).toBe(Event.BUBBLING_PHASE);
expect(event.target).toBe(grandchild);
expect(event.currentTarget).toBe(grandchild);
called = true;
});
registry.dispatch(grandchild, 'command');
expect(called).toBe(true);
});
it("returns a promise if any listeners matched the command", () => {
registry.add('.grandchild', 'command', () => {});
expect(registry.dispatch(grandchild, 'command').constructor.name).toBe("Promise");
expect(registry.dispatch(grandchild, 'bogus')).toBe(null);
expect(registry.dispatch(parent, 'command')).toBe(null);
});
it("returns a promise that resolves when the listeners resolve", async () => {
jasmine.useRealClock();
registry.add('.grandchild', 'command', () => 1);
registry.add('.grandchild', 'command', () => Promise.resolve(2));
registry.add('.grandchild', 'command', () => new Promise((resolve) => {
setTimeout(() => { resolve(3); }, 1);
}));
const values = await registry.dispatch(grandchild, 'command');
expect(values).toEqual([3, 2, 1]);
});
it("returns a promise that rejects when a listener is rejected", async () => {
jasmine.useRealClock();
registry.add('.grandchild', 'command', () => 1);
registry.add('.grandchild', 'command', () => Promise.resolve(2));
registry.add('.grandchild', 'command', () => new Promise((resolve, reject) => {
setTimeout(() => { reject(3); }, 1);
}));
let value;
try {
value = await registry.dispatch(grandchild, 'command');
} catch (err) {
value = err;
}
expect(value).toBe(3);
});
});
describe("::getSnapshot and ::restoreSnapshot", () =>
it("removes all command handlers except for those in the snapshot", () => {
registry.add('.parent', 'namespace:command-1', () => {});
registry.add('.child', 'namespace:command-2', () => {});
const snapshot = registry.getSnapshot();
registry.add('.grandchild', 'namespace:command-3', () => {});
expect(registry.findCommands({target: grandchild}).slice(0, 3)).toEqual([
{name: 'namespace:command-3', displayName: 'Namespace: Command 3'},
{name: 'namespace:command-2', displayName: 'Namespace: Command 2'},
{name: 'namespace:command-1', displayName: 'Namespace: Command 1'}
]);
registry.restoreSnapshot(snapshot);
expect(registry.findCommands({target: grandchild}).slice(0, 2)).toEqual([
{name: 'namespace:command-2', displayName: 'Namespace: Command 2'},
{name: 'namespace:command-1', displayName: 'Namespace: Command 1'}
]);
registry.add('.grandchild', 'namespace:command-3', () => {});
registry.restoreSnapshot(snapshot);
expect(registry.findCommands({target: grandchild}).slice(0, 2)).toEqual([
{name: 'namespace:command-2', displayName: 'Namespace: Command 2'},
{name: 'namespace:command-1', displayName: 'Namespace: Command 1'}
]);
})
);
describe("::attach(rootNode)", () =>
it("adds event listeners for any previously-added commands", () => {
const registry2 = new CommandRegistry;
const commandSpy = jasmine.createSpy('command-callback');
registry2.add('.grandchild', 'command-1', commandSpy);
grandchild.dispatchEvent(new CustomEvent('command-1', {bubbles: true}));
expect(commandSpy).not.toHaveBeenCalled();
registry2.attach(parent);
grandchild.dispatchEvent(new CustomEvent('command-1', {bubbles: true}));
expect(commandSpy).toHaveBeenCalled();
])
})
);
});
})
describe('::dispatch(target, commandName)', () => {
it('simulates invocation of the given command ', () => {
let called = false
registry.add('.grandchild', 'command', function (event) {
expect(this).toBe(grandchild)
expect(event.type).toBe('command')
expect(event.eventPhase).toBe(Event.BUBBLING_PHASE)
expect(event.target).toBe(grandchild)
expect(event.currentTarget).toBe(grandchild)
called = true
})
registry.dispatch(grandchild, 'command')
expect(called).toBe(true)
})
it('returns a promise if any listeners matched the command', () => {
registry.add('.grandchild', 'command', () => {})
expect(registry.dispatch(grandchild, 'command').constructor.name).toBe(
'Promise'
)
expect(registry.dispatch(grandchild, 'bogus')).toBe(null)
expect(registry.dispatch(parent, 'command')).toBe(null)
})
it('returns a promise that resolves when the listeners resolve', async () => {
jasmine.useRealClock()
registry.add('.grandchild', 'command', () => 1)
registry.add('.grandchild', 'command', () => Promise.resolve(2))
registry.add(
'.grandchild',
'command',
() =>
new Promise(resolve => {
setTimeout(() => {
resolve(3)
}, 1)
})
)
const values = await registry.dispatch(grandchild, 'command')
expect(values).toEqual([3, 2, 1])
})
it('returns a promise that rejects when a listener is rejected', async () => {
jasmine.useRealClock()
registry.add('.grandchild', 'command', () => 1)
registry.add('.grandchild', 'command', () => Promise.resolve(2))
registry.add(
'.grandchild',
'command',
() =>
new Promise((resolve, reject) => {
setTimeout(() => {
reject(3)
}, 1)
})
)
let value
try {
value = await registry.dispatch(grandchild, 'command')
} catch (err) {
value = err
}
expect(value).toBe(3)
})
})
describe('::getSnapshot and ::restoreSnapshot', () =>
it('removes all command handlers except for those in the snapshot', () => {
registry.add('.parent', 'namespace:command-1', () => {})
registry.add('.child', 'namespace:command-2', () => {})
const snapshot = registry.getSnapshot()
registry.add('.grandchild', 'namespace:command-3', () => {})
expect(registry.findCommands({ target: grandchild }).slice(0, 3)).toEqual(
[
{ name: 'namespace:command-3', displayName: 'Namespace: Command 3' },
{ name: 'namespace:command-2', displayName: 'Namespace: Command 2' },
{ name: 'namespace:command-1', displayName: 'Namespace: Command 1' }
]
)
registry.restoreSnapshot(snapshot)
expect(registry.findCommands({ target: grandchild }).slice(0, 2)).toEqual(
[
{ name: 'namespace:command-2', displayName: 'Namespace: Command 2' },
{ name: 'namespace:command-1', displayName: 'Namespace: Command 1' }
]
)
registry.add('.grandchild', 'namespace:command-3', () => {})
registry.restoreSnapshot(snapshot)
expect(registry.findCommands({ target: grandchild }).slice(0, 2)).toEqual(
[
{ name: 'namespace:command-2', displayName: 'Namespace: Command 2' },
{ name: 'namespace:command-1', displayName: 'Namespace: Command 1' }
]
)
}))
describe('::attach(rootNode)', () =>
it('adds event listeners for any previously-added commands', () => {
const registry2 = new CommandRegistry()
const commandSpy = jasmine.createSpy('command-callback')
registry2.add('.grandchild', 'command-1', commandSpy)
grandchild.dispatchEvent(new CustomEvent('command-1', { bubbles: true }))
expect(commandSpy).not.toHaveBeenCalled()
registry2.attach(parent)
grandchild.dispatchEvent(new CustomEvent('command-1', { bubbles: true }))
expect(commandSpy).toHaveBeenCalled()
}))
})

View File

@@ -1,4 +1,9 @@
const {it, fit, ffit, beforeEach, afterEach, conditionPromise} = require('./async-spec-helpers')
const {
it,
beforeEach,
afterEach
} = require('./async-spec-helpers')
const fs = require('fs-plus')
const path = require('path')
const temp = require('temp').track()
@@ -42,22 +47,25 @@ describe('ConfigFile', () => {
const event = new Promise(resolve => configFile.onDidChange(resolve))
writeFileSync(filePath, dedent `
writeFileSync(
filePath,
dedent`
'*':
foo: 'bar'
'javascript':
foo: 'baz'
`)
`
)
expect(await event).toEqual({
'*': {foo: 'bar'},
'javascript': {foo: 'baz'}
'*': { foo: 'bar' },
javascript: { foo: 'baz' }
})
expect(configFile.get()).toEqual({
'*': {foo: 'bar'},
'javascript': {foo: 'baz'}
'*': { foo: 'bar' },
javascript: { foo: 'baz' }
})
})
})
@@ -69,25 +77,33 @@ describe('ConfigFile', () => {
const message = new Promise(resolve => configFile.onDidError(resolve))
writeFileSync(filePath, dedent `
writeFileSync(
filePath,
dedent`
um what?
`, 2)
`,
2
)
expect(await message).toContain('Failed to load `the-config.cson`')
const event = new Promise(resolve => configFile.onDidChange(resolve))
writeFileSync(filePath, dedent `
writeFileSync(
filePath,
dedent`
'*':
foo: 'bar'
'javascript':
foo: 'baz'
`, 4)
`,
4
)
expect(await event).toEqual({
'*': {foo: 'bar'},
'javascript': {foo: 'baz'}
'*': { foo: 'bar' },
javascript: { foo: 'baz' }
})
})
})
@@ -115,7 +131,7 @@ describe('ConfigFile', () => {
})
function writeFileSync (filePath, content, seconds = 2) {
const utime = (Date.now() / 1000) + seconds
const utime = Date.now() / 1000 + seconds
fs.writeFileSync(filePath, content)
fs.utimesSync(filePath, utime, utime)
}

File diff suppressed because it is too large Load Diff

View File

@@ -2,7 +2,7 @@
const Grim = require('grim')
import {it, fit, ffit, fffit, beforeEach, afterEach} from './async-spec-helpers'
import { it } from './async-spec-helpers'
import etch from 'etch'
const getNextUpdatePromise = () => etch.getScheduler().nextUpdatePromise
@@ -16,7 +16,12 @@ describe('Dock', () => {
dock.onDidChangeVisible(didChangeVisibleSpy)
expect(dock.isVisible()).toBe(false)
expect(document.activeElement).toBe(atom.workspace.getCenter().getActivePane().getElement())
expect(document.activeElement).toBe(
atom.workspace
.getCenter()
.getActivePane()
.getElement()
)
dock.activate()
expect(dock.isVisible()).toBe(true)
expect(document.activeElement).toBe(dock.getActivePane().getElement())
@@ -36,7 +41,12 @@ describe('Dock', () => {
expect(didChangeVisibleSpy.mostRecentCall.args[0]).toBe(true)
dock.hide()
expect(document.activeElement).toBe(atom.workspace.getCenter().getActivePane().getElement())
expect(document.activeElement).toBe(
atom.workspace
.getCenter()
.getActivePane()
.getElement()
)
expect(didChangeVisibleSpy.mostRecentCall.args[0]).toBe(false)
dock.activate()
@@ -44,13 +54,18 @@ describe('Dock', () => {
expect(didChangeVisibleSpy.mostRecentCall.args[0]).toBe(true)
dock.toggle()
expect(document.activeElement).toBe(atom.workspace.getCenter().getActivePane().getElement())
expect(document.activeElement).toBe(
atom.workspace
.getCenter()
.getActivePane()
.getElement()
)
expect(didChangeVisibleSpy.mostRecentCall.args[0]).toBe(false)
// Don't change focus if the dock was not focused in the first place
const modalElement = document.createElement('div')
modalElement.setAttribute('tabindex', -1)
atom.workspace.addModalPanel({item: modalElement})
atom.workspace.addModalPanel({ item: modalElement })
modalElement.focus()
expect(document.activeElement).toBe(modalElement)
@@ -68,13 +83,18 @@ describe('Dock', () => {
it('opens the dock', async () => {
const item = {
element: document.createElement('div'),
getDefaultLocation() { return 'left' }
getDefaultLocation () {
return 'left'
}
}
await atom.workspace.open(item, {activatePane: false})
await atom.workspace.open(item, { activatePane: false })
expect(atom.workspace.getLeftDock().isVisible()).toBe(false)
atom.workspace.getLeftDock().getPanes()[0].activate()
atom.workspace
.getLeftDock()
.getPanes()[0]
.activate()
expect(atom.workspace.getLeftDock().isVisible()).toBe(true)
})
})
@@ -145,47 +165,63 @@ describe('Dock', () => {
describe('when the dock resize handle is double-clicked', () => {
describe('when the dock is open', () => {
it('resizes a vertically-oriented dock to the current item\'s preferred width', async () => {
it("resizes a vertically-oriented dock to the current item's preferred width", async () => {
jasmine.attachToDOM(atom.workspace.getElement())
const item = {
element: document.createElement('div'),
getDefaultLocation() { return 'left' },
getPreferredWidth() { return 142 },
getPreferredHeight() { return 122 }
getDefaultLocation () {
return 'left'
},
getPreferredWidth () {
return 142
},
getPreferredHeight () {
return 122
}
}
await atom.workspace.open(item)
const dock = atom.workspace.getLeftDock()
const dockElement = dock.getElement()
dock.setState({size: 300})
dock.setState({ size: 300 })
await getNextUpdatePromise()
expect(dockElement.offsetWidth).toBe(300)
dockElement.querySelector('.atom-dock-resize-handle').dispatchEvent(new MouseEvent('mousedown', {detail: 2}))
dockElement
.querySelector('.atom-dock-resize-handle')
.dispatchEvent(new MouseEvent('mousedown', { detail: 2 }))
await getNextUpdatePromise()
expect(dockElement.offsetWidth).toBe(item.getPreferredWidth())
})
it('resizes a horizontally-oriented dock to the current item\'s preferred width', async () => {
it("resizes a horizontally-oriented dock to the current item's preferred width", async () => {
jasmine.attachToDOM(atom.workspace.getElement())
const item = {
element: document.createElement('div'),
getDefaultLocation() { return 'bottom' },
getPreferredWidth() { return 122 },
getPreferredHeight() { return 142 }
getDefaultLocation () {
return 'bottom'
},
getPreferredWidth () {
return 122
},
getPreferredHeight () {
return 142
}
}
await atom.workspace.open(item)
const dock = atom.workspace.getBottomDock()
const dockElement = dock.getElement()
dock.setState({size: 300})
dock.setState({ size: 300 })
await getNextUpdatePromise()
expect(dockElement.offsetHeight).toBe(300)
dockElement.querySelector('.atom-dock-resize-handle').dispatchEvent(new MouseEvent('mousedown', {detail: 2}))
dockElement
.querySelector('.atom-dock-resize-handle')
.dispatchEvent(new MouseEvent('mousedown', { detail: 2 }))
await getNextUpdatePromise()
expect(dockElement.offsetHeight).toBe(item.getPreferredHeight())
@@ -198,19 +234,31 @@ describe('Dock', () => {
const item = {
element: document.createElement('div'),
getDefaultLocation() { return 'bottom' },
getPreferredWidth() { return 122 },
getPreferredHeight() { return 142 }
getDefaultLocation () {
return 'bottom'
},
getPreferredWidth () {
return 122
},
getPreferredHeight () {
return 142
}
}
await atom.workspace.open(item, {activatePane: false})
await atom.workspace.open(item, { activatePane: false })
const dockElement = atom.workspace.getBottomDock().getElement()
dockElement.querySelector('.atom-dock-resize-handle').dispatchEvent(new MouseEvent('mousedown', {detail: 2}))
dockElement
.querySelector('.atom-dock-resize-handle')
.dispatchEvent(new MouseEvent('mousedown', { detail: 2 }))
expect(dockElement.offsetHeight).toBe(0)
expect(dockElement.querySelector('.atom-dock-inner').offsetHeight).toBe(0)
expect(dockElement.querySelector('.atom-dock-inner').offsetHeight).toBe(
0
)
// The content should be masked away.
expect(dockElement.querySelector('.atom-dock-mask').offsetHeight).toBe(0)
expect(dockElement.querySelector('.atom-dock-mask').offsetHeight).toBe(
0
)
})
})
})
@@ -222,8 +270,12 @@ describe('Dock', () => {
const createItem = preferredWidth => ({
element: document.createElement('div'),
getDefaultLocation() { return 'left' },
getPreferredWidth() { return preferredWidth }
getDefaultLocation () {
return 'left'
},
getPreferredWidth () {
return preferredWidth
}
})
const dock = atom.workspace.getLeftDock()
@@ -257,7 +309,9 @@ describe('Dock', () => {
const item = {
element: document.createElement('div'),
getDefaultLocation() { return 'left' }
getDefaultLocation () {
return 'left'
}
}
const dock = atom.workspace.getLeftDock()
expect(dock.getPaneItems()).toHaveLength(0)
@@ -275,11 +329,15 @@ describe('Dock', () => {
const item = {
element: document.createElement('div'),
getDefaultLocation() { return 'left' },
getPreferredWidth() { return 122 },
serialize: () => ({deserializer: 'DockTestItem'})
getDefaultLocation () {
return 'left'
},
getPreferredWidth () {
return 122
},
serialize: () => ({ deserializer: 'DockTestItem' })
}
const itemDeserializer = atom.deserializers.add({
atom.deserializers.add({
name: 'DockTestItem',
deserialize: () => item
})
@@ -287,10 +345,10 @@ describe('Dock', () => {
const dockElement = dock.getElement()
await atom.workspace.open(item)
dock.setState({size: 150})
dock.setState({ size: 150 })
expect(dockElement.offsetWidth).toBe(150)
const serialized = dock.serialize()
dock.setState({size: 122})
dock.setState({ size: 122 })
expect(dockElement.offsetWidth).toBe(122)
dock.destroyActivePane()
dock.deserialize(serialized, atom.deserializers)
@@ -302,8 +360,12 @@ describe('Dock', () => {
const item = {
element: document.createElement('div'),
getDefaultLocation() { return 'left' },
getPreferredWidth() { return 122 }
getDefaultLocation () {
return 'left'
},
getPreferredWidth () {
return 122
}
}
const dock = atom.workspace.getLeftDock()
@@ -324,16 +386,22 @@ describe('Dock', () => {
element.setAttribute('is', 'tabs-tab')
element.item = {
element,
getDefaultLocation() { return 'left' },
getPreferredWidth() { return 144 }
getDefaultLocation () {
return 'left'
},
getPreferredWidth () {
return 144
}
}
const dragEvent = new DragEvent('dragstart')
Object.defineProperty(dragEvent, 'target', {value: element})
Object.defineProperty(dragEvent, 'target', { value: element })
atom.workspace.getElement().handleDragStart(dragEvent)
await getNextUpdatePromise()
expect(atom.workspace.getLeftDock().refs.wrapperElement.offsetWidth).toBe(144)
expect(atom.workspace.getLeftDock().refs.wrapperElement.offsetWidth).toBe(
144
)
})
it('does nothing when text nodes are dragged', () => {
@@ -342,9 +410,11 @@ describe('Dock', () => {
const textNode = document.createTextNode('hello')
const dragEvent = new DragEvent('dragstart')
Object.defineProperty(dragEvent, 'target', {value: textNode})
Object.defineProperty(dragEvent, 'target', { value: textNode })
expect(() => atom.workspace.getElement().handleDragStart(dragEvent)).not.toThrow()
expect(() =>
atom.workspace.getElement().handleDragStart(dragEvent)
).not.toThrow()
})
})

View File

@@ -1,16 +1,20 @@
const path = require('path')
const fs = require('fs-plus')
const temp = require('temp').track()
const {Directory} = require('pathwatcher')
const { Directory } = require('pathwatcher')
const GitRepository = require('../src/git-repository')
const GitRepositoryProvider = require('../src/git-repository-provider')
const {it, fit, ffit, fffit, beforeEach} = require('./async-spec-helpers')
const { it, beforeEach } = require('./async-spec-helpers')
describe('GitRepositoryProvider', () => {
let provider
beforeEach(() => {
provider = new GitRepositoryProvider(atom.project, atom.config, atom.confirm)
provider = new GitRepositoryProvider(
atom.project,
atom.config,
atom.confirm
)
})
afterEach(() => {
@@ -24,7 +28,9 @@ describe('GitRepositoryProvider', () => {
describe('.repositoryForDirectory(directory)', () => {
describe('when specified a Directory with a Git repository', () => {
it('resolves with a GitRepository', async () => {
const directory = new Directory(path.join(__dirname, 'fixtures', 'git', 'master.git'))
const directory = new Directory(
path.join(__dirname, 'fixtures', 'git', 'master.git')
)
const result = await provider.repositoryForDirectory(directory)
expect(result).toBeInstanceOf(GitRepository)
expect(provider.pathToRepository[result.getPath()]).toBeTruthy()
@@ -39,7 +45,9 @@ describe('GitRepositoryProvider', () => {
new Directory(path.join(__dirname, 'fixtures', 'git', 'master.git'))
)
const secondRepo = await provider.repositoryForDirectory(
new Directory(path.join(__dirname, 'fixtures', 'git', 'master.git', 'objects'))
new Directory(
path.join(__dirname, 'fixtures', 'git', 'master.git', 'objects')
)
)
expect(firstRepo).toBeInstanceOf(GitRepository)
@@ -72,7 +80,10 @@ describe('GitRepositoryProvider', () => {
it('returns a Promise that resolves to a GitRepository', async () => {
const gitDirPath = path.join(__dirname, 'fixtures', 'git', 'master.git')
const workDirPath = temp.mkdirSync('git-workdir')
fs.writeFileSync(path.join(workDirPath, '.git'), `gitdir: ${gitDirPath}\n`)
fs.writeFileSync(
path.join(workDirPath, '.git'),
`gitdir: ${gitDirPath}\n`
)
const directory = new Directory(workDirPath)
const result = await provider.repositoryForDirectory(directory)
@@ -90,7 +101,9 @@ describe('GitRepositoryProvider', () => {
const subdirectory = {}
directory = {
getSubdirectory () {},
isRoot () { return true }
isRoot () {
return true
}
}
spyOn(directory, 'getSubdirectory').andReturn(subdirectory)
})
@@ -145,7 +158,7 @@ describe('GitRepositoryProvider', () => {
fs.writeFileSync(path.join(dirPath, '.git', 'refs'), '')
const directory = new Directory(dirPath)
const repo = provider.repositoryForDirectorySync(directory)
const repo = provider.repositoryForDirectorySync(directory)
expect(repo).toBe(null)
})
})

View File

@@ -1,4 +1,4 @@
const {it, fit, ffit, fffit, beforeEach, afterEach} = require('./async-spec-helpers')
const { it, beforeEach, afterEach } = require('./async-spec-helpers')
const path = require('path')
const fs = require('fs-plus')
const temp = require('temp').track()
@@ -25,30 +25,44 @@ describe('GitRepository', () => {
describe('new GitRepository(path)', () => {
it('throws an exception when no repository is found', () => {
expect(() => new GitRepository(path.join(temp.dir, 'nogit.txt'))).toThrow()
expect(
() => new GitRepository(path.join(temp.dir, 'nogit.txt'))
).toThrow()
})
})
describe('.getPath()', () => {
it('returns the repository path for a .git directory path with a directory', () => {
repo = new GitRepository(path.join(__dirname, 'fixtures', 'git', 'master.git', 'objects'))
expect(repo.getPath()).toBe(path.join(__dirname, 'fixtures', 'git', 'master.git'))
repo = new GitRepository(
path.join(__dirname, 'fixtures', 'git', 'master.git', 'objects')
)
expect(repo.getPath()).toBe(
path.join(__dirname, 'fixtures', 'git', 'master.git')
)
})
it('returns the repository path for a repository path', () => {
repo = new GitRepository(path.join(__dirname, 'fixtures', 'git', 'master.git'))
expect(repo.getPath()).toBe(path.join(__dirname, 'fixtures', 'git', 'master.git'))
repo = new GitRepository(
path.join(__dirname, 'fixtures', 'git', 'master.git')
)
expect(repo.getPath()).toBe(
path.join(__dirname, 'fixtures', 'git', 'master.git')
)
})
})
describe('.isPathIgnored(path)', () => {
it('returns true for an ignored path', () => {
repo = new GitRepository(path.join(__dirname, 'fixtures', 'git', 'ignore.git'))
repo = new GitRepository(
path.join(__dirname, 'fixtures', 'git', 'ignore.git')
)
expect(repo.isPathIgnored('a.txt')).toBeTruthy()
})
it('returns false for a non-ignored path', () => {
repo = new GitRepository(path.join(__dirname, 'fixtures', 'git', 'ignore.git'))
repo = new GitRepository(
path.join(__dirname, 'fixtures', 'git', 'ignore.git')
)
expect(repo.isPathIgnored('b.txt')).toBeFalsy()
})
})
@@ -136,7 +150,10 @@ describe('GitRepository', () => {
repo.onDidChangeStatus(statusHandler)
repo.checkoutHead(filePath)
expect(statusHandler.callCount).toBe(1)
expect(statusHandler.argsForCall[0][0]).toEqual({path: filePath, pathStatus: 0})
expect(statusHandler.argsForCall[0][0]).toEqual({
path: filePath,
pathStatus: 0
})
repo.checkoutHead(filePath)
expect(statusHandler.callCount).toBe(1)
@@ -150,7 +167,11 @@ describe('GitRepository', () => {
spyOn(atom, 'confirm')
const workingDirPath = copyRepository()
repo = new GitRepository(workingDirPath, {project: atom.project, config: atom.config, confirm: atom.confirm})
repo = new GitRepository(workingDirPath, {
project: atom.project,
config: atom.config,
confirm: atom.confirm
})
filePath = path.join(workingDirPath, 'a.txt')
fs.writeFileSync(filePath, 'ch ch changes')
@@ -161,7 +182,7 @@ describe('GitRepository', () => {
// Permissions issues with this test on Windows
if (process.platform === 'win32') return
atom.confirm.andCallFake(({buttons}) => buttons.OK())
atom.confirm.andCallFake(({ buttons }) => buttons.OK())
atom.config.set('editor.confirmCheckoutHeadRevision', true)
repo.checkoutHeadForEditor(editor)
@@ -183,7 +204,9 @@ describe('GitRepository', () => {
describe('.destroy()', () => {
it('throws an exception when any method is called after it is called', () => {
repo = new GitRepository(path.join(__dirname, 'fixtures', 'git', 'master.git'))
repo = new GitRepository(
path.join(__dirname, 'fixtures', 'git', 'master.git')
)
repo.destroy()
expect(() => repo.getShortHead()).toThrow()
})
@@ -204,7 +227,10 @@ describe('GitRepository', () => {
fs.writeFileSync(filePath, '')
let status = repo.getPathStatus(filePath)
expect(statusHandler.callCount).toBe(1)
expect(statusHandler.argsForCall[0][0]).toEqual({path: filePath, pathStatus: status})
expect(statusHandler.argsForCall[0][0]).toEqual({
path: filePath,
pathStatus: status
})
fs.writeFileSync(filePath, 'abc')
status = repo.getPathStatus(filePath)
@@ -223,10 +249,14 @@ describe('GitRepository', () => {
})
it('gets the status based on the files inside the directory', () => {
expect(repo.isStatusModified(repo.getDirectoryStatus(directoryPath))).toBe(false)
expect(
repo.isStatusModified(repo.getDirectoryStatus(directoryPath))
).toBe(false)
fs.writeFileSync(filePath, 'abc')
repo.getPathStatus(filePath)
expect(repo.isStatusModified(repo.getDirectoryStatus(directoryPath))).toBe(true)
expect(
repo.isStatusModified(repo.getDirectoryStatus(directoryPath))
).toBe(true)
})
})
@@ -235,7 +265,10 @@ describe('GitRepository', () => {
beforeEach(() => {
workingDirectory = copyRepository()
repo = new GitRepository(workingDirectory, {project: atom.project, config: atom.config})
repo = new GitRepository(workingDirectory, {
project: atom.project,
config: atom.config
})
modifiedPath = path.join(workingDirectory, 'file.txt')
newPath = path.join(workingDirectory, 'untracked.txt')
cleanPath = path.join(workingDirectory, 'other.txt')
@@ -252,8 +285,10 @@ describe('GitRepository', () => {
await repo.refreshStatus()
expect(statusHandler.callCount).toBe(1)
expect(repo.getCachedPathStatus(cleanPath)).toBeUndefined()
expect(repo.isStatusNew(repo.getCachedPathStatus(newPath) )).toBeTruthy()
expect(repo.isStatusModified(repo.getCachedPathStatus(modifiedPath))).toBeTruthy()
expect(repo.isStatusNew(repo.getCachedPathStatus(newPath))).toBeTruthy()
expect(
repo.isStatusModified(repo.getCachedPathStatus(modifiedPath))
).toBeTruthy()
})
it('caches the proper statuses when a subdir is open', async () => {
@@ -278,7 +313,9 @@ describe('GitRepository', () => {
await repo.refreshStatus()
expect(repo.getCachedPathStatus(cleanPath)).toBeUndefined()
expect(repo.isStatusNew(repo.getCachedPathStatus(newPath))).toBeTruthy()
expect(repo.isStatusModified(repo.getCachedPathStatus(modifiedPath))).toBeTruthy()
expect(
repo.isStatusModified(repo.getCachedPathStatus(modifiedPath))
).toBeTruthy()
})
it('caches statuses that were looked up synchronously', async () => {
@@ -288,7 +325,9 @@ describe('GitRepository', () => {
fs.writeFileSync(modifiedPath, originalContent)
await repo.refreshStatus()
expect(repo.isStatusModified(repo.getCachedPathStatus(modifiedPath))).toBeFalsy()
expect(
repo.isStatusModified(repo.getCachedPathStatus(modifiedPath))
).toBeFalsy()
})
})
@@ -297,7 +336,9 @@ describe('GitRepository', () => {
beforeEach(async () => {
atom.project.setPaths([copyRepository()])
const refreshPromise = new Promise(resolve => atom.project.getRepositories()[0].onDidChangeStatuses(resolve))
const refreshPromise = new Promise(resolve =>
atom.project.getRepositories()[0].onDidChangeStatuses(resolve)
)
editor = await atom.workspace.open('other.txt')
await refreshPromise
})
@@ -310,7 +351,10 @@ describe('GitRepository', () => {
await editor.save()
expect(statusHandler.callCount).toBe(1)
expect(statusHandler).toHaveBeenCalledWith({path: editor.getPath(), pathStatus: 256})
expect(statusHandler).toHaveBeenCalledWith({
path: editor.getPath(),
pathStatus: 256
})
})
it('emits a status-changed event when a buffer is reloaded', async () => {
@@ -321,7 +365,10 @@ describe('GitRepository', () => {
await editor.getBuffer().reload()
expect(statusHandler.callCount).toBe(1)
expect(statusHandler).toHaveBeenCalledWith({path: editor.getPath(), pathStatus: 256})
expect(statusHandler).toHaveBeenCalledWith({
path: editor.getPath(),
pathStatus: 256
})
await editor.getBuffer().reload()
expect(statusHandler.callCount).toBe(1)
@@ -334,7 +381,10 @@ describe('GitRepository', () => {
atom.project.getRepositories()[0].onDidChangeStatus(statusHandler)
editor.getBuffer().emitter.emit('did-change-path')
expect(statusHandler.callCount).toBe(1)
expect(statusHandler).toHaveBeenCalledWith({path: editor.getPath(), pathStatus: 256})
expect(statusHandler).toHaveBeenCalledWith({
path: editor.getPath(),
pathStatus: 256
})
editor.getBuffer().emitter.emit('did-change-path')
expect(statusHandler.callCount).toBe(1)
})
@@ -364,11 +414,9 @@ describe('GitRepository', () => {
grammarRegistry: atom.grammars,
applicationDelegate: atom.applicationDelegate
})
await project2.deserialize(atom.project.serialize({isUnloading: false}))
await project2.deserialize(atom.project.serialize({ isUnloading: false }))
buffer = project2.getBuffers()[0]
const originalContent = buffer.getText()
buffer.append('changes')
statusHandler = jasmine.createSpy('statusHandler')
@@ -376,14 +424,23 @@ describe('GitRepository', () => {
await buffer.save()
expect(statusHandler.callCount).toBe(1)
expect(statusHandler).toHaveBeenCalledWith({path: buffer.getPath(), pathStatus: 256})
expect(statusHandler).toHaveBeenCalledWith({
path: buffer.getPath(),
pathStatus: 256
})
})
})
})
function copyRepository () {
const workingDirPath = temp.mkdirSync('atom-spec-git')
fs.copySync(path.join(__dirname, 'fixtures', 'git', 'working-dir'), workingDirPath)
fs.renameSync(path.join(workingDirPath, 'git.git'), path.join(workingDirPath, '.git'))
fs.copySync(
path.join(__dirname, 'fixtures', 'git', 'working-dir'),
workingDirPath
)
fs.renameSync(
path.join(workingDirPath, 'git.git'),
path.join(workingDirPath, '.git')
)
return workingDirPath
}

View File

@@ -1,4 +1,4 @@
const {it, fit, ffit, fffit, beforeEach, afterEach} = require('./async-spec-helpers')
const { it, beforeEach, afterEach } = require('./async-spec-helpers')
const dedent = require('dedent')
const path = require('path')
@@ -13,14 +13,18 @@ describe('GrammarRegistry', () => {
let grammarRegistry
beforeEach(() => {
grammarRegistry = new GrammarRegistry({config: atom.config})
grammarRegistry = new GrammarRegistry({ config: atom.config })
expect(subscriptionCount(grammarRegistry)).toBe(1)
})
describe('.assignLanguageMode(buffer, languageId)', () => {
it('assigns to the buffer a language mode with the given language id', async () => {
grammarRegistry.loadGrammarSync(require.resolve('language-javascript/grammars/javascript.cson'))
grammarRegistry.loadGrammarSync(require.resolve('language-css/grammars/css.cson'))
grammarRegistry.loadGrammarSync(
require.resolve('language-javascript/grammars/javascript.cson')
)
grammarRegistry.loadGrammarSync(
require.resolve('language-css/grammars/css.cson')
)
const buffer = new TextBuffer()
expect(grammarRegistry.assignLanguageMode(buffer, 'source.js')).toBe(true)
@@ -31,7 +35,9 @@ describe('GrammarRegistry', () => {
expect(grammarRegistry.assignLanguageMode(buffer, 'source.js')).toBe(true)
// Language names are not case-sensitive
expect(grammarRegistry.assignLanguageMode(buffer, 'source.css')).toBe(true)
expect(grammarRegistry.assignLanguageMode(buffer, 'source.css')).toBe(
true
)
expect(buffer.getLanguageMode().getLanguageId()).toBe('source.css')
// Returns false if no language is found
@@ -41,14 +47,20 @@ describe('GrammarRegistry', () => {
describe('when no languageId is passed', () => {
it('makes the buffer use the null grammar', () => {
grammarRegistry.loadGrammarSync(require.resolve('language-css/grammars/css.cson'))
grammarRegistry.loadGrammarSync(
require.resolve('language-css/grammars/css.cson')
)
const buffer = new TextBuffer()
expect(grammarRegistry.assignLanguageMode(buffer, 'source.css')).toBe(true)
expect(grammarRegistry.assignLanguageMode(buffer, 'source.css')).toBe(
true
)
expect(buffer.getLanguageMode().getLanguageId()).toBe('source.css')
expect(grammarRegistry.assignLanguageMode(buffer, null)).toBe(true)
expect(buffer.getLanguageMode().getLanguageId()).toBe('text.plain.null-grammar')
expect(buffer.getLanguageMode().getLanguageId()).toBe(
'text.plain.null-grammar'
)
expect(grammarRegistry.getAssignedLanguageId(buffer)).toBe(null)
})
})
@@ -56,10 +68,18 @@ describe('GrammarRegistry', () => {
describe('.grammarForId(languageId)', () => {
it('returns a text-mate grammar when `core.useTreeSitterParsers` is false', () => {
atom.config.set('core.useTreeSitterParsers', false, {scopeSelector: '.source.js'})
atom.config.set('core.useTreeSitterParsers', false, {
scopeSelector: '.source.js'
})
grammarRegistry.loadGrammarSync(require.resolve('language-javascript/grammars/javascript.cson'))
grammarRegistry.loadGrammarSync(require.resolve('language-javascript/grammars/tree-sitter-javascript.cson'))
grammarRegistry.loadGrammarSync(
require.resolve('language-javascript/grammars/javascript.cson')
)
grammarRegistry.loadGrammarSync(
require.resolve(
'language-javascript/grammars/tree-sitter-javascript.cson'
)
)
const grammar = grammarRegistry.grammarForId('source.js')
expect(grammar instanceof FirstMate.Grammar).toBe(true)
@@ -72,26 +92,40 @@ describe('GrammarRegistry', () => {
it('returns a tree-sitter grammar when `core.useTreeSitterParsers` is true', () => {
atom.config.set('core.useTreeSitterParsers', true)
grammarRegistry.loadGrammarSync(require.resolve('language-javascript/grammars/javascript.cson'))
grammarRegistry.loadGrammarSync(require.resolve('language-javascript/grammars/tree-sitter-javascript.cson'))
grammarRegistry.loadGrammarSync(
require.resolve('language-javascript/grammars/javascript.cson')
)
grammarRegistry.loadGrammarSync(
require.resolve(
'language-javascript/grammars/tree-sitter-javascript.cson'
)
)
const grammar = grammarRegistry.grammarForId('source.js')
expect(grammar instanceof TreeSitterGrammar).toBe(true)
expect(grammar.scopeName).toBe('source.js')
grammarRegistry.removeGrammar(grammar)
expect(grammarRegistry.grammarForId('source.js') instanceof FirstMate.Grammar).toBe(true)
expect(
grammarRegistry.grammarForId('source.js') instanceof FirstMate.Grammar
).toBe(true)
})
})
describe('.autoAssignLanguageMode(buffer)', () => {
it('assigns to the buffer a language mode based on the best available grammar', () => {
grammarRegistry.loadGrammarSync(require.resolve('language-javascript/grammars/javascript.cson'))
grammarRegistry.loadGrammarSync(require.resolve('language-css/grammars/css.cson'))
grammarRegistry.loadGrammarSync(
require.resolve('language-javascript/grammars/javascript.cson')
)
grammarRegistry.loadGrammarSync(
require.resolve('language-css/grammars/css.cson')
)
const buffer = new TextBuffer()
buffer.setPath('foo.js')
expect(grammarRegistry.assignLanguageMode(buffer, 'source.css')).toBe(true)
expect(grammarRegistry.assignLanguageMode(buffer, 'source.css')).toBe(
true
)
expect(buffer.getLanguageMode().getLanguageId()).toBe('source.css')
grammarRegistry.autoAssignLanguageMode(buffer)
@@ -103,8 +137,12 @@ describe('GrammarRegistry', () => {
it('assigns a grammar to the buffer based on its path', async () => {
const buffer = new TextBuffer()
grammarRegistry.loadGrammarSync(require.resolve('language-javascript/grammars/javascript.cson'))
grammarRegistry.loadGrammarSync(require.resolve('language-c/grammars/c.cson'))
grammarRegistry.loadGrammarSync(
require.resolve('language-javascript/grammars/javascript.cson')
)
grammarRegistry.loadGrammarSync(
require.resolve('language-c/grammars/c.cson')
)
buffer.setPath('test.js')
grammarRegistry.maintainLanguageMode(buffer)
@@ -114,7 +152,7 @@ describe('GrammarRegistry', () => {
expect(buffer.getLanguageMode().getLanguageId()).toBe('source.c')
})
it('updates the buffer\'s grammar when a more appropriate text-mate grammar is added for its path', async () => {
it("updates the buffer's grammar when a more appropriate text-mate grammar is added for its path", async () => {
atom.config.set('core.useTreeSitterParsers', false)
const buffer = new TextBuffer()
@@ -123,14 +161,20 @@ describe('GrammarRegistry', () => {
buffer.setPath('test.js')
grammarRegistry.maintainLanguageMode(buffer)
const textMateGrammar = grammarRegistry.loadGrammarSync(require.resolve('language-javascript/grammars/javascript.cson'))
const textMateGrammar = grammarRegistry.loadGrammarSync(
require.resolve('language-javascript/grammars/javascript.cson')
)
expect(buffer.getLanguageMode().grammar).toBe(textMateGrammar)
grammarRegistry.loadGrammarSync(require.resolve('language-javascript/grammars/tree-sitter-javascript.cson'))
grammarRegistry.loadGrammarSync(
require.resolve(
'language-javascript/grammars/tree-sitter-javascript.cson'
)
)
expect(buffer.getLanguageMode().grammar).toBe(textMateGrammar)
})
it('updates the buffer\'s grammar when a more appropriate tree-sitter grammar is added for its path', async () => {
it("updates the buffer's grammar when a more appropriate tree-sitter grammar is added for its path", async () => {
atom.config.set('core.useTreeSitterParsers', true)
const buffer = new TextBuffer()
@@ -139,10 +183,16 @@ describe('GrammarRegistry', () => {
buffer.setPath('test.js')
grammarRegistry.maintainLanguageMode(buffer)
const treeSitterGrammar = grammarRegistry.loadGrammarSync(require.resolve('language-javascript/grammars/tree-sitter-javascript.cson'))
const treeSitterGrammar = grammarRegistry.loadGrammarSync(
require.resolve(
'language-javascript/grammars/tree-sitter-javascript.cson'
)
)
expect(buffer.getLanguageMode().grammar).toBe(treeSitterGrammar)
grammarRegistry.loadGrammarSync(require.resolve('language-javascript/grammars/javascript.cson'))
grammarRegistry.loadGrammarSync(
require.resolve('language-javascript/grammars/javascript.cson')
)
expect(buffer.getLanguageMode().grammar).toBe(treeSitterGrammar)
})
@@ -152,41 +202,59 @@ describe('GrammarRegistry', () => {
buffer.setPath('test.js')
grammarRegistry.maintainLanguageMode(buffer)
grammarRegistry.loadGrammarSync(require.resolve('language-css/grammars/css.cson'))
expect(grammarRegistry.assignLanguageMode(buffer, 'source.css')).toBe(true)
grammarRegistry.loadGrammarSync(
require.resolve('language-css/grammars/css.cson')
)
expect(grammarRegistry.assignLanguageMode(buffer, 'source.css')).toBe(
true
)
expect(buffer.getLanguageMode().getLanguageId()).toBe('source.css')
grammarRegistry.loadGrammarSync(require.resolve('language-javascript/grammars/javascript.cson'))
grammarRegistry.loadGrammarSync(
require.resolve('language-javascript/grammars/javascript.cson')
)
expect(buffer.getLanguageMode().getLanguageId()).toBe('source.css')
})
it('returns a disposable that can be used to stop the registry from updating the buffer', async () => {
const buffer = new TextBuffer()
grammarRegistry.loadGrammarSync(require.resolve('language-javascript/grammars/javascript.cson'))
grammarRegistry.loadGrammarSync(
require.resolve('language-javascript/grammars/javascript.cson')
)
const previousSubscriptionCount = buffer.emitter.getTotalListenerCount()
const disposable = grammarRegistry.maintainLanguageMode(buffer)
expect(buffer.emitter.getTotalListenerCount()).toBeGreaterThan(previousSubscriptionCount)
expect(buffer.emitter.getTotalListenerCount()).toBeGreaterThan(
previousSubscriptionCount
)
expect(retainedBufferCount(grammarRegistry)).toBe(1)
buffer.setPath('test.js')
expect(buffer.getLanguageMode().getLanguageId()).toBe('source.js')
buffer.setPath('test.txt')
expect(buffer.getLanguageMode().getLanguageId()).toBe('text.plain.null-grammar')
expect(buffer.getLanguageMode().getLanguageId()).toBe(
'text.plain.null-grammar'
)
disposable.dispose()
expect(buffer.emitter.getTotalListenerCount()).toBe(previousSubscriptionCount)
expect(buffer.emitter.getTotalListenerCount()).toBe(
previousSubscriptionCount
)
expect(retainedBufferCount(grammarRegistry)).toBe(0)
buffer.setPath('test.js')
expect(buffer.getLanguageMode().getLanguageId()).toBe('text.plain.null-grammar')
expect(buffer.getLanguageMode().getLanguageId()).toBe(
'text.plain.null-grammar'
)
expect(retainedBufferCount(grammarRegistry)).toBe(0)
})
it('doesn\'t do anything when called a second time with the same buffer', async () => {
it("doesn't do anything when called a second time with the same buffer", async () => {
const buffer = new TextBuffer()
grammarRegistry.loadGrammarSync(require.resolve('language-javascript/grammars/javascript.cson'))
grammarRegistry.loadGrammarSync(
require.resolve('language-javascript/grammars/javascript.cson')
)
const disposable1 = grammarRegistry.maintainLanguageMode(buffer)
const disposable2 = grammarRegistry.maintainLanguageMode(buffer)
@@ -195,16 +263,22 @@ describe('GrammarRegistry', () => {
disposable2.dispose()
buffer.setPath('test.txt')
expect(buffer.getLanguageMode().getLanguageId()).toBe('text.plain.null-grammar')
expect(buffer.getLanguageMode().getLanguageId()).toBe(
'text.plain.null-grammar'
)
disposable1.dispose()
buffer.setPath('test.js')
expect(buffer.getLanguageMode().getLanguageId()).toBe('text.plain.null-grammar')
expect(buffer.getLanguageMode().getLanguageId()).toBe(
'text.plain.null-grammar'
)
})
it('does not retain the buffer after the buffer is destroyed', () => {
const buffer = new TextBuffer()
grammarRegistry.loadGrammarSync(require.resolve('language-javascript/grammars/javascript.cson'))
grammarRegistry.loadGrammarSync(
require.resolve('language-javascript/grammars/javascript.cson')
)
const disposable = grammarRegistry.maintainLanguageMode(buffer)
expect(retainedBufferCount(grammarRegistry)).toBe(1)
@@ -222,9 +296,11 @@ describe('GrammarRegistry', () => {
it('does not retain the buffer when the grammar registry is destroyed', () => {
const buffer = new TextBuffer()
grammarRegistry.loadGrammarSync(require.resolve('language-javascript/grammars/javascript.cson'))
grammarRegistry.loadGrammarSync(
require.resolve('language-javascript/grammars/javascript.cson')
)
const disposable = grammarRegistry.maintainLanguageMode(buffer)
grammarRegistry.maintainLanguageMode(buffer)
expect(retainedBufferCount(grammarRegistry)).toBe(1)
expect(subscriptionCount(grammarRegistry)).toBe(3)
@@ -238,19 +314,25 @@ describe('GrammarRegistry', () => {
describe('.selectGrammar(filePath)', () => {
it('always returns a grammar', () => {
const registry = new GrammarRegistry({config: atom.config})
const registry = new GrammarRegistry({ config: atom.config })
expect(registry.selectGrammar().scopeName).toBe('text.plain.null-grammar')
})
it('selects the text.plain grammar over the null grammar', async () => {
await atom.packages.activatePackage('language-text')
expect(atom.grammars.selectGrammar('test.txt').scopeName).toBe('text.plain')
expect(atom.grammars.selectGrammar('test.txt').scopeName).toBe(
'text.plain'
)
})
it('selects a grammar based on the file path case insensitively', async () => {
await atom.packages.activatePackage('language-coffee-script')
expect(atom.grammars.selectGrammar('/tmp/source.coffee').scopeName).toBe('source.coffee')
expect(atom.grammars.selectGrammar('/tmp/source.COFFEE').scopeName).toBe('source.coffee')
expect(atom.grammars.selectGrammar('/tmp/source.coffee').scopeName).toBe(
'source.coffee'
)
expect(atom.grammars.selectGrammar('/tmp/source.COFFEE').scopeName).toBe(
'source.coffee'
)
})
describe('on Windows', () => {
@@ -258,16 +340,18 @@ describe('GrammarRegistry', () => {
beforeEach(() => {
originalPlatform = process.platform
Object.defineProperty(process, 'platform', {value: 'win32'})
Object.defineProperty(process, 'platform', { value: 'win32' })
})
afterEach(() => {
Object.defineProperty(process, 'platform', {value: originalPlatform})
Object.defineProperty(process, 'platform', { value: originalPlatform })
})
it('normalizes back slashes to forward slashes when matching the fileTypes', async () => {
await atom.packages.activatePackage('language-git')
expect(atom.grammars.selectGrammar('something\\.git\\config').scopeName).toBe('source.git-config')
expect(
atom.grammars.selectGrammar('something\\.git\\config').scopeName
).toBe('source.git-config')
})
})
@@ -277,10 +361,14 @@ describe('GrammarRegistry', () => {
await atom.packages.activatePackage('language-ruby')
expect(atom.grammars.selectGrammar('file.js').name).toBe('JavaScript') // based on extension (.js)
expect(atom.grammars.selectGrammar(path.join(temp.dir, '.git', 'config')).name).toBe('Git Config') // based on end of the path (.git/config)
expect(
atom.grammars.selectGrammar(path.join(temp.dir, '.git', 'config')).name
).toBe('Git Config') // based on end of the path (.git/config)
expect(atom.grammars.selectGrammar('Rakefile').name).toBe('Ruby') // based on the file's basename (Rakefile)
expect(atom.grammars.selectGrammar('curb').name).toBe('Null Grammar')
expect(atom.grammars.selectGrammar('/hu.git/config').name).toBe('Null Grammar')
expect(atom.grammars.selectGrammar('/hu.git/config').name).toBe(
'Null Grammar'
)
})
it("uses the filePath's shebang line if the grammar cannot be determined by the extension or basename", async () => {
@@ -296,13 +384,20 @@ describe('GrammarRegistry', () => {
await atom.packages.activatePackage('language-coffee-script')
let fileContent = 'first-line\n<html>'
expect(atom.grammars.selectGrammar('dummy.coffee', fileContent).name).toBe('CoffeeScript')
expect(
atom.grammars.selectGrammar('dummy.coffee', fileContent).name
).toBe('CoffeeScript')
fileContent = '<?xml version="1.0" encoding="UTF-8"?>'
expect(atom.grammars.selectGrammar('grammar.tmLanguage', fileContent).name).toBe('Null Grammar')
expect(
atom.grammars.selectGrammar('grammar.tmLanguage', fileContent).name
).toBe('Null Grammar')
fileContent += '\n<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">'
expect(atom.grammars.selectGrammar('grammar.tmLanguage', fileContent).name).toBe('Property List (XML)')
fileContent +=
'\n<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">'
expect(
atom.grammars.selectGrammar('grammar.tmLanguage', fileContent).name
).toBe('Property List (XML)')
})
it("doesn't read the file when the file contents are specified", async () => {
@@ -311,28 +406,36 @@ describe('GrammarRegistry', () => {
const filePath = require.resolve('./fixtures/shebang')
const filePathContents = fs.readFileSync(filePath, 'utf8')
spyOn(fs, 'read').andCallThrough()
expect(atom.grammars.selectGrammar(filePath, filePathContents).name).toBe('Ruby')
expect(atom.grammars.selectGrammar(filePath, filePathContents).name).toBe(
'Ruby'
)
expect(fs.read).not.toHaveBeenCalled()
})
describe('when multiple grammars have matching fileTypes', () => {
it('selects the grammar with the longest fileType match', () => {
const grammarPath1 = temp.path({suffix: '.json'})
fs.writeFileSync(grammarPath1, JSON.stringify({
name: 'test1',
scopeName: 'source1',
fileTypes: ['test']
}))
const grammarPath1 = temp.path({ suffix: '.json' })
fs.writeFileSync(
grammarPath1,
JSON.stringify({
name: 'test1',
scopeName: 'source1',
fileTypes: ['test']
})
)
const grammar1 = atom.grammars.loadGrammarSync(grammarPath1)
expect(atom.grammars.selectGrammar('more.test', '')).toBe(grammar1)
fs.removeSync(grammarPath1)
const grammarPath2 = temp.path({suffix: '.json'})
fs.writeFileSync(grammarPath2, JSON.stringify({
name: 'test2',
scopeName: 'source2',
fileTypes: ['test', 'more.test']
}))
const grammarPath2 = temp.path({ suffix: '.json' })
fs.writeFileSync(
grammarPath2,
JSON.stringify({
name: 'test2',
scopeName: 'source2',
fileTypes: ['test', 'more.test']
})
)
const grammar2 = atom.grammars.loadGrammarSync(grammarPath2)
expect(atom.grammars.selectGrammar('more.test', '')).toBe(grammar2)
return fs.removeSync(grammarPath2)
@@ -341,19 +444,28 @@ describe('GrammarRegistry', () => {
it('favors non-bundled packages when breaking scoring ties', async () => {
await atom.packages.activatePackage('language-ruby')
await atom.packages.activatePackage(path.join(__dirname, 'fixtures', 'packages', 'package-with-rb-filetype'))
await atom.packages.activatePackage(
path.join(__dirname, 'fixtures', 'packages', 'package-with-rb-filetype')
)
atom.grammars.grammarForScopeName('source.ruby').bundledPackage = true
atom.grammars.grammarForScopeName('test.rb').bundledPackage = false
expect(atom.grammars.selectGrammar('test.rb', '#!/usr/bin/env ruby').scopeName).toBe('source.ruby')
expect(atom.grammars.selectGrammar('test.rb', '#!/usr/bin/env testruby').scopeName).toBe('test.rb')
expect(
atom.grammars.selectGrammar('test.rb', '#!/usr/bin/env ruby').scopeName
).toBe('source.ruby')
expect(
atom.grammars.selectGrammar('test.rb', '#!/usr/bin/env testruby')
.scopeName
).toBe('test.rb')
expect(atom.grammars.selectGrammar('test.rb').scopeName).toBe('test.rb')
})
describe('when there is no file path', () => {
it('does not throw an exception (regression)', () => {
expect(() => atom.grammars.selectGrammar(null, '#!/usr/bin/ruby')).not.toThrow()
expect(() =>
atom.grammars.selectGrammar(null, '#!/usr/bin/ruby')
).not.toThrow()
expect(() => atom.grammars.selectGrammar(null, '')).not.toThrow()
expect(() => atom.grammars.selectGrammar(null, null)).not.toThrow()
})
@@ -362,8 +474,11 @@ describe('GrammarRegistry', () => {
describe('when the user has custom grammar file types', () => {
it('considers the custom file types as well as those defined in the grammar', async () => {
await atom.packages.activatePackage('language-ruby')
atom.config.set('core.customFileTypes', {'source.ruby': ['Cheffile']})
expect(atom.grammars.selectGrammar('build/Cheffile', 'cookbook "postgres"').scopeName).toBe('source.ruby')
atom.config.set('core.customFileTypes', { 'source.ruby': ['Cheffile'] })
expect(
atom.grammars.selectGrammar('build/Cheffile', 'cookbook "postgres"')
.scopeName
).toBe('source.ruby')
})
it('favors user-defined file types over built-in ones of equal length', async () => {
@@ -374,31 +489,50 @@ describe('GrammarRegistry', () => {
'source.coffee': ['Rakefile'],
'source.ruby': ['Cakefile']
})
expect(atom.grammars.selectGrammar('Rakefile', '').scopeName).toBe('source.coffee')
expect(atom.grammars.selectGrammar('Cakefile', '').scopeName).toBe('source.ruby')
expect(atom.grammars.selectGrammar('Rakefile', '').scopeName).toBe(
'source.coffee'
)
expect(atom.grammars.selectGrammar('Cakefile', '').scopeName).toBe(
'source.ruby'
)
})
it('favors user-defined file types over grammars with matching first-line-regexps', async () => {
await atom.packages.activatePackage('language-ruby')
await atom.packages.activatePackage('language-javascript')
atom.config.set('core.customFileTypes', {'source.ruby': ['bootstrap']})
expect(atom.grammars.selectGrammar('bootstrap', '#!/usr/bin/env node').scopeName).toBe('source.ruby')
atom.config.set('core.customFileTypes', {
'source.ruby': ['bootstrap']
})
expect(
atom.grammars.selectGrammar('bootstrap', '#!/usr/bin/env node')
.scopeName
).toBe('source.ruby')
})
})
it('favors a grammar with a matching file type over one with m matching first line pattern', async () => {
await atom.packages.activatePackage('language-ruby')
await atom.packages.activatePackage('language-javascript')
expect(atom.grammars.selectGrammar('foo.rb', '#!/usr/bin/env node').scopeName).toBe('source.ruby')
expect(
atom.grammars.selectGrammar('foo.rb', '#!/usr/bin/env node').scopeName
).toBe('source.ruby')
})
describe('tree-sitter vs text-mate', () => {
it('favors a text-mate grammar over a tree-sitter grammar when `core.useTreeSitterParsers` is false', () => {
atom.config.set('core.useTreeSitterParsers', false, {scopeSelector: '.source.js'})
atom.config.set('core.useTreeSitterParsers', false, {
scopeSelector: '.source.js'
})
grammarRegistry.loadGrammarSync(require.resolve('language-javascript/grammars/javascript.cson'))
grammarRegistry.loadGrammarSync(require.resolve('language-javascript/grammars/tree-sitter-javascript.cson'))
grammarRegistry.loadGrammarSync(
require.resolve('language-javascript/grammars/javascript.cson')
)
grammarRegistry.loadGrammarSync(
require.resolve(
'language-javascript/grammars/tree-sitter-javascript.cson'
)
)
const grammar = grammarRegistry.selectGrammar('test.js')
expect(grammar.scopeName).toBe('source.js')
@@ -408,8 +542,14 @@ describe('GrammarRegistry', () => {
it('favors a tree-sitter grammar over a text-mate grammar when `core.useTreeSitterParsers` is true', () => {
atom.config.set('core.useTreeSitterParsers', true)
grammarRegistry.loadGrammarSync(require.resolve('language-javascript/grammars/javascript.cson'))
grammarRegistry.loadGrammarSync(require.resolve('language-javascript/grammars/tree-sitter-javascript.cson'))
grammarRegistry.loadGrammarSync(
require.resolve('language-javascript/grammars/javascript.cson')
)
grammarRegistry.loadGrammarSync(
require.resolve(
'language-javascript/grammars/tree-sitter-javascript.cson'
)
)
const grammar = grammarRegistry.selectGrammar('test.js')
expect(grammar instanceof TreeSitterGrammar).toBe(true)
@@ -417,7 +557,11 @@ describe('GrammarRegistry', () => {
it('only favors a tree-sitter grammar if it actually matches in some way (regression)', () => {
atom.config.set('core.useTreeSitterParsers', true)
grammarRegistry.loadGrammarSync(require.resolve('language-javascript/grammars/tree-sitter-javascript.cson'))
grammarRegistry.loadGrammarSync(
require.resolve(
'language-javascript/grammars/tree-sitter-javascript.cson'
)
)
const grammar = grammarRegistry.selectGrammar('test', '')
expect(grammar.name).toBe('Null Grammar')
@@ -427,110 +571,164 @@ describe('GrammarRegistry', () => {
describe('tree-sitter grammars with content regexes', () => {
it('recognizes C++ header files', () => {
atom.config.set('core.useTreeSitterParsers', true)
grammarRegistry.loadGrammarSync(require.resolve('language-c/grammars/tree-sitter-c.cson'))
grammarRegistry.loadGrammarSync(require.resolve('language-c/grammars/tree-sitter-cpp.cson'))
grammarRegistry.loadGrammarSync(require.resolve('language-coffee-script/grammars/coffeescript.cson'))
grammarRegistry.loadGrammarSync(
require.resolve('language-c/grammars/tree-sitter-c.cson')
)
grammarRegistry.loadGrammarSync(
require.resolve('language-c/grammars/tree-sitter-cpp.cson')
)
grammarRegistry.loadGrammarSync(
require.resolve('language-coffee-script/grammars/coffeescript.cson')
)
let grammar = grammarRegistry.selectGrammar('test.h', dedent `
let grammar = grammarRegistry.selectGrammar(
'test.h',
dedent`
#include <string.h>
typedef struct {
void verb();
} Noun;
`)
`
)
expect(grammar.name).toBe('C')
grammar = grammarRegistry.selectGrammar('test.h', dedent `
grammar = grammarRegistry.selectGrammar(
'test.h',
dedent`
#include <string>
class Noun {
public:
void verb();
};
`)
`
)
expect(grammar.name).toBe('C++')
// The word `class` only indicates C++ in `.h` files, not in all files.
grammar = grammarRegistry.selectGrammar('test.coffee', dedent `
grammar = grammarRegistry.selectGrammar(
'test.coffee',
dedent`
module.exports =
class Noun
verb: -> true
`)
`
)
expect(grammar.name).toBe('CoffeeScript')
})
it('recognizes C++ files that do not match the content regex (regression)', () => {
atom.config.set('core.useTreeSitterParsers', true)
grammarRegistry.loadGrammarSync(require.resolve('language-c/grammars/tree-sitter-c.cson'))
grammarRegistry.loadGrammarSync(require.resolve('language-c/grammars/c++.cson'))
grammarRegistry.loadGrammarSync(require.resolve('language-c/grammars/tree-sitter-cpp.cson'))
grammarRegistry.loadGrammarSync(
require.resolve('language-c/grammars/tree-sitter-c.cson')
)
grammarRegistry.loadGrammarSync(
require.resolve('language-c/grammars/c++.cson')
)
grammarRegistry.loadGrammarSync(
require.resolve('language-c/grammars/tree-sitter-cpp.cson')
)
let grammar = grammarRegistry.selectGrammar('test.cc', dedent `
let grammar = grammarRegistry.selectGrammar(
'test.cc',
dedent`
int a();
`)
`
)
expect(grammar.name).toBe('C++')
})
it('does not apply content regexes from grammars without filetype or first line matches', () => {
atom.config.set('core.useTreeSitterParsers', true)
grammarRegistry.loadGrammarSync(require.resolve('language-c/grammars/tree-sitter-cpp.cson'))
grammarRegistry.loadGrammarSync(
require.resolve('language-c/grammars/tree-sitter-cpp.cson')
)
let grammar = grammarRegistry.selectGrammar('', dedent `
let grammar = grammarRegistry.selectGrammar(
'',
dedent`
class Foo
# this is ruby, not C++
end
`)
`
)
expect(grammar.name).toBe('Null Grammar')
})
it('recognizes shell scripts with shebang lines', () => {
atom.config.set('core.useTreeSitterParsers', true)
grammarRegistry.loadGrammarSync(require.resolve('language-shellscript/grammars/shell-unix-bash.cson'))
grammarRegistry.loadGrammarSync(require.resolve('language-shellscript/grammars/tree-sitter-bash.cson'))
grammarRegistry.loadGrammarSync(
require.resolve('language-shellscript/grammars/shell-unix-bash.cson')
)
grammarRegistry.loadGrammarSync(
require.resolve('language-shellscript/grammars/tree-sitter-bash.cson')
)
let grammar = grammarRegistry.selectGrammar('test.h', dedent `
let grammar = grammarRegistry.selectGrammar(
'test.h',
dedent`
#!/bin/bash
echo "hi"
`)
`
)
expect(grammar.name).toBe('Shell Script')
expect(grammar instanceof TreeSitterGrammar).toBeTruthy()
grammar = grammarRegistry.selectGrammar('test.h', dedent `
grammar = grammarRegistry.selectGrammar(
'test.h',
dedent`
# vim: set ft=bash
echo "hi"
`)
`
)
expect(grammar.name).toBe('Shell Script')
expect(grammar instanceof TreeSitterGrammar).toBeTruthy()
atom.config.set('core.useTreeSitterParsers', false)
grammar = grammarRegistry.selectGrammar('test.h', dedent `
grammar = grammarRegistry.selectGrammar(
'test.h',
dedent`
#!/bin/bash
echo "hi"
`)
`
)
expect(grammar.name).toBe('Shell Script')
expect(grammar instanceof TreeSitterGrammar).toBeFalsy()
})
it('recognizes JavaScript files that use Flow', () => {
atom.config.set('core.useTreeSitterParsers', true)
grammarRegistry.loadGrammarSync(require.resolve('language-javascript/grammars/tree-sitter-javascript.cson'))
grammarRegistry.loadGrammarSync(require.resolve('language-typescript/grammars/tree-sitter-flow.cson'))
grammarRegistry.loadGrammarSync(
require.resolve(
'language-javascript/grammars/tree-sitter-javascript.cson'
)
)
grammarRegistry.loadGrammarSync(
require.resolve('language-typescript/grammars/tree-sitter-flow.cson')
)
let grammar = grammarRegistry.selectGrammar('test.js', dedent`
let grammar = grammarRegistry.selectGrammar(
'test.js',
dedent`
// Copyright something
// @flow
module.exports = function () { return 1 + 1 }
`)
`
)
expect(grammar.name).toBe('Flow JavaScript')
grammar = grammarRegistry.selectGrammar('test.js', dedent`
grammar = grammarRegistry.selectGrammar(
'test.js',
dedent`
module.exports = function () { return 1 + 1 }
`)
`
)
expect(grammar.name).toBe('JavaScript')
})
})
@@ -548,8 +746,12 @@ describe('GrammarRegistry', () => {
describe('.addInjectionPoint(languageId, {type, language, content})', () => {
const injectionPoint = {
type: 'some_node_type',
language() { return 'some_language_name' },
content(node) { return node }
language () {
return 'some_language_name'
},
content (node) {
return node
}
}
beforeEach(() => {
@@ -574,13 +776,19 @@ describe('GrammarRegistry', () => {
})
describe('serialization', () => {
it('persists editors\' grammar overrides', async () => {
it("persists editors' grammar overrides", async () => {
const buffer1 = new TextBuffer()
const buffer2 = new TextBuffer()
grammarRegistry.loadGrammarSync(require.resolve('language-c/grammars/c.cson'))
grammarRegistry.loadGrammarSync(require.resolve('language-html/grammars/html.cson'))
grammarRegistry.loadGrammarSync(require.resolve('language-javascript/grammars/javascript.cson'))
grammarRegistry.loadGrammarSync(
require.resolve('language-c/grammars/c.cson')
)
grammarRegistry.loadGrammarSync(
require.resolve('language-html/grammars/html.cson')
)
grammarRegistry.loadGrammarSync(
require.resolve('language-javascript/grammars/javascript.cson')
)
grammarRegistry.maintainLanguageMode(buffer1)
grammarRegistry.maintainLanguageMode(buffer2)
@@ -590,11 +798,17 @@ describe('GrammarRegistry', () => {
const buffer1Copy = await TextBuffer.deserialize(buffer1.serialize())
const buffer2Copy = await TextBuffer.deserialize(buffer2.serialize())
const grammarRegistryCopy = new GrammarRegistry({config: atom.config})
grammarRegistryCopy.deserialize(JSON.parse(JSON.stringify(grammarRegistry.serialize())))
const grammarRegistryCopy = new GrammarRegistry({ config: atom.config })
grammarRegistryCopy.deserialize(
JSON.parse(JSON.stringify(grammarRegistry.serialize()))
)
grammarRegistryCopy.loadGrammarSync(require.resolve('language-c/grammars/c.cson'))
grammarRegistryCopy.loadGrammarSync(require.resolve('language-html/grammars/html.cson'))
grammarRegistryCopy.loadGrammarSync(
require.resolve('language-c/grammars/c.cson')
)
grammarRegistryCopy.loadGrammarSync(
require.resolve('language-html/grammars/html.cson')
)
expect(buffer1Copy.getLanguageMode().getLanguageId()).toBe(null)
expect(buffer2Copy.getLanguageMode().getLanguageId()).toBe(null)
@@ -604,7 +818,9 @@ describe('GrammarRegistry', () => {
expect(buffer1Copy.getLanguageMode().getLanguageId()).toBe('source.c')
expect(buffer2Copy.getLanguageMode().getLanguageId()).toBe(null)
grammarRegistryCopy.loadGrammarSync(require.resolve('language-javascript/grammars/javascript.cson'))
grammarRegistryCopy.loadGrammarSync(
require.resolve('language-javascript/grammars/javascript.cson')
)
expect(buffer1Copy.getLanguageMode().getLanguageId()).toBe('source.c')
expect(buffer2Copy.getLanguageMode().getLanguageId()).toBe('source.js')
})

View File

@@ -14,26 +14,28 @@ describe('GutterContainer', () => {
describe('when initialized', () =>
it('it has no gutters', () => {
expect(gutterContainer.getGutters().length).toBe(0)
})
)
}))
describe('::addGutter', () => {
it('creates a new gutter', () => {
const newGutter = gutterContainer.addGutter({'test-gutter': 'test-gutter', priority: 1})
const newGutter = gutterContainer.addGutter({
'test-gutter': 'test-gutter',
priority: 1
})
expect(gutterContainer.getGutters()).toEqual([newGutter])
expect(newGutter.priority).toBe(1)
})
it('throws an error if the provided gutter name is already in use', () => {
const name = 'test-gutter'
gutterContainer.addGutter({name})
expect(gutterContainer.addGutter.bind(null, {name})).toThrow()
gutterContainer.addGutter({ name })
expect(gutterContainer.addGutter.bind(null, { name })).toThrow()
})
it('keeps added gutters sorted by ascending priority', () => {
const gutter1 = gutterContainer.addGutter({name: 'first', priority: 1})
const gutter3 = gutterContainer.addGutter({name: 'third', priority: 3})
const gutter2 = gutterContainer.addGutter({name: 'second', priority: 2})
const gutter1 = gutterContainer.addGutter({ name: 'first', priority: 1 })
const gutter3 = gutterContainer.addGutter({ name: 'third', priority: 3 })
const gutter2 = gutterContainer.addGutter({ name: 'second', priority: 2 })
expect(gutterContainer.getGutters()).toEqual([gutter1, gutter2, gutter3])
})
})
@@ -44,11 +46,13 @@ describe('GutterContainer', () => {
beforeEach(function () {
gutterContainer = new GutterContainer(fakeTextEditor)
removedGutters = []
gutterContainer.onDidRemoveGutter(gutterName => removedGutters.push(gutterName))
gutterContainer.onDidRemoveGutter(gutterName =>
removedGutters.push(gutterName)
)
})
it('removes the gutter if it is contained by this GutterContainer', () => {
const gutter = gutterContainer.addGutter({'test-gutter': 'test-gutter'})
const gutter = gutterContainer.addGutter({ 'test-gutter': 'test-gutter' })
expect(gutterContainer.getGutters()).toEqual([gutter])
gutterContainer.removeGutter(gutter)
expect(gutterContainer.getGutters().length).toBe(0)
@@ -65,13 +69,15 @@ describe('GutterContainer', () => {
describe('::destroy', () =>
it('clears its array of gutters and destroys custom gutters', () => {
const newGutter = gutterContainer.addGutter({'test-gutter': 'test-gutter', priority: 1})
const newGutter = gutterContainer.addGutter({
'test-gutter': 'test-gutter',
priority: 1
})
const newGutterSpy = jasmine.createSpy()
newGutter.onDidDestroy(newGutterSpy)
gutterContainer.destroy()
expect(newGutterSpy).toHaveBeenCalled()
expect(gutterContainer.getGutters()).toEqual([])
})
)
}))
})

View File

@@ -24,8 +24,7 @@ describe('Gutter', () => {
expect(gutter.isVisible()).toBe(false)
// An event should only be emitted when the visibility changes.
expect(events.length).toBe(1)
})
)
}))
describe('::show', () =>
it('shows the gutter if it is hidden.', () => {
@@ -45,8 +44,7 @@ describe('Gutter', () => {
expect(gutter.isVisible()).toBe(true)
// An event should only be emitted when the visibility changes.
expect(events.length).toBe(1)
})
)
}))
describe('::destroy', () => {
let mockGutterContainer, mockGutterContainerRemovedGutters
@@ -61,21 +59,23 @@ describe('Gutter', () => {
})
it('removes the gutter from its container.', () => {
const gutter = new Gutter(mockGutterContainer, {name})
const gutter = new Gutter(mockGutterContainer, { name })
gutter.destroy()
expect(mockGutterContainerRemovedGutters).toEqual([gutter])
})
it('calls all callbacks registered on ::onDidDestroy.', () => {
const gutter = new Gutter(mockGutterContainer, {name})
const gutter = new Gutter(mockGutterContainer, { name })
let didDestroy = false
gutter.onDidDestroy(() => { didDestroy = true })
gutter.onDidDestroy(() => {
didDestroy = true
})
gutter.destroy()
expect(didDestroy).toBe(true)
})
it('does not allow destroying the line-number gutter', () => {
const gutter = new Gutter(mockGutterContainer, {name: 'line-number'})
const gutter = new Gutter(mockGutterContainer, { name: 'line-number' })
expect(gutter.destroy).toThrow()
})
})

View File

@@ -1,5 +1,5 @@
const WORDS = require('./words')
const {Point, Range} = require('text-buffer')
const { Point, Range } = require('text-buffer')
exports.getRandomBufferRange = function getRandomBufferRange (random, buffer) {
const endRow = random(buffer.getLineCount())

View File

@@ -1,10 +1,9 @@
const {it, fit, ffit, fffit, beforeEach, afterEach} = require('./async-spec-helpers')
const {Emitter, Disposable, CompositeDisposable} = require('event-kit')
const { it, beforeEach, afterEach } = require('./async-spec-helpers')
const {HistoryManager, HistoryProject} = require('../src/history-manager')
const { HistoryManager, HistoryProject } = require('../src/history-manager')
const StateStore = require('../src/state-store')
describe("HistoryManager", () => {
describe('HistoryManager', () => {
let historyManager, commandRegistry, project, stateStore
let commandDisposable, projectDisposable
@@ -16,19 +15,26 @@ describe("HistoryManager", () => {
stateStore = new StateStore('history-manager-test', 1)
await stateStore.save('history-manager', {
projects: [
{paths: ['/1', 'c:\\2'], lastOpened: new Date(2016, 9, 17, 17, 16, 23)},
{paths: ['/test'], lastOpened: new Date(2016, 9, 17, 11, 12, 13)}
{
paths: ['/1', 'c:\\2'],
lastOpened: new Date(2016, 9, 17, 17, 16, 23)
},
{ paths: ['/test'], lastOpened: new Date(2016, 9, 17, 11, 12, 13) }
]
})
projectDisposable = jasmine.createSpyObj('Disposable', ['dispose'])
project = jasmine.createSpyObj('Project', ['onDidChangePaths'])
project.onDidChangePaths.andCallFake((f) => {
project.onDidChangePaths.andCallFake(f => {
project.didChangePathsListener = f
return projectDisposable
})
historyManager = new HistoryManager({stateStore, project, commands: commandRegistry})
historyManager = new HistoryManager({
stateStore,
project,
commands: commandRegistry
})
await historyManager.loadState()
})
@@ -36,24 +42,29 @@ describe("HistoryManager", () => {
await stateStore.clear()
})
describe("constructor", () => {
describe('constructor', () => {
it("registers the 'clear-project-history' command function", () => {
expect(commandRegistry.add).toHaveBeenCalled()
const cmdCall = commandRegistry.add.calls[0]
expect(cmdCall.args.length).toBe(3)
expect(cmdCall.args[0]).toBe('atom-workspace')
expect(typeof cmdCall.args[1]['application:clear-project-history']).toBe('function')
expect(typeof cmdCall.args[1]['application:clear-project-history']).toBe(
'function'
)
})
describe("getProjects", () => {
it("returns an array of HistoryProjects", () => {
describe('getProjects', () => {
it('returns an array of HistoryProjects', () => {
expect(historyManager.getProjects()).toEqual([
new HistoryProject(['/1', 'c:\\2'], new Date(2016, 9, 17, 17, 16, 23)),
new HistoryProject(
['/1', 'c:\\2'],
new Date(2016, 9, 17, 17, 16, 23)
),
new HistoryProject(['/test'], new Date(2016, 9, 17, 11, 12, 13))
])
})
it("returns an array of HistoryProjects that is not mutable state", () => {
it('returns an array of HistoryProjects that is not mutable state', () => {
const firstProjects = historyManager.getProjects()
firstProjects.pop()
firstProjects[0].path = 'modified'
@@ -64,21 +75,25 @@ describe("HistoryManager", () => {
})
})
describe("clearProjects", () => {
it("clears the list of projects", async () => {
describe('clearProjects', () => {
it('clears the list of projects', async () => {
expect(historyManager.getProjects().length).not.toBe(0)
await historyManager.clearProjects()
expect(historyManager.getProjects().length).toBe(0)
})
it("saves the state", async () => {
it('saves the state', async () => {
await historyManager.clearProjects()
const historyManager2 = new HistoryManager({stateStore, project, commands: commandRegistry})
const historyManager2 = new HistoryManager({
stateStore,
project,
commands: commandRegistry
})
await historyManager2.loadState()
expect(historyManager.getProjects().length).toBe(0)
})
it("fires the onDidChangeProjects event", async () => {
it('fires the onDidChangeProjects event', async () => {
const didChangeSpy = jasmine.createSpy()
historyManager.onDidChangeProjects(didChangeSpy)
await historyManager.clearProjects()
@@ -87,7 +102,7 @@ describe("HistoryManager", () => {
})
})
it("listens to project.onDidChangePaths adding a new project", () => {
it('listens to project.onDidChangePaths adding a new project', () => {
const start = new Date()
project.didChangePathsListener(['/a/new', '/path/or/two'])
const projects = historyManager.getProjects()
@@ -96,7 +111,7 @@ describe("HistoryManager", () => {
expect(projects[0].lastOpened).not.toBeLessThan(start)
})
it("listens to project.onDidChangePaths updating an existing project", () => {
it('listens to project.onDidChangePaths updating an existing project', () => {
const start = new Date()
project.didChangePathsListener(['/test'])
const projects = historyManager.getProjects()
@@ -106,22 +121,22 @@ describe("HistoryManager", () => {
})
})
describe("loadState", () => {
it("defaults to an empty array if no state", async () => {
describe('loadState', () => {
it('defaults to an empty array if no state', async () => {
await stateStore.clear()
await historyManager.loadState()
expect(historyManager.getProjects()).toEqual([])
})
it("defaults to an empty array if no projects", async () => {
it('defaults to an empty array if no projects', async () => {
await stateStore.save('history-manager', {})
await historyManager.loadState()
expect(historyManager.getProjects()).toEqual([])
})
})
describe("addProject", () => {
it("adds a new project to the end", async () => {
describe('addProject', () => {
it('adds a new project to the end', async () => {
const date = new Date(2010, 10, 9, 8, 7, 6)
await historyManager.addProject(['/a/b'], date)
const projects = historyManager.getProjects()
@@ -130,7 +145,7 @@ describe("HistoryManager", () => {
expect(projects[2].lastOpened).toBe(date)
})
it("adds a new project to the start", async () => {
it('adds a new project to the start', async () => {
const date = new Date()
await historyManager.addProject(['/so/new'], date)
const projects = historyManager.getProjects()
@@ -139,7 +154,7 @@ describe("HistoryManager", () => {
expect(projects[0].lastOpened).toBe(date)
})
it("updates an existing project and moves it to the start", async () => {
it('updates an existing project and moves it to the start', async () => {
const date = new Date()
await historyManager.addProject(['/test'], date)
const projects = historyManager.getProjects()
@@ -148,7 +163,7 @@ describe("HistoryManager", () => {
expect(projects[0].lastOpened).toBe(date)
})
it("fires the onDidChangeProjects event when adding a project", async () => {
it('fires the onDidChangeProjects event when adding a project', async () => {
const didChangeSpy = jasmine.createSpy()
const beforeCount = historyManager.getProjects().length
historyManager.onDidChangeProjects(didChangeSpy)
@@ -157,7 +172,7 @@ describe("HistoryManager", () => {
expect(historyManager.getProjects().length).toBe(beforeCount + 1)
})
it("fires the onDidChangeProjects event when updating a project", async () => {
it('fires the onDidChangeProjects event when updating a project', async () => {
const didChangeSpy = jasmine.createSpy()
const beforeCount = historyManager.getProjects().length
historyManager.onDidChangeProjects(didChangeSpy)
@@ -167,8 +182,8 @@ describe("HistoryManager", () => {
})
})
describe("getProject", () => {
it("returns a project that matches the paths", () => {
describe('getProject', () => {
it('returns a project that matches the paths', () => {
const project = historyManager.getProject(['/1', 'c:\\2'])
expect(project).not.toBeNull()
expect(project.paths).toEqual(['/1', 'c:\\2'])
@@ -180,7 +195,7 @@ describe("HistoryManager", () => {
})
})
describe("saveState", () => {
describe('saveState', () => {
let savedHistory
beforeEach(() => {
// historyManager.saveState is spied on globally to prevent specs from
@@ -195,11 +210,17 @@ describe("HistoryManager", () => {
})
})
it("saves the state", async () => {
await historyManager.addProject(["/save/state"])
it('saves the state', async () => {
await historyManager.addProject(['/save/state'])
await historyManager.saveState()
const historyManager2 = new HistoryManager({stateStore, project, commands: commandRegistry})
spyOn(historyManager2.stateStore, 'load').andCallFake(name => Promise.resolve(savedHistory))
const historyManager2 = new HistoryManager({
stateStore,
project,
commands: commandRegistry
})
spyOn(historyManager2.stateStore, 'load').andCallFake(name =>
Promise.resolve(savedHistory)
)
await historyManager2.loadState()
expect(historyManager2.getProjects()[0].paths).toEqual(['/save/state'])
})

View File

@@ -1,4 +1,4 @@
const {TerminalReporter} = require('jasmine-tagged')
const { TerminalReporter } = require('jasmine-tagged')
class JasmineListReporter extends TerminalReporter {
fullDescription (spec) {

View File

@@ -9,14 +9,21 @@ const path = require('path')
const sinon = require('sinon')
const AtomApplication = require('../../src/main-process/atom-application')
const parseCommandLine = require('../../src/main-process/parse-command-line')
const {timeoutPromise, conditionPromise, emitterEventPromise} = require('../async-spec-helpers')
const {
timeoutPromise,
conditionPromise,
emitterEventPromise
} = require('../async-spec-helpers')
const ATOM_RESOURCE_PATH = path.resolve(__dirname, '..', '..')
describe('AtomApplication', function () {
this.timeout(60 * 1000)
let originalAppQuit, originalShowMessageBox, originalAtomHome, atomApplicationsToDestroy
let originalAppQuit,
originalShowMessageBox,
originalAtomHome,
atomApplicationsToDestroy
beforeEach(() => {
originalAppQuit = electron.app.quit
@@ -25,11 +32,15 @@ describe('AtomApplication', function () {
originalAtomHome = process.env.ATOM_HOME
process.env.ATOM_HOME = makeTempDir('atom-home')
// Symlinking the compile cache into the temporary home dir makes the windows load much faster
fs.symlinkSync(path.join(originalAtomHome, 'compile-cache'), path.join(process.env.ATOM_HOME, 'compile-cache'), 'junction')
fs.symlinkSync(
path.join(originalAtomHome, 'compile-cache'),
path.join(process.env.ATOM_HOME, 'compile-cache'),
'junction'
)
season.writeFileSync(path.join(process.env.ATOM_HOME, 'config.cson'), {
'*': {
welcome: {showOnStartup: false},
core: {telemetryConsent: 'no'}
welcome: { showOnStartup: false },
core: { telemetryConsent: 'no' }
}
})
atomApplicationsToDestroy = []
@@ -54,10 +65,14 @@ describe('AtomApplication', function () {
const tempDirPath2 = makeTempDir()
const atomApplication1 = buildAtomApplication()
const [app1Window1] = await atomApplication1.launch(parseCommandLine([tempDirPath1]))
const [app1Window1] = await atomApplication1.launch(
parseCommandLine([tempDirPath1])
)
await emitterEventPromise(app1Window1, 'window:locations-opened')
const [app1Window2] = await atomApplication1.launch(parseCommandLine([tempDirPath2]))
const [app1Window2] = await atomApplication1.launch(
parseCommandLine([tempDirPath2])
)
await emitterEventPromise(app1Window2, 'window:locations-opened')
await Promise.all([
@@ -66,30 +81,46 @@ describe('AtomApplication', function () {
])
const atomApplication2 = buildAtomApplication()
const [app2Window1, app2Window2] = await atomApplication2.launch(parseCommandLine([]))
const [app2Window1, app2Window2] = await atomApplication2.launch(
parseCommandLine([])
)
await Promise.all([
emitterEventPromise(app2Window1, 'window:locations-opened'),
emitterEventPromise(app2Window2, 'window:locations-opened')
])
assert.deepEqual(await getTreeViewRootDirectories(app2Window1), [tempDirPath1])
assert.deepEqual(await getTreeViewRootDirectories(app2Window2), [tempDirPath2])
assert.deepEqual(await getTreeViewRootDirectories(app2Window1), [
tempDirPath1
])
assert.deepEqual(await getTreeViewRootDirectories(app2Window2), [
tempDirPath2
])
})
it('when windows already exist, opens a new window with a single untitled buffer', async () => {
const atomApplication = buildAtomApplication()
const [window1] = await atomApplication.launch(parseCommandLine([]))
await focusWindow(window1)
const window1EditorTitle = await evalInWebContents(window1.browserWindow.webContents, sendBackToMainProcess => {
sendBackToMainProcess(atom.workspace.getActiveTextEditor().getTitle())
})
const window1EditorTitle = await evalInWebContents(
window1.browserWindow.webContents,
sendBackToMainProcess => {
sendBackToMainProcess(
atom.workspace.getActiveTextEditor().getTitle()
)
}
)
assert.equal(window1EditorTitle, 'untitled')
const window2 = atomApplication.openWithOptions(parseCommandLine([]))
await window2.loadedPromise
const window2EditorTitle = await evalInWebContents(window1.browserWindow.webContents, sendBackToMainProcess => {
sendBackToMainProcess(atom.workspace.getActiveTextEditor().getTitle())
})
const window2EditorTitle = await evalInWebContents(
window1.browserWindow.webContents,
sendBackToMainProcess => {
sendBackToMainProcess(
atom.workspace.getActiveTextEditor().getTitle()
)
}
)
assert.equal(window2EditorTitle, 'untitled')
assert.deepEqual(atomApplication.getAllWindows(), [window2, window1])
@@ -101,10 +132,14 @@ describe('AtomApplication', function () {
const tempDirPath2 = makeTempDir()
const atomApplication1 = buildAtomApplication()
const [app1Window1] = await atomApplication1.launch(parseCommandLine([tempDirPath1]))
const [app1Window1] = await atomApplication1.launch(
parseCommandLine([tempDirPath1])
)
await emitterEventPromise(app1Window1, 'window:locations-opened')
const [app1Window2] = await atomApplication1.launch(parseCommandLine([tempDirPath2]))
const [app1Window2] = await atomApplication1.launch(
parseCommandLine([tempDirPath2])
)
await emitterEventPromise(app1Window2, 'window:locations-opened')
await Promise.all([
@@ -114,13 +149,20 @@ describe('AtomApplication', function () {
// Launch with --new-window
const atomApplication2 = buildAtomApplication()
const appWindows2 = await atomApplication2.launch(parseCommandLine(['--new-window']))
const appWindows2 = await atomApplication2.launch(
parseCommandLine(['--new-window'])
)
assert.lengthOf(appWindows2, 1)
const [appWindow2] = appWindows2
await appWindow2.loadedPromise
const window2EditorTitle = await evalInWebContents(appWindow2.browserWindow.webContents, sendBackToMainProcess => {
sendBackToMainProcess(atom.workspace.getActiveTextEditor().getTitle())
})
const window2EditorTitle = await evalInWebContents(
appWindow2.browserWindow.webContents,
sendBackToMainProcess => {
sendBackToMainProcess(
atom.workspace.getActiveTextEditor().getTitle()
)
}
)
assert.equal(window2EditorTitle, 'untitled')
})
@@ -135,12 +177,17 @@ describe('AtomApplication', function () {
const [window1] = await atomApplication.launch(parseCommandLine([]))
await focusWindow(window1)
// wait a bit just to make sure we don't pass due to querying the render process before it loads
// wait a bit just to make sure we don't pass due to querying the render process before it loads
await timeoutPromise(1000)
const itemCount = await evalInWebContents(window1.browserWindow.webContents, sendBackToMainProcess => {
sendBackToMainProcess(atom.workspace.getActivePane().getItems().length)
})
const itemCount = await evalInWebContents(
window1.browserWindow.webContents,
sendBackToMainProcess => {
sendBackToMainProcess(
atom.workspace.getActivePane().getItems().length
)
}
)
assert.equal(itemCount, 0)
})
})
@@ -153,11 +200,18 @@ describe('AtomApplication', function () {
fs.mkdirSync(dirBSubdirPath)
const atomApplication = buildAtomApplication()
const [window1] = await atomApplication.launch(parseCommandLine([dirAPath, dirBPath]))
const [window1] = await atomApplication.launch(
parseCommandLine([dirAPath, dirBPath])
)
await focusWindow(window1)
await conditionPromise(async () => (await getTreeViewRootDirectories(window1)).length === 2)
assert.deepEqual(await getTreeViewRootDirectories(window1), [dirAPath, dirBPath])
await conditionPromise(
async () => (await getTreeViewRootDirectories(window1)).length === 2
)
assert.deepEqual(await getTreeViewRootDirectories(window1), [
dirAPath,
dirBPath
])
})
it('can open to a specific line number of a file', async () => {
@@ -165,14 +219,19 @@ describe('AtomApplication', function () {
fs.writeFileSync(filePath, '1\n2\n3\n4\n')
const atomApplication = buildAtomApplication()
const [window] = await atomApplication.launch(parseCommandLine([filePath + ':3']))
const [window] = await atomApplication.launch(
parseCommandLine([filePath + ':3'])
)
await focusWindow(window)
const cursorRow = await evalInWebContents(window.browserWindow.webContents, sendBackToMainProcess => {
atom.workspace.observeTextEditors(textEditor => {
sendBackToMainProcess(textEditor.getCursorBufferPosition().row)
})
})
const cursorRow = await evalInWebContents(
window.browserWindow.webContents,
sendBackToMainProcess => {
atom.workspace.observeTextEditors(textEditor => {
sendBackToMainProcess(textEditor.getCursorBufferPosition().row)
})
}
)
assert.equal(cursorRow, 2)
})
@@ -182,16 +241,21 @@ describe('AtomApplication', function () {
fs.writeFileSync(filePath, '1\n2\n3\n4\n')
const atomApplication = buildAtomApplication()
const [window] = await atomApplication.launch(parseCommandLine([filePath + ':2:2']))
const [window] = await atomApplication.launch(
parseCommandLine([filePath + ':2:2'])
)
await focusWindow(window)
const cursorPosition = await evalInWebContents(window.browserWindow.webContents, sendBackToMainProcess => {
atom.workspace.observeTextEditors(textEditor => {
sendBackToMainProcess(textEditor.getCursorBufferPosition())
})
})
const cursorPosition = await evalInWebContents(
window.browserWindow.webContents,
sendBackToMainProcess => {
atom.workspace.observeTextEditors(textEditor => {
sendBackToMainProcess(textEditor.getCursorBufferPosition())
})
}
)
assert.deepEqual(cursorPosition, {row: 1, column: 1})
assert.deepEqual(cursorPosition, { row: 1, column: 1 })
})
it('removes all trailing whitespace and colons from the specified path', async () => {
@@ -199,31 +263,44 @@ describe('AtomApplication', function () {
fs.writeFileSync(filePath, '1\n2\n3\n4\n')
const atomApplication = buildAtomApplication()
const [window] = await atomApplication.launch(parseCommandLine([filePath + ':: ']))
const [window] = await atomApplication.launch(
parseCommandLine([filePath + ':: '])
)
await focusWindow(window)
const openedPath = await evalInWebContents(window.browserWindow.webContents, sendBackToMainProcess => {
atom.workspace.observeTextEditors(textEditor => {
sendBackToMainProcess(textEditor.getPath())
})
})
const openedPath = await evalInWebContents(
window.browserWindow.webContents,
sendBackToMainProcess => {
atom.workspace.observeTextEditors(textEditor => {
sendBackToMainProcess(textEditor.getPath())
})
}
)
assert.equal(openedPath, filePath)
})
it('opens an empty text editor when launched with a new file path', async () => {
// Choosing "Don't save"
mockElectronShowMessageBox({response: 2})
mockElectronShowMessageBox({ response: 2 })
const atomApplication = buildAtomApplication()
const newFilePath = path.join(makeTempDir(), 'new-file')
const [window] = await atomApplication.launch(parseCommandLine([newFilePath]))
const [window] = await atomApplication.launch(
parseCommandLine([newFilePath])
)
await focusWindow(window)
const {editorTitle, editorText} = await evalInWebContents(window.browserWindow.webContents, sendBackToMainProcess => {
atom.workspace.observeTextEditors(editor => {
sendBackToMainProcess({editorTitle: editor.getTitle(), editorText: editor.getText()})
})
})
const { editorTitle, editorText } = await evalInWebContents(
window.browserWindow.webContents,
sendBackToMainProcess => {
atom.workspace.observeTextEditors(editor => {
sendBackToMainProcess({
editorTitle: editor.getTitle(),
editorText: editor.getText()
})
})
}
)
assert.equal(editorTitle, path.basename(newFilePath))
assert.equal(editorText, '')
assert.deepEqual(await getTreeViewRootDirectories(window), [])
@@ -239,32 +316,51 @@ describe('AtomApplication', function () {
fs.writeFileSync(existingDirCFilePath, 'this is an existing file')
const atomApplication = buildAtomApplication()
const [window1] = await atomApplication.launch(parseCommandLine([dirAPath]))
const [window1] = await atomApplication.launch(
parseCommandLine([dirAPath])
)
await focusWindow(window1)
await conditionPromise(async () => (await getTreeViewRootDirectories(window1)).length === 1)
await conditionPromise(
async () => (await getTreeViewRootDirectories(window1)).length === 1
)
assert.deepEqual(await getTreeViewRootDirectories(window1), [dirAPath])
// When opening *files* with --add, reuses an existing window
let [reusedWindow] = await atomApplication.launch(parseCommandLine([existingDirCFilePath, '--add']))
let [reusedWindow] = await atomApplication.launch(
parseCommandLine([existingDirCFilePath, '--add'])
)
assert.equal(reusedWindow, window1)
assert.deepEqual(atomApplication.getAllWindows(), [window1])
let activeEditorPath = await evalInWebContents(window1.browserWindow.webContents, sendBackToMainProcess => {
const subscription = atom.workspace.onDidChangeActivePaneItem(textEditor => {
sendBackToMainProcess(textEditor.getPath())
subscription.dispose()
})
})
let activeEditorPath = await evalInWebContents(
window1.browserWindow.webContents,
sendBackToMainProcess => {
const subscription = atom.workspace.onDidChangeActivePaneItem(
textEditor => {
sendBackToMainProcess(textEditor.getPath())
subscription.dispose()
}
)
}
)
assert.equal(activeEditorPath, existingDirCFilePath)
assert.deepEqual(await getTreeViewRootDirectories(window1), [dirAPath])
// When opening *directories* with --add, reuses an existing window and adds the directory to the project
reusedWindow = (await atomApplication.launch(parseCommandLine([dirBPath, '-a'])))[0]
reusedWindow = (await atomApplication.launch(
parseCommandLine([dirBPath, '-a'])
))[0]
assert.equal(reusedWindow, window1)
assert.deepEqual(atomApplication.getAllWindows(), [window1])
await conditionPromise(async () => (await getTreeViewRootDirectories(reusedWindow)).length === 2)
assert.deepEqual(await getTreeViewRootDirectories(window1), [dirAPath, dirBPath])
await conditionPromise(
async () =>
(await getTreeViewRootDirectories(reusedWindow)).length === 2
)
assert.deepEqual(await getTreeViewRootDirectories(window1), [
dirAPath,
dirBPath
])
})
})
@@ -272,11 +368,15 @@ describe('AtomApplication', function () {
it('positions new windows at an offset distance from the previous window', async () => {
const atomApplication = buildAtomApplication()
const [window1] = await atomApplication.launch(parseCommandLine([makeTempDir()]))
const [window1] = await atomApplication.launch(
parseCommandLine([makeTempDir()])
)
await focusWindow(window1)
window1.browserWindow.setBounds({width: 400, height: 400, x: 0, y: 0})
window1.browserWindow.setBounds({ width: 400, height: 400, x: 0, y: 0 })
const [window2] = await atomApplication.launch(parseCommandLine([makeTempDir()]))
const [window2] = await atomApplication.launch(
parseCommandLine([makeTempDir()])
)
await focusWindow(window2)
assert.notEqual(window1, window2)
@@ -289,70 +389,109 @@ describe('AtomApplication', function () {
it('persists window state based on the project directories', async () => {
// Choosing "Don't save"
mockElectronShowMessageBox({response: 2})
mockElectronShowMessageBox({ response: 2 })
const tempDirPath = makeTempDir()
const atomApplication = buildAtomApplication()
const nonExistentFilePath = path.join(tempDirPath, 'new-file')
const [window1] = await atomApplication.launch(parseCommandLine([tempDirPath, nonExistentFilePath]))
await evalInWebContents(window1.browserWindow.webContents, sendBackToMainProcess => {
atom.workspace.observeTextEditors(textEditor => {
textEditor.insertText('Hello World!')
sendBackToMainProcess(null)
})
})
const [window1] = await atomApplication.launch(
parseCommandLine([tempDirPath, nonExistentFilePath])
)
await evalInWebContents(
window1.browserWindow.webContents,
sendBackToMainProcess => {
atom.workspace.observeTextEditors(textEditor => {
textEditor.insertText('Hello World!')
sendBackToMainProcess(null)
})
}
)
await window1.prepareToUnload()
window1.close()
await window1.closedPromise
// Restore unsaved state when opening the same project directory
const [window2] = await atomApplication.launch(parseCommandLine([tempDirPath]))
const [window2] = await atomApplication.launch(
parseCommandLine([tempDirPath])
)
await window2.loadedPromise
const window2Text = await evalInWebContents(window2.browserWindow.webContents, sendBackToMainProcess => {
const textEditor = atom.workspace.getActiveTextEditor()
textEditor.moveToBottom()
textEditor.insertText(' How are you?')
sendBackToMainProcess(textEditor.getText())
})
const window2Text = await evalInWebContents(
window2.browserWindow.webContents,
sendBackToMainProcess => {
const textEditor = atom.workspace.getActiveTextEditor()
textEditor.moveToBottom()
textEditor.insertText(' How are you?')
sendBackToMainProcess(textEditor.getText())
}
)
assert.equal(window2Text, 'Hello World! How are you?')
})
it('adds a remote directory to the project when launched with a remote directory', async () => {
const packagePath = path.join(__dirname, '..', 'fixtures', 'packages', 'package-with-directory-provider')
const packagePath = path.join(
__dirname,
'..',
'fixtures',
'packages',
'package-with-directory-provider'
)
const packagesDirPath = path.join(process.env.ATOM_HOME, 'packages')
fs.mkdirSync(packagesDirPath)
fs.symlinkSync(packagePath, path.join(packagesDirPath, 'package-with-directory-provider'), 'junction')
fs.symlinkSync(
packagePath,
path.join(packagesDirPath, 'package-with-directory-provider'),
'junction'
)
const atomApplication = buildAtomApplication()
atomApplication.config.set('core.disabledPackages', ['fuzzy-finder'])
const remotePath = 'remote://server:3437/some/directory/path'
let [window] = await atomApplication.launch(parseCommandLine([remotePath]))
let [window] = await atomApplication.launch(
parseCommandLine([remotePath])
)
await focusWindow(window)
await conditionPromise(async () => (await getProjectDirectories()).length > 0)
await conditionPromise(
async () => (await getProjectDirectories()).length > 0
)
let directories = await getProjectDirectories()
assert.deepEqual(directories, [{type: 'FakeRemoteDirectory', path: remotePath}])
assert.deepEqual(directories, [
{ type: 'FakeRemoteDirectory', path: remotePath }
])
await window.reload()
await focusWindow(window)
directories = await getProjectDirectories()
assert.deepEqual(directories, [{type: 'FakeRemoteDirectory', path: remotePath}])
assert.deepEqual(directories, [
{ type: 'FakeRemoteDirectory', path: remotePath }
])
function getProjectDirectories () {
return evalInWebContents(window.browserWindow.webContents, sendBackToMainProcess => {
sendBackToMainProcess(atom.project.getDirectories().map(d => ({ type: d.constructor.name, path: d.getPath() })))
})
return evalInWebContents(
window.browserWindow.webContents,
sendBackToMainProcess => {
sendBackToMainProcess(
atom.project
.getDirectories()
.map(d => ({ type: d.constructor.name, path: d.getPath() }))
)
}
)
}
})
it('does not reopen any previously opened windows when launched with no path and `core.restorePreviousWindowsOnStart` is no', async () => {
const atomApplication1 = buildAtomApplication()
const [app1Window1] = await atomApplication1.launch(parseCommandLine([makeTempDir()]))
const [app1Window1] = await atomApplication1.launch(
parseCommandLine([makeTempDir()])
)
await focusWindow(app1Window1)
const [app1Window2] = await atomApplication1.launch(parseCommandLine([makeTempDir()]))
const [app1Window2] = await atomApplication1.launch(
parseCommandLine([makeTempDir()])
)
await focusWindow(app1Window2)
const configPath = path.join(process.env.ATOM_HOME, 'config.cson')
@@ -382,19 +521,27 @@ describe('AtomApplication', function () {
})
it('kills the specified pid after a newly-opened window is closed', async () => {
const [window1] = await atomApplication.launch(parseCommandLine(['--wait', '--pid', '101']))
const [window1] = await atomApplication.launch(
parseCommandLine(['--wait', '--pid', '101'])
)
await focusWindow(window1)
const [window2] = await atomApplication.launch(parseCommandLine(['--new-window', '--wait', '--pid', '102']))
const [window2] = await atomApplication.launch(
parseCommandLine(['--new-window', '--wait', '--pid', '102'])
)
await focusWindow(window2)
assert.deepEqual(killedPids, [])
let processKillPromise = new Promise(resolve => { onDidKillProcess = resolve })
let processKillPromise = new Promise(resolve => {
onDidKillProcess = resolve
})
window1.close()
await processKillPromise
assert.deepEqual(killedPids, [101])
processKillPromise = new Promise(resolve => { onDidKillProcess = resolve })
processKillPromise = new Promise(resolve => {
onDidKillProcess = resolve
})
window2.close()
await processKillPromise
assert.deepEqual(killedPids, [101, 102])
@@ -407,18 +554,34 @@ describe('AtomApplication', function () {
fs.writeFileSync(filePath1, 'File 1')
fs.writeFileSync(filePath2, 'File 2')
const [window] = await atomApplication.launch(parseCommandLine(['--wait', '--pid', '101', projectDir]))
const [window] = await atomApplication.launch(
parseCommandLine(['--wait', '--pid', '101', projectDir])
)
await focusWindow(window)
const [reusedWindow] = await atomApplication.launch(parseCommandLine(['--add', '--wait', '--pid', '102', filePath1, filePath2]))
const [reusedWindow] = await atomApplication.launch(
parseCommandLine([
'--add',
'--wait',
'--pid',
'102',
filePath1,
filePath2
])
)
assert.equal(reusedWindow, window)
const activeEditorPath = await evalInWebContents(window.browserWindow.webContents, send => {
const subscription = atom.workspace.onDidChangeActivePaneItem(editor => {
send(editor.getPath())
subscription.dispose()
})
})
const activeEditorPath = await evalInWebContents(
window.browserWindow.webContents,
send => {
const subscription = atom.workspace.onDidChangeActivePaneItem(
editor => {
send(editor.getPath())
subscription.dispose()
}
)
}
)
assert([filePath1, filePath2].includes(activeEditorPath))
assert.deepEqual(killedPids, [])
@@ -430,7 +593,9 @@ describe('AtomApplication', function () {
await timeoutPromise(100)
assert.deepEqual(killedPids, [])
let processKillPromise = new Promise(resolve => { onDidKillProcess = resolve })
let processKillPromise = new Promise(resolve => {
onDidKillProcess = resolve
})
await evalInWebContents(window.browserWindow.webContents, send => {
atom.workspace.getActivePaneItem().destroy()
send()
@@ -438,7 +603,9 @@ describe('AtomApplication', function () {
await processKillPromise
assert.deepEqual(killedPids, [102])
processKillPromise = new Promise(resolve => { onDidKillProcess = resolve })
processKillPromise = new Promise(resolve => {
onDidKillProcess = resolve
})
window.close()
await processKillPromise
assert.deepEqual(killedPids, [102, 101])
@@ -449,25 +616,40 @@ describe('AtomApplication', function () {
await focusWindow(window)
const dirPath1 = makeTempDir()
const [reusedWindow] = await atomApplication.launch(parseCommandLine(['--add', '--wait', '--pid', '101', dirPath1]))
const [reusedWindow] = await atomApplication.launch(
parseCommandLine(['--add', '--wait', '--pid', '101', dirPath1])
)
assert.equal(reusedWindow, window)
await conditionPromise(async () => (await getTreeViewRootDirectories(window)).length === 1)
await conditionPromise(
async () => (await getTreeViewRootDirectories(window)).length === 1
)
assert.deepEqual(await getTreeViewRootDirectories(window), [dirPath1])
assert.deepEqual(killedPids, [])
const dirPath2 = makeTempDir()
await evalInWebContents(window.browserWindow.webContents, (send, dirPath1, dirPath2) => {
atom.project.setPaths([dirPath1, dirPath2])
send()
}, dirPath1, dirPath2)
await evalInWebContents(
window.browserWindow.webContents,
(send, dirPath1, dirPath2) => {
atom.project.setPaths([dirPath1, dirPath2])
send()
},
dirPath1,
dirPath2
)
await timeoutPromise(100)
assert.deepEqual(killedPids, [])
let processKillPromise = new Promise(resolve => { onDidKillProcess = resolve })
await evalInWebContents(window.browserWindow.webContents, (send, dirPath2) => {
atom.project.setPaths([dirPath2])
send()
}, dirPath2)
let processKillPromise = new Promise(resolve => {
onDidKillProcess = resolve
})
await evalInWebContents(
window.browserWindow.webContents,
(send, dirPath2) => {
atom.project.setPaths([dirPath2])
send()
},
dirPath2
)
await processKillPromise
assert.deepEqual(killedPids, [101])
})
@@ -477,7 +659,9 @@ describe('AtomApplication', function () {
if (process.platform === 'linux' || process.platform === 'win32') {
it('quits the application', async () => {
const atomApplication = buildAtomApplication()
const [window] = await atomApplication.launch(parseCommandLine([path.join(makeTempDir('a'), 'file-a')]))
const [window] = await atomApplication.launch(
parseCommandLine([path.join(makeTempDir('a'), 'file-a')])
)
await focusWindow(window)
window.close()
await window.closedPromise
@@ -487,7 +671,9 @@ describe('AtomApplication', function () {
} else if (process.platform === 'darwin') {
it('leaves the application open', async () => {
const atomApplication = buildAtomApplication()
const [window] = await atomApplication.launch(parseCommandLine([path.join(makeTempDir('a'), 'file-a')]))
const [window] = await atomApplication.launch(
parseCommandLine([path.join(makeTempDir('a'), 'file-a')])
)
await focusWindow(window)
window.close()
await window.closedPromise
@@ -503,16 +689,29 @@ describe('AtomApplication', function () {
const dirB = makeTempDir()
const atomApplication = buildAtomApplication()
const [window0] = await atomApplication.launch(parseCommandLine([dirA, dirB]))
const [window0] = await atomApplication.launch(
parseCommandLine([dirA, dirB])
)
await focusWindow(window0)
await conditionPromise(async () => (await getTreeViewRootDirectories(window0)).length === 2)
assert.deepEqual(await getTreeViewRootDirectories(window0), [dirA, dirB])
await conditionPromise(
async () => (await getTreeViewRootDirectories(window0)).length === 2
)
assert.deepEqual(await getTreeViewRootDirectories(window0), [
dirA,
dirB
])
const saveStatePromise = emitterEventPromise(atomApplication, 'application:did-save-state')
await evalInWebContents(window0.browserWindow.webContents, (sendBackToMainProcess) => {
atom.project.removePath(atom.project.getPaths()[0])
sendBackToMainProcess(null)
})
const saveStatePromise = emitterEventPromise(
atomApplication,
'application:did-save-state'
)
await evalInWebContents(
window0.browserWindow.webContents,
sendBackToMainProcess => {
atom.project.removePath(atom.project.getPaths()[0])
sendBackToMainProcess(null)
}
)
assert.deepEqual(await getTreeViewRootDirectories(window0), [dirB])
await saveStatePromise
@@ -520,17 +719,29 @@ describe('AtomApplication', function () {
const atomApplication2 = buildAtomApplication()
const [window2] = await atomApplication2.launch(parseCommandLine([]))
await focusWindow(window2)
await conditionPromise(async () => (await getTreeViewRootDirectories(window2)).length === 1)
await conditionPromise(
async () => (await getTreeViewRootDirectories(window2)).length === 1
)
assert.deepEqual(await getTreeViewRootDirectories(window2), [dirB])
})
})
describe('when opening atom:// URLs', () => {
it('loads the urlMain file in a new window', async () => {
const packagePath = path.join(__dirname, '..', 'fixtures', 'packages', 'package-with-url-main')
const packagePath = path.join(
__dirname,
'..',
'fixtures',
'packages',
'package-with-url-main'
)
const packagesDirPath = path.join(process.env.ATOM_HOME, 'packages')
fs.mkdirSync(packagesDirPath)
fs.symlinkSync(packagePath, path.join(packagesDirPath, 'package-with-url-main'), 'junction')
fs.symlinkSync(
packagePath,
path.join(packagesDirPath, 'package-with-url-main'),
'junction'
)
const atomApplication = buildAtomApplication()
const launchOptions = parseCommandLine([])
@@ -538,9 +749,12 @@ describe('AtomApplication', function () {
let [windows] = await atomApplication.launch(launchOptions)
await windows[0].loadedPromise
let reached = await evalInWebContents(windows[0].browserWindow.webContents, sendBackToMainProcess => {
sendBackToMainProcess(global.reachedUrlMain)
})
let reached = await evalInWebContents(
windows[0].browserWindow.webContents,
sendBackToMainProcess => {
sendBackToMainProcess(global.reachedUrlMain)
}
)
assert.isTrue(reached)
windows[0].close()
})
@@ -550,9 +764,13 @@ describe('AtomApplication', function () {
const dirBPath = makeTempDir('b')
const atomApplication = buildAtomApplication()
const [window1] = await atomApplication.launch(parseCommandLine([path.join(dirAPath)]))
const [window1] = await atomApplication.launch(
parseCommandLine([path.join(dirAPath)])
)
await focusWindow(window1)
const [window2] = await atomApplication.launch(parseCommandLine([path.join(dirBPath)]))
const [window2] = await atomApplication.launch(
parseCommandLine([path.join(dirBPath)])
)
await focusWindow(window2)
const fileA = path.join(dirAPath, 'file-a')
@@ -564,10 +782,16 @@ describe('AtomApplication', function () {
sinon.spy(window2, 'sendURIMessage')
atomApplication.launch(parseCommandLine(['--uri-handler', uriA]))
await conditionPromise(() => window1.sendURIMessage.calledWith(uriA), `window1 to be focused from ${fileA}`)
await conditionPromise(
() => window1.sendURIMessage.calledWith(uriA),
`window1 to be focused from ${fileA}`
)
atomApplication.launch(parseCommandLine(['--uri-handler', uriB]))
await conditionPromise(() => window2.sendURIMessage.calledWith(uriB), `window2 to be focused from ${fileB}`)
await conditionPromise(
() => window2.sendURIMessage.calledWith(uriB),
`window2 to be focused from ${fileB}`
)
})
})
})
@@ -584,7 +808,10 @@ describe('AtomApplication', function () {
await new Promise(process.nextTick)
assert(!electron.app.didQuit())
await Promise.all([window1.lastPrepareToUnloadPromise, window2.lastPrepareToUnloadPromise])
await Promise.all([
window1.lastPrepareToUnloadPromise,
window2.lastPrepareToUnloadPromise
])
assert(!electron.app.didQuit())
await atomApplication.lastBeforeQuitPromise
await new Promise(process.nextTick)
@@ -596,20 +823,23 @@ describe('AtomApplication', function () {
const [window1] = await atomApplication.launch(parseCommandLine([]))
const [window2] = await atomApplication.launch(parseCommandLine([]))
await Promise.all([window1.loadedPromise, window2.loadedPromise])
await evalInWebContents(window1.browserWindow.webContents, sendBackToMainProcess => {
atom.workspace.getActiveTextEditor().insertText('unsaved text')
sendBackToMainProcess()
})
await evalInWebContents(
window1.browserWindow.webContents,
sendBackToMainProcess => {
atom.workspace.getActiveTextEditor().insertText('unsaved text')
sendBackToMainProcess()
}
)
// Choosing "Cancel"
mockElectronShowMessageBox({response: 1})
mockElectronShowMessageBox({ response: 1 })
electron.app.quit()
await atomApplication.lastBeforeQuitPromise
assert(!electron.app.didQuit())
assert.equal(electron.app.quit.callCount, 1) // Ensure choosing "Cancel" doesn't try to quit the electron app more than once (regression)
// Choosing "Don't save"
mockElectronShowMessageBox({response: 2})
mockElectronShowMessageBox({ response: 2 })
electron.app.quit()
await atomApplication.lastBeforeQuitPromise
assert(electron.app.didQuit())
@@ -620,19 +850,22 @@ describe('AtomApplication', function () {
const [window1] = await atomApplication.launch(parseCommandLine([]))
const [window2] = await atomApplication.launch(parseCommandLine([]))
await Promise.all([window1.loadedPromise, window2.loadedPromise])
await evalInWebContents(window1.browserWindow.webContents, sendBackToMainProcess => {
atom.workspace.getActiveTextEditor().insertText('unsaved text')
sendBackToMainProcess()
})
await evalInWebContents(
window1.browserWindow.webContents,
sendBackToMainProcess => {
atom.workspace.getActiveTextEditor().insertText('unsaved text')
sendBackToMainProcess()
}
)
// Choosing "Cancel"
mockElectronShowMessageBox({response: 1})
mockElectronShowMessageBox({ response: 1 })
electron.app.quit()
await atomApplication.lastBeforeQuitPromise
assert(atomApplication.getAllWindows().length === 1)
// Choosing "Don't save"
mockElectronShowMessageBox({response: 2})
mockElectronShowMessageBox({ response: 2 })
electron.app.quit()
await atomApplication.lastBeforeQuitPromise
assert(atomApplication.getAllWindows().length === 0)
@@ -650,24 +883,35 @@ describe('AtomApplication', function () {
await window.closedPromise
atomApplication.emit('application:open')
await conditionPromise(() => atomApplication.promptForPathToOpen.calledWith('all'))
await conditionPromise(() =>
atomApplication.promptForPathToOpen.calledWith('all')
)
atomApplication.promptForPathToOpen.reset()
atomApplication.emit('application:open-file')
await conditionPromise(() => atomApplication.promptForPathToOpen.calledWith('file'))
await conditionPromise(() =>
atomApplication.promptForPathToOpen.calledWith('file')
)
atomApplication.promptForPathToOpen.reset()
atomApplication.emit('application:open-folder')
await conditionPromise(() => atomApplication.promptForPathToOpen.calledWith('folder'))
await conditionPromise(() =>
atomApplication.promptForPathToOpen.calledWith('folder')
)
atomApplication.promptForPathToOpen.reset()
})
}
function buildAtomApplication (params = {}) {
const atomApplication = new AtomApplication(Object.assign({
resourcePath: ATOM_RESOURCE_PATH,
atomHomeDirPath: process.env.ATOM_HOME
}, params))
const atomApplication = new AtomApplication(
Object.assign(
{
resourcePath: ATOM_RESOURCE_PATH,
atomHomeDirPath: process.env.ATOM_HOME
},
params
)
)
atomApplicationsToDestroy.push(atomApplication)
return atomApplication
}
@@ -675,7 +919,9 @@ describe('AtomApplication', function () {
async function focusWindow (window) {
window.focus()
await window.loadedPromise
await conditionPromise(() => window.atomApplication.getLastFocusedWindow() === window)
await conditionPromise(
() => window.atomApplication.getLastFocusedWindow() === window
)
}
function mockElectronAppQuit () {
@@ -684,7 +930,11 @@ describe('AtomApplication', function () {
electron.app.quit = function () {
this.quit.callCount++
let defaultPrevented = false
this.emit('before-quit', {preventDefault () { defaultPrevented = true }})
this.emit('before-quit', {
preventDefault () {
defaultPrevented = true
}
})
if (!defaultPrevented) didQuit = true
}
@@ -693,7 +943,7 @@ describe('AtomApplication', function () {
electron.app.didQuit = () => didQuit
}
function mockElectronShowMessageBox ({response}) {
function mockElectronShowMessageBox ({ response }) {
electron.dialog.showMessageBox = (window, options, callback) => {
callback(response)
}
@@ -718,7 +968,9 @@ describe('AtomApplication', function () {
function sendBackToMainProcess (result) {
require('electron').ipcRenderer.send('${channelId}', result)
}
(${source})(sendBackToMainProcess, ${args.map(JSON.stringify).join(', ')})
(${source})(sendBackToMainProcess, ${args
.map(JSON.stringify)
.join(', ')})
`
// console.log(`about to execute:\n${js}`)
@@ -727,19 +979,24 @@ describe('AtomApplication', function () {
}
function getTreeViewRootDirectories (atomWindow) {
return evalInWebContents(atomWindow.browserWindow.webContents, sendBackToMainProcess => {
atom.workspace.getLeftDock().observeActivePaneItem((treeView) => {
if (treeView) {
sendBackToMainProcess(
Array
.from(treeView.element.querySelectorAll('.project-root > .header .name'))
.map(element => element.dataset.path)
)
} else {
sendBackToMainProcess([])
}
})
})
return evalInWebContents(
atomWindow.browserWindow.webContents,
sendBackToMainProcess => {
atom.workspace.getLeftDock().observeActivePaneItem(treeView => {
if (treeView) {
sendBackToMainProcess(
Array.from(
treeView.element.querySelectorAll(
'.project-root > .header .name'
)
).map(element => element.dataset.path)
)
} else {
sendBackToMainProcess([])
}
})
}
)
}
function clearElectronSession () {

View File

@@ -1,13 +1,14 @@
const {dialog} = require('electron')
const { dialog } = require('electron')
const FileRecoveryService = require('../../src/main-process/file-recovery-service')
const fs = require('fs-plus')
const fsreal = require('fs')
const EventEmitter = require('events').EventEmitter
const { assert } = require('chai')
const sinon = require('sinon')
const {escapeRegExp} = require('underscore-plus')
const { escapeRegExp } = require('underscore-plus')
const temp = require('temp').track()
describe("FileRecoveryService", () => {
describe('FileRecoveryService', () => {
let recoveryService, recoveryDirectory, spies
beforeEach(() => {
@@ -25,90 +26,90 @@ describe("FileRecoveryService", () => {
}
})
describe("when no crash happens during a save", () => {
it("creates a recovery file and deletes it after saving", async () => {
describe('when no crash happens during a save', () => {
it('creates a recovery file and deletes it after saving', async () => {
const mockWindow = {}
const filePath = temp.path()
fs.writeFileSync(filePath, "some content")
fs.writeFileSync(filePath, 'some content')
await recoveryService.willSavePath(mockWindow, filePath)
assert.equal(fs.listTreeSync(recoveryDirectory).length, 1)
fs.writeFileSync(filePath, "changed")
fs.writeFileSync(filePath, 'changed')
await recoveryService.didSavePath(mockWindow, filePath)
assert.equal(fs.listTreeSync(recoveryDirectory).length, 0)
assert.equal(fs.readFileSync(filePath, 'utf8'), "changed")
assert.equal(fs.readFileSync(filePath, 'utf8'), 'changed')
fs.removeSync(filePath)
})
it("creates only one recovery file when many windows attempt to save the same file, deleting it when the last one finishes saving it", async () => {
it('creates only one recovery file when many windows attempt to save the same file, deleting it when the last one finishes saving it', async () => {
const mockWindow = {}
const anotherMockWindow = {}
const filePath = temp.path()
fs.writeFileSync(filePath, "some content")
fs.writeFileSync(filePath, 'some content')
await recoveryService.willSavePath(mockWindow, filePath)
await recoveryService.willSavePath(anotherMockWindow, filePath)
assert.equal(fs.listTreeSync(recoveryDirectory).length, 1)
fs.writeFileSync(filePath, "changed")
fs.writeFileSync(filePath, 'changed')
await recoveryService.didSavePath(mockWindow, filePath)
assert.equal(fs.listTreeSync(recoveryDirectory).length, 1)
assert.equal(fs.readFileSync(filePath, 'utf8'), "changed")
assert.equal(fs.readFileSync(filePath, 'utf8'), 'changed')
await recoveryService.didSavePath(anotherMockWindow, filePath)
assert.equal(fs.listTreeSync(recoveryDirectory).length, 0)
assert.equal(fs.readFileSync(filePath, 'utf8'), "changed")
assert.equal(fs.readFileSync(filePath, 'utf8'), 'changed')
fs.removeSync(filePath)
})
})
describe("when a crash happens during a save", () => {
it("restores the created recovery file and deletes it", async () => {
describe('when a crash happens during a save', () => {
it('restores the created recovery file and deletes it', async () => {
const mockWindow = {}
const filePath = temp.path()
fs.writeFileSync(filePath, "some content")
fs.writeFileSync(filePath, 'some content')
await recoveryService.willSavePath(mockWindow, filePath)
assert.equal(fs.listTreeSync(recoveryDirectory).length, 1)
fs.writeFileSync(filePath, "changed")
fs.writeFileSync(filePath, 'changed')
await recoveryService.didCrashWindow(mockWindow)
assert.equal(fs.listTreeSync(recoveryDirectory).length, 0)
assert.equal(fs.readFileSync(filePath, 'utf8'), "some content")
assert.equal(fs.readFileSync(filePath, 'utf8'), 'some content')
fs.removeSync(filePath)
})
it("restores the created recovery file when many windows attempt to save the same file and one of them crashes", async () => {
it('restores the created recovery file when many windows attempt to save the same file and one of them crashes', async () => {
const mockWindow = {}
const anotherMockWindow = {}
const filePath = temp.path()
fs.writeFileSync(filePath, "A")
fs.writeFileSync(filePath, 'A')
await recoveryService.willSavePath(mockWindow, filePath)
fs.writeFileSync(filePath, "B")
fs.writeFileSync(filePath, 'B')
await recoveryService.willSavePath(anotherMockWindow, filePath)
assert.equal(fs.listTreeSync(recoveryDirectory).length, 1)
fs.writeFileSync(filePath, "C")
fs.writeFileSync(filePath, 'C')
await recoveryService.didCrashWindow(mockWindow)
assert.equal(fs.readFileSync(filePath, 'utf8'), "A")
assert.equal(fs.readFileSync(filePath, 'utf8'), 'A')
assert.equal(fs.listTreeSync(recoveryDirectory).length, 0)
fs.writeFileSync(filePath, "D")
fs.writeFileSync(filePath, 'D')
await recoveryService.willSavePath(mockWindow, filePath)
fs.writeFileSync(filePath, "E")
fs.writeFileSync(filePath, 'E')
await recoveryService.willSavePath(anotherMockWindow, filePath)
assert.equal(fs.listTreeSync(recoveryDirectory).length, 1)
fs.writeFileSync(filePath, "F")
fs.writeFileSync(filePath, 'F')
await recoveryService.didCrashWindow(anotherMockWindow)
assert.equal(fs.readFileSync(filePath, 'utf8'), "D")
assert.equal(fs.readFileSync(filePath, 'utf8'), 'D')
assert.equal(fs.listTreeSync(recoveryDirectory).length, 0)
fs.removeSync(filePath)
@@ -117,10 +118,10 @@ describe("FileRecoveryService", () => {
it("emits a warning when a file can't be recovered", async () => {
const mockWindow = {}
const filePath = temp.path()
fs.writeFileSync(filePath, "content")
fs.writeFileSync(filePath, 'content')
let logs = []
spies.stub(console, 'log', (message) => logs.push(message))
spies.stub(console, 'log', message => logs.push(message))
spies.stub(dialog, 'showMessageBox')
// Copy files to be recovered before mocking fs.createWriteStream
@@ -130,9 +131,15 @@ describe("FileRecoveryService", () => {
// attempting to copy the recovered file to its original location
var fakeEmitter = new EventEmitter()
var onStub = spies.stub(fakeEmitter, 'on')
onStub.withArgs('error').yields(new Error('Nope')).returns(fakeEmitter)
onStub
.withArgs('error')
.yields(new Error('Nope'))
.returns(fakeEmitter)
onStub.withArgs('open').returns(fakeEmitter)
spies.stub(fsreal, 'createWriteStream').withArgs(filePath).returns(fakeEmitter)
spies
.stub(fsreal, 'createWriteStream')
.withArgs(filePath)
.returns(fakeEmitter)
await recoveryService.didCrashWindow(mockWindow)
let recoveryFiles = fs.listTreeSync(recoveryDirectory)
@@ -148,10 +155,10 @@ describe("FileRecoveryService", () => {
it("doesn't create a recovery file when the file that's being saved doesn't exist yet", async () => {
const mockWindow = {}
await recoveryService.willSavePath(mockWindow, "a-file-that-doesnt-exist")
await recoveryService.willSavePath(mockWindow, 'a-file-that-doesnt-exist')
assert.equal(fs.listTreeSync(recoveryDirectory).length, 0)
await recoveryService.didSavePath(mockWindow, "a-file-that-doesnt-exist")
await recoveryService.didSavePath(mockWindow, 'a-file-that-doesnt-exist')
assert.equal(fs.listTreeSync(recoveryDirectory).length, 0)
})
})

View File

@@ -1,9 +1,8 @@
const Mocha = require('mocha')
const fs = require('fs-plus')
const {assert} = require('chai')
const { assert } = require('chai')
module.exports =
function (testPaths) {
module.exports = function (testPaths) {
global.assert = assert
let reporterOptions = {

View File

@@ -1,9 +1,17 @@
const { assert } = require('chai')
const parseCommandLine = require('../../src/main-process/parse-command-line')
describe('parseCommandLine', () => {
describe('when --uri-handler is not passed', () => {
it('parses arguments as normal', () => {
const args = parseCommandLine(['-d', '--safe', '--test', '/some/path', 'atom://test/url', 'atom://other/url'])
const args = parseCommandLine([
'-d',
'--safe',
'--test',
'/some/path',
'atom://test/url',
'atom://other/url'
])
assert.isTrue(args.devMode)
assert.isTrue(args.safeMode)
assert.isTrue(args.test)
@@ -14,7 +22,15 @@ describe('parseCommandLine', () => {
describe('when --uri-handler is passed', () => {
it('ignores other arguments and limits to one URL', () => {
const args = parseCommandLine(['-d', '--uri-handler', '--safe', '--test', '/some/path', 'atom://test/url', 'atom://other/url'])
const args = parseCommandLine([
'-d',
'--uri-handler',
'--safe',
'--test',
'/some/path',
'atom://test/url',
'atom://other/url'
])
assert.isUndefined(args.devMode)
assert.isUndefined(args.safeMode)
assert.isUndefined(args.test)

View File

@@ -1,4 +1,4 @@
const {sortMenuItems} = require('../src/menu-sort-helpers')
const { sortMenuItems } = require('../src/menu-sort-helpers')
describe('contextMenu', () => {
describe('dedupes separators', () => {
@@ -18,7 +18,8 @@ describe('contextMenu', () => {
it('preserves separators at the begining of set two', () => {
const items = [
{ command: 'core:one' },
{ type: 'separator' }, { command: 'core:two' }
{ type: 'separator' },
{ command: 'core:two' }
]
const expected = [
{ command: 'core:one' },
@@ -36,8 +37,10 @@ describe('contextMenu', () => {
it('removes duplicate separators across sets', () => {
const items = [
{ command: 'core:one' }, { type: 'separator' },
{ type: 'separator' }, { command: 'core:two' }
{ command: 'core:one' },
{ type: 'separator' },
{ type: 'separator' },
{ command: 'core:two' }
]
const expected = [
{ command: 'core:one' },

View File

@@ -1,11 +1,11 @@
/** @babel */
import {it, beforeEach} from './async-spec-helpers'
import { it, beforeEach } from './async-spec-helpers'
import path from 'path'
import {Emitter} from 'event-kit'
import { Emitter } from 'event-kit'
import {NativeWatcherRegistry} from '../src/native-watcher-registry'
import { NativeWatcherRegistry } from '../src/native-watcher-registry'
function findRootDirectory () {
let current = process.cwd()
@@ -42,7 +42,9 @@ class MockWatcher {
attachToNative (native, nativePath) {
if (this.normalizedPath.startsWith(nativePath)) {
if (this.native) {
this.native.attached = this.native.attached.filter(each => each !== this)
this.native.attached = this.native.attached.filter(
each => each !== this
)
}
this.native = native
this.native.attached.push(this)
@@ -84,7 +86,9 @@ describe('NativeWatcherRegistry', function () {
let createNative, registry
beforeEach(function () {
registry = new NativeWatcherRegistry(normalizedPath => createNative(normalizedPath))
registry = new NativeWatcherRegistry(normalizedPath =>
createNative(normalizedPath)
)
})
it('attaches a Watcher to a newly created NativeWatcher for a new directory', async function () {
@@ -201,9 +205,20 @@ describe('NativeWatcherRegistry', function () {
const RUNNING = new MockNative('running')
const stoppedPath = absolute('watcher', 'that', 'will', 'be', 'stopped')
const stoppedPathParts = stoppedPath.split(path.sep).filter(part => part.length > 0)
const runningPath = absolute('watcher', 'that', 'will', 'continue', 'to', 'exist')
const runningPathParts = runningPath.split(path.sep).filter(part => part.length > 0)
const stoppedPathParts = stoppedPath
.split(path.sep)
.filter(part => part.length > 0)
const runningPath = absolute(
'watcher',
'that',
'will',
'continue',
'to',
'exist'
)
const runningPathParts = runningPath
.split(path.sep)
.filter(part => part.length > 0)
createNative = dir => {
if (dir === stoppedPath) {
@@ -281,23 +296,29 @@ describe('NativeWatcherRegistry', function () {
expect(childWatcher0.native).toBe(CHILD0)
expect(childWatcher1.native).toBe(CHILD1)
expect(registry.tree.root.lookup(parts(parentDir)).when({
parent: () => false,
missing: () => false,
children: () => true
})).toBe(true)
expect(
registry.tree.root.lookup(parts(parentDir)).when({
parent: () => false,
missing: () => false,
children: () => true
})
).toBe(true)
expect(registry.tree.root.lookup(parts(childDir0)).when({
parent: () => true,
missing: () => false,
children: () => false
})).toBe(true)
expect(
registry.tree.root.lookup(parts(childDir0)).when({
parent: () => true,
missing: () => false,
children: () => false
})
).toBe(true)
expect(registry.tree.root.lookup(parts(childDir1)).when({
parent: () => true,
missing: () => false,
children: () => false
})).toBe(true)
expect(
registry.tree.root.lookup(parts(childDir1)).when({
parent: () => true,
missing: () => false,
children: () => false
})
).toBe(true)
})
it('consolidates children when splitting a parent watcher', async function () {
@@ -340,23 +361,29 @@ describe('NativeWatcherRegistry', function () {
expect(childWatcher0.native).toBe(CHILD0)
expect(childWatcher1.native).toBe(CHILD0)
expect(registry.tree.root.lookup(parts(parentDir)).when({
parent: () => false,
missing: () => false,
children: () => true
})).toBe(true)
expect(
registry.tree.root.lookup(parts(parentDir)).when({
parent: () => false,
missing: () => false,
children: () => true
})
).toBe(true)
expect(registry.tree.root.lookup(parts(childDir0)).when({
parent: () => true,
missing: () => false,
children: () => false
})).toBe(true)
expect(
registry.tree.root.lookup(parts(childDir0)).when({
parent: () => true,
missing: () => false,
children: () => false
})
).toBe(true)
expect(registry.tree.root.lookup(parts(childDir1)).when({
parent: () => true,
missing: () => false,
children: () => false
})).toBe(true)
expect(
registry.tree.root.lookup(parts(childDir1)).when({
parent: () => true,
missing: () => false,
children: () => false
})
).toBe(true)
})
})
})

View File

@@ -10,8 +10,7 @@ describe('NotificationManager', () => {
describe('the atom global', () =>
it('has a notifications instance', () => {
expect(atom.notifications instanceof NotificationManager).toBe(true)
})
)
}))
describe('adding events', () => {
let addSpy
@@ -22,7 +21,7 @@ describe('NotificationManager', () => {
})
it('emits an event when a notification has been added', () => {
manager.add('error', 'Some error!', {icon: 'someIcon'})
manager.add('error', 'Some error!', { icon: 'someIcon' })
expect(addSpy).toHaveBeenCalled()
const notification = addSpy.mostRecentCall.args[0]
@@ -32,35 +31,35 @@ describe('NotificationManager', () => {
})
it('emits a fatal error when ::addFatalError has been called', () => {
manager.addFatalError('Some error!', {icon: 'someIcon'})
manager.addFatalError('Some error!', { icon: 'someIcon' })
expect(addSpy).toHaveBeenCalled()
const notification = addSpy.mostRecentCall.args[0]
expect(notification.getType()).toBe('fatal')
})
it('emits an error when ::addError has been called', () => {
manager.addError('Some error!', {icon: 'someIcon'})
manager.addError('Some error!', { icon: 'someIcon' })
expect(addSpy).toHaveBeenCalled()
const notification = addSpy.mostRecentCall.args[0]
expect(notification.getType()).toBe('error')
})
it('emits a warning notification when ::addWarning has been called', () => {
manager.addWarning('Something!', {icon: 'someIcon'})
manager.addWarning('Something!', { icon: 'someIcon' })
expect(addSpy).toHaveBeenCalled()
const notification = addSpy.mostRecentCall.args[0]
expect(notification.getType()).toBe('warning')
})
it('emits an info notification when ::addInfo has been called', () => {
manager.addInfo('Something!', {icon: 'someIcon'})
manager.addInfo('Something!', { icon: 'someIcon' })
expect(addSpy).toHaveBeenCalled()
const notification = addSpy.mostRecentCall.args[0]
expect(notification.getType()).toBe('info')
})
it('emits a success notification when ::addSuccess has been called', () => {
manager.addSuccess('Something!', {icon: 'someIcon'})
manager.addSuccess('Something!', { icon: 'someIcon' })
expect(addSpy).toHaveBeenCalled()
const notification = addSpy.mostRecentCall.args[0]
expect(notification.getType()).toBe('success')

View File

@@ -20,8 +20,7 @@ describe('Notification', () => {
it('returns a Date object', () => {
const notification = new Notification('error', 'message!')
expect(notification.getTimestamp() instanceof Date).toBe(true)
})
)
}))
describe('::getIcon()', () => {
it('returns a default when no icon specified', () => {
@@ -30,7 +29,9 @@ describe('Notification', () => {
})
it('returns the icon specified', () => {
const notification = new Notification('error', 'message!', {icon: 'my-icon'})
const notification = new Notification('error', 'message!', {
icon: 'my-icon'
})
expect(notification.getIcon()).toBe('my-icon')
})
})
@@ -39,7 +40,9 @@ describe('Notification', () => {
describe('when the notfication is dismissable', () =>
it('calls a callback when the notification is dismissed', () => {
const dismissedSpy = jasmine.createSpy()
const notification = new Notification('error', 'message', {dismissable: true})
const notification = new Notification('error', 'message', {
dismissable: true
})
notification.onDidDismiss(dismissedSpy)
expect(notification.isDismissable()).toBe(true)
@@ -49,8 +52,7 @@ describe('Notification', () => {
expect(dismissedSpy).toHaveBeenCalled()
expect(notification.isDismissed()).toBe(true)
})
)
}))
describe('when the notfication is not dismissable', () =>
it('does nothing when ::dismiss() is called', () => {
@@ -65,7 +67,6 @@ describe('Notification', () => {
expect(dismissedSpy).not.toHaveBeenCalled()
expect(notification.isDismissed()).toBe(true)
})
)
}))
})
})

File diff suppressed because it is too large Load Diff

View File

@@ -1,18 +1,17 @@
/** @babel */
import fs from 'fs'
import path from 'path'
import {it, fit, ffit, fffit, beforeEach, afterEach} from './async-spec-helpers'
import { it, beforeEach } from './async-spec-helpers'
import PackageTranspilationRegistry from '../src/package-transpilation-registry'
const originalCompiler = {
getCachePath: (sourceCode, filePath) => {
return "orig-cache-path"
return 'orig-cache-path'
},
compile: (sourceCode, filePath) => {
return sourceCode + "-original-compiler"
return sourceCode + '-original-compiler'
},
shouldCompile: (sourceCode, filePath) => {
@@ -20,7 +19,7 @@ const originalCompiler = {
}
}
describe("PackageTranspilationRegistry", () => {
describe('PackageTranspilationRegistry', () => {
let registry
let wrappedCompiler
@@ -47,20 +46,36 @@ describe("PackageTranspilationRegistry", () => {
const hitPath = path.join('/path/to/lib/file.js')
const hitPathCoffee = path.join('/path/to/file2.coffee')
const missPath = path.join('/path/other/file3.js')
const hitPathMissSubdir =path.join('/path/to/file4.js')
const hitPathMissSubdir = path.join('/path/to/file4.js')
const hitPathMissExt = path.join('/path/to/file5.ts')
const nodeModulesFolder = path.join('/path/to/lib/node_modules/file6.js')
const hitNonStandardExt = path.join('/path/to/file7.omgwhatisthis')
const jsSpec = { glob: "lib/**/*.js", transpiler: './transpiler-js', options: { type: 'js' } }
const coffeeSpec = { glob: "*.coffee", transpiler: './transpiler-coffee', options: { type: 'coffee' } }
const omgSpec = { glob: "*.omgwhatisthis", transpiler: './transpiler-omg', options: { type: 'omg' } }
const jsSpec = {
glob: 'lib/**/*.js',
transpiler: './transpiler-js',
options: { type: 'js' }
}
const coffeeSpec = {
glob: '*.coffee',
transpiler: './transpiler-coffee',
options: { type: 'coffee' }
}
const omgSpec = {
glob: '*.omgwhatisthis',
transpiler: './transpiler-omg',
options: { type: 'omg' }
}
const expectedMeta = { name: 'my-package', path: path.join('/path/to'), meta: { some: 'metadata' } }
const expectedMeta = {
name: 'my-package',
path: path.join('/path/to'),
meta: { some: 'metadata' }
}
const jsTranspiler = {
transpile: (sourceCode, filePath, options) => {
return {code: sourceCode + "-transpiler-js"}
return { code: sourceCode + '-transpiler-js' }
},
getCacheKeyData: (sourceCode, filePath, options) => {
@@ -70,7 +85,7 @@ describe("PackageTranspilationRegistry", () => {
const coffeeTranspiler = {
transpile: (sourceCode, filePath, options) => {
return {code: sourceCode + "-transpiler-coffee"}
return { code: sourceCode + '-transpiler-coffee' }
},
getCacheKeyData: (sourceCode, filePath, options) => {
@@ -80,7 +95,7 @@ describe("PackageTranspilationRegistry", () => {
const omgTranspiler = {
transpile: (sourceCode, filePath, options) => {
return {code: sourceCode + "-transpiler-omg"}
return { code: sourceCode + '-transpiler-omg' }
},
getCacheKeyData: (sourceCode, filePath, options) => {
@@ -89,72 +104,135 @@ describe("PackageTranspilationRegistry", () => {
}
beforeEach(() => {
jsSpec._transpilerSource = "js-transpiler-source"
coffeeSpec._transpilerSource = "coffee-transpiler-source"
omgTranspiler._transpilerSource = "omg-transpiler-source"
jsSpec._transpilerSource = 'js-transpiler-source'
coffeeSpec._transpilerSource = 'coffee-transpiler-source'
omgTranspiler._transpilerSource = 'omg-transpiler-source'
spyOn(registry, "getTranspiler").andCallFake(spec => {
spyOn(registry, 'getTranspiler').andCallFake(spec => {
if (spec.transpiler === './transpiler-js') return jsTranspiler
if (spec.transpiler === './transpiler-coffee') return coffeeTranspiler
if (spec.transpiler === './transpiler-omg') return omgTranspiler
throw new Error('bad transpiler path ' + spec.transpiler)
})
registry.addTranspilerConfigForPath(path.join('/path/to'), 'my-package', { some: 'metadata' }, [
jsSpec, coffeeSpec, omgSpec
])
registry.addTranspilerConfigForPath(
path.join('/path/to'),
'my-package',
{ some: 'metadata' },
[jsSpec, coffeeSpec, omgSpec]
)
})
it('always returns true from shouldCompile for a file in that dir that match a glob', () => {
spyOn(originalCompiler, 'shouldCompile').andReturn(false)
expect(wrappedCompiler.shouldCompile('source', hitPath)).toBe(true)
expect(wrappedCompiler.shouldCompile('source', hitPathCoffee)).toBe(true)
expect(wrappedCompiler.shouldCompile('source', hitNonStandardExt)).toBe(true)
expect(wrappedCompiler.shouldCompile('source', hitPathMissExt)).toBe(false)
expect(wrappedCompiler.shouldCompile('source', hitPathMissSubdir)).toBe(false)
expect(wrappedCompiler.shouldCompile('source', hitNonStandardExt)).toBe(
true
)
expect(wrappedCompiler.shouldCompile('source', hitPathMissExt)).toBe(
false
)
expect(wrappedCompiler.shouldCompile('source', hitPathMissSubdir)).toBe(
false
)
expect(wrappedCompiler.shouldCompile('source', missPath)).toBe(false)
expect(wrappedCompiler.shouldCompile('source', nodeModulesFolder)).toBe(false)
expect(wrappedCompiler.shouldCompile('source', nodeModulesFolder)).toBe(
false
)
})
it('calls getCacheKeyData on the transpiler to get additional cache key data', () => {
spyOn(registry, "getTranspilerPath").andReturn("./transpiler-js")
spyOn(registry, 'getTranspilerPath').andReturn('./transpiler-js')
spyOn(jsTranspiler, 'getCacheKeyData').andCallThrough()
wrappedCompiler.getCachePath('source', missPath, jsSpec)
expect(jsTranspiler.getCacheKeyData).not.toHaveBeenCalledWith('source', missPath, jsSpec.options, expectedMeta)
expect(jsTranspiler.getCacheKeyData).not.toHaveBeenCalledWith(
'source',
missPath,
jsSpec.options,
expectedMeta
)
wrappedCompiler.getCachePath('source', hitPath, jsSpec)
expect(jsTranspiler.getCacheKeyData).toHaveBeenCalledWith('source', hitPath, jsSpec.options, expectedMeta)
expect(jsTranspiler.getCacheKeyData).toHaveBeenCalledWith(
'source',
hitPath,
jsSpec.options,
expectedMeta
)
})
it('compiles files matching a glob with the associated transpiler, and the old one otherwise', () => {
spyOn(jsTranspiler, "transpile").andCallThrough()
spyOn(coffeeTranspiler, "transpile").andCallThrough()
spyOn(omgTranspiler, "transpile").andCallThrough()
spyOn(jsTranspiler, 'transpile').andCallThrough()
spyOn(coffeeTranspiler, 'transpile').andCallThrough()
spyOn(omgTranspiler, 'transpile').andCallThrough()
expect(wrappedCompiler.compile('source', hitPath)).toEqual('source-transpiler-js')
expect(jsTranspiler.transpile).toHaveBeenCalledWith('source', hitPath, jsSpec.options, expectedMeta)
expect(wrappedCompiler.compile('source', hitPathCoffee)).toEqual('source-transpiler-coffee')
expect(coffeeTranspiler.transpile).toHaveBeenCalledWith('source', hitPathCoffee, coffeeSpec.options, expectedMeta)
expect(wrappedCompiler.compile('source', hitNonStandardExt)).toEqual('source-transpiler-omg')
expect(omgTranspiler.transpile).toHaveBeenCalledWith('source', hitNonStandardExt, omgSpec.options, expectedMeta)
expect(wrappedCompiler.compile('source', hitPath)).toEqual(
'source-transpiler-js'
)
expect(jsTranspiler.transpile).toHaveBeenCalledWith(
'source',
hitPath,
jsSpec.options,
expectedMeta
)
expect(wrappedCompiler.compile('source', hitPathCoffee)).toEqual(
'source-transpiler-coffee'
)
expect(coffeeTranspiler.transpile).toHaveBeenCalledWith(
'source',
hitPathCoffee,
coffeeSpec.options,
expectedMeta
)
expect(wrappedCompiler.compile('source', hitNonStandardExt)).toEqual(
'source-transpiler-omg'
)
expect(omgTranspiler.transpile).toHaveBeenCalledWith(
'source',
hitNonStandardExt,
omgSpec.options,
expectedMeta
)
expect(wrappedCompiler.compile('source', missPath)).toEqual('source-original-compiler')
expect(wrappedCompiler.compile('source', hitPathMissExt)).toEqual('source-original-compiler')
expect(wrappedCompiler.compile('source', hitPathMissSubdir)).toEqual('source-original-compiler')
expect(wrappedCompiler.compile('source', nodeModulesFolder)).toEqual('source-original-compiler')
expect(wrappedCompiler.compile('source', missPath)).toEqual(
'source-original-compiler'
)
expect(wrappedCompiler.compile('source', hitPathMissExt)).toEqual(
'source-original-compiler'
)
expect(wrappedCompiler.compile('source', hitPathMissSubdir)).toEqual(
'source-original-compiler'
)
expect(wrappedCompiler.compile('source', nodeModulesFolder)).toEqual(
'source-original-compiler'
)
})
describe('when the packages root path contains node_modules', () =>{
describe('when the packages root path contains node_modules', () => {
beforeEach(() => {
registry.addTranspilerConfigForPath(path.join('/path/with/node_modules/in/root'), 'my-other-package', { some: 'metadata' }, [
jsSpec
])
registry.addTranspilerConfigForPath(
path.join('/path/with/node_modules/in/root'),
'my-other-package',
{ some: 'metadata' },
[jsSpec]
)
})
it('returns appropriate values from shouldCompile', () => {
spyOn(originalCompiler, 'shouldCompile').andReturn(false)
expect(wrappedCompiler.shouldCompile('source', '/path/with/node_modules/in/root/lib/test.js')).toBe(true)
expect(wrappedCompiler.shouldCompile('source', '/path/with/node_modules/in/root/lib/node_modules/test.js')).toBe(false)
expect(
wrappedCompiler.shouldCompile(
'source',
'/path/with/node_modules/in/root/lib/test.js'
)
).toBe(true)
expect(
wrappedCompiler.shouldCompile(
'source',
'/path/with/node_modules/in/root/lib/node_modules/test.js'
)
).toBe(false)
})
})
})

View File

@@ -1,11 +1,17 @@
const PaneContainer = require('../src/pane-container')
const {it, fit, ffit, fffit, beforeEach, afterEach} = require('./async-spec-helpers')
const {
it,
beforeEach
} = require('./async-spec-helpers')
describe('PaneContainer', () => {
let confirm, params
beforeEach(() => {
confirm = spyOn(atom.applicationDelegate, 'confirm').andCallFake((options, callback) => callback(0))
confirm = spyOn(atom.applicationDelegate, 'confirm').andCallFake(
(options, callback) => callback(0)
)
params = {
location: 'center',
config: atom.config,
@@ -21,16 +27,20 @@ describe('PaneContainer', () => {
beforeEach(() => {
// This is a dummy item to prevent panes from being empty on deserialization
class Item {
static deserialize () { return new (this)() }
serialize () { return {deserializer: 'Item'} }
static deserialize () {
return new this()
}
serialize () {
return { deserializer: 'Item' }
}
}
atom.deserializers.add(Item)
containerA = new PaneContainer(params)
pane1A = containerA.getActivePane()
pane1A.addItem(new Item())
pane2A = pane1A.splitRight({items: [new Item()]})
pane3A = pane2A.splitDown({items: [new Item()]})
pane2A = pane1A.splitRight({ items: [new Item()] })
pane3A = pane2A.splitDown({ items: [new Item()] })
pane3A.focus()
})
@@ -64,7 +74,7 @@ describe('PaneContainer', () => {
describe('if there are empty panes after deserialization', () => {
beforeEach(() => {
pane3A.getItems()[0].serialize = () => ({deserializer: 'Bogus'})
pane3A.getItems()[0].serialize = () => ({ deserializer: 'Bogus' })
})
describe("if the 'core.destroyEmptyPanes' config option is false (the default)", () =>
@@ -78,8 +88,7 @@ describe('PaneContainer', () => {
expect(leftPane.getItems().length).toBe(1)
expect(topPane.getItems().length).toBe(1)
expect(bottomPane.getItems().length).toBe(0)
})
)
}))
describe("if the 'core.destroyEmptyPanes' config option is true", () =>
it('removes empty panes on deserialization', () => {
@@ -92,8 +101,7 @@ describe('PaneContainer', () => {
expect(leftPane.getItems().length).toBe(1)
expect(rightPane.getItems().length).toBe(1)
})
)
}))
})
})
@@ -144,8 +152,8 @@ describe('PaneContainer', () => {
beforeEach(() => {
container = new PaneContainer(params)
container.getRoot().addItems([{}, {}])
container.getRoot().splitRight({items: [{}, {}]});
[pane1, pane2] = container.getPanes()
container.getRoot().splitRight({ items: [{}, {}] })
;[pane1, pane2] = container.getPanes()
observed = []
container.onDidChangeActivePane(pane => observed.push(pane))
@@ -164,8 +172,8 @@ describe('PaneContainer', () => {
beforeEach(() => {
container = new PaneContainer(params)
container.getRoot().addItems([{}, {}])
container.getRoot().splitRight({items: [{}, {}]});
[pane1, pane2] = container.getPanes()
container.getRoot().splitRight({ items: [{}, {}] })
;[pane1, pane2] = container.getPanes()
observed = []
container.onDidChangeActivePaneItem(item => observed.push(item))
@@ -190,8 +198,8 @@ describe('PaneContainer', () => {
beforeEach(() => {
container = new PaneContainer(params)
container.getRoot().addItems([{}, {}])
container.getRoot().splitRight({items: [{}, {}]});
[pane1, pane2] = container.getPanes()
container.getRoot().splitRight({ items: [{}, {}] })
;[pane1, pane2] = container.getPanes()
observed = []
container.onDidStopChangingActivePaneItem(item => observed.push(item))
@@ -251,30 +259,33 @@ describe('PaneContainer', () => {
it('invokes observers with all current and future pane items', () => {
const container = new PaneContainer(params)
container.getRoot().addItems([{}, {}])
container.getRoot().splitRight({items: [{}]})
container.getRoot().splitRight({ items: [{}] })
const pane2 = container.getPanes()[1]
const observed = []
container.observePaneItems(pane => observed.push(pane))
const pane3 = pane2.splitDown({items: [{}]})
const pane3 = pane2.splitDown({ items: [{}] })
pane3.addItems([{}, {}])
expect(observed).toEqual(container.getPaneItems())
})
)
}))
describe('::confirmClose()', () => {
let container, pane1, pane2
beforeEach(() => {
class TestItem {
shouldPromptToSave () { return true }
getURI () { return 'test' }
shouldPromptToSave () {
return true
}
getURI () {
return 'test'
}
}
container = new PaneContainer(params)
container.getRoot().splitRight();
[pane1, pane2] = container.getPanes()
container.getRoot().splitRight()
;[pane1, pane2] = container.getPanes()
pane1.addItem(new TestItem())
pane2.addItem(new TestItem())
})
@@ -298,7 +309,7 @@ describe('PaneContainer', () => {
it('invokes the given callback when panes are added', () => {
const container = new PaneContainer(params)
const events = []
container.onDidAddPane((event) => {
container.onDidAddPane(event => {
expect(container.getPanes().includes(event.pane)).toBe(true)
events.push(event)
})
@@ -307,23 +318,31 @@ describe('PaneContainer', () => {
const pane2 = pane1.splitRight()
const pane3 = pane2.splitDown()
expect(events).toEqual([{pane: pane2}, {pane: pane3}])
expect(events).toEqual([{ pane: pane2 }, { pane: pane3 }])
})
})
describe('::onWillDestroyPane(callback)', () => {
it('invokes the given callback before panes or their items are destroyed', () => {
class TestItem {
constructor () { this._isDestroyed = false }
destroy () { this._isDestroyed = true }
isDestroyed () { return this._isDestroyed }
constructor () {
this._isDestroyed = false
}
destroy () {
this._isDestroyed = true
}
isDestroyed () {
return this._isDestroyed
}
}
const container = new PaneContainer(params)
const events = []
container.onWillDestroyPane((event) => {
const itemsDestroyed = event.pane.getItems().map((item) => item.isDestroyed())
events.push([event, {itemsDestroyed}])
container.onWillDestroyPane(event => {
const itemsDestroyed = event.pane
.getItems()
.map(item => item.isDestroyed())
events.push([event, { itemsDestroyed }])
})
const pane1 = container.getActivePane()
@@ -332,7 +351,7 @@ describe('PaneContainer', () => {
pane2.destroy()
expect(events).toEqual([[{pane: pane2}, {itemsDestroyed: [false]}]])
expect(events).toEqual([[{ pane: pane2 }, { itemsDestroyed: [false] }]])
})
})
@@ -340,7 +359,7 @@ describe('PaneContainer', () => {
it('invokes the given callback when panes are destroyed', () => {
const container = new PaneContainer(params)
const events = []
container.onDidDestroyPane((event) => {
container.onDidDestroyPane(event => {
expect(container.getPanes().includes(event.pane)).toBe(false)
events.push(event)
})
@@ -352,13 +371,13 @@ describe('PaneContainer', () => {
pane2.destroy()
pane3.destroy()
expect(events).toEqual([{pane: pane2}, {pane: pane3}])
expect(events).toEqual([{ pane: pane2 }, { pane: pane3 }])
})
it('invokes the given callback when the container is destroyed', () => {
const container = new PaneContainer(params)
const events = []
container.onDidDestroyPane((event) => {
container.onDidDestroyPane(event => {
expect(container.getPanes().includes(event.pane)).toBe(false)
events.push(event)
})
@@ -369,7 +388,11 @@ describe('PaneContainer', () => {
container.destroy()
expect(events).toEqual([{pane: pane1}, {pane: pane2}, {pane: pane3}])
expect(events).toEqual([
{ pane: pane1 },
{ pane: pane2 },
{ pane: pane3 }
])
})
})
@@ -385,19 +408,19 @@ describe('PaneContainer', () => {
const events = []
container.onWillDestroyPaneItem(event => events.push(['will', event]))
container.onDidDestroyPaneItem(event => events.push(['did', event]))
const pane2 = pane1.splitRight({items: [item2, item3]})
const pane2 = pane1.splitRight({ items: [item2, item3] })
await pane1.destroyItem(item1)
await pane2.destroyItem(item3)
await pane2.destroyItem(item2)
expect(events).toEqual([
['will', {item: item1, pane: pane1, index: 0}],
['did', {item: item1, pane: pane1, index: 0}],
['will', {item: item3, pane: pane2, index: 1}],
['did', {item: item3, pane: pane2, index: 1}],
['will', {item: item2, pane: pane2, index: 0}],
['did', {item: item2, pane: pane2, index: 0}]
['will', { item: item1, pane: pane1, index: 0 }],
['did', { item: item1, pane: pane1, index: 0 }],
['will', { item: item3, pane: pane2, index: 1 }],
['did', { item: item3, pane: pane2, index: 1 }],
['will', { item: item2, pane: pane2, index: 0 }],
['did', { item: item2, pane: pane2, index: 0 }]
])
})
})
@@ -410,21 +433,39 @@ describe('PaneContainer', () => {
const item1 = {
saved: false,
getURI () { return '' },
isModified () { return true },
save () { this.saved = true }
getURI () {
return ''
},
isModified () {
return true
},
save () {
this.saved = true
}
}
const item2 = {
saved: false,
getURI () { return '' },
isModified () { return false },
save () { this.saved = true }
getURI () {
return ''
},
isModified () {
return false
},
save () {
this.saved = true
}
}
const item3 = {
saved: false,
getURI () { return '' },
isModified () { return true },
save () { this.saved = true }
getURI () {
return ''
},
isModified () {
return true
},
save () {
this.saved = true
}
}
pane1.addItem(item1)
@@ -436,37 +477,38 @@ describe('PaneContainer', () => {
expect(item1.saved).toBe(true)
expect(item2.saved).toBe(false)
expect(item3.saved).toBe(true)
})
)
}))
describe('::moveActiveItemToPane(destPane) and ::copyActiveItemToPane(destPane)', () => {
let container, pane1, pane2, item1
beforeEach(() => {
class TestItem {
constructor (id) { this.id = id }
copy () { return new TestItem(this.id) }
constructor (id) {
this.id = id
}
copy () {
return new TestItem(this.id)
}
}
container = new PaneContainer(params)
pane1 = container.getRoot()
item1 = new TestItem('1')
pane2 = pane1.splitRight({items: [item1]})
pane2 = pane1.splitRight({ items: [item1] })
})
describe('::::moveActiveItemToPane(destPane)', () =>
it('moves active item to given pane and focuses it', () => {
container.moveActiveItemToPane(pane1)
expect(pane1.getActiveItem()).toBe(item1)
})
)
}))
describe('::::copyActiveItemToPane(destPane)', () =>
it('copies active item to given pane and focuses it', () => {
container.copyActiveItemToPane(pane1)
expect(container.paneForItem(item1)).toBe(pane2)
expect(pane1.getActiveItem().id).toBe(item1.id)
})
)
}))
})
})

View File

@@ -1,15 +1,20 @@
const {extend} = require('underscore-plus')
const {Emitter} = require('event-kit')
const { extend } = require('underscore-plus')
const { Emitter } = require('event-kit')
const Grim = require('grim')
const Pane = require('../src/pane')
const PaneContainer = require('../src/pane-container')
const {it, fit, ffit, fffit, beforeEach, conditionPromise, timeoutPromise} = require('./async-spec-helpers')
const {
it,
beforeEach,
conditionPromise,
timeoutPromise
} = require('./async-spec-helpers')
describe('Pane', () => {
let confirm, showSaveDialog, deserializerDisposable
class Item {
static deserialize ({name, uri}) {
static deserialize ({ name, uri }) {
return new Item(name, uri)
}
@@ -20,14 +25,24 @@ describe('Pane', () => {
this.destroyed = false
}
getURI () { return this.uri }
getPath () { return this.path }
isEqual (other) { return this.name === (other && other.name) }
isPermanentDockItem () { return false }
isDestroyed () { return this.destroyed }
getURI () {
return this.uri
}
getPath () {
return this.path
}
isEqual (other) {
return this.name === (other && other.name)
}
isPermanentDockItem () {
return false
}
isDestroyed () {
return this.destroyed
}
serialize () {
return {deserializer: 'Item', name: this.name, uri: this.uri}
return { deserializer: 'Item', name: this.name, uri: this.uri }
}
copy () {
@@ -63,22 +78,29 @@ describe('Pane', () => {
})
function paneParams (params) {
return extend({
applicationDelegate: atom.applicationDelegate,
config: atom.config,
deserializerManager: atom.deserializers,
notificationManager: atom.notifications
}, params)
return extend(
{
applicationDelegate: atom.applicationDelegate,
config: atom.config,
deserializerManager: atom.deserializers,
notificationManager: atom.notifications
},
params
)
}
describe('construction', () => {
it('sets the active item to the first item', () => {
const pane = new Pane(paneParams({items: [new Item('A'), new Item('B')]}))
const pane = new Pane(
paneParams({ items: [new Item('A'), new Item('B')] })
)
expect(pane.getActiveItem()).toBe(pane.itemAtIndex(0))
})
it('compacts the items array', () => {
const pane = new Pane(paneParams({items: [undefined, new Item('A'), null, new Item('B')]}))
const pane = new Pane(
paneParams({ items: [undefined, new Item('A'), null, new Item('B')] })
)
expect(pane.getItems().length).toBe(2)
expect(pane.getActiveItem()).toBe(pane.itemAtIndex(0))
})
@@ -136,15 +158,19 @@ describe('Pane', () => {
describe('::addItem(item, index)', () => {
it('adds the item at the given index', () => {
const pane = new Pane(paneParams({items: [new Item('A'), new Item('B')]}))
const pane = new Pane(
paneParams({ items: [new Item('A'), new Item('B')] })
)
const [item1, item2] = pane.getItems()
const item3 = new Item('C')
pane.addItem(item3, {index: 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', () => {
const pane = new Pane(paneParams({items: [new Item('A'), new Item('B'), new Item('C')]}))
const pane = new Pane(
paneParams({ items: [new Item('A'), new Item('B'), new Item('C')] })
)
const [item1, item2, item3] = pane.getItems()
pane.activateItem(item2)
const item4 = new Item('D')
@@ -160,18 +186,23 @@ describe('Pane', () => {
})
it('invokes ::onDidAddItem() observers', () => {
const pane = new Pane(paneParams({items: [new Item('A'), new Item('B')]}))
const pane = new Pane(
paneParams({ items: [new Item('A'), new Item('B')] })
)
const events = []
pane.onDidAddItem(event => events.push(event))
const item = new Item('C')
pane.addItem(item, {index: 1})
expect(events).toEqual([{item, index: 1, moved: false}])
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', () => {
const item = new Item('A')
const container = new PaneContainer({config: atom.config, applicationDelegate: atom.applicationDelegate})
const container = new PaneContainer({
config: atom.config,
applicationDelegate: atom.applicationDelegate
})
const pane1 = container.getActivePane()
pane1.addItem(item)
const pane2 = pane1.splitRight()
@@ -179,36 +210,36 @@ describe('Pane', () => {
})
it("throws an exception if the item isn't an object", () => {
const pane = new Pane(paneParams({items: []}))
const pane = new Pane(paneParams({ items: [] }))
expect(() => pane.addItem(null)).toThrow()
expect(() => pane.addItem('foo')).toThrow()
expect(() => pane.addItem(1)).toThrow()
})
it('destroys any existing pending item', () => {
const pane = new Pane(paneParams({items: []}))
const pane = new Pane(paneParams({ items: [] }))
const itemA = new Item('A')
const itemB = new Item('B')
const itemC = new Item('C')
pane.addItem(itemA, {pending: false})
pane.addItem(itemB, {pending: true})
pane.addItem(itemC, {pending: false})
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', () => {
const eventOrder = []
const pane = new Pane(paneParams({items: []}))
const pane = new Pane(paneParams({ items: [] }))
const itemA = new Item('A')
const itemB = new Item('B')
pane.addItem(itemA, {pending: true})
pane.addItem(itemA, { pending: true })
pane.onDidAddItem(function ({item}) {
pane.onDidAddItem(function ({ item }) {
if (item === itemB) eventOrder.push('add')
})
pane.onDidRemoveItem(function ({item}) {
pane.onDidRemoveItem(function ({ item }) {
if (item === itemA) eventOrder.push('remove')
})
@@ -221,9 +252,11 @@ describe('Pane', () => {
it('subscribes to be notified when item terminates its pending state', () => {
const fakeDisposable = { dispose: () => {} }
const spy = jasmine.createSpy('onDidTerminatePendingState').andReturn((fakeDisposable))
const spy = jasmine
.createSpy('onDidTerminatePendingState')
.andReturn(fakeDisposable)
const pane = new Pane(paneParams({items: []}))
const pane = new Pane(paneParams({ items: [] }))
const item = {
getTitle: () => '',
onDidTerminatePendingState: spy
@@ -235,9 +268,9 @@ describe('Pane', () => {
it('subscribes to be notified when item is destroyed', () => {
const fakeDisposable = { dispose: () => {} }
const spy = jasmine.createSpy('onDidDestroy').andReturn((fakeDisposable))
const spy = jasmine.createSpy('onDidDestroy').andReturn(fakeDisposable)
const pane = new Pane(paneParams({items: []}))
const pane = new Pane(paneParams({ items: [] }))
const item = {
getTitle: () => '',
onDidDestroy: spy
@@ -251,7 +284,7 @@ describe('Pane', () => {
beforeEach(() => spyOn(Grim, 'deprecate'))
it('supports the older public API', () => {
const pane = new Pane(paneParams({items: []}))
const pane = new Pane(paneParams({ items: [] }))
const itemA = new Item('A')
const itemB = new Item('B')
const itemC = new Item('C')
@@ -262,9 +295,11 @@ describe('Pane', () => {
})
it('shows a deprecation warning', () => {
const pane = new Pane(paneParams({items: []}))
const 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})')
expect(Grim.deprecate).toHaveBeenCalledWith(
'Pane::addItem(item, 2) is deprecated in favor of Pane::addItem(item, {index: 2})'
)
})
})
})
@@ -273,7 +308,7 @@ describe('Pane', () => {
let pane = null
beforeEach(() => {
pane = new Pane(paneParams({items: [new Item('A'), new Item('B')]}))
pane = new Pane(paneParams({ items: [new Item('A'), new Item('B')] }))
})
it('changes the active item to the current item', () => {
@@ -306,16 +341,16 @@ describe('Pane', () => {
})
it('replaces the active item if it is pending', () => {
pane.activateItem(itemC, {pending: true})
pane.activateItem(itemC, { pending: true })
expect(pane.getItems().map(item => item.name)).toEqual(['A', 'C', 'B'])
pane.activateItem(itemD, {pending: true})
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, {pending: true})
pane.activateItem(itemC, { pending: true })
pane.activateItemAtIndex(2)
pane.activateItem(itemD, {pending: true})
pane.activateItem(itemD, { pending: true })
expect(pane.getItems().map(item => item.name)).toEqual(['A', 'B', 'D'])
})
})
@@ -369,14 +404,14 @@ describe('Pane', () => {
const pendingSpy = jasmine.createSpy('onItemDidTerminatePendingState')
const destroySpy = jasmine.createSpy('onWillDestroyItem')
await atom.workspace.open('sample.txt', {pending: true}).then(() => {
await atom.workspace.open('sample.txt', { pending: true }).then(() => {
pane = atom.workspace.getActivePane()
})
pane.onItemDidTerminatePendingState(pendingSpy)
pane.onWillDestroyItem(destroySpy)
await atom.workspace.open('sample.js', {pending: true})
await atom.workspace.open('sample.js', { pending: true })
expect(destroySpy).toHaveBeenCalled()
expect(pendingSpy).not.toHaveBeenCalled()
@@ -385,7 +420,17 @@ describe('Pane', () => {
describe('::activateNextRecentlyUsedItem() and ::activatePreviousRecentlyUsedItem()', () => {
it('sets the active item to the next/previous item in the itemStack, looping around at either end', () => {
const pane = new Pane(paneParams({items: [new Item('A'), new Item('B'), new Item('C'), new Item('D'), new Item('E')]}))
const pane = new Pane(
paneParams({
items: [
new Item('A'),
new Item('B'),
new Item('C'),
new Item('D'),
new Item('E')
]
})
)
const [item1, item2, item3, item4, item5] = pane.getItems()
pane.itemStack = [item3, item1, item2, item5, item4]
@@ -416,7 +461,9 @@ describe('Pane', () => {
describe('::activateNextItem() and ::activatePreviousItem()', () => {
it('sets the active item to the next/previous item, looping around at either end', () => {
const pane = new Pane(paneParams({items: [new Item('A'), new Item('B'), new Item('C')]}))
const pane = new Pane(
paneParams({ items: [new Item('A'), new Item('B'), new Item('C')] })
)
const [item1, item2, item3] = pane.getItems()
expect(pane.getActiveItem()).toBe(item1)
@@ -433,8 +480,10 @@ describe('Pane', () => {
describe('::activateLastItem()', () => {
it('sets the active item to the last item', () => {
const pane = new Pane(paneParams({items: [new Item('A'), new Item('B'), new Item('C')]}))
const [item1,, item3] = pane.getItems()
const pane = new Pane(
paneParams({ items: [new Item('A'), new Item('B'), new Item('C')] })
)
const [item1, , item3] = pane.getItems()
expect(pane.getActiveItem()).toBe(item1)
pane.activateLastItem()
@@ -444,7 +493,9 @@ describe('Pane', () => {
describe('::moveItemRight() and ::moveItemLeft()', () => {
it('moves the active item to the right and left, without looping around at either end', () => {
const pane = new Pane(paneParams({items: [new Item('A'), new Item('B'), new Item('C')]}))
const pane = new Pane(
paneParams({ items: [new Item('A'), new Item('B'), new Item('C')] })
)
const [item1, item2, item3] = pane.getItems()
pane.activateItemAtIndex(0)
@@ -464,7 +515,9 @@ describe('Pane', () => {
describe('::activateItemAtIndex(index)', () => {
it('activates the item at the given index', () => {
const pane = new Pane(paneParams({items: [new Item('A'), new Item('B'), new Item('C')]}))
const pane = new Pane(
paneParams({ items: [new Item('A'), new Item('B'), new Item('C')] })
)
const [item1, item2, item3] = pane.getItems()
pane.activateItemAtIndex(2)
expect(pane.getActiveItem()).toBe(item3)
@@ -485,7 +538,9 @@ describe('Pane', () => {
let pane, item1, item2, item3
beforeEach(() => {
pane = new Pane(paneParams({items: [new Item('A'), new Item('B'), new Item('C')]}))
pane = new Pane(
paneParams({ items: [new Item('A'), new Item('B'), new Item('C')] })
)
;[item1, item2, item3] = pane.getItems()
})
@@ -521,17 +576,17 @@ describe('Pane', () => {
it('invokes ::onWillDestroyItem() and PaneContainer::onWillDestroyPaneItem observers before destroying the item', async () => {
jasmine.useRealClock()
pane.container = new PaneContainer({config: atom.config, confirm})
pane.container = new PaneContainer({ config: atom.config, confirm })
const events = []
pane.onWillDestroyItem(async (event) => {
pane.onWillDestroyItem(async event => {
expect(item2.isDestroyed()).toBe(false)
await timeoutPromise(50)
expect(item2.isDestroyed()).toBe(false)
events.push(['will-destroy-item', event])
})
pane.container.onWillDestroyPaneItem(async (event) => {
pane.container.onWillDestroyPaneItem(async event => {
expect(item2.isDestroyed()).toBe(false)
await timeoutPromise(50)
expect(item2.isDestroyed()).toBe(false)
@@ -541,8 +596,8 @@ describe('Pane', () => {
await pane.destroyItem(item2)
expect(item2.isDestroyed()).toBe(true)
expect(events).toEqual([
['will-destroy-item', {item: item2, index: 1}],
['will-destroy-pane-item', {item: item2, index: 1, pane}]
['will-destroy-item', { item: item2, index: 1 }],
['will-destroy-pane-item', { item: item2, index: 1, pane }]
])
})
@@ -550,14 +605,18 @@ describe('Pane', () => {
const events = []
pane.onWillRemoveItem(event => events.push(event))
pane.destroyItem(item2)
expect(events).toEqual([{item: item2, index: 1, moved: false, destroyed: true}])
expect(events).toEqual([
{ item: item2, index: 1, moved: false, destroyed: true }
])
})
it('invokes ::onDidRemoveItem() observers', () => {
const events = []
pane.onDidRemoveItem(event => events.push(event))
pane.destroyItem(item2)
expect(events).toEqual([{item: item2, index: 1, moved: false, destroyed: true}])
expect(events).toEqual([
{ item: item2, index: 1, moved: false, destroyed: true }
])
})
describe('when the destroyed item is the active item and is the first item', () => {
@@ -608,7 +667,9 @@ describe('Pane', () => {
itemURI = null
showSaveDialog.andCallFake((options, callback) => callback('/selected/path'))
showSaveDialog.andCallFake((options, callback) =>
callback('/selected/path')
)
confirm.andCallFake((options, callback) => callback(0))
const success = await pane.destroyItem(item1)
@@ -631,7 +692,7 @@ describe('Pane', () => {
expect(item1.save).not.toHaveBeenCalled()
expect(pane.getItems().includes(item1)).toBe(false)
expect(item1.isDestroyed()).toBe(true)
expect(success).toBe(true);
expect(success).toBe(true)
})
})
@@ -662,7 +723,9 @@ describe('Pane', () => {
describe("when the 'core.destroyEmptyPanes' config option is false (the default)", () => {
it('does not destroy the pane, but leaves it in place with empty items', () => {
expect(atom.config.get('core.destroyEmptyPanes')).toBe(false)
for (let item of pane.getItems()) { pane.destroyItem(item) }
for (let item of pane.getItems()) {
pane.destroyItem(item)
}
expect(pane.isDestroyed()).toBe(false)
expect(pane.getActiveItem()).toBeUndefined()
expect(() => pane.saveActiveItem()).not.toThrow()
@@ -687,7 +750,7 @@ describe('Pane', () => {
const success = await pane.destroyItem(item1)
expect(pane.getItems().includes(item1)).toBe(true)
expect(item1.isDestroyed()).toBe(false)
expect(success).toBe(false);
expect(success).toBe(false)
})
it('destroy the item if force=true', async () => {
@@ -702,7 +765,9 @@ describe('Pane', () => {
describe('::destroyActiveItem()', () => {
it('destroys the active item', () => {
const pane = new Pane(paneParams({items: [new Item('A'), new Item('B')]}))
const pane = new Pane(
paneParams({ items: [new Item('A'), new Item('B')] })
)
const activeItem = pane.getActiveItem()
pane.destroyActiveItem()
expect(activeItem.isDestroyed()).toBe(true)
@@ -717,7 +782,9 @@ describe('Pane', () => {
describe('::destroyItems()', () => {
it('destroys all items', async () => {
const pane = new Pane(paneParams({items: [new Item('A'), new Item('B'), new Item('C')]}))
const pane = new Pane(
paneParams({ items: [new Item('A'), new Item('B'), new Item('C')] })
)
const [item1, item2, item3] = pane.getItems()
await pane.destroyItems()
@@ -730,7 +797,7 @@ describe('Pane', () => {
describe('::observeItems()', () => {
it('invokes the observer with all current and future items', () => {
const pane = new Pane(paneParams({items: [new Item(), new Item()]}))
const pane = new Pane(paneParams({ items: [new Item(), new Item()] }))
const [item1, item2] = pane.getItems()
const observed = []
@@ -745,8 +812,10 @@ describe('Pane', () => {
describe('when an item emits a destroyed event', () => {
it('removes it from the list of items', () => {
const pane = new Pane(paneParams({items: [new Item('A'), new Item('B'), new Item('C')]}))
const [item1,, item3] = pane.getItems()
const pane = new Pane(
paneParams({ items: [new Item('A'), new Item('B'), new Item('C')] })
)
const [item1, , item3] = pane.getItems()
pane.itemAtIndex(1).destroy()
expect(pane.getItems()).toEqual([item1, item3])
})
@@ -754,7 +823,9 @@ describe('Pane', () => {
describe('::destroyInactiveItems()', () => {
it('destroys all items but the active item', () => {
const pane = new Pane(paneParams({items: [new Item('A'), new Item('B'), new Item('C')]}))
const pane = new Pane(
paneParams({ items: [new Item('A'), new Item('B'), new Item('C')] })
)
const [, item2] = pane.getItems()
pane.activateItem(item2)
pane.destroyInactiveItems()
@@ -766,8 +837,10 @@ describe('Pane', () => {
let pane
beforeEach(() => {
pane = new Pane(paneParams({items: [new Item('A')]}))
showSaveDialog.andCallFake((options, callback) => callback('/selected/path'))
pane = new Pane(paneParams({ items: [new Item('A')] }))
showSaveDialog.andCallFake((options, callback) =>
callback('/selected/path')
)
})
describe('when the active item has a uri', () => {
@@ -797,7 +870,9 @@ describe('Pane', () => {
pane.getActiveItem().saveAs = jasmine.createSpy('saveAs')
await pane.saveActiveItem()
expect(showSaveDialog.mostRecentCall.args[0]).toEqual({})
expect(pane.getActiveItem().saveAs).toHaveBeenCalledWith('/selected/path')
expect(pane.getActiveItem().saveAs).toHaveBeenCalledWith(
'/selected/path'
)
})
})
@@ -826,14 +901,16 @@ describe('Pane', () => {
return Promise.reject(error)
}
waitsFor((done) => {
const subscription = atom.notifications.onDidAddNotification(function (notification) {
expect(notification.getType()).toBe('warning')
expect(notification.getMessage()).toContain('Permission denied')
expect(notification.getMessage()).toContain('/foo')
subscription.dispose()
done()
})
waitsFor(done => {
const subscription = atom.notifications.onDidAddNotification(
function (notification) {
expect(notification.getType()).toBe('warning')
expect(notification.getMessage()).toContain('Permission denied')
expect(notification.getMessage()).toContain('/foo')
subscription.dispose()
done()
}
)
pane.saveActiveItem()
})
})
@@ -848,14 +925,16 @@ describe('Pane', () => {
throw error
}
waitsFor((done) => {
const subscription = atom.notifications.onDidAddNotification(function (notification) {
expect(notification.getType()).toBe('warning')
expect(notification.getMessage()).toContain('Permission denied')
expect(notification.getMessage()).toContain('/foo')
subscription.dispose()
done()
})
waitsFor(done => {
const subscription = atom.notifications.onDidAddNotification(
function (notification) {
expect(notification.getType()).toBe('warning')
expect(notification.getMessage()).toContain('Permission denied')
expect(notification.getMessage()).toContain('/foo')
subscription.dispose()
done()
}
)
pane.saveActiveItem()
})
})
@@ -866,8 +945,10 @@ describe('Pane', () => {
let pane = null
beforeEach(() => {
pane = new Pane(paneParams({items: [new Item('A')]}))
showSaveDialog.andCallFake((options, callback) => callback('/selected/path'))
pane = new Pane(paneParams({ items: [new Item('A')] }))
showSaveDialog.andCallFake((options, callback) =>
callback('/selected/path')
)
})
describe('when the current item has a saveAs method', () => {
@@ -877,10 +958,16 @@ describe('Pane', () => {
pane.getActiveItem().path = __filename
pane.getActiveItem().saveAs = jasmine.createSpy('saveAs')
pane.saveActiveItemAs()
expect(showSaveDialog.mostRecentCall.args[0]).toEqual({defaultPath: __filename})
expect(showSaveDialog.mostRecentCall.args[0]).toEqual({
defaultPath: __filename
})
await conditionPromise(() => pane.getActiveItem().saveAs.callCount === 1)
expect(pane.getActiveItem().saveAs).toHaveBeenCalledWith('/selected/path')
await conditionPromise(
() => pane.getActiveItem().saveAs.callCount === 1
)
expect(pane.getActiveItem().saveAs).toHaveBeenCalledWith(
'/selected/path'
)
})
})
@@ -901,14 +988,16 @@ describe('Pane', () => {
return Promise.reject(error)
}
waitsFor((done) => {
const subscription = atom.notifications.onDidAddNotification(function (notification) {
expect(notification.getType()).toBe('warning')
expect(notification.getMessage()).toContain('Permission denied')
expect(notification.getMessage()).toContain('/foo')
subscription.dispose()
done()
})
waitsFor(done => {
const subscription = atom.notifications.onDidAddNotification(
function (notification) {
expect(notification.getType()).toBe('warning')
expect(notification.getMessage()).toContain('Permission denied')
expect(notification.getMessage()).toContain('/foo')
subscription.dispose()
done()
}
)
pane.saveActiveItemAs()
})
})
@@ -917,7 +1006,11 @@ describe('Pane', () => {
describe('::itemForURI(uri)', () => {
it('returns the item for which a call to .getURI() returns the given uri', () => {
const pane = new Pane(paneParams({items: [new Item('A'), new Item('B'), new Item('C'), new Item('D')]}))
const pane = new Pane(
paneParams({
items: [new Item('A'), new Item('B'), new Item('C'), new Item('D')]
})
)
const [item1, item2] = pane.getItems()
item1.uri = 'a'
item2.uri = 'b'
@@ -931,7 +1024,11 @@ describe('Pane', () => {
let pane, item1, item2, item3, item4
beforeEach(() => {
pane = new Pane(paneParams({items: [new Item('A'), new Item('B'), new Item('C'), new Item('D')]}))
pane = new Pane(
paneParams({
items: [new Item('A'), new Item('B'), new Item('C'), new Item('D')]
})
)
;[item1, item2, item3, item4] = pane.getItems()
})
@@ -953,8 +1050,8 @@ describe('Pane', () => {
pane.moveItem(item1, 2)
pane.moveItem(item2, 3)
expect(events).toEqual([
{item: item1, oldIndex: 0, newIndex: 2},
{item: item2, oldIndex: 0, newIndex: 3}
{ item: item1, oldIndex: 0, newIndex: 2 },
{ item: item2, oldIndex: 0, newIndex: 3 }
])
})
})
@@ -964,12 +1061,12 @@ describe('Pane', () => {
let item1, item2, item3, item4, item5
beforeEach(() => {
container = new PaneContainer({config: atom.config, confirm})
container = new PaneContainer({ config: atom.config, confirm })
pane1 = container.getActivePane()
pane1.addItems([new Item('A'), new Item('B'), new Item('C')])
pane2 = pane1.splitRight({items: [new Item('D'), new Item('E')]});
[item1, item2, item3] = pane1.getItems();
[item4, item5] = pane2.getItems()
pane2 = pane1.splitRight({ items: [new Item('D'), new Item('E')] })
;[item1, item2, item3] = pane1.getItems()
;[item4, item5] = pane2.getItems()
})
it('moves the item to the given pane at the given index', () => {
@@ -983,7 +1080,9 @@ describe('Pane', () => {
pane1.onWillRemoveItem(event => events.push(event))
pane1.moveItemToPane(item2, pane2, 1)
expect(events).toEqual([{item: item2, index: 1, moved: true, destroyed: false}])
expect(events).toEqual([
{ item: item2, index: 1, moved: true, destroyed: false }
])
})
it('invokes ::onDidRemoveItem() observers', () => {
@@ -991,7 +1090,9 @@ describe('Pane', () => {
pane1.onDidRemoveItem(event => events.push(event))
pane1.moveItemToPane(item2, pane2, 1)
expect(events).toEqual([{item: item2, index: 1, moved: true, destroyed: false}])
expect(events).toEqual([
{ item: item2, index: 1, moved: true, destroyed: false }
])
})
it('does not invoke ::onDidAddPaneItem observers on the container', () => {
@@ -1025,7 +1126,7 @@ describe('Pane', () => {
describe('when the item being moved is pending', () => {
it('is made permanent in the new pane', () => {
const item6 = new Item('F')
pane1.addItem(item6, {pending: true})
pane1.addItem(item6, { pending: true })
expect(pane1.getPendingItem()).toEqual(item6)
pane1.moveItemToPane(item6, pane2, 0)
expect(pane2.getPendingItem()).not.toEqual(item6)
@@ -1035,7 +1136,7 @@ describe('Pane', () => {
describe('when the target pane has a pending item', () => {
it('does not destroy the pending item', () => {
const item6 = new Item('F')
pane1.addItem(item6, {pending: true})
pane1.addItem(item6, { pending: true })
expect(pane1.getPendingItem()).toEqual(item6)
pane2.moveItemToPane(item5, pane1, 0)
expect(pane1.getPendingItem()).toEqual(item6)
@@ -1047,7 +1148,11 @@ describe('Pane', () => {
let pane1, item1, container
beforeEach(() => {
container = new PaneContainer({config: atom.config, confirm, deserializerManager: atom.deserializers})
container = new PaneContainer({
config: atom.config,
confirm,
deserializerManager: atom.deserializers
})
pane1 = container.getActivePane()
item1 = new Item('A')
pane1.addItem(item1)
@@ -1056,8 +1161,8 @@ describe('Pane', () => {
describe('::splitLeft(params)', () => {
describe('when the parent is the container root', () => {
it('replaces itself with a row and inserts a new pane to the left of itself', () => {
const pane2 = pane1.splitLeft({items: [new Item('B')]})
const pane3 = pane1.splitLeft({items: [new Item('C')]})
const pane2 = pane1.splitLeft({ items: [new Item('B')] })
const pane3 = pane1.splitLeft({ items: [new Item('C')] })
expect(container.root.orientation).toBe('horizontal')
expect(container.root.children).toEqual([pane2, pane3, pane1])
})
@@ -1065,20 +1170,20 @@ describe('Pane', () => {
describe('when `moveActiveItem: true` is passed in the params', () => {
it('moves the active item', () => {
const pane2 = pane1.splitLeft({moveActiveItem: true})
const pane2 = pane1.splitLeft({ moveActiveItem: true })
expect(pane2.getActiveItem()).toBe(item1)
})
})
describe('when `copyActiveItem: true` is passed in the params', () => {
it('duplicates the active item', () => {
const pane2 = pane1.splitLeft({copyActiveItem: true})
const pane2 = pane1.splitLeft({ copyActiveItem: true })
expect(pane2.getActiveItem()).toEqual(pane1.getActiveItem())
})
it("does nothing if the active item doesn't implement .copy()", () => {
item1.copy = null
const pane2 = pane1.splitLeft({copyActiveItem: true})
const pane2 = pane1.splitLeft({ copyActiveItem: true })
expect(pane2.getActiveItem()).toBeUndefined()
})
})
@@ -1086,8 +1191,8 @@ describe('Pane', () => {
describe('when the parent is a column', () => {
it('replaces itself with a row and inserts a new pane to the left of itself', () => {
pane1.splitDown()
const pane2 = pane1.splitLeft({items: [new Item('B')]})
const pane3 = pane1.splitLeft({items: [new Item('C')]})
const pane2 = pane1.splitLeft({ items: [new Item('B')] })
const pane3 = pane1.splitLeft({ items: [new Item('C')] })
const row = container.root.children[0]
expect(row.orientation).toBe('horizontal')
expect(row.children).toEqual([pane2, pane3, pane1])
@@ -1098,8 +1203,8 @@ describe('Pane', () => {
describe('::splitRight(params)', () => {
describe('when the parent is the container root', () => {
it('replaces itself with a row and inserts a new pane to the right of itself', () => {
const pane2 = pane1.splitRight({items: [new Item('B')]})
const pane3 = pane1.splitRight({items: [new Item('C')]})
const pane2 = pane1.splitRight({ items: [new Item('B')] })
const pane3 = pane1.splitRight({ items: [new Item('C')] })
expect(container.root.orientation).toBe('horizontal')
expect(container.root.children).toEqual([pane1, pane3, pane2])
})
@@ -1107,14 +1212,14 @@ describe('Pane', () => {
describe('when `moveActiveItem: true` is passed in the params', () => {
it('moves the active item', () => {
const pane2 = pane1.splitRight({moveActiveItem: true})
const pane2 = pane1.splitRight({ moveActiveItem: true })
expect(pane2.getActiveItem()).toBe(item1)
})
})
describe('when `copyActiveItem: true` is passed in the params', () => {
it('duplicates the active item', () => {
const pane2 = pane1.splitRight({copyActiveItem: true})
const pane2 = pane1.splitRight({ copyActiveItem: true })
expect(pane2.getActiveItem()).toEqual(pane1.getActiveItem())
})
})
@@ -1122,8 +1227,8 @@ describe('Pane', () => {
describe('when the parent is a column', () => {
it('replaces itself with a row and inserts a new pane to the right of itself', () => {
pane1.splitDown()
const pane2 = pane1.splitRight({items: [new Item('B')]})
const pane3 = pane1.splitRight({items: [new Item('C')]})
const pane2 = pane1.splitRight({ items: [new Item('B')] })
const pane3 = pane1.splitRight({ items: [new Item('C')] })
const row = container.root.children[0]
expect(row.orientation).toBe('horizontal')
expect(row.children).toEqual([pane1, pane3, pane2])
@@ -1134,8 +1239,8 @@ describe('Pane', () => {
describe('::splitUp(params)', () => {
describe('when the parent is the container root', () => {
it('replaces itself with a column and inserts a new pane above itself', () => {
const pane2 = pane1.splitUp({items: [new Item('B')]})
const pane3 = pane1.splitUp({items: [new Item('C')]})
const pane2 = pane1.splitUp({ items: [new Item('B')] })
const pane3 = pane1.splitUp({ items: [new Item('C')] })
expect(container.root.orientation).toBe('vertical')
expect(container.root.children).toEqual([pane2, pane3, pane1])
})
@@ -1143,14 +1248,14 @@ describe('Pane', () => {
describe('when `moveActiveItem: true` is passed in the params', () => {
it('moves the active item', () => {
const pane2 = pane1.splitUp({moveActiveItem: true})
const pane2 = pane1.splitUp({ moveActiveItem: true })
expect(pane2.getActiveItem()).toBe(item1)
})
})
describe('when `copyActiveItem: true` is passed in the params', () => {
it('duplicates the active item', () => {
const pane2 = pane1.splitUp({copyActiveItem: true})
const pane2 = pane1.splitUp({ copyActiveItem: true })
expect(pane2.getActiveItem()).toEqual(pane1.getActiveItem())
})
})
@@ -1158,8 +1263,8 @@ describe('Pane', () => {
describe('when the parent is a row', () => {
it('replaces itself with a column and inserts a new pane above itself', () => {
pane1.splitRight()
const pane2 = pane1.splitUp({items: [new Item('B')]})
const pane3 = pane1.splitUp({items: [new Item('C')]})
const pane2 = pane1.splitUp({ items: [new Item('B')] })
const pane3 = pane1.splitUp({ items: [new Item('C')] })
const column = container.root.children[0]
expect(column.orientation).toBe('vertical')
expect(column.children).toEqual([pane2, pane3, pane1])
@@ -1170,8 +1275,8 @@ describe('Pane', () => {
describe('::splitDown(params)', () => {
describe('when the parent is the container root', () => {
it('replaces itself with a column and inserts a new pane below itself', () => {
const pane2 = pane1.splitDown({items: [new Item('B')]})
const pane3 = pane1.splitDown({items: [new Item('C')]})
const pane2 = pane1.splitDown({ items: [new Item('B')] })
const pane3 = pane1.splitDown({ items: [new Item('C')] })
expect(container.root.orientation).toBe('vertical')
expect(container.root.children).toEqual([pane1, pane3, pane2])
})
@@ -1179,14 +1284,14 @@ describe('Pane', () => {
describe('when `moveActiveItem: true` is passed in the params', () => {
it('moves the active item', () => {
const pane2 = pane1.splitDown({moveActiveItem: true})
const pane2 = pane1.splitDown({ moveActiveItem: true })
expect(pane2.getActiveItem()).toBe(item1)
})
})
describe('when `copyActiveItem: true` is passed in the params', () => {
it('duplicates the active item', () => {
const pane2 = pane1.splitDown({copyActiveItem: true})
const pane2 = pane1.splitDown({ copyActiveItem: true })
expect(pane2.getActiveItem()).toEqual(pane1.getActiveItem())
})
})
@@ -1194,8 +1299,8 @@ describe('Pane', () => {
describe('when the parent is a row', () => {
it('replaces itself with a column and inserts a new pane below itself', () => {
pane1.splitRight()
const pane2 = pane1.splitDown({items: [new Item('B')]})
const pane3 = pane1.splitDown({items: [new Item('C')]})
const pane2 = pane1.splitDown({ items: [new Item('B')] })
const pane3 = pane1.splitDown({ items: [new Item('C')] })
const column = container.root.children[0]
expect(column.orientation).toBe('vertical')
expect(column.children).toEqual([pane1, pane3, pane2])
@@ -1209,7 +1314,9 @@ describe('Pane', () => {
pane1.destroyItem(item1)
expect(pane1.getActiveItem()).toBe(undefined)
const pane2 = pane1.split('horizontal', 'before', {moveActiveItem: true})
const pane2 = pane1.split('horizontal', 'before', {
moveActiveItem: true
})
expect(container.root.children).toEqual([pane2, pane1])
expect(pane2.getActiveItem()).toBe(undefined)
@@ -1221,7 +1328,9 @@ describe('Pane', () => {
pane1.destroyItem(item1)
expect(pane1.getActiveItem()).toBe(undefined)
const pane2 = pane1.split('horizontal', 'before', {copyActiveItem: true})
const pane2 = pane1.split('horizontal', 'before', {
copyActiveItem: true
})
expect(container.root.children).toEqual([pane2, pane1])
expect(pane2.getActiveItem()).toBe(undefined)
@@ -1239,7 +1348,9 @@ describe('Pane', () => {
describe('::close()', () => {
it('prompts to save unsaved items before destroying the pane', async () => {
const pane = new Pane(paneParams({items: [new Item('A'), new Item('B')]}))
const pane = new Pane(
paneParams({ items: [new Item('A'), new Item('B')] })
)
const [item1] = pane.getItems()
item1.shouldPromptToSave = () => true
@@ -1254,7 +1365,9 @@ describe('Pane', () => {
})
it('does not destroy the pane if the user clicks cancel', async () => {
const pane = new Pane(paneParams({items: [new Item('A'), new Item('B')]}))
const pane = new Pane(
paneParams({ items: [new Item('A'), new Item('B')] })
)
const [item1] = pane.getItems()
item1.shouldPromptToSave = () => true
@@ -1270,7 +1383,9 @@ describe('Pane', () => {
})
it('does not destroy the pane if the user starts to save but then does not choose a path', async () => {
const pane = new Pane(paneParams({items: [new Item('A'), new Item('B')]}))
const pane = new Pane(
paneParams({ items: [new Item('A'), new Item('B')] })
)
const [item1] = pane.getItems()
item1.shouldPromptToSave = () => true
@@ -1290,8 +1405,12 @@ describe('Pane', () => {
let pane, item1
beforeEach(() => {
pane = new Pane({items: [new Item('A'), new Item('B')], applicationDelegate: atom.applicationDelegate, config: atom.config});
[item1] = pane.getItems()
pane = new Pane({
items: [new Item('A'), new Item('B')],
applicationDelegate: atom.applicationDelegate,
config: atom.config
})
;[item1] = pane.getItems()
item1.shouldPromptToSave = () => true
item1.getURI = () => '/test/path'
@@ -1336,7 +1455,9 @@ describe('Pane', () => {
await pane.close()
expect(atom.applicationDelegate.confirm).toHaveBeenCalled()
expect(confirmations).toBe(2)
expect(atom.applicationDelegate.showSaveDialog.mostRecentCall.args[0]).toEqual({})
expect(
atom.applicationDelegate.showSaveDialog.mostRecentCall.args[0]
).toEqual({})
expect(item1.save).toHaveBeenCalled()
expect(item1.saveAs).toHaveBeenCalled()
expect(pane.isDestroyed()).toBe(true)
@@ -1365,7 +1486,9 @@ describe('Pane', () => {
await pane.close()
expect(atom.applicationDelegate.confirm).toHaveBeenCalled()
expect(confirmations).toBe(3)
expect(atom.applicationDelegate.showSaveDialog.mostRecentCall.args[0]).toEqual({})
expect(
atom.applicationDelegate.showSaveDialog.mostRecentCall.args[0]
).toEqual({})
expect(item1.save).toHaveBeenCalled()
expect(item1.saveAs).toHaveBeenCalled()
expect(pane.isDestroyed()).toBe(true)
@@ -1377,7 +1500,7 @@ describe('Pane', () => {
let container, pane1, pane2
beforeEach(() => {
container = new PaneContainer({config: atom.config, confirm})
container = new PaneContainer({ config: atom.config, confirm })
pane1 = container.root
pane1.addItems([new Item('A'), new Item('B')])
pane2 = pane1.splitRight()
@@ -1386,7 +1509,7 @@ describe('Pane', () => {
it('invokes ::onWillDestroy observers before destroying items', () => {
let itemsDestroyed = null
pane1.onWillDestroy(() => {
itemsDestroyed = (pane1.getItems().map((item) => item.isDestroyed()))
itemsDestroyed = pane1.getItems().map(item => item.isDestroyed())
})
pane1.destroy()
expect(itemsDestroyed).toEqual([false, false])
@@ -1435,7 +1558,7 @@ describe('Pane', () => {
let editor1, pane, eventCount
beforeEach(async () => {
editor1 = await atom.workspace.open('sample.txt', {pending: true})
editor1 = await atom.workspace.open('sample.txt', { pending: true })
pane = atom.workspace.getActivePane()
eventCount = 0
editor1.onDidTerminatePendingState(() => eventCount++)
@@ -1458,7 +1581,7 @@ describe('Pane', () => {
})
it('terminates pending state when buffer is changed', () => {
editor1.insertText('I\'ll be back!')
editor1.insertText("I'll be back!")
advanceClock(editor1.getBuffer().stoppedChangingDelay)
expect(pane.getPendingItem()).toBeNull()
@@ -1498,10 +1621,12 @@ describe('Pane', () => {
let pane = null
beforeEach(() => {
pane = new Pane(paneParams({
items: [new Item('A', 'a'), new Item('B', 'b'), new Item('C', 'c')],
flexScale: 2
}))
pane = new Pane(
paneParams({
items: [new Item('A', 'a'), new Item('B', 'b'), new Item('C', 'c')],
flexScale: 2
})
)
})
it('can serialize and deserialize the pane and all its items', () => {
@@ -1524,7 +1649,7 @@ describe('Pane', () => {
it("restores the correct item when it doesn't implement getURI() and some items weren't deserialized", () => {
const unserializable = {}
pane.addItem(unserializable, {index: 0})
pane.addItem(unserializable, { index: 0 })
pane.items[2].getURI = null
pane.activateItemAtIndex(2)
const newPane = Pane.deserialize(pane.serialize(), atom)

View File

@@ -6,8 +6,7 @@ const PanelContainer = require('../src/panel-container')
describe('PanelContainerElement', () => {
let jasmineContent, element, container
class TestPanelContainerItem {
}
class TestPanelContainerItem {}
class TestPanelContainerItemElement_ extends HTMLElement {
createdCallback () {
@@ -17,23 +16,25 @@ describe('PanelContainerElement', () => {
this.model = model
return this
}
focus() {}
focus () {}
}
const TestPanelContainerItemElement = document.registerElement(
'atom-test-container-item-element',
{prototype: TestPanelContainerItemElement_.prototype}
{ prototype: TestPanelContainerItemElement_.prototype }
)
beforeEach(() => {
jasmineContent = document.body.querySelector('#jasmine-content')
atom.views.addViewProvider(
TestPanelContainerItem,
model => new TestPanelContainerItemElement().initialize(model)
atom.views.addViewProvider(TestPanelContainerItem, model =>
new TestPanelContainerItemElement().initialize(model)
)
container = new PanelContainer({viewRegistry: atom.views, location: 'left'})
container = new PanelContainer({
viewRegistry: atom.views,
location: 'left'
})
element = container.getElement()
jasmineContent.appendChild(element)
})
@@ -50,9 +51,18 @@ describe('PanelContainerElement', () => {
describe('adding and removing panels', () => {
it('allows panels to be inserted at any position', () => {
const panel1 = new Panel({item: new TestPanelContainerItem(), priority: 10}, atom.views)
const panel2 = new Panel({item: new TestPanelContainerItem(), priority: 5}, atom.views)
const panel3 = new Panel({item: new TestPanelContainerItem(), priority: 8}, atom.views)
const panel1 = new Panel(
{ item: new TestPanelContainerItem(), priority: 10 },
atom.views
)
const panel2 = new Panel(
{ item: new TestPanelContainerItem(), priority: 5 },
atom.views
)
const panel3 = new Panel(
{ item: new TestPanelContainerItem(), priority: 8 },
atom.views
)
container.addPanel(panel1)
container.addPanel(panel2)
@@ -67,7 +77,10 @@ describe('PanelContainerElement', () => {
it('adds atom-panel elements when a new panel is added to the container; removes them when the panels are destroyed', () => {
expect(element.childNodes.length).toBe(0)
const panel1 = new Panel({item: new TestPanelContainerItem()}, atom.views)
const panel1 = new Panel(
{ item: new TestPanelContainerItem() },
atom.views
)
container.addPanel(panel1)
expect(element.childNodes.length).toBe(1)
expect(element.childNodes[0]).toHaveClass('left')
@@ -76,7 +89,10 @@ describe('PanelContainerElement', () => {
expect(element.childNodes[0].tagName).toBe('ATOM-PANEL')
const panel2 = new Panel({item: new TestPanelContainerItem()}, atom.views)
const panel2 = new Panel(
{ item: new TestPanelContainerItem() },
atom.views
)
container.addPanel(panel2)
expect(element.childNodes.length).toBe(2)
@@ -88,12 +104,14 @@ describe('PanelContainerElement', () => {
panel2.destroy()
expect(element.childNodes.length).toBe(0)
})
)
}))
describe('when the container is at the bottom location', () => {
beforeEach(() => {
container = new PanelContainer({viewRegistry: atom.views, location: 'bottom'})
container = new PanelContainer({
viewRegistry: atom.views,
location: 'bottom'
})
element = container.getElement()
jasmineContent.appendChild(element)
})
@@ -101,7 +119,10 @@ describe('PanelContainerElement', () => {
it('adds atom-panel elements when a new panel is added to the container; removes them when the panels are destroyed', () => {
expect(element.childNodes.length).toBe(0)
const panel1 = new Panel({item: new TestPanelContainerItem(), className: 'one'}, atom.views)
const panel1 = new Panel(
{ item: new TestPanelContainerItem(), className: 'one' },
atom.views
)
container.addPanel(panel1)
expect(element.childNodes.length).toBe(1)
expect(element.childNodes[0]).toHaveClass('bottom')
@@ -110,7 +131,10 @@ describe('PanelContainerElement', () => {
expect(element.childNodes[0].tagName).toBe('ATOM-PANEL')
expect(panel1.getElement()).toHaveClass('one')
const panel2 = new Panel({item: new TestPanelContainerItem(), className: 'two'}, atom.views)
const panel2 = new Panel(
{ item: new TestPanelContainerItem(), className: 'two' },
atom.views
)
container.addPanel(panel2)
expect(element.childNodes.length).toBe(2)
expect(panel2.getElement()).toHaveClass('two')
@@ -126,18 +150,27 @@ describe('PanelContainerElement', () => {
describe('when the container is modal', () => {
beforeEach(() => {
container = new PanelContainer({viewRegistry: atom.views, location: 'modal'})
container = new PanelContainer({
viewRegistry: atom.views,
location: 'modal'
})
element = container.getElement()
jasmineContent.appendChild(element)
})
it('allows only one panel to be visible at a time', () => {
const panel1 = new Panel({item: new TestPanelContainerItem()}, atom.views)
const panel1 = new Panel(
{ item: new TestPanelContainerItem() },
atom.views
)
container.addPanel(panel1)
expect(panel1.getElement().style.display).not.toBe('none')
const panel2 = new Panel({item: new TestPanelContainerItem()}, atom.views)
const panel2 = new Panel(
{ item: new TestPanelContainerItem() },
atom.views
)
container.addPanel(panel2)
expect(panel1.getElement().style.display).toBe('none')
@@ -150,7 +183,10 @@ describe('PanelContainerElement', () => {
})
it("adds the 'modal' class to panels", () => {
const panel1 = new Panel({item: new TestPanelContainerItem()}, atom.views)
const panel1 = new Panel(
{ item: new TestPanelContainerItem() },
atom.views
)
container.addPanel(panel1)
expect(panel1.getElement()).toHaveClass('modal')
@@ -161,8 +197,8 @@ describe('PanelContainerElement', () => {
expect(panel1.getElement()).toHaveClass('from-top')
})
describe("autoFocus", () => {
function createPanel() {
describe('autoFocus', () => {
function createPanel () {
const panel = new Panel(
{
item: new TestPanelContainerItem(),
@@ -176,7 +212,7 @@ describe('PanelContainerElement', () => {
return panel
}
it("focuses the first tabbable item if available", () => {
it('focuses the first tabbable item if available', () => {
const panel = createPanel()
const panelEl = panel.getElement()
const inputEl = document.createElement('input')
@@ -188,7 +224,7 @@ describe('PanelContainerElement', () => {
expect(document.activeElement).toBe(inputEl)
})
it("focuses the entire panel item when no tabbable item is available and the panel is focusable", () => {
it('focuses the entire panel item when no tabbable item is available and the panel is focusable', () => {
const panel = createPanel()
const panelEl = panel.getElement()
@@ -197,7 +233,7 @@ describe('PanelContainerElement', () => {
expect(panelEl.focus).toHaveBeenCalled()
})
it("returns focus to the original activeElement", () => {
it('returns focus to the original activeElement', () => {
const panel = createPanel()
const previousActiveElement = document.activeElement
const panelEl = panel.getElement()

View File

@@ -6,11 +6,10 @@ const PanelContainer = require('../src/panel-container')
describe('PanelContainer', () => {
let container
class TestPanelItem {
}
class TestPanelItem {}
beforeEach(() => {
container = new PanelContainer({viewRegistry: atom.views})
container = new PanelContainer({ viewRegistry: atom.views })
})
describe('::addPanel(panel)', () => {
@@ -18,13 +17,13 @@ describe('PanelContainer', () => {
const addPanelSpy = jasmine.createSpy()
container.onDidAddPanel(addPanelSpy)
const panel1 = new Panel({item: new TestPanelItem()}, atom.views)
const panel1 = new Panel({ item: new TestPanelItem() }, atom.views)
container.addPanel(panel1)
expect(addPanelSpy).toHaveBeenCalledWith({panel: panel1, index: 0})
expect(addPanelSpy).toHaveBeenCalledWith({ panel: panel1, index: 0 })
const panel2 = new Panel({item: new TestPanelItem()}, atom.views)
const panel2 = new Panel({ item: new TestPanelItem() }, atom.views)
container.addPanel(panel2)
expect(addPanelSpy).toHaveBeenCalledWith({panel: panel2, index: 1})
expect(addPanelSpy).toHaveBeenCalledWith({ panel: panel2, index: 1 })
})
})
@@ -33,18 +32,18 @@ describe('PanelContainer', () => {
const removePanelSpy = jasmine.createSpy()
container.onDidRemovePanel(removePanelSpy)
const panel1 = new Panel({item: new TestPanelItem()}, atom.views)
const panel1 = new Panel({ item: new TestPanelItem() }, atom.views)
container.addPanel(panel1)
const panel2 = new Panel({item: new TestPanelItem()}, atom.views)
const panel2 = new Panel({ item: new TestPanelItem() }, atom.views)
container.addPanel(panel2)
expect(removePanelSpy).not.toHaveBeenCalled()
panel2.destroy()
expect(removePanelSpy).toHaveBeenCalledWith({panel: panel2, index: 1})
expect(removePanelSpy).toHaveBeenCalledWith({ panel: panel2, index: 1 })
panel1.destroy()
expect(removePanelSpy).toHaveBeenCalledWith({panel: panel1, index: 0})
expect(removePanelSpy).toHaveBeenCalledWith({ panel: panel1, index: 0 })
})
})
@@ -52,12 +51,16 @@ describe('PanelContainer', () => {
it('destroys the container and all of its panels', () => {
const destroyedPanels = []
const panel1 = new Panel({item: new TestPanelItem()}, atom.views)
panel1.onDidDestroy(() => { destroyedPanels.push(panel1) })
const panel1 = new Panel({ item: new TestPanelItem() }, atom.views)
panel1.onDidDestroy(() => {
destroyedPanels.push(panel1)
})
container.addPanel(panel1)
const panel2 = new Panel({item: new TestPanelItem()}, atom.views)
panel2.onDidDestroy(() => { destroyedPanels.push(panel2) })
const panel2 = new Panel({ item: new TestPanelItem() }, atom.views)
panel2.onDidDestroy(() => {
destroyedPanels.push(panel2)
})
container.addPanel(panel2)
container.destroy()
@@ -72,8 +75,8 @@ describe('PanelContainer', () => {
let initialPanel
beforeEach(() => {
// 'left' logic is the same as 'top'
container = new PanelContainer({location: 'left'})
initialPanel = new Panel({item: new TestPanelItem()}, atom.views)
container = new PanelContainer({ location: 'left' })
initialPanel = new Panel({ item: new TestPanelItem() }, atom.views)
container.addPanel(initialPanel)
})
@@ -81,10 +84,13 @@ describe('PanelContainer', () => {
it('is inserted at the beginning of the list', () => {
const addPanelSpy = jasmine.createSpy()
container.onDidAddPanel(addPanelSpy)
const panel = new Panel({item: new TestPanelItem(), priority: 0}, atom.views)
const panel = new Panel(
{ item: new TestPanelItem(), priority: 0 },
atom.views
)
container.addPanel(panel)
expect(addPanelSpy).toHaveBeenCalledWith({panel, index: 0})
expect(addPanelSpy).toHaveBeenCalledWith({ panel, index: 0 })
expect(container.getPanels()[0]).toBe(panel)
})
})
@@ -92,14 +98,20 @@ describe('PanelContainer', () => {
describe('when a panel with priority between two other panels is added', () => {
it('is inserted at the between the two panels', () => {
const addPanelSpy = jasmine.createSpy()
let panel = new Panel({item: new TestPanelItem(), priority: 1000}, atom.views)
let panel = new Panel(
{ item: new TestPanelItem(), priority: 1000 },
atom.views
)
container.addPanel(panel)
container.onDidAddPanel(addPanelSpy)
panel = new Panel({item: new TestPanelItem(), priority: 101}, atom.views)
panel = new Panel(
{ item: new TestPanelItem(), priority: 101 },
atom.views
)
container.addPanel(panel)
expect(addPanelSpy).toHaveBeenCalledWith({panel, index: 1})
expect(addPanelSpy).toHaveBeenCalledWith({ panel, index: 1 })
expect(container.getPanels()[1]).toBe(panel)
})
})
@@ -109,8 +121,8 @@ describe('PanelContainer', () => {
let initialPanel
beforeEach(() => {
// 'bottom' logic is the same as 'right'
container = new PanelContainer({location: 'right'})
initialPanel = new Panel({item: new TestPanelItem()}, atom.views)
container = new PanelContainer({ location: 'right' })
initialPanel = new Panel({ item: new TestPanelItem() }, atom.views)
container.addPanel(initialPanel)
})
@@ -118,10 +130,13 @@ describe('PanelContainer', () => {
it('is inserted at the beginning of the list', () => {
const addPanelSpy = jasmine.createSpy()
container.onDidAddPanel(addPanelSpy)
const panel = new Panel({item: new TestPanelItem(), priority: 1000}, atom.views)
const panel = new Panel(
{ item: new TestPanelItem(), priority: 1000 },
atom.views
)
container.addPanel(panel)
expect(addPanelSpy).toHaveBeenCalledWith({panel, index: 0})
expect(addPanelSpy).toHaveBeenCalledWith({ panel, index: 0 })
expect(container.getPanels()[0]).toBe(panel)
})
})
@@ -130,10 +145,13 @@ describe('PanelContainer', () => {
it('is inserted at the end of the list', () => {
const addPanelSpy = jasmine.createSpy()
container.onDidAddPanel(addPanelSpy)
const panel = new Panel({item: new TestPanelItem(), priority: 0}, atom.views)
const panel = new Panel(
{ item: new TestPanelItem(), priority: 0 },
atom.views
)
container.addPanel(panel)
expect(addPanelSpy).toHaveBeenCalledWith({panel, index: 1})
expect(addPanelSpy).toHaveBeenCalledWith({ panel, index: 1 })
expect(container.getPanels()[1]).toBe(panel)
})
})

View File

@@ -12,8 +12,8 @@ describe('Panel', () => {
}
}
it('adds the item\'s element as a child of the panel', () => {
const panel = new Panel({item: new TestPanelItem()}, atom.views)
it("adds the item's element as a child of the panel", () => {
const panel = new Panel({ item: new TestPanelItem() }, atom.views)
const element = panel.getElement()
expect(element.tagName.toLowerCase()).toBe('atom-panel')
expect(element.firstChild).toBe(panel.getItem().getElement())
@@ -21,7 +21,7 @@ describe('Panel', () => {
describe('destroying the panel', () => {
it('removes the element when the panel is destroyed', () => {
const panel = new Panel({item: new TestPanelItem()}, atom.views)
const panel = new Panel({ item: new TestPanelItem() }, atom.views)
const element = panel.getElement()
const jasmineContent = document.getElementById('jasmine-content')
jasmineContent.appendChild(element)
@@ -33,7 +33,7 @@ describe('Panel', () => {
it('does not try to remove the element twice', () => {
const item = new TestPanelItem()
const panel = new Panel({item}, atom.views)
const panel = new Panel({ item }, atom.views)
const element = panel.getElement()
const jasmineContent = document.getElementById('jasmine-content')
jasmineContent.appendChild(element)
@@ -52,7 +52,7 @@ describe('Panel', () => {
describe('changing panel visibility', () => {
it('notifies observers added with onDidChangeVisible', () => {
const panel = new Panel({item: new TestPanelItem()}, atom.views)
const panel = new Panel({ item: new TestPanelItem() }, atom.views)
const spy = jasmine.createSpy()
panel.onDidChangeVisible(spy)
@@ -72,13 +72,16 @@ describe('Panel', () => {
})
it('initially renders panel created with visible: false', () => {
const panel = new Panel({visible: false, item: new TestPanelItem()}, atom.views)
const panel = new Panel(
{ visible: false, item: new TestPanelItem() },
atom.views
)
const element = panel.getElement()
expect(element.style.display).toBe('none')
})
it('hides and shows the panel element when Panel::hide() and Panel::show() are called', () => {
const panel = new Panel({item: new TestPanelItem()}, atom.views)
const panel = new Panel({ item: new TestPanelItem() }, atom.views)
const element = panel.getElement()
expect(element.style.display).not.toBe('none')
@@ -92,7 +95,10 @@ describe('Panel', () => {
describe('when a class name is specified', () => {
it('initially renders panel created with visible: false', () => {
const panel = new Panel({className: 'some classes', item: new TestPanelItem()}, atom.views)
const panel = new Panel(
{ className: 'some classes', item: new TestPanelItem() },
atom.views
)
const element = panel.getElement()
expect(element).toHaveClass('some')
@@ -102,7 +108,7 @@ describe('Panel', () => {
describe('creating an atom-panel via markup', () => {
it('does not throw an error', () => {
const element = document.createElement('atom-panel')
document.createElement('atom-panel')
})
})
})

View File

@@ -1,16 +1,22 @@
/** @babel */
import {it, beforeEach, afterEach, promisifySome} from './async-spec-helpers'
import { it, beforeEach, afterEach, promisifySome } from './async-spec-helpers'
import tempCb from 'temp'
import fsCb from 'fs-plus'
import path from 'path'
import {CompositeDisposable} from 'event-kit'
import {watchPath, stopAllWatchers} from '../src/path-watcher'
import { CompositeDisposable } from 'event-kit'
import { watchPath, stopAllWatchers } from '../src/path-watcher'
tempCb.track()
const fs = promisifySome(fsCb, ['writeFile', 'mkdir', 'symlink', 'appendFile', 'realpath'])
const fs = promisifySome(fsCb, [
'writeFile',
'mkdir',
'symlink',
'appendFile',
'realpath'
])
const temp = promisifySome(tempCb, ['mkdir'])
describe('watchPath', function () {
@@ -105,16 +111,18 @@ describe('watchPath', function () {
waitForChanges(rootWatcher, subFile),
waitForChanges(childWatcher, subFile)
])
await fs.writeFile(subFile, 'subfile\n', {encoding: 'utf8'})
await fs.writeFile(subFile, 'subfile\n', { encoding: 'utf8' })
await firstChanges
const nextRootEvent = waitForChanges(rootWatcher, rootFile)
await fs.writeFile(rootFile, 'rootfile\n', {encoding: 'utf8'})
await fs.writeFile(rootFile, 'rootfile\n', { encoding: 'utf8' })
await nextRootEvent
})
it('adopts existing child watchers and filters events appropriately to them', async function () {
const parentDir = await temp.mkdir('atom-fsmanager-test-').then(fs.realpath)
const parentDir = await temp
.mkdir('atom-fsmanager-test-')
.then(fs.realpath)
// Create the directory tree
const rootFile = path.join(parentDir, 'rootfile.txt')
@@ -126,9 +134,9 @@ describe('watchPath', function () {
await fs.mkdir(subDir0)
await fs.mkdir(subDir1)
await Promise.all([
fs.writeFile(rootFile, 'rootfile\n', {encoding: 'utf8'}),
fs.writeFile(subFile0, 'subfile 0\n', {encoding: 'utf8'}),
fs.writeFile(subFile1, 'subfile 1\n', {encoding: 'utf8'})
fs.writeFile(rootFile, 'rootfile\n', { encoding: 'utf8' }),
fs.writeFile(subFile0, 'subfile 0\n', { encoding: 'utf8' }),
fs.writeFile(subFile1, 'subfile 1\n', { encoding: 'utf8' })
])
// Begin the child watchers and keep them alive
@@ -142,16 +150,21 @@ describe('watchPath', function () {
// Create the parent watcher
const parentWatcher = await watchPath(parentDir, {}, () => {})
const parentWatcherChanges = waitForChanges(parentWatcher, rootFile, subFile0, subFile1)
const parentWatcherChanges = waitForChanges(
parentWatcher,
rootFile,
subFile0,
subFile1
)
expect(subWatcher0.native).toBe(parentWatcher.native)
expect(subWatcher1.native).toBe(parentWatcher.native)
// Ensure events are filtered correctly
await Promise.all([
fs.appendFile(rootFile, 'change\n', {encoding: 'utf8'}),
fs.appendFile(subFile0, 'change\n', {encoding: 'utf8'}),
fs.appendFile(subFile1, 'change\n', {encoding: 'utf8'})
fs.appendFile(rootFile, 'change\n', { encoding: 'utf8' }),
fs.appendFile(subFile0, 'change\n', { encoding: 'utf8' }),
fs.appendFile(subFile1, 'change\n', { encoding: 'utf8' })
])
await Promise.all([

View File

@@ -3,8 +3,8 @@ const TextBuffer = require('text-buffer')
const Project = require('../src/project')
const fs = require('fs-plus')
const path = require('path')
const {Directory} = require('pathwatcher')
const {stopAllWatchers} = require('../src/path-watcher')
const { Directory } = require('pathwatcher')
const { stopAllWatchers } = require('../src/path-watcher')
const GitRepository = require('../src/git-repository')
describe('Project', () => {
@@ -46,13 +46,16 @@ describe('Project', () => {
let err = null
waitsForPromise(() =>
deserializedProject.deserialize(state, atom.deserializers)
.catch(e => { err = e })
deserializedProject.deserialize(state, atom.deserializers).catch(e => {
err = e
})
)
runs(() => {
expect(deserializedProject.getPaths()).toEqual(atom.project.getPaths())
expect(err.missingProjectPaths).toEqual(['/directory/that/does/not/exist'])
expect(err.missingProjectPaths).toEqual([
'/directory/that/does/not/exist'
])
})
})
@@ -74,8 +77,9 @@ describe('Project', () => {
let err = null
waitsForPromise(() =>
deserializedProject.deserialize(state, atom.deserializers)
.catch(e => { err = e })
deserializedProject.deserialize(state, atom.deserializers).catch(e => {
err = e
})
)
runs(() => {
@@ -98,7 +102,11 @@ describe('Project', () => {
})
})
waitsForPromise(() => deserializedProject.deserialize(atom.project.serialize({isUnloading: false})))
waitsForPromise(() =>
deserializedProject.deserialize(
atom.project.serialize({ isUnloading: false })
)
)
runs(() => expect(deserializedProject.getBuffers().length).toBe(0))
})
@@ -116,7 +124,11 @@ describe('Project', () => {
})
})
waitsForPromise(() => deserializedProject.deserialize(atom.project.serialize({isUnloading: false})))
waitsForPromise(() =>
deserializedProject.deserialize(
atom.project.serialize({ isUnloading: false })
)
)
runs(() => {
expect(deserializedProject.getBuffers().length).toBe(1)
@@ -126,7 +138,10 @@ describe('Project', () => {
})
it('does not deserialize buffers when their path is now a directory', () => {
const pathToOpen = path.join(temp.mkdirSync('atom-spec-project'), 'file.txt')
const pathToOpen = path.join(
temp.mkdirSync('atom-spec-project'),
'file.txt'
)
waitsForPromise(() => atom.workspace.open(pathToOpen))
@@ -141,14 +156,23 @@ describe('Project', () => {
})
})
waitsForPromise(() => deserializedProject.deserialize(atom.project.serialize({isUnloading: false})))
waitsForPromise(() =>
deserializedProject.deserialize(
atom.project.serialize({ isUnloading: false })
)
)
runs(() => expect(deserializedProject.getBuffers().length).toBe(0))
})
it('does not deserialize buffers when their path is inaccessible', () => {
if (process.platform === 'win32') { return } // chmod not supported on win32
const pathToOpen = path.join(temp.mkdirSync('atom-spec-project'), 'file.txt')
if (process.platform === 'win32') {
return
} // chmod not supported on win32
const pathToOpen = path.join(
temp.mkdirSync('atom-spec-project'),
'file.txt'
)
fs.writeFileSync(pathToOpen, '')
waitsForPromise(() => atom.workspace.open(pathToOpen))
@@ -164,13 +188,20 @@ describe('Project', () => {
})
})
waitsForPromise(() => deserializedProject.deserialize(atom.project.serialize({isUnloading: false})))
waitsForPromise(() =>
deserializedProject.deserialize(
atom.project.serialize({ isUnloading: false })
)
)
runs(() => expect(deserializedProject.getBuffers().length).toBe(0))
})
it('does not deserialize buffers with their path is no longer present', () => {
const pathToOpen = path.join(temp.mkdirSync('atom-spec-project'), 'file.txt')
const pathToOpen = path.join(
temp.mkdirSync('atom-spec-project'),
'file.txt'
)
fs.writeFileSync(pathToOpen, '')
waitsForPromise(() => atom.workspace.open(pathToOpen))
@@ -186,13 +217,20 @@ describe('Project', () => {
})
})
waitsForPromise(() => deserializedProject.deserialize(atom.project.serialize({isUnloading: false})))
waitsForPromise(() =>
deserializedProject.deserialize(
atom.project.serialize({ isUnloading: false })
)
)
runs(() => expect(deserializedProject.getBuffers().length).toBe(0))
})
it('deserializes buffers that have never been saved before', () => {
const pathToOpen = path.join(temp.mkdirSync('atom-spec-project'), 'file.txt')
const pathToOpen = path.join(
temp.mkdirSync('atom-spec-project'),
'file.txt'
)
waitsForPromise(() => atom.workspace.open(pathToOpen))
@@ -208,7 +246,11 @@ describe('Project', () => {
})
})
waitsForPromise(() => deserializedProject.deserialize(atom.project.serialize({isUnloading: false})))
waitsForPromise(() =>
deserializedProject.deserialize(
atom.project.serialize({ isUnloading: false })
)
)
runs(() => {
expect(deserializedProject.getBuffers().length).toBe(1)
@@ -226,7 +268,7 @@ describe('Project', () => {
runs(() => {
bufferA = atom.project.getBuffers()[0]
layerA = bufferA.addMarkerLayer({persistent: true})
layerA = bufferA.addMarkerLayer({ persistent: true })
markerA = layerA.markPosition([0, 3])
bufferA.append('!')
notQuittingProject = new Project({
@@ -237,10 +279,17 @@ describe('Project', () => {
})
})
waitsForPromise(() => notQuittingProject.deserialize(atom.project.serialize({isUnloading: false})))
waitsForPromise(() =>
notQuittingProject.deserialize(
atom.project.serialize({ isUnloading: false })
)
)
runs(() => {
expect(notQuittingProject.getBuffers()[0].getMarkerLayer(layerA.id), x => x.getMarker(markerA.id)).toBeUndefined()
expect(
notQuittingProject.getBuffers()[0].getMarkerLayer(layerA.id),
x => x.getMarker(markerA.id)
).toBeUndefined()
expect(notQuittingProject.getBuffers()[0].undo()).toBe(false)
quittingProject = new Project({
notificationManager: atom.notifications,
@@ -250,10 +299,16 @@ describe('Project', () => {
})
})
waitsForPromise(() => quittingProject.deserialize(atom.project.serialize({isUnloading: true})))
waitsForPromise(() =>
quittingProject.deserialize(
atom.project.serialize({ isUnloading: true })
)
)
runs(() => {
expect(quittingProject.getBuffers()[0].getMarkerLayer(layerA.id), x => x.getMarker(markerA.id)).not.toBeUndefined()
expect(quittingProject.getBuffers()[0].getMarkerLayer(layerA.id), x =>
x.getMarker(markerA.id)
).not.toBeUndefined()
expect(quittingProject.getBuffers()[0].undo()).toBe(true)
})
})
@@ -266,11 +321,17 @@ describe('Project', () => {
expect(atom.project.getPaths()[0]).toBeUndefined()
let editor = null
waitsForPromise(() => atom.workspace.open().then(o => { editor = o }))
waitsForPromise(() =>
atom.workspace.open().then(o => {
editor = o
})
)
waitsForPromise(() => editor.saveAs(tempFile))
runs(() => expect(atom.project.getPaths()[0]).toBe(path.dirname(tempFile)))
runs(() =>
expect(atom.project.getPaths()[0]).toBe(path.dirname(tempFile))
)
})
})
@@ -284,7 +345,7 @@ describe('Project', () => {
paths: [projectPath1, projectPath2],
originPath: 'originPath',
config: {
'baz': 'buzz'
baz: 'buzz'
}
}
})
@@ -323,10 +384,12 @@ describe('Project', () => {
let buffer
beforeEach(() =>
waitsForPromise(() =>
atom.project.bufferForPath(path.join(__dirname, 'fixtures', 'sample.js')).then((o) => {
buffer = o
buffer.retain()
})
atom.project
.bufferForPath(path.join(__dirname, 'fixtures', 'sample.js'))
.then(o => {
buffer = o
buffer.retain()
})
)
)
@@ -339,10 +402,18 @@ describe('Project', () => {
waitsForPromise(() => buffer.save())
runs(() => {
expect(atom.project.applicationDelegate.emitDidSavePath.calls.length).toBe(1)
expect(atom.project.applicationDelegate.emitDidSavePath).toHaveBeenCalledWith(buffer.getPath())
expect(atom.project.applicationDelegate.emitWillSavePath.calls.length).toBe(1)
expect(atom.project.applicationDelegate.emitWillSavePath).toHaveBeenCalledWith(buffer.getPath())
expect(
atom.project.applicationDelegate.emitDidSavePath.calls.length
).toBe(1)
expect(
atom.project.applicationDelegate.emitDidSavePath
).toHaveBeenCalledWith(buffer.getPath())
expect(
atom.project.applicationDelegate.emitWillSavePath.calls.length
).toBe(1)
expect(
atom.project.applicationDelegate.emitWillSavePath
).toHaveBeenCalledWith(buffer.getPath())
})
})
})
@@ -350,20 +421,23 @@ describe('Project', () => {
describe('when a watch error is thrown from the TextBuffer', () => {
let editor = null
beforeEach(() =>
waitsForPromise(() => atom.workspace.open(require.resolve('./fixtures/dir/a')).then(o => { editor = o }))
waitsForPromise(() =>
atom.workspace.open(require.resolve('./fixtures/dir/a')).then(o => {
editor = o
})
)
)
it('creates a warning notification', () => {
let noteSpy
atom.notifications.onDidAddNotification(noteSpy = jasmine.createSpy())
atom.notifications.onDidAddNotification((noteSpy = jasmine.createSpy()))
const error = new Error('SomeError')
error.eventType = 'resurrect'
editor.buffer.emitter.emit('will-throw-watch-error', {
handle: jasmine.createSpy(),
error
}
)
})
expect(noteSpy).toHaveBeenCalled()
@@ -371,7 +445,9 @@ describe('Project', () => {
expect(notification.getType()).toBe('warning')
expect(notification.getDetail()).toBe('SomeError')
expect(notification.getMessage()).toContain('`resurrect`')
expect(notification.getMessage()).toContain(path.join('fixtures', 'dir', 'a'))
expect(notification.getMessage()).toContain(
path.join('fixtures', 'dir', 'a')
)
})
})
@@ -379,10 +455,18 @@ describe('Project', () => {
let fakeRepositoryProvider, fakeRepository
beforeEach(() => {
fakeRepository = {destroy () { return null }}
fakeRepository = {
destroy () {
return null
}
}
fakeRepositoryProvider = {
repositoryForDirectory (directory) { return Promise.resolve(fakeRepository) },
repositoryForDirectorySync (directory) { return fakeRepository }
repositoryForDirectory (directory) {
return Promise.resolve(fakeRepository)
},
repositoryForDirectorySync (directory) {
return fakeRepository
}
}
})
@@ -391,7 +475,11 @@ describe('Project', () => {
atom.project.setPaths([projectPath])
expect(atom.project.getRepositories()).toEqual([null])
atom.packages.serviceHub.provide('atom.repository-provider', '0.1.0', fakeRepositoryProvider)
atom.packages.serviceHub.provide(
'atom.repository-provider',
'0.1.0',
fakeRepositoryProvider
)
waitsFor(() => atom.project.repositoryProviders.length > 1)
runs(() => atom.project.getRepositories()[0] === fakeRepository)
})
@@ -401,7 +489,11 @@ describe('Project', () => {
expect(repositories.length).toEqual(1)
expect(repositories[0]).toBeTruthy()
atom.packages.serviceHub.provide('atom.repository-provider', '0.1.0', fakeRepositoryProvider)
atom.packages.serviceHub.provide(
'atom.repository-provider',
'0.1.0',
fakeRepositoryProvider
)
waitsFor(() => atom.project.repositoryProviders.length > 1)
runs(() => expect(atom.project.getRepositories()).toBe(repositories))
})
@@ -409,7 +501,11 @@ describe('Project', () => {
it('stops using it to create repositories when the service is removed', () => {
atom.project.setPaths([])
const disposable = atom.packages.serviceHub.provide('atom.repository-provider', '0.1.0', fakeRepositoryProvider)
const disposable = atom.packages.serviceHub.provide(
'atom.repository-provider',
'0.1.0',
fakeRepositoryProvider
)
waitsFor(() => atom.project.repositoryProviders.length > 1)
runs(() => {
disposable.dispose()
@@ -424,15 +520,35 @@ describe('Project', () => {
constructor (aPath) {
this.path = aPath
}
getPath () { return this.path }
getFile () { return {existsSync () { return false }} }
getSubdirectory () { return {existsSync () { return false }} }
isRoot () { return true }
existsSync () { return this.path.endsWith('does-exist') }
contains (filePath) { return filePath.startsWith(this.path) }
getPath () {
return this.path
}
getFile () {
return {
existsSync () {
return false
}
}
}
getSubdirectory () {
return {
existsSync () {
return false
}
}
}
isRoot () {
return true
}
existsSync () {
return this.path.endsWith('does-exist')
}
contains (filePath) {
return filePath.startsWith(this.path)
}
onDidChangeFiles (callback) {
onDidChangeFilesCallback = callback
return {dispose: () => {}}
return { dispose: () => {} }
}
}
@@ -440,15 +556,19 @@ describe('Project', () => {
let onDidChangeFilesCallback = null
beforeEach(() => {
serviceDisposable = atom.packages.serviceHub.provide('atom.directory-provider', '0.1.0', {
directoryForURISync (uri) {
if (uri.startsWith('ssh://')) {
return new DummyDirectory(uri)
} else {
return null
serviceDisposable = atom.packages.serviceHub.provide(
'atom.directory-provider',
'0.1.0',
{
directoryForURISync (uri) {
if (uri.startsWith('ssh://')) {
return new DummyDirectory(uri)
} else {
return null
}
}
}
})
)
onDidChangeFilesCallback = null
waitsFor(() => atom.project.directoryProviders.length > 0)
@@ -467,7 +587,8 @@ describe('Project', () => {
expect(directories[1] instanceof DummyDirectory).toBe(true)
// It does not add new remote paths that do not exist
const nonExistentRemotePath = 'ssh://another-directory:8080/does-not-exist'
const nonExistentRemotePath =
'ssh://another-directory:8080/does-not-exist'
atom.project.addPath(nonExistentRemotePath)
expect(atom.project.getDirectories().length).toBe(2)
@@ -499,7 +620,7 @@ describe('Project', () => {
const changeSpy = jasmine.createSpy('atom.project.onDidChangeFiles')
const disposable = atom.project.onDidChangeFiles(changeSpy)
const events = [{action: 'created', path: remotePath + '/test.txt'}]
const events = [{ action: 'created', path: remotePath + '/test.txt' }]
onDidChangeFilesCallback(events)
expect(changeSpy).toHaveBeenCalledWith(events)
@@ -520,7 +641,11 @@ describe('Project', () => {
describe("when given an absolute path that isn't currently open", () => {
it("returns a new edit session for the given path and emits 'buffer-created'", () => {
let editor = null
waitsForPromise(() => atom.workspace.open(absolutePath).then(o => { editor = o }))
waitsForPromise(() =>
atom.workspace.open(absolutePath).then(o => {
editor = o
})
)
runs(() => {
expect(editor.buffer.getPath()).toBe(absolutePath)
@@ -532,7 +657,11 @@ describe('Project', () => {
describe("when given a relative path that isn't currently opened", () => {
it("returns a new edit session for the given path (relative to the project root) and emits 'buffer-created'", () => {
let editor = null
waitsForPromise(() => atom.workspace.open(absolutePath).then(o => { editor = o }))
waitsForPromise(() =>
atom.workspace.open(absolutePath).then(o => {
editor = o
})
)
runs(() => {
expect(editor.buffer.getPath()).toBe(absolutePath)
@@ -545,16 +674,22 @@ describe('Project', () => {
it('returns a new edit session containing currently opened buffer', () => {
let editor = null
waitsForPromise(() => atom.workspace.open(absolutePath).then(o => { editor = o }))
waitsForPromise(() =>
atom.workspace.open(absolutePath).then(o => {
editor = o
})
)
runs(() => newBufferHandler.reset())
waitsForPromise(() =>
atom.workspace.open(absolutePath).then(({buffer}) => expect(buffer).toBe(editor.buffer))
atom.workspace
.open(absolutePath)
.then(({ buffer }) => expect(buffer).toBe(editor.buffer))
)
waitsForPromise(() =>
atom.workspace.open('a').then(({buffer}) => {
atom.workspace.open('a').then(({ buffer }) => {
expect(buffer).toBe(editor.buffer)
expect(newBufferHandler).not.toHaveBeenCalled()
})
@@ -565,7 +700,11 @@ describe('Project', () => {
describe('when not passed a path', () => {
it("returns a new edit session and emits 'buffer-created'", () => {
let editor = null
waitsForPromise(() => atom.workspace.open().then(o => { editor = o }))
waitsForPromise(() =>
atom.workspace.open().then(o => {
editor = o
})
)
runs(() => {
expect(editor.buffer.getPath()).toBeUndefined()
@@ -580,7 +719,7 @@ describe('Project', () => {
beforeEach(() =>
waitsForPromise(() =>
atom.project.bufferForPath('a').then((o) => {
atom.project.bufferForPath('a').then(o => {
buffer = o
buffer.retain()
})
@@ -592,11 +731,15 @@ describe('Project', () => {
describe('when opening a previously opened path', () => {
it('does not create a new buffer', () => {
waitsForPromise(() =>
atom.project.bufferForPath('a').then(anotherBuffer => expect(anotherBuffer).toBe(buffer))
atom.project
.bufferForPath('a')
.then(anotherBuffer => expect(anotherBuffer).toBe(buffer))
)
waitsForPromise(() =>
atom.project.bufferForPath('b').then(anotherBuffer => expect(anotherBuffer).not.toBe(buffer))
atom.project
.bufferForPath('b')
.then(anotherBuffer => expect(anotherBuffer).not.toBe(buffer))
)
waitsForPromise(() =>
@@ -610,12 +753,14 @@ describe('Project', () => {
})
it('retries loading the buffer if it previously failed', () => {
waitsForPromise({shouldReject: true}, () => {
spyOn(TextBuffer, 'load').andCallFake(() => Promise.reject(new Error('Could not open file')))
waitsForPromise({ shouldReject: true }, () => {
spyOn(TextBuffer, 'load').andCallFake(() =>
Promise.reject(new Error('Could not open file'))
)
return atom.project.bufferForPath('b')
})
waitsForPromise({shouldReject: false}, () => {
waitsForPromise({ shouldReject: false }, () => {
TextBuffer.load.andCallThrough()
return atom.project.bufferForPath('b')
})
@@ -625,7 +770,9 @@ describe('Project', () => {
buffer.release()
waitsForPromise(() =>
atom.project.bufferForPath('b').then(anotherBuffer => expect(anotherBuffer).not.toBe(buffer))
atom.project
.bufferForPath('b')
.then(anotherBuffer => expect(anotherBuffer).not.toBe(buffer))
)
})
})
@@ -635,7 +782,7 @@ describe('Project', () => {
it('resolves to null when the directory does not have a repository', () => {
waitsForPromise(() => {
const directory = new Directory('/tmp')
return atom.project.repositoryForDirectory(directory).then((result) => {
return atom.project.repositoryForDirectory(directory).then(result => {
expect(result).toBeNull()
expect(atom.project.repositoryProviders.length).toBeGreaterThan(0)
expect(atom.project.repositoryPromisesByPath.size).toBe(0)
@@ -647,7 +794,7 @@ describe('Project', () => {
waitsForPromise(() => {
const directory = new Directory(path.join(__dirname, '..'))
const promise = atom.project.repositoryForDirectory(directory)
return promise.then((result) => {
return promise.then(result => {
expect(result).toBeInstanceOf(GitRepository)
const dirPath = directory.getRealPathSync()
expect(result.getPath()).toBe(path.join(dirPath, '.git'))
@@ -662,7 +809,11 @@ describe('Project', () => {
let repository = null
const directory = new Directory(path.join(__dirname, '..'))
waitsForPromise(() => atom.project.repositoryForDirectory(directory).then(repo => { repository = repo }))
waitsForPromise(() =>
atom.project.repositoryForDirectory(directory).then(repo => {
repository = repo
})
)
runs(() => {
expect(repository.isDestroyed()).toBe(false)
@@ -670,7 +821,11 @@ describe('Project', () => {
expect(repository.isDestroyed()).toBe(true)
})
waitsForPromise(() => atom.project.repositoryForDirectory(directory).then(repo => { repository = repo }))
waitsForPromise(() =>
atom.project.repositoryForDirectory(directory).then(repo => {
repository = repo
})
)
runs(() => expect(repository.isDestroyed()).toBe(false))
})
@@ -682,7 +837,9 @@ describe('Project', () => {
const filePath = require.resolve('./fixtures/dir/a')
atom.project.setPaths([filePath])
expect(atom.project.getPaths()[0]).toEqual(path.dirname(filePath))
expect(atom.project.getDirectories()[0].path).toEqual(path.dirname(filePath))
expect(atom.project.getDirectories()[0].path).toEqual(
path.dirname(filePath)
)
})
})
@@ -692,7 +849,9 @@ describe('Project', () => {
const directory2 = temp.mkdirSync('git-repo1')
const directory3 = temp.mkdirSync('git-repo2')
const gitDirPath = fs.absolute(path.join(__dirname, 'fixtures', 'git', 'master.git'))
const gitDirPath = fs.absolute(
path.join(__dirname, 'fixtures', 'git', 'master.git')
)
fs.copySync(gitDirPath, path.join(directory2, '.git'))
fs.copySync(gitDirPath, path.join(directory3, '.git'))
@@ -701,16 +860,20 @@ describe('Project', () => {
const [repo1, repo2, repo3] = atom.project.getRepositories()
expect(repo1).toBeNull()
expect(repo2.getShortHead()).toBe('master')
expect(repo2.getPath()).toBe(fs.realpathSync(path.join(directory2, '.git')))
expect(repo2.getPath()).toBe(
fs.realpathSync(path.join(directory2, '.git'))
)
expect(repo3.getShortHead()).toBe('master')
expect(repo3.getPath()).toBe(fs.realpathSync(path.join(directory3, '.git')))
expect(repo3.getPath()).toBe(
fs.realpathSync(path.join(directory3, '.git'))
)
})
it('calls callbacks registered with ::onDidChangePaths', () => {
const onDidChangePathsSpy = jasmine.createSpy('onDidChangePaths spy')
atom.project.onDidChangePaths(onDidChangePathsSpy)
const paths = [ temp.mkdirSync('dir1'), temp.mkdirSync('dir2') ]
const paths = [temp.mkdirSync('dir1'), temp.mkdirSync('dir2')]
atom.project.setPaths(paths)
expect(onDidChangePathsSpy.callCount).toBe(1)
@@ -718,10 +881,15 @@ describe('Project', () => {
})
it('optionally throws an error with any paths that did not exist', () => {
const paths = [temp.mkdirSync('exists0'), '/doesnt-exists/0', temp.mkdirSync('exists1'), '/doesnt-exists/1']
const paths = [
temp.mkdirSync('exists0'),
'/doesnt-exists/0',
temp.mkdirSync('exists1'),
'/doesnt-exists/1'
]
try {
atom.project.setPaths(paths, {mustExist: true})
atom.project.setPaths(paths, { mustExist: true })
expect('no exception thrown').toBeUndefined()
} catch (e) {
expect(e.missingProjectPaths).toEqual([paths[1], paths[3]])
@@ -740,9 +908,17 @@ describe('Project', () => {
})
it('normalizes the path to remove consecutive slashes, ., and .. segments', () => {
atom.project.setPaths([`${require.resolve('./fixtures/dir/a')}${path.sep}b${path.sep}${path.sep}..`])
expect(atom.project.getPaths()[0]).toEqual(path.dirname(require.resolve('./fixtures/dir/a')))
expect(atom.project.getDirectories()[0].path).toEqual(path.dirname(require.resolve('./fixtures/dir/a')))
atom.project.setPaths([
`${require.resolve('./fixtures/dir/a')}${path.sep}b${path.sep}${
path.sep
}..`
])
expect(atom.project.getPaths()[0]).toEqual(
path.dirname(require.resolve('./fixtures/dir/a'))
)
expect(atom.project.getDirectories()[0].path).toEqual(
path.dirname(require.resolve('./fixtures/dir/a'))
)
})
})
@@ -757,7 +933,10 @@ describe('Project', () => {
atom.project.addPath(newPath)
expect(onDidChangePathsSpy.callCount).toBe(1)
expect(onDidChangePathsSpy.mostRecentCall.args[0]).toEqual([oldPath, newPath])
expect(onDidChangePathsSpy.mostRecentCall.args[0]).toEqual([
oldPath,
newPath
])
})
it("doesn't add redundant paths", () => {
@@ -789,7 +968,11 @@ describe('Project', () => {
})
it('optionally throws on non-existent directories', () => {
expect(() => atom.project.addPath('/this-definitely/does-not-exist', {mustExist: true})).toThrow()
expect(() =>
atom.project.addPath('/this-definitely/does-not-exist', {
mustExist: true
})
).toThrow()
})
})
@@ -821,7 +1004,9 @@ describe('Project', () => {
it("doesn't destroy the repository if it is shared by another root directory", () => {
atom.project.setPaths([__dirname, path.join(__dirname, '..', 'src')])
atom.project.removePath(__dirname)
expect(atom.project.getPaths()).toEqual([path.join(__dirname, '..', 'src')])
expect(atom.project.getPaths()).toEqual([
path.join(__dirname, '..', 'src')
])
expect(atom.project.getRepositories()[0].isSubmodule('src')).toBe(false)
})
@@ -829,10 +1014,18 @@ describe('Project', () => {
atom.packages.serviceHub.provide('atom.directory-provider', '0.1.0', {
directoryForURISync (uri) {
return {
getPath () { return uri },
getSubdirectory () { return {} },
isRoot () { return true },
existsSync () { return true },
getPath () {
return uri
},
getSubdirectory () {
return {}
},
isRoot () {
return true
},
existsSync () {
return true
},
off () {}
}
}
@@ -854,7 +1047,7 @@ describe('Project', () => {
let checkCallback = () => {}
beforeEach(() => {
sub = atom.project.onDidChangeFiles((incoming) => {
sub = atom.project.onDidChangeFiles(incoming => {
events.push(...incoming)
checkCallback()
})
@@ -862,18 +1055,24 @@ describe('Project', () => {
afterEach(() => sub.dispose())
const waitForEvents = (paths) => {
const remaining = new Set(paths.map((p) => fs.realpathSync(p)))
const waitForEvents = paths => {
const remaining = new Set(paths.map(p => fs.realpathSync(p)))
return new Promise((resolve, reject) => {
checkCallback = () => {
for (let event of events) { remaining.delete(event.path) }
if (remaining.size === 0) { resolve() }
for (let event of events) {
remaining.delete(event.path)
}
if (remaining.size === 0) {
resolve()
}
}
const expire = () => {
checkCallback = () => {}
console.error('Paths not seen:', remaining)
reject(new Error('Expired before all expected events were delivered.'))
reject(
new Error('Expired before all expected events were delivered.')
)
}
checkCallback()
@@ -904,7 +1103,9 @@ describe('Project', () => {
waitsForPromise(() => waitForEvents([fileOne, fileTwo]))
runs(() => expect(events.some(event => event.path === fileThree)).toBeFalsy())
runs(() =>
expect(events.some(event => event.path === fileThree)).toBeFalsy()
)
})
})
@@ -914,7 +1115,8 @@ describe('Project', () => {
const added = []
waitsForPromise(() =>
atom.project.buildBuffer(require.resolve('./fixtures/dir/a'))
atom.project
.buildBuffer(require.resolve('./fixtures/dir/a'))
.then(o => buffers.push(o))
)
@@ -924,7 +1126,8 @@ describe('Project', () => {
})
waitsForPromise(() =>
atom.project.buildBuffer(require.resolve('./fixtures/dir/b'))
atom.project
.buildBuffer(require.resolve('./fixtures/dir/b'))
.then(o => buffers.push(o))
)
@@ -941,12 +1144,14 @@ describe('Project', () => {
const observed = []
waitsForPromise(() =>
atom.project.buildBuffer(require.resolve('./fixtures/dir/a'))
atom.project
.buildBuffer(require.resolve('./fixtures/dir/a'))
.then(o => buffers.push(o))
)
waitsForPromise(() =>
atom.project.buildBuffer(require.resolve('./fixtures/dir/b'))
atom.project
.buildBuffer(require.resolve('./fixtures/dir/b'))
.then(o => buffers.push(o))
)
@@ -957,7 +1162,8 @@ describe('Project', () => {
})
waitsForPromise(() =>
atom.project.buildBuffer(require.resolve('./fixtures/dir/b'))
atom.project
.buildBuffer(require.resolve('./fixtures/dir/b'))
.then(o => buffers.push(o))
)
@@ -974,22 +1180,38 @@ describe('Project', () => {
const observed = []
const directory1 = temp.mkdirSync('git-repo1')
const gitDirPath1 = fs.absolute(path.join(__dirname, 'fixtures', 'git', 'master.git'))
const gitDirPath1 = fs.absolute(
path.join(__dirname, 'fixtures', 'git', 'master.git')
)
fs.copySync(gitDirPath1, path.join(directory1, '.git'))
const directory2 = temp.mkdirSync('git-repo2')
const gitDirPath2 = fs.absolute(path.join(__dirname, 'fixtures', 'git', 'repo-with-submodules', 'git.git'))
const gitDirPath2 = fs.absolute(
path.join(
__dirname,
'fixtures',
'git',
'repo-with-submodules',
'git.git'
)
)
fs.copySync(gitDirPath2, path.join(directory2, '.git'))
atom.project.setPaths([directory1])
const disposable = atom.project.observeRepositories((repo) => observed.push(repo))
const disposable = atom.project.observeRepositories(repo =>
observed.push(repo)
)
expect(observed.length).toBe(1)
expect(observed[0].getReferenceTarget('refs/heads/master')).toBe('ef046e9eecaa5255ea5e9817132d4001724d6ae1')
expect(observed[0].getReferenceTarget('refs/heads/master')).toBe(
'ef046e9eecaa5255ea5e9817132d4001724d6ae1'
)
atom.project.addPath(directory2)
expect(observed.length).toBe(2)
expect(observed[1].getReferenceTarget('refs/heads/master')).toBe('d2b0ad9cbc6f6c4372e8956e5cc5af771b2342e5')
expect(observed[1].getReferenceTarget('refs/heads/master')).toBe(
'd2b0ad9cbc6f6c4372e8956e5cc5af771b2342e5'
)
disposable.dispose()
})
@@ -998,25 +1220,35 @@ describe('Project', () => {
describe('.onDidAddRepository()', () => {
it('invokes callback when a path is added and the path is the root of a repository', () => {
const observed = []
const disposable = atom.project.onDidAddRepository((repo) => observed.push(repo))
const disposable = atom.project.onDidAddRepository(repo =>
observed.push(repo)
)
const projectRootPath = temp.mkdirSync()
const fixtureRepoPath = fs.absolute(path.join(__dirname, 'fixtures', 'git', 'master.git'))
const fixtureRepoPath = fs.absolute(
path.join(__dirname, 'fixtures', 'git', 'master.git')
)
fs.copySync(fixtureRepoPath, path.join(projectRootPath, '.git'))
atom.project.addPath(projectRootPath)
expect(observed.length).toBe(1)
expect(observed[0].getOriginURL()).toEqual('https://github.com/example-user/example-repo.git')
expect(observed[0].getOriginURL()).toEqual(
'https://github.com/example-user/example-repo.git'
)
disposable.dispose()
})
it('invokes callback when a path is added and the path is subdirectory of a repository', () => {
const observed = []
const disposable = atom.project.onDidAddRepository((repo) => observed.push(repo))
const disposable = atom.project.onDidAddRepository(repo =>
observed.push(repo)
)
const projectRootPath = temp.mkdirSync()
const fixtureRepoPath = fs.absolute(path.join(__dirname, 'fixtures', 'git', 'master.git'))
const fixtureRepoPath = fs.absolute(
path.join(__dirname, 'fixtures', 'git', 'master.git')
)
fs.copySync(fixtureRepoPath, path.join(projectRootPath, '.git'))
const projectSubDirPath = path.join(projectRootPath, 'sub-dir')
@@ -1024,14 +1256,18 @@ describe('Project', () => {
atom.project.addPath(projectSubDirPath)
expect(observed.length).toBe(1)
expect(observed[0].getOriginURL()).toEqual('https://github.com/example-user/example-repo.git')
expect(observed[0].getOriginURL()).toEqual(
'https://github.com/example-user/example-repo.git'
)
disposable.dispose()
})
it('does not invoke callback when a path is added and the path is not part of a repository', () => {
const observed = []
const disposable = atom.project.onDidAddRepository((repo) => observed.push(repo))
const disposable = atom.project.onDidAddRepository(repo =>
observed.push(repo)
)
atom.project.addPath(temp.mkdirSync('not-a-repository'))
expect(observed.length).toBe(0)
@@ -1046,11 +1282,15 @@ describe('Project', () => {
let rootPath = atom.project.getPaths()[0]
let childPath = path.join(rootPath, 'some', 'child', 'directory')
expect(atom.project.relativize(childPath)).toBe(path.join('some', 'child', 'directory'))
expect(atom.project.relativize(childPath)).toBe(
path.join('some', 'child', 'directory')
)
rootPath = atom.project.getPaths()[1]
childPath = path.join(rootPath, 'some', 'child', 'directory')
expect(atom.project.relativize(childPath)).toBe(path.join('some', 'child', 'directory'))
expect(atom.project.relativize(childPath)).toBe(
path.join('some', 'child', 'directory')
)
})
it('returns the given path if it is not in any of the root directories', () => {
@@ -1065,17 +1305,26 @@ describe('Project', () => {
let rootPath = atom.project.getPaths()[0]
let childPath = path.join(rootPath, 'some', 'child', 'directory')
expect(atom.project.relativizePath(childPath)).toEqual([rootPath, path.join('some', 'child', 'directory')])
expect(atom.project.relativizePath(childPath)).toEqual([
rootPath,
path.join('some', 'child', 'directory')
])
rootPath = atom.project.getPaths()[1]
childPath = path.join(rootPath, 'some', 'child', 'directory')
expect(atom.project.relativizePath(childPath)).toEqual([rootPath, path.join('some', 'child', 'directory')])
expect(atom.project.relativizePath(childPath)).toEqual([
rootPath,
path.join('some', 'child', 'directory')
])
})
describe("when the given path isn't inside of any of the project's path", () => {
it('returns null for the root path, and the given path unchanged', () => {
const randomPath = path.join('some', 'random', 'path')
expect(atom.project.relativizePath(randomPath)).toEqual([null, randomPath])
expect(atom.project.relativizePath(randomPath)).toEqual([
null,
randomPath
])
})
})
@@ -1090,7 +1339,10 @@ describe('Project', () => {
it('uses the root folder that is closest to the given path', () => {
atom.project.addPath(path.join(atom.project.getPaths()[0], 'a-dir'))
const inputPath = path.join(atom.project.getPaths()[1], 'somewhere/something.txt')
const inputPath = path.join(
atom.project.getPaths()[1],
'somewhere/something.txt'
)
expect(atom.project.getDirectories()[0].contains(inputPath)).toBe(true)
expect(atom.project.getDirectories()[1].contains(inputPath)).toBe(true)

View File

@@ -1,21 +1,21 @@
/** @babel */
import {it, fit, ffit, fffit, beforeEach, afterEach} from './async-spec-helpers'
import {Emitter, Disposable, CompositeDisposable} from 'event-kit'
import { it, beforeEach } from './async-spec-helpers'
import { Disposable } from 'event-kit'
const ReopenProjectMenuManager = require('../src/reopen-project-menu-manager')
numberRange = (low, high) => {
function numberRange (low, high) {
const size = high - low
const result = new Array(size)
for (var i = 0; i < size; i++)
result[i] = low + i
for (var i = 0; i < size; i++) result[i] = low + i
return result
}
describe("ReopenProjectMenuManager", () => {
describe('ReopenProjectMenuManager', () => {
let menuManager, commandRegistry, config, historyManager, reopenProjects
let commandDisposable, configDisposable, historyDisposable
let openFunction
beforeEach(() => {
menuManager = jasmine.createSpyObj('MenuManager', ['add'])
@@ -28,43 +28,54 @@ describe("ReopenProjectMenuManager", () => {
config = jasmine.createSpyObj('Config', ['onDidChange', 'get'])
config.get.andReturn(10)
configDisposable = jasmine.createSpyObj('Disposable', ['dispose'])
config.didChangeListener = { }
config.didChangeListener = {}
config.onDidChange.andCallFake((key, fn) => {
config.didChangeListener[key] = fn
return configDisposable
})
historyManager = jasmine.createSpyObj('historyManager', ['getProjects','onDidChangeProjects'])
historyManager = jasmine.createSpyObj('historyManager', [
'getProjects',
'onDidChangeProjects'
])
historyManager.getProjects.andReturn([])
historyDisposable = jasmine.createSpyObj('Disposable', ['dispose'])
historyManager.onDidChangeProjects.andCallFake((fn) => {
historyManager.onDidChangeProjects.andCallFake(fn => {
historyManager.changeProjectsListener = fn
return historyDisposable
})
openFunction = jasmine.createSpy()
reopenProjects = new ReopenProjectMenuManager({menu:menuManager, commands: commandRegistry, history: historyManager, config, open:openFunction})
reopenProjects = new ReopenProjectMenuManager({
menu: menuManager,
commands: commandRegistry,
history: historyManager,
config,
open: openFunction
})
})
describe("constructor", () => {
describe('constructor', () => {
it("registers the 'reopen-project' command function", () => {
expect(commandRegistry.add).toHaveBeenCalled()
const cmdCall = commandRegistry.add.calls[0]
expect(cmdCall.args.length).toBe(2)
expect(cmdCall.args[0]).toBe('atom-workspace')
expect(typeof cmdCall.args[1]['application:reopen-project']).toBe('function')
expect(typeof cmdCall.args[1]['application:reopen-project']).toBe(
'function'
)
})
})
describe("dispose", () => {
it("disposes of the history, command and config disposables", () => {
describe('dispose', () => {
it('disposes of the history, command and config disposables', () => {
reopenProjects.dispose()
expect(historyDisposable.dispose).toHaveBeenCalled()
expect(configDisposable.dispose).toHaveBeenCalled()
expect(commandDisposable.dispose).toHaveBeenCalled()
})
it("disposes of the menu disposable once used", () => {
it('disposes of the menu disposable once used', () => {
const menuDisposable = jasmine.createSpyObj('Disposable', ['dispose'])
menuManager.add.andReturn(menuDisposable)
reopenProjects.update()
@@ -74,36 +85,45 @@ describe("ReopenProjectMenuManager", () => {
})
})
describe("the command", () => {
it("calls open with the paths of the project specified by the detail index", () => {
historyManager.getProjects.andReturn([ { paths: ['/a'] }, { paths: ['/b', 'c:\\'] }])
describe('the command', () => {
it('calls open with the paths of the project specified by the detail index', () => {
historyManager.getProjects.andReturn([
{ paths: ['/a'] },
{ paths: ['/b', 'c:\\'] }
])
reopenProjects.update()
reopenProjectCommand = commandRegistry.add.calls[0].args[1]['application:reopen-project']
const reopenProjectCommand =
commandRegistry.add.calls[0].args[1]['application:reopen-project']
reopenProjectCommand({ detail: { index: 1 } })
expect(openFunction).toHaveBeenCalled()
expect(openFunction.calls[0].args[0]).toEqual(['/b', 'c:\\'])
})
it("does not call open when no command detail is supplied", () => {
reopenProjectCommand = commandRegistry.add.calls[0].args[1]['application:reopen-project']
it('does not call open when no command detail is supplied', () => {
const reopenProjectCommand =
commandRegistry.add.calls[0].args[1]['application:reopen-project']
reopenProjectCommand({})
expect(openFunction).not.toHaveBeenCalled()
})
it("does not call open when no command detail index is supplied", () => {
reopenProjectCommand = commandRegistry.add.calls[0].args[1]['application:reopen-project']
it('does not call open when no command detail index is supplied', () => {
const reopenProjectCommand =
commandRegistry.add.calls[0].args[1]['application:reopen-project']
reopenProjectCommand({ detail: { anything: 'here' } })
expect(openFunction).not.toHaveBeenCalled()
})
})
describe("update", () => {
it("adds menu items to MenuManager based on projects from HistoryManager", () => {
historyManager.getProjects.andReturn([ { paths: ['/a'] }, { paths: ['/b', 'c:\\'] }])
describe('update', () => {
it('adds menu items to MenuManager based on projects from HistoryManager', () => {
historyManager.getProjects.andReturn([
{ paths: ['/a'] },
{ paths: ['/b', 'c:\\'] }
])
reopenProjects.update()
expect(historyManager.getProjects).toHaveBeenCalled()
expect(menuManager.add).toHaveBeenCalled()
@@ -127,7 +147,9 @@ describe("ReopenProjectMenuManager", () => {
})
it("adds only the number of menu items specified in the 'core.reopenProjectMenuCount' config", () => {
historyManager.getProjects.andReturn(numberRange(1, 100).map(i => ({ paths: [ '/test/' + i ] })))
historyManager.getProjects.andReturn(
numberRange(1, 100).map(i => ({ paths: ['/test/' + i] }))
)
reopenProjects.update()
expect(menuManager.add).toHaveBeenCalled()
const menu = menuManager.add.calls[0].args[0][0]
@@ -137,7 +159,7 @@ describe("ReopenProjectMenuManager", () => {
expect(menu.submenu[0].submenu.length).toBe(10)
})
it("disposes the previously menu built", () => {
it('disposes the previously menu built', () => {
const menuDisposable = jasmine.createSpyObj('Disposable', ['dispose'])
menuManager.add.andReturn(menuDisposable)
reopenProjects.update()
@@ -147,10 +169,15 @@ describe("ReopenProjectMenuManager", () => {
})
it("is called when the Config changes for 'core.reopenProjectMenuCount'", () => {
historyManager.getProjects.andReturn(numberRange(1, 100).map(i => ({ paths: [ '/test/' + i ] })))
historyManager.getProjects.andReturn(
numberRange(1, 100).map(i => ({ paths: ['/test/' + i] }))
)
reopenProjects.update()
config.get.andReturn(25)
config.didChangeListener['core.reopenProjectMenuCount']({oldValue:10, newValue: 25})
config.didChangeListener['core.reopenProjectMenuCount']({
oldValue: 10,
newValue: 25
})
const finalArgs = menuManager.add.calls[1].args[0]
const projectsMenu = finalArgs[0].submenu[0].submenu
@@ -160,7 +187,10 @@ describe("ReopenProjectMenuManager", () => {
it("is called when the HistoryManager's projects change", () => {
reopenProjects.update()
historyManager.getProjects.andReturn([ { paths: ['/a'] }, { paths: ['/b', 'c:\\'] } ])
historyManager.getProjects.andReturn([
{ paths: ['/a'] },
{ paths: ['/b', 'c:\\'] }
])
historyManager.changeProjectsListener()
expect(menuManager.add.calls.length).toBe(2)
@@ -179,11 +209,11 @@ describe("ReopenProjectMenuManager", () => {
})
})
describe("updateProjects", () => {
it("creates correct menu items commands for recent projects", () => {
describe('updateProjects', () => {
it('creates correct menu items commands for recent projects', () => {
const projects = [
{ paths: [ '/users/neila' ] },
{ paths: [ '/users/buzza', 'users/michaelc' ] }
{ paths: ['/users/neila'] },
{ paths: ['/users/buzza', 'users/michaelc'] }
]
const menu = ReopenProjectMenuManager.createProjectsMenu(projects)
@@ -197,69 +227,81 @@ describe("ReopenProjectMenuManager", () => {
const first = recentMenu.submenu[0]
expect(first.label).toBe('/users/neila')
expect(first.command).toBe('application:reopen-project')
expect(first.commandDetail).toEqual({index: 0})
expect(first.commandDetail).toEqual({ index: 0 })
const second = recentMenu.submenu[1]
expect(second.label).toBe('buzza, michaelc')
expect(second.command).toBe('application:reopen-project')
expect(second.commandDetail).toEqual({index: 1})
expect(second.commandDetail).toEqual({ index: 1 })
})
})
describe("createLabel", () => {
it("returns the Unix path unchanged if there is only one", () => {
const label = ReopenProjectMenuManager.createLabel({ paths: ['/a/b/c/d/e/f'] })
describe('createLabel', () => {
it('returns the Unix path unchanged if there is only one', () => {
const label = ReopenProjectMenuManager.createLabel({
paths: ['/a/b/c/d/e/f']
})
expect(label).toBe('/a/b/c/d/e/f')
})
it("returns the Windows path unchanged if there is only one", () => {
const label = ReopenProjectMenuManager.createLabel({ paths: ['c:\\missions\\apollo11'] })
it('returns the Windows path unchanged if there is only one', () => {
const label = ReopenProjectMenuManager.createLabel({
paths: ['c:\\missions\\apollo11']
})
expect(label).toBe('c:\\missions\\apollo11')
})
it("returns the URL unchanged if there is only one", () => {
const label = ReopenProjectMenuManager.createLabel({ paths: ['https://launch.pad/apollo/11'] })
it('returns the URL unchanged if there is only one', () => {
const label = ReopenProjectMenuManager.createLabel({
paths: ['https://launch.pad/apollo/11']
})
expect(label).toBe('https://launch.pad/apollo/11')
})
it("returns a comma-separated list of base names if there are multiple", () => {
const project = { paths: [ '/var/one', '/usr/bin/two', '/etc/mission/control/three' ] }
it('returns a comma-separated list of base names if there are multiple', () => {
const project = {
paths: ['/var/one', '/usr/bin/two', '/etc/mission/control/three']
}
const label = ReopenProjectMenuManager.createLabel(project)
expect(label).toBe('one, two, three')
})
describe("betterBaseName", () => {
it("returns the standard base name for an absolute Unix path", () => {
describe('betterBaseName', () => {
it('returns the standard base name for an absolute Unix path', () => {
const name = ReopenProjectMenuManager.betterBaseName('/one/to/three')
expect(name).toBe('three')
})
it("returns the standard base name for a relative Windows path", () => {
it('returns the standard base name for a relative Windows path', () => {
if (process.platform === 'win32') {
const name = ReopenProjectMenuManager.betterBaseName('.\\one\\two')
expect(name).toBe('two')
}
})
it("returns the standard base name for an absolute Windows path", () => {
it('returns the standard base name for an absolute Windows path', () => {
if (process.platform === 'win32') {
const name = ReopenProjectMenuManager.betterBaseName('c:\\missions\\apollo\\11')
const name = ReopenProjectMenuManager.betterBaseName(
'c:\\missions\\apollo\\11'
)
expect(name).toBe('11')
}
})
it("returns the drive root for a Windows drive name", () => {
it('returns the drive root for a Windows drive name', () => {
const name = ReopenProjectMenuManager.betterBaseName('d:')
expect(name).toBe('d:\\')
})
it("returns the drive root for a Windows drive root", () => {
it('returns the drive root for a Windows drive root', () => {
const name = ReopenProjectMenuManager.betterBaseName('e:\\')
expect(name).toBe('e:\\')
})
it("returns the final path for a URI", () => {
const name = ReopenProjectMenuManager.betterBaseName('https://something/else')
it('returns the final path for a URI', () => {
const name = ReopenProjectMenuManager.betterBaseName(
'https://something/else'
)
expect(name).toBe('else')
})
})

View File

@@ -5,7 +5,7 @@ describe('Selection', () => {
beforeEach(() => {
buffer = atom.project.bufferForPathSync('sample.js')
editor = new TextEditor({buffer, tabLength: 2})
editor = new TextEditor({ buffer, tabLength: 2 })
selection = editor.getLastSelection()
})
@@ -88,23 +88,31 @@ describe('Selection', () => {
describe("when the selection's range is moved", () => {
it('notifies ::onDidChangeRange observers', () => {
selection.setBufferRange([[2, 0], [2, 10]])
const changeScreenRangeHandler = jasmine.createSpy('changeScreenRangeHandler')
const changeScreenRangeHandler = jasmine.createSpy(
'changeScreenRangeHandler'
)
selection.onDidChangeRange(changeScreenRangeHandler)
buffer.insert([2, 5], 'abc')
expect(changeScreenRangeHandler).toHaveBeenCalled()
expect(changeScreenRangeHandler.mostRecentCall.args[0]).not.toBeUndefined()
});
});
expect(
changeScreenRangeHandler.mostRecentCall.args[0]
).not.toBeUndefined()
})
})
describe("when only the selection's tail is moved (regression)", () => {
it('notifies ::onDidChangeRange observers', () => {
selection.setBufferRange([[2, 0], [2, 10]], {reversed: true})
const changeScreenRangeHandler = jasmine.createSpy('changeScreenRangeHandler')
selection.setBufferRange([[2, 0], [2, 10]], { reversed: true })
const changeScreenRangeHandler = jasmine.createSpy(
'changeScreenRangeHandler'
)
selection.onDidChangeRange(changeScreenRangeHandler)
buffer.insert([2, 5], 'abc')
expect(changeScreenRangeHandler).toHaveBeenCalled()
expect(changeScreenRangeHandler.mostRecentCall.args[0]).not.toBeUndefined()
expect(
changeScreenRangeHandler.mostRecentCall.args[0]
).not.toBeUndefined()
})
})
@@ -120,7 +128,7 @@ describe('Selection', () => {
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})
selection.insertText(' \n \n\n', { autoIndent: true })
expect(buffer.lineForRow(0)).toBe(' ')
expect(buffer.lineForRow(1)).toBe(' ')
expect(buffer.lineForRow(2)).toBe('')
@@ -128,19 +136,22 @@ describe('Selection', () => {
it('auto-indents if only a newline is inserted', () => {
selection.setBufferRange([[2, 0], [3, 0]])
selection.insertText('\n', {autoIndent: true})
selection.insertText('\n', { autoIndent: true })
expect(buffer.lineForRow(2)).toBe(' ')
})
it('auto-indents if only a carriage return + newline is inserted', () => {
selection.setBufferRange([[2, 0], [3, 0]])
selection.insertText('\r\n', {autoIndent: true})
selection.insertText('\r\n', { autoIndent: true })
expect(buffer.lineForRow(2)).toBe(' ')
})
it('does not adjust the indent of trailing lines if preserveTrailingLineIndentation is true', () => {
selection.setBufferRange([[5, 0], [5, 0]])
selection.insertText(' foo\n bar\n', {preserveTrailingLineIndentation: true, indentBasis: 1})
selection.insertText(' foo\n bar\n', {
preserveTrailingLineIndentation: true,
indentBasis: 1
})
expect(buffer.lineForRow(6)).toBe(' bar')
})
})
@@ -152,7 +163,9 @@ describe('Selection', () => {
expect(selection.getScreenRange()).toEqual([[0, 4], [0, 4]])
expect(selection.getBufferRange()).toEqual([[1, 6], [1, 6]])
expect(editor.lineTextForScreenRow(0)).toBe(`var${editor.displayLayer.foldCharacter}sort = function(items) {`)
expect(editor.lineTextForScreenRow(0)).toBe(
`var${editor.displayLayer.foldCharacter}sort = function(items) {`
)
expect(editor.isFoldedAtBufferRow(0)).toBe(true)
})
@@ -162,7 +175,9 @@ describe('Selection', () => {
expect(selection.getScreenRange()).toEqual([[0, 3], [0, 3]])
expect(selection.getBufferRange()).toEqual([[0, 3], [0, 3]])
expect(editor.lineTextForScreenRow(0)).toBe('var quicksort = function () {')
expect(editor.lineTextForScreenRow(0)).toBe(
'var quicksort = function () {'
)
expect(editor.isFoldedAtBufferRow(0)).toBe(false)
})
})
@@ -261,11 +276,11 @@ describe('Selection', () => {
{
name: 'indentSelectedRows',
op: opts => selection.indentSelectedRows(opts)
},
}
]
describe('without bypassReadOnly', () => {
for (const {name, op} of modifications) {
for (const { name, op } of modifications) {
it(`throws an error on ${name}`, () => {
expect(op).toThrow()
})
@@ -273,9 +288,9 @@ describe('Selection', () => {
})
describe('with bypassReadOnly', () => {
for (const {name, op} of modifications) {
for (const { name, op } of modifications) {
it(`permits ${name}`, () => {
op({bypassReadOnly: true})
op({ bypassReadOnly: true })
})
}
})

View File

@@ -1,68 +1,69 @@
/** @babel */
import {it, fit, ffit, fffit, beforeEach, afterEach} from './async-spec-helpers'
import { it } from './async-spec-helpers'
const StateStore = require('../src/state-store.js')
describe("StateStore", () => {
describe('StateStore', () => {
let databaseName = `test-database-${Date.now()}`
let version = 1
it("can save, load, and delete states", () => {
it('can save, load, and delete states', () => {
const store = new StateStore(databaseName, version)
return store.save('key', {foo:'bar'})
return store
.save('key', { foo: 'bar' })
.then(() => store.load('key'))
.then((state) => {
expect(state).toEqual({foo:'bar'})
.then(state => {
expect(state).toEqual({ foo: 'bar' })
})
.then(() => store.delete('key'))
.then(() => store.load('key'))
.then((value) => {
.then(value => {
expect(value).toBeNull()
})
.then(() => store.count())
.then((count) => {
.then(count => {
expect(count).toBe(0)
})
})
it("resolves with null when a non-existent key is loaded", () => {
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) => {
return store.load('no-such-key').then(value => {
expect(value).toBeNull()
})
})
it("can clear the state object store", () => {
it('can clear the state object store', () => {
const store = new StateStore(databaseName, version)
return store.save('key', {foo:'bar'})
return store
.save('key', { foo: 'bar' })
.then(() => store.count())
.then((count) =>
expect(count).toBe(1)
)
.then(count => expect(count).toBe(1))
.then(() => store.clear())
.then(() => store.count())
.then((count) => {
.then(count => {
expect(count).toBe(0)
})
})
describe("when there is an error reading from the database", () => {
it("rejects the promise returned by load", () => {
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"}}
const fakeErrorEvent = { target: { errorCode: 'Something bad happened' } }
spyOn(IDBObjectStore.prototype, 'get').andCallFake((key) => {
spyOn(IDBObjectStore.prototype, 'get').andCallFake(key => {
let request = {}
process.nextTick(() => request.onerror(fakeErrorEvent))
return request
})
return store.load('nonexistentKey')
return store
.load('nonexistentKey')
.then(() => {
throw new Error("Promise should have been rejected")
throw new Error('Promise should have been rejected')
})
.catch((event) => {
.catch(event => {
expect(event).toBe(fakeErrorEvent)
})
})

View File

@@ -5,13 +5,21 @@ describe('StyleManager', () => {
let [styleManager, addEvents, removeEvents, updateEvents] = []
beforeEach(() => {
styleManager = new StyleManager({configDirPath: temp.mkdirSync('atom-config')})
styleManager = new StyleManager({
configDirPath: temp.mkdirSync('atom-config')
})
addEvents = []
removeEvents = []
updateEvents = []
styleManager.onDidAddStyleElement((event) => { addEvents.push(event) })
styleManager.onDidRemoveStyleElement((event) => { removeEvents.push(event) })
styleManager.onDidUpdateStyleElement((event) => { updateEvents.push(event) })
styleManager.onDidAddStyleElement(event => {
addEvents.push(event)
})
styleManager.onDidRemoveStyleElement(event => {
removeEvents.push(event)
})
styleManager.onDidUpdateStyleElement(event => {
updateEvents.push(event)
})
})
afterEach(() => {
@@ -39,7 +47,9 @@ describe('StyleManager', () => {
describe('atom-text-editor shadow DOM selectors upgrades', () => {
beforeEach(() => {
// attach styles element to the DOM to parse CSS rules
styleManager.onDidAddStyleElement((styleElement) => { jasmine.attachToDOM(styleElement) })
styleManager.onDidAddStyleElement(styleElement => {
jasmine.attachToDOM(styleElement)
})
})
it('removes the ::shadow pseudo-element from atom-text-editor selectors', () => {
@@ -51,25 +61,36 @@ describe('StyleManager', () => {
atom-text-editor[data-grammar*=\"js\"]::shadow .class-6 { color: green; }
atom-text-editor[mini].is-focused::shadow .class-7 { color: green; }
`)
expect(Array.from(styleManager.getStyleElements()[0].sheet.cssRules).map((r) => r.selectorText)).toEqual([
expect(
Array.from(styleManager.getStyleElements()[0].sheet.cssRules).map(
r => r.selectorText
)
).toEqual([
'atom-text-editor.editor .class-1, atom-text-editor.editor .class-2',
'atom-text-editor.editor > .class-3',
'atom-text-editor .class-4',
'another-element::shadow .class-5',
'atom-text-editor[data-grammar*=\"js\"].editor .class-6',
'atom-text-editor[data-grammar*="js"].editor .class-6',
'atom-text-editor[mini].is-focused.editor .class-7'
])
})
describe('when a selector targets the atom-text-editor shadow DOM', () => {
it('prepends "--syntax" to class selectors matching a grammar scope name and not already starting with "syntax--"', () => {
styleManager.addStyleSheet(`
styleManager.addStyleSheet(
`
.class-1 { color: red }
.source > .js, .source.coffee { color: green }
.syntax--source { color: gray }
#id-1 { color: blue }
`, {context: 'atom-text-editor'})
expect(Array.from(styleManager.getStyleElements()[0].sheet.cssRules).map((r) => r.selectorText)).toEqual([
`,
{ context: 'atom-text-editor' }
)
expect(
Array.from(styleManager.getStyleElements()[0].sheet.cssRules).map(
r => r.selectorText
)
).toEqual([
'.class-1',
'.syntax--source > .syntax--js, .syntax--source.syntax--coffee',
'.syntax--source',
@@ -82,7 +103,11 @@ describe('StyleManager', () => {
atom-text-editor[mini].is-focused::shadow .source > .js { color: gray }
atom-text-editor .source > .js { color: red }
`)
expect(Array.from(styleManager.getStyleElements()[1].sheet.cssRules).map((r) => r.selectorText)).toEqual([
expect(
Array.from(styleManager.getStyleElements()[1].sheet.cssRules).map(
r => r.selectorText
)
).toEqual([
'.source > .js, .source.coffee',
'atom-text-editor.editor .syntax--source > .syntax--js',
'atom-text-editor[mini].is-focused.editor .syntax--source > .syntax--js',
@@ -92,14 +117,23 @@ describe('StyleManager', () => {
})
it('replaces ":host" with "atom-text-editor" only when the context of a style sheet is "atom-text-editor"', () => {
styleManager.addStyleSheet(':host .class-1, :host .class-2 { color: red; }')
expect(Array.from(styleManager.getStyleElements()[0].sheet.cssRules).map((r) => r.selectorText)).toEqual([
':host .class-1, :host .class-2'
])
styleManager.addStyleSheet(':host .class-1, :host .class-2 { color: red; }', {context: 'atom-text-editor'})
expect(Array.from(styleManager.getStyleElements()[1].sheet.cssRules).map((r) => r.selectorText)).toEqual([
'atom-text-editor .class-1, atom-text-editor .class-2'
])
styleManager.addStyleSheet(
':host .class-1, :host .class-2 { color: red; }'
)
expect(
Array.from(styleManager.getStyleElements()[0].sheet.cssRules).map(
r => r.selectorText
)
).toEqual([':host .class-1, :host .class-2'])
styleManager.addStyleSheet(
':host .class-1, :host .class-2 { color: red; }',
{ context: 'atom-text-editor' }
)
expect(
Array.from(styleManager.getStyleElements()[1].sheet.cssRules).map(
r => r.selectorText
)
).toEqual(['atom-text-editor .class-1, atom-text-editor .class-2'])
})
it('does not transform CSS rules with invalid syntax', () => {
@@ -110,17 +144,23 @@ describe('StyleManager', () => {
})
it('does not throw exceptions on rules with no selectors', () => {
styleManager.addStyleSheet('@media screen {font-size: 10px}', {context: 'atom-text-editor'})
styleManager.addStyleSheet('@media screen {font-size: 10px}', {
context: 'atom-text-editor'
})
})
})
describe('when a sourcePath parameter is specified', () => {
it('ensures a maximum of one style element for the given source path, updating a previous if it exists', () => {
const disposable1 = styleManager.addStyleSheet('a {color: red}', {sourcePath: '/foo/bar'})
styleManager.addStyleSheet('a {color: red}', {
sourcePath: '/foo/bar'
})
expect(addEvents.length).toBe(1)
expect(addEvents[0].getAttribute('source-path')).toBe('/foo/bar')
const disposable2 = styleManager.addStyleSheet('a {color: blue}', {sourcePath: '/foo/bar'})
const disposable2 = styleManager.addStyleSheet('a {color: blue}', {
sourcePath: '/foo/bar'
})
expect(addEvents.length).toBe(1)
expect(updateEvents.length).toBe(1)
expect(updateEvents[0].getAttribute('source-path')).toBe('/foo/bar')
@@ -128,7 +168,9 @@ describe('StyleManager', () => {
disposable2.dispose()
addEvents = []
styleManager.addStyleSheet('a {color: yellow}', {sourcePath: '/foo/bar'})
styleManager.addStyleSheet('a {color: yellow}', {
sourcePath: '/foo/bar'
})
expect(addEvents.length).toBe(1)
expect(addEvents[0].getAttribute('source-path')).toBe('/foo/bar')
expect(addEvents[0].textContent).toBe('a {color: yellow}')
@@ -137,11 +179,13 @@ describe('StyleManager', () => {
describe('when a priority parameter is specified', () => {
it('inserts the style sheet based on the priority', () => {
styleManager.addStyleSheet('a {color: red}', {priority: 1})
styleManager.addStyleSheet('a {color: blue}', {priority: 0})
styleManager.addStyleSheet('a {color: green}', {priority: 2})
styleManager.addStyleSheet('a {color: yellow}', {priority: 1})
expect(styleManager.getStyleElements().map((elt) => elt.textContent)).toEqual([
styleManager.addStyleSheet('a {color: red}', { priority: 1 })
styleManager.addStyleSheet('a {color: blue}', { priority: 0 })
styleManager.addStyleSheet('a {color: green}', { priority: 2 })
styleManager.addStyleSheet('a {color: yellow}', { priority: 1 })
expect(
styleManager.getStyleElements().map(elt => elt.textContent)
).toEqual([
'a {color: blue}',
'a {color: red}',
'a {color: yellow}',

View File

@@ -5,7 +5,7 @@ describe('SyntaxScopeMap', () => {
const map = new SyntaxScopeMap({
'a > b > c': 'x',
'b > c': 'y',
'c': 'z'
c: 'z'
})
expect(map.get(['a', 'b', 'c'], [0, 0, 0])).toBe('x')
@@ -20,7 +20,7 @@ describe('SyntaxScopeMap', () => {
const map = new SyntaxScopeMap({
'a > b': 'w',
'a > b:nth-child(1)': 'x',
'b': 'y',
b: 'y',
'b:nth-child(2)': 'z'
})

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,8 @@
const {it, fit, ffit, fffit, beforeEach, afterEach, conditionPromise, timeoutPromise} = require('./async-spec-helpers')
const {
it,
beforeEach
} = require('./async-spec-helpers')
const TextEditor = require('../src/text-editor')
const TextEditorElement = require('../src/text-editor-element')
@@ -9,7 +13,8 @@ describe('TextEditorElement', () => {
jasmineContent = document.body.querySelector('#jasmine-content')
// Force scrollbars to be visible regardless of local system configuration
const scrollbarStyle = document.createElement('style')
scrollbarStyle.textContent = 'atom-text-editor ::-webkit-scrollbar { -webkit-appearance: none }'
scrollbarStyle.textContent =
'atom-text-editor ::-webkit-scrollbar { -webkit-appearance: none }'
jasmine.attachToDOM(scrollbarStyle)
})
@@ -53,7 +58,7 @@ describe('TextEditorElement', () => {
})
it("only assigns 'placeholder-text' on the model if the attribute is present", () => {
const editor = new TextEditor({placeholderText: 'placeholder'})
const editor = new TextEditor({ placeholderText: 'placeholder' })
editor.getElement()
expect(editor.getPlaceholderText()).toBe('placeholder')
})
@@ -70,8 +75,8 @@ describe('TextEditorElement', () => {
expect(element.getModel().isLineNumberGutterVisible()).toBe(false)
})
it("honors the 'readonly' attribute", async function() {
jasmineContent.innerHTML = "<atom-text-editor readonly>"
it("honors the 'readonly' attribute", async function () {
jasmineContent.innerHTML = '<atom-text-editor readonly>'
const element = jasmineContent.firstChild
expect(element.getComponent().isInputEnabled()).toBe(false)
@@ -108,11 +113,10 @@ describe('TextEditorElement', () => {
describe('when the model is assigned', () =>
it("adds the 'mini' attribute if .isMini() returns true on the model", async () => {
const element = buildTextEditorElement()
element.getModel().update({mini: true})
element.getModel().update({ mini: true })
await atom.views.getNextUpdatePromise()
expect(element.hasAttribute('mini')).toBe(true)
})
)
}))
describe('when the editor is attached to the DOM', () =>
it('mounts the component and unmounts when removed from the dom', () => {
@@ -125,8 +129,7 @@ describe('TextEditorElement', () => {
jasmine.attachToDOM(element)
expect(element.component.attached).toBe(true)
})
)
}))
describe('when the editor is detached from the DOM and then reattached', () => {
it('does not render duplicate line numbers', () => {
@@ -145,17 +148,23 @@ describe('TextEditorElement', () => {
it('does not render duplicate decorations in custom gutters', () => {
const editor = new TextEditor()
editor.setText('1\n2\n3')
editor.addGutter({name: 'test-gutter'})
editor.addGutter({ name: 'test-gutter' })
const marker = editor.markBufferRange([[0, 0], [2, 0]])
editor.decorateMarker(marker, {type: 'gutter', gutterName: 'test-gutter'})
editor.decorateMarker(marker, {
type: 'gutter',
gutterName: 'test-gutter'
})
const element = editor.getElement()
jasmine.attachToDOM(element)
const initialDecorationCount = element.querySelectorAll('.decoration').length
const initialDecorationCount = element.querySelectorAll('.decoration')
.length
element.remove()
jasmine.attachToDOM(element)
expect(element.querySelectorAll('.decoration').length).toBe(initialDecorationCount)
expect(element.querySelectorAll('.decoration').length).toBe(
initialDecorationCount
)
})
it('can be re-focused using the previous `document.activeElement`', () => {
@@ -194,7 +203,9 @@ describe('TextEditorElement', () => {
it("doesn't trigger a blur event on the editor element when focusing an already focused editor element", () => {
let blurCalled = false
const element = buildTextEditorElement()
element.addEventListener('blur', () => { blurCalled = true })
element.addEventListener('blur', () => {
blurCalled = true
})
jasmineContent.appendChild(element)
expect(document.activeElement).toBe(document.body)
@@ -216,13 +227,15 @@ describe('TextEditorElement', () => {
}
}
document.registerElement('element-that-focuses-child',
{prototype: ElementThatFocusesChild.prototype}
)
document.registerElement('element-that-focuses-child', {
prototype: ElementThatFocusesChild.prototype
})
it('proxies the focus event to the hidden input', () => {
const element = buildTextEditorElement()
const parentElement = document.createElement('element-that-focuses-child')
const parentElement = document.createElement(
'element-that-focuses-child'
)
parentElement.appendChild(element)
jasmineContent.appendChild(parentElement)
expect(document.activeElement).toBe(element.querySelector('input'))
@@ -236,7 +249,7 @@ describe('TextEditorElement', () => {
parentElement.style.width = '0px'
parentElement.style.height = '0px'
const element = buildTextEditorElement({attach: false})
const element = buildTextEditorElement({ attach: false })
parentElement.appendChild(element)
jasmineContent.appendChild(parentElement)
@@ -249,7 +262,7 @@ describe('TextEditorElement', () => {
describe('::setModel', () => {
describe('when the element does not have an editor yet', () => {
it('uses the supplied one', () => {
const element = buildTextEditorElement({attach: false})
const element = buildTextEditorElement({ attach: false })
const editor = new TextEditor()
element.setModel(editor)
jasmine.attachToDOM(element)
@@ -260,7 +273,7 @@ describe('TextEditorElement', () => {
describe('when the element already has an editor', () => {
it('unbinds it and then swaps it with the supplied one', async () => {
const element = buildTextEditorElement({attach: true})
const element = buildTextEditorElement({ attach: true })
const previousEditor = element.getModel()
expect(previousEditor.element).toBe(element)
@@ -275,7 +288,7 @@ describe('TextEditorElement', () => {
describe('::onDidAttach and ::onDidDetach', () =>
it('invokes callbacks when the element is attached and detached', () => {
const element = buildTextEditorElement({attach: false})
const element = buildTextEditorElement({ attach: false })
const attachedCallback = jasmine.createSpy('attachedCallback')
const detachedCallback = jasmine.createSpy('detachedCallback')
@@ -292,8 +305,7 @@ describe('TextEditorElement', () => {
expect(attachedCallback).not.toHaveBeenCalled()
expect(detachedCallback).toHaveBeenCalled()
})
)
}))
describe('::setUpdatedSynchronously', () => {
it('controls whether the text editor is updated synchronously', () => {
@@ -318,7 +330,7 @@ describe('TextEditorElement', () => {
describe('::getDefaultCharacterWidth', () => {
it('returns 0 before the element is attached', () => {
const element = buildTextEditorElement({attach: false})
const element = buildTextEditorElement({ attach: false })
expect(element.getDefaultCharacterWidth()).toBe(0)
})
@@ -341,7 +353,7 @@ describe('TextEditorElement', () => {
const horizontalScrollbarHeight = element.component.getHorizontalScrollbarHeight()
expect(element.getMaxScrollTop()).toBe(0)
await editor.update({autoHeight: false})
await editor.update({ autoHeight: false })
element.style.height = 100 + horizontalScrollbarHeight + 'px'
await element.getNextUpdatePromise()
@@ -354,13 +366,12 @@ describe('TextEditorElement', () => {
element.style.height = 200 + horizontalScrollbarHeight + 'px'
await element.getNextUpdatePromise()
expect(element.getMaxScrollTop()).toBe(0)
})
)
}))
describe('::setScrollTop and ::setScrollLeft', () => {
it('changes the scroll position', async () => {
element = buildTextEditorElement()
element.getModel().update({autoHeight: false})
const element = buildTextEditorElement()
element.getModel().update({ autoHeight: false })
element.getModel().setText('lorem\nipsum\ndolor\nsit\namet')
element.setHeight(20)
await element.getNextUpdatePromise()
@@ -387,8 +398,7 @@ describe('TextEditorElement', () => {
element.getModel().setMini(false)
await element.getNextUpdatePromise()
expect(element.hasAttribute('mini')).toBe(false)
})
)
}))
describe('::intersectsVisibleRowRange(start, end)', () => {
it('returns true if the given row range intersects the visible row range', async () => {
@@ -396,7 +406,7 @@ describe('TextEditorElement', () => {
const editor = element.getModel()
const horizontalScrollbarHeight = element.component.getHorizontalScrollbarHeight()
editor.update({autoHeight: false})
editor.update({ autoHeight: false })
element.getModel().setText('x\n'.repeat(20))
element.style.height = 120 + horizontalScrollbarHeight + 'px'
await element.getNextUpdatePromise()
@@ -419,7 +429,7 @@ describe('TextEditorElement', () => {
const editor = element.getModel()
const horizontalScrollbarHeight = element.component.getHorizontalScrollbarHeight()
editor.update({autoHeight: false})
editor.update({ autoHeight: false })
element.getModel().setText('xxxxxxxxxxxxxxxxxxxxxx\n'.repeat(20))
element.style.height = 120 + horizontalScrollbarHeight + 'px'
await element.getNextUpdatePromise()
@@ -445,7 +455,7 @@ describe('TextEditorElement', () => {
beforeEach(async () => {
element = buildTextEditorElement()
element.getModel().update({autoHeight: false})
element.getModel().update({ autoHeight: false })
element.getModel().setText('lorem\nipsum\ndolor\nsit\namet')
element.setHeight(20)
await element.getNextUpdatePromise()
@@ -456,7 +466,9 @@ describe('TextEditorElement', () => {
describe('::onDidChangeScrollTop(callback)', () =>
it('triggers even when subscribing before attaching the element', () => {
const positions = []
const subscription1 = element.onDidChangeScrollTop(p => positions.push(p))
const subscription1 = element.onDidChangeScrollTop(p =>
positions.push(p)
)
element.onDidChangeScrollTop(p => positions.push(p))
positions.length = 0
@@ -475,13 +487,14 @@ describe('TextEditorElement', () => {
positions.length = 0
element.setScrollTop(30)
expect(positions).toEqual([30])
})
)
}))
describe('::onDidChangeScrollLeft(callback)', () =>
it('triggers even when subscribing before attaching the element', () => {
const positions = []
const subscription1 = element.onDidChangeScrollLeft(p => positions.push(p))
const subscription1 = element.onDidChangeScrollLeft(p =>
positions.push(p)
)
element.onDidChangeScrollLeft(p => positions.push(p))
positions.length = 0
@@ -500,7 +513,6 @@ describe('TextEditorElement', () => {
positions.length = 0
element.setScrollLeft(30)
expect(positions).toEqual([30])
})
)
}))
})
})

View File

@@ -1,8 +1,8 @@
const TextEditorRegistry = require('../src/text-editor-registry')
const TextEditor = require('../src/text-editor')
const TextBuffer = require('text-buffer')
const {Point, Range} = TextBuffer
const {it, fit, ffit, fffit} = require('./async-spec-helpers')
const { Point, Range } = TextBuffer
const { it } = require('./async-spec-helpers')
const dedent = require('dedent')
describe('TextEditorRegistry', function () {
@@ -15,11 +15,13 @@ describe('TextEditorRegistry', function () {
assert: atom.assert,
config: atom.config,
grammarRegistry: atom.grammars,
packageManager: {deferredActivationHooks: null}
packageManager: { deferredActivationHooks: null }
})
editor = new TextEditor({autoHeight: false})
expect(atom.grammars.assignLanguageMode(editor, 'text.plain.null-grammar')).toBe(true)
editor = new TextEditor({ autoHeight: false })
expect(
atom.grammars.assignLanguageMode(editor, 'text.plain.null-grammar')
).toBe(true)
})
afterEach(function () {
@@ -68,9 +70,11 @@ describe('TextEditorRegistry', function () {
await atom.packages.activatePackage('language-javascript')
await atom.packages.activatePackage('language-c')
atom.config.set('editor.tabLength', 8, {scope: '.source.js'})
atom.config.set('editor.tabLength', 8, { scope: '.source.js' })
const editor = registry.build({buffer: new TextBuffer({filePath: 'test.js'})})
const editor = registry.build({
buffer: new TextBuffer({ filePath: 'test.js' })
})
expect(editor.getTabLength()).toBe(8)
})
})
@@ -87,14 +91,22 @@ describe('TextEditorRegistry', function () {
registry.maintainConfig(editor2)
await initialPackageActivation
expect(editor.getRootScopeDescriptor().getScopesArray()).toEqual(['text.plain.null-grammar'])
expect(editor2.getRootScopeDescriptor().getScopesArray()).toEqual(['source.js'])
expect(editor.getRootScopeDescriptor().getScopesArray()).toEqual([
'text.plain.null-grammar'
])
expect(editor2.getRootScopeDescriptor().getScopesArray()).toEqual([
'source.js'
])
expect(editor.getEncoding()).toBe('utf8')
expect(editor2.getEncoding()).toBe('utf8')
atom.config.set('core.fileEncoding', 'utf16le', {scopeSelector: '.text.plain.null-grammar'})
atom.config.set('core.fileEncoding', 'utf16be', {scopeSelector: '.source.js'})
atom.config.set('core.fileEncoding', 'utf16le', {
scopeSelector: '.text.plain.null-grammar'
})
atom.config.set('core.fileEncoding', 'utf16be', {
scopeSelector: '.source.js'
})
expect(editor.getEncoding()).toBe('utf16le')
expect(editor2.getEncoding()).toBe('utf16be')
@@ -131,23 +143,29 @@ describe('TextEditorRegistry', function () {
expect(editor.getEncoding()).toBe('utf16be')
})
it('updates the editor\'s settings when its grammar changes', async function () {
it("updates the editor's settings when its grammar changes", async function () {
await atom.packages.activatePackage('language-javascript')
registry.maintainConfig(editor)
await initialPackageActivation
atom.config.set('core.fileEncoding', 'utf16be', {scopeSelector: '.source.js'})
atom.config.set('core.fileEncoding', 'utf16be', {
scopeSelector: '.source.js'
})
expect(editor.getEncoding()).toBe('utf8')
atom.config.set('core.fileEncoding', 'utf16le', {scopeSelector: '.source.js'})
atom.config.set('core.fileEncoding', 'utf16le', {
scopeSelector: '.source.js'
})
expect(editor.getEncoding()).toBe('utf8')
atom.grammars.assignLanguageMode(editor, 'source.js')
await initialPackageActivation
expect(editor.getEncoding()).toBe('utf16le')
atom.config.set('core.fileEncoding', 'utf16be', {scopeSelector: '.source.js'})
atom.config.set('core.fileEncoding', 'utf16be', {
scopeSelector: '.source.js'
})
expect(editor.getEncoding()).toBe('utf16be')
atom.grammars.assignLanguageMode(editor, 'text.plain.null-grammar')
@@ -155,7 +173,7 @@ describe('TextEditorRegistry', function () {
expect(editor.getEncoding()).toBe('utf8')
})
it('preserves editor settings that haven\'t changed between previous and current language modes', async function () {
it("preserves editor settings that haven't changed between previous and current language modes", async function () {
await atom.packages.activatePackage('language-javascript')
registry.maintainConfig(editor)
@@ -182,8 +200,12 @@ describe('TextEditorRegistry', function () {
await initialPackageActivation
expect(editor.getEncoding()).toBe('utf8')
atom.config.set('core.fileEncoding', 'utf16be', {scopeSelector: '.text.plain.null-grammar'})
atom.config.set('core.fileEncoding', 'utf16le', {scopeSelector: '.source.js'})
atom.config.set('core.fileEncoding', 'utf16be', {
scopeSelector: '.text.plain.null-grammar'
})
atom.config.set('core.fileEncoding', 'utf16le', {
scopeSelector: '.source.js'
})
expect(editor.getEncoding()).toBe('utf16be')
editor.setEncoding('utf8')
@@ -194,13 +216,15 @@ describe('TextEditorRegistry', function () {
expect(editor.getEncoding()).toBe('utf16le')
})
it('returns a disposable that can be used to stop the registry from updating the editor\'s config', async function () {
it("returns a disposable that can be used to stop the registry from updating the editor's config", async function () {
await atom.packages.activatePackage('language-javascript')
const previousSubscriptionCount = getSubscriptionCount(editor)
const disposable = registry.maintainConfig(editor)
await initialPackageActivation
expect(getSubscriptionCount(editor)).toBeGreaterThan(previousSubscriptionCount)
expect(getSubscriptionCount(editor)).toBeGreaterThan(
previousSubscriptionCount
)
expect(registry.editorsWithMaintainedConfig.size).toBe(1)
atom.config.set('core.fileEncoding', 'utf16be')
@@ -217,7 +241,7 @@ describe('TextEditorRegistry', function () {
})
it('sets the encoding based on the config', async function () {
editor.update({encoding: 'utf8'})
editor.update({ encoding: 'utf8' })
expect(editor.getEncoding()).toBe('utf8')
atom.config.set('core.fileEncoding', 'utf16le')
@@ -230,7 +254,7 @@ describe('TextEditorRegistry', function () {
})
it('sets the tab length based on the config', async function () {
editor.update({tabLength: 4})
editor.update({ tabLength: 4 })
expect(editor.getTabLength()).toBe(4)
atom.config.set('editor.tabLength', 8)
@@ -257,13 +281,12 @@ describe('TextEditorRegistry', function () {
})
describe('when the "tabType" config setting is "auto"', function () {
it('enables or disables soft tabs based on the editor\'s content', async function () {
it("enables or disables soft tabs based on the editor's content", async function () {
await initialPackageActivation
await atom.packages.activatePackage('language-javascript')
atom.grammars.assignLanguageMode(editor, 'source.js')
atom.config.set('editor.tabType', 'auto')
await initialPackageActivation
const languageMode = editor.getBuffer().getLanguageMode()
editor.setText(dedent`
{
@@ -273,24 +296,30 @@ describe('TextEditorRegistry', function () {
let disposable = registry.maintainConfig(editor)
expect(editor.getSoftTabs()).toBe(true)
/* eslint-disable no-tabs */
editor.setText(dedent`
{
hello;
}
`)
/* eslint-enable no-tabs */
disposable.dispose()
disposable = registry.maintainConfig(editor)
expect(editor.getSoftTabs()).toBe(false)
editor.setTextInBufferRange(new Range(Point.ZERO, Point.ZERO), dedent`
editor.setTextInBufferRange(
new Range(Point.ZERO, Point.ZERO),
dedent`
/*
* Comment with a leading space.
*/
` + '\n')
` + '\n'
)
disposable.dispose()
disposable = registry.maintainConfig(editor)
expect(editor.getSoftTabs()).toBe(false)
/* eslint-disable no-tabs */
editor.setText(dedent`
/*
* Comment with a leading space.
@@ -300,6 +329,7 @@ describe('TextEditorRegistry', function () {
hello;
}
`)
/* eslint-enable no-tabs */
disposable.dispose()
disposable = registry.maintainConfig(editor)
expect(editor.getSoftTabs()).toBe(false)
@@ -335,7 +365,7 @@ describe('TextEditorRegistry', function () {
})
it('enables or disables soft tabs based on the config', async function () {
editor.update({softTabs: true})
editor.update({ softTabs: true })
expect(editor.getSoftTabs()).toBe(true)
atom.config.set('editor.tabType', 'hard')
@@ -352,7 +382,7 @@ describe('TextEditorRegistry', function () {
})
it('enables or disables atomic soft tabs based on the config', async function () {
editor.update({atomicSoftTabs: true})
editor.update({ atomicSoftTabs: true })
expect(editor.hasAtomicSoftTabs()).toBe(true)
atom.config.set('editor.atomicSoftTabs', false)
@@ -365,7 +395,7 @@ describe('TextEditorRegistry', function () {
})
it('enables or disables cursor on selection visibility based on the config', async function () {
editor.update({showCursorOnSelection: true})
editor.update({ showCursorOnSelection: true })
expect(editor.getShowCursorOnSelection()).toBe(true)
atom.config.set('editor.showCursorOnSelection', false)
@@ -378,7 +408,7 @@ describe('TextEditorRegistry', function () {
})
it('enables or disables line numbers based on the config', async function () {
editor.update({showLineNumbers: true})
editor.update({ showLineNumbers: true })
expect(editor.showLineNumbers).toBe(true)
atom.config.set('editor.showLineNumbers', false)
@@ -391,8 +421,8 @@ describe('TextEditorRegistry', function () {
})
it('sets the invisibles based on the config', async function () {
const invisibles1 = {'tab': 'a', 'cr': false, eol: false, space: false}
const invisibles2 = {'tab': 'b', 'cr': false, eol: false, space: false}
const invisibles1 = { tab: 'a', cr: false, eol: false, space: false }
const invisibles2 = { tab: 'b', cr: false, eol: false, space: false }
editor.update({
showInvisibles: true,
@@ -414,7 +444,7 @@ describe('TextEditorRegistry', function () {
})
it('enables or disables the indent guide based on the config', async function () {
editor.update({showIndentGuide: true})
editor.update({ showIndentGuide: true })
expect(editor.doesShowIndentGuide()).toBe(true)
atom.config.set('editor.showIndentGuide', false)
@@ -427,7 +457,7 @@ describe('TextEditorRegistry', function () {
})
it('enables or disables soft wrap based on the config', async function () {
editor.update({softWrapped: true})
editor.update({ softWrapped: true })
expect(editor.isSoftWrapped()).toBe(true)
atom.config.set('editor.softWrap', false)
@@ -440,7 +470,7 @@ describe('TextEditorRegistry', function () {
})
it('sets the soft wrap indent length based on the config', async function () {
editor.update({softWrapHangingIndentLength: 4})
editor.update({ softWrapHangingIndentLength: 4 })
expect(editor.getSoftWrapHangingIndentLength()).toBe(4)
atom.config.set('editor.softWrapHangingIndent', 2)
@@ -457,7 +487,7 @@ describe('TextEditorRegistry', function () {
softWrapped: true,
preferredLineLength: 80,
editorWidthInChars: 120,
softWrapAtPreferredLineLength: true,
softWrapAtPreferredLineLength: true
})
expect(editor.getSoftWrapColumn()).toBe(80)
@@ -475,7 +505,7 @@ describe('TextEditorRegistry', function () {
it('allows for custom definition of maximum soft wrap based on config', async function () {
editor.update({
softWrapped: false,
maxScreenLineLength: 1500,
maxScreenLineLength: 1500
})
expect(editor.getSoftWrapColumn()).toBe(1500)
@@ -488,7 +518,7 @@ describe('TextEditorRegistry', function () {
})
it('sets the preferred line length based on the config', async function () {
editor.update({preferredLineLength: 80})
editor.update({ preferredLineLength: 80 })
expect(editor.getPreferredLineLength()).toBe(80)
atom.config.set('editor.preferredLineLength', 110)
@@ -501,7 +531,7 @@ describe('TextEditorRegistry', function () {
})
it('enables or disables auto-indent based on the config', async function () {
editor.update({autoIndent: true})
editor.update({ autoIndent: true })
expect(editor.shouldAutoIndent()).toBe(true)
atom.config.set('editor.autoIndent', false)
@@ -514,7 +544,7 @@ describe('TextEditorRegistry', function () {
})
it('enables or disables auto-indent-on-paste based on the config', async function () {
editor.update({autoIndentOnPaste: true})
editor.update({ autoIndentOnPaste: true })
expect(editor.shouldAutoIndentOnPaste()).toBe(true)
atom.config.set('editor.autoIndentOnPaste', false)
@@ -527,7 +557,7 @@ describe('TextEditorRegistry', function () {
})
it('enables or disables scrolling past the end of the buffer based on the config', async function () {
editor.update({scrollPastEnd: true})
editor.update({ scrollPastEnd: true })
expect(editor.getScrollPastEnd()).toBe(true)
atom.config.set('editor.scrollPastEnd', false)
@@ -540,7 +570,7 @@ describe('TextEditorRegistry', function () {
})
it('sets the undo grouping interval based on the config', async function () {
editor.update({undoGroupingInterval: 300})
editor.update({ undoGroupingInterval: 300 })
expect(editor.getUndoGroupingInterval()).toBe(300)
atom.config.set('editor.undoGroupingInterval', 600)
@@ -553,7 +583,7 @@ describe('TextEditorRegistry', function () {
})
it('sets the scroll sensitivity based on the config', async function () {
editor.update({scrollSensitivity: 50})
editor.update({ scrollSensitivity: 50 })
expect(editor.getScrollSensitivity()).toBe(50)
atom.config.set('editor.scrollSensitivity', 60)
@@ -567,7 +597,7 @@ describe('TextEditorRegistry', function () {
describe('when called twice with a given editor', function () {
it('does nothing the second time', async function () {
editor.update({scrollSensitivity: 50})
editor.update({ scrollSensitivity: 50 })
const disposable1 = registry.maintainConfig(editor)
const disposable2 = registry.maintainConfig(editor)
@@ -589,10 +619,12 @@ describe('TextEditorRegistry', function () {
})
function getSubscriptionCount (editor) {
return editor.emitter.getTotalListenerCount() +
return (
editor.emitter.getTotalListenerCount() +
editor.tokenizedBuffer.emitter.getTotalListenerCount() +
editor.buffer.emitter.getTotalListenerCount() +
editor.displayLayer.emitter.getTotalListenerCount()
)
}
function retainedEditorCount (registry) {

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -4,7 +4,9 @@ describe('text utilities', () => {
describe('.hasPairedCharacter(string)', () =>
it('returns true when the string contains a surrogate pair, variation sequence, or combined character', () => {
expect(textUtils.hasPairedCharacter('abc')).toBe(false)
expect(textUtils.hasPairedCharacter('a\uD835\uDF97b\uD835\uDF97c')).toBe(true)
expect(textUtils.hasPairedCharacter('a\uD835\uDF97b\uD835\uDF97c')).toBe(
true
)
expect(textUtils.hasPairedCharacter('\uD835\uDF97')).toBe(true)
expect(textUtils.hasPairedCharacter('\u2714\uFE0E')).toBe(true)
expect(textUtils.hasPairedCharacter('e\u0301')).toBe(true)
@@ -16,18 +18,31 @@ describe('text utilities', () => {
expect(textUtils.hasPairedCharacter('\uFE0E\uFE0E')).toBe(false)
expect(textUtils.hasPairedCharacter('\u0301\u0301')).toBe(false)
})
)
}))
describe('.isPairedCharacter(string, index)', () =>
it('returns true when the index is the start of a high/low surrogate pair, variation sequence, or combined character', () => {
expect(textUtils.isPairedCharacter('a\uD835\uDF97b\uD835\uDF97c', 0)).toBe(false)
expect(textUtils.isPairedCharacter('a\uD835\uDF97b\uD835\uDF97c', 1)).toBe(true)
expect(textUtils.isPairedCharacter('a\uD835\uDF97b\uD835\uDF97c', 2)).toBe(false)
expect(textUtils.isPairedCharacter('a\uD835\uDF97b\uD835\uDF97c', 3)).toBe(false)
expect(textUtils.isPairedCharacter('a\uD835\uDF97b\uD835\uDF97c', 4)).toBe(true)
expect(textUtils.isPairedCharacter('a\uD835\uDF97b\uD835\uDF97c', 5)).toBe(false)
expect(textUtils.isPairedCharacter('a\uD835\uDF97b\uD835\uDF97c', 6)).toBe(false)
expect(
textUtils.isPairedCharacter('a\uD835\uDF97b\uD835\uDF97c', 0)
).toBe(false)
expect(
textUtils.isPairedCharacter('a\uD835\uDF97b\uD835\uDF97c', 1)
).toBe(true)
expect(
textUtils.isPairedCharacter('a\uD835\uDF97b\uD835\uDF97c', 2)
).toBe(false)
expect(
textUtils.isPairedCharacter('a\uD835\uDF97b\uD835\uDF97c', 3)
).toBe(false)
expect(
textUtils.isPairedCharacter('a\uD835\uDF97b\uD835\uDF97c', 4)
).toBe(true)
expect(
textUtils.isPairedCharacter('a\uD835\uDF97b\uD835\uDF97c', 5)
).toBe(false)
expect(
textUtils.isPairedCharacter('a\uD835\uDF97b\uD835\uDF97c', 6)
).toBe(false)
expect(textUtils.isPairedCharacter('a\u2714\uFE0E', 0)).toBe(false)
expect(textUtils.isPairedCharacter('a\u2714\uFE0E', 1)).toBe(true)
@@ -46,8 +61,7 @@ describe('text utilities', () => {
expect(textUtils.isPairedCharacter('ae\u0301c', 2)).toBe(false)
expect(textUtils.isPairedCharacter('ae\u0301c', 3)).toBe(false)
expect(textUtils.isPairedCharacter('ae\u0301c', 4)).toBe(false)
})
)
}))
describe('.isDoubleWidthCharacter(character)', () =>
it('returns true when the character is either japanese, chinese or a full width form', () => {
@@ -60,8 +74,7 @@ describe('text utilities', () => {
expect(textUtils.isDoubleWidthCharacter('¢')).toBe(true)
expect(textUtils.isDoubleWidthCharacter('a')).toBe(false)
})
)
}))
describe('.isHalfWidthCharacter(character)', () =>
it('returns true when the character is an half width form', () => {
@@ -71,8 +84,7 @@ describe('text utilities', () => {
expect(textUtils.isHalfWidthCharacter('■')).toBe(true)
expect(textUtils.isHalfWidthCharacter('B')).toBe(false)
})
)
}))
describe('.isKoreanCharacter(character)', () =>
it('returns true when the character is a korean character', () => {
@@ -82,8 +94,7 @@ describe('text utilities', () => {
expect(textUtils.isKoreanCharacter('ㄼ')).toBe(true)
expect(textUtils.isKoreanCharacter('O')).toBe(false)
})
)
}))
describe('.isWrapBoundary(previousCharacter, character)', () =>
it('returns true when the character is CJK or when the previous character is a space/tab', () => {
@@ -105,6 +116,5 @@ describe('text utilities', () => {
expect(textUtils.isWrapBoundary(' ', 'h')).toBe(true)
expect(textUtils.isWrapBoundary('\t', 'h')).toBe(true)
expect(textUtils.isWrapBoundary('a', 'h')).toBe(false)
})
)
}))
})

View File

@@ -29,8 +29,7 @@ describe('atom.themes', function () {
it('gets all the loaded themes', function () {
const themes = atom.themes.getLoadedThemes()
expect(themes.length).toBeGreaterThan(2)
})
)
}))
describe('getActiveThemes', () =>
it('gets all the active themes', function () {
@@ -42,8 +41,7 @@ describe('atom.themes', function () {
const themes = atom.themes.getActiveThemes()
expect(themes).toHaveLength(names.length)
})
})
)
}))
})
describe('when the core.themes config value contains invalid entry', () =>
@@ -60,13 +58,19 @@ describe('atom.themes', function () {
'atom-dark-ui'
])
expect(atom.themes.getEnabledThemeNames()).toEqual(['atom-dark-ui', 'atom-light-ui'])
})
)
expect(atom.themes.getEnabledThemeNames()).toEqual([
'atom-dark-ui',
'atom-light-ui'
])
}))
describe('::getImportPaths()', function () {
it('returns the theme directories before the themes are loaded', function () {
atom.config.set('core.themes', ['theme-with-index-less', 'atom-dark-ui', 'atom-light-ui'])
atom.config.set('core.themes', [
'theme-with-index-less',
'atom-dark-ui',
'atom-light-ui'
])
const paths = atom.themes.getImportPaths()
@@ -85,7 +89,9 @@ describe('atom.themes', function () {
describe('when the core.themes config value changes', function () {
it('add/removes stylesheets to reflect the new config value', function () {
let didChangeActiveThemesHandler
atom.themes.onDidChangeActiveThemes(didChangeActiveThemesHandler = jasmine.createSpy())
atom.themes.onDidChangeActiveThemes(
(didChangeActiveThemesHandler = jasmine.createSpy())
)
spyOn(atom.styles, 'getUserStyleSheetPath').andCallFake(() => null)
waitsForPromise(() => atom.themes.activateThemes())
@@ -108,7 +114,11 @@ describe('atom.themes', function () {
runs(function () {
didChangeActiveThemesHandler.reset()
expect(document.querySelectorAll('style[priority="1"]')).toHaveLength(2)
expect(document.querySelector('style[priority="1"]').getAttribute('source-path')).toMatch(/atom-dark-ui/)
expect(
document
.querySelector('style[priority="1"]')
.getAttribute('source-path')
).toMatch(/atom-dark-ui/)
atom.config.set('core.themes', ['atom-light-ui', 'atom-dark-ui'])
})
@@ -117,8 +127,16 @@ describe('atom.themes', function () {
runs(function () {
didChangeActiveThemesHandler.reset()
expect(document.querySelectorAll('style[priority="1"]')).toHaveLength(2)
expect(document.querySelectorAll('style[priority="1"]')[0].getAttribute('source-path')).toMatch(/atom-dark-ui/)
expect(document.querySelectorAll('style[priority="1"]')[1].getAttribute('source-path')).toMatch(/atom-light-ui/)
expect(
document
.querySelectorAll('style[priority="1"]')[0]
.getAttribute('source-path')
).toMatch(/atom-dark-ui/)
expect(
document
.querySelectorAll('style[priority="1"]')[1]
.getAttribute('source-path')
).toMatch(/atom-light-ui/)
atom.config.set('core.themes', [])
})
@@ -128,7 +146,10 @@ describe('atom.themes', function () {
didChangeActiveThemesHandler.reset()
expect(document.querySelectorAll('style[priority="1"]')).toHaveLength(2)
// atom-dark-ui has a directory path, the syntax one doesn't
atom.config.set('core.themes', ['theme-with-index-less', 'atom-dark-ui'])
atom.config.set('core.themes', [
'theme-with-index-less',
'atom-dark-ui'
])
})
waitsFor(() => didChangeActiveThemesHandler.callCount === 1)
@@ -145,15 +166,22 @@ describe('atom.themes', function () {
atom.config.set('core.themes', ['atom-dark-ui', 'atom-dark-syntax'])
let didChangeActiveThemesHandler
atom.themes.onDidChangeActiveThemes(didChangeActiveThemesHandler = jasmine.createSpy())
atom.themes.onDidChangeActiveThemes(
(didChangeActiveThemesHandler = jasmine.createSpy())
)
waitsForPromise(() => atom.themes.activateThemes())
const workspaceElement = atom.workspace.getElement()
runs(function () {
expect(workspaceElement).toHaveClass('theme-atom-dark-ui')
atom.themes.onDidChangeActiveThemes(didChangeActiveThemesHandler = jasmine.createSpy())
atom.config.set('core.themes', ['theme-with-ui-variables', 'theme-with-syntax-variables'])
atom.themes.onDidChangeActiveThemes(
(didChangeActiveThemesHandler = jasmine.createSpy())
)
atom.config.set('core.themes', [
'theme-with-ui-variables',
'theme-with-syntax-variables'
])
})
waitsFor(() => didChangeActiveThemesHandler.callCount > 0)
@@ -161,7 +189,9 @@ describe('atom.themes', function () {
runs(function () {
// `theme-` twice as it prefixes the name with `theme-`
expect(workspaceElement).toHaveClass('theme-theme-with-ui-variables')
expect(workspaceElement).toHaveClass('theme-theme-with-syntax-variables')
expect(workspaceElement).toHaveClass(
'theme-theme-with-syntax-variables'
)
expect(workspaceElement).not.toHaveClass('theme-atom-dark-ui')
expect(workspaceElement).not.toHaveClass('theme-atom-dark-syntax')
})
@@ -171,11 +201,14 @@ describe('atom.themes', function () {
describe('when a theme fails to load', () =>
it('logs a warning', function () {
console.warn.reset()
atom.packages.activatePackage('a-theme-that-will-not-be-found').then(function () {}, function () {})
atom.packages
.activatePackage('a-theme-that-will-not-be-found')
.then(function () {}, function () {})
expect(console.warn.callCount).toBe(1)
expect(console.warn.argsForCall[0][0]).toContain("Could not resolve 'a-theme-that-will-not-be-found'")
})
)
expect(console.warn.argsForCall[0][0]).toContain(
"Could not resolve 'a-theme-that-will-not-be-found'"
)
}))
describe('::requireStylesheet(path)', function () {
beforeEach(() => jasmine.snapshotDeprecations())
@@ -184,38 +217,60 @@ describe('atom.themes', function () {
it('synchronously loads css at the given path and installs a style tag for it in the head', function () {
let styleElementAddedHandler
atom.styles.onDidAddStyleElement(styleElementAddedHandler = jasmine.createSpy('styleElementAddedHandler'))
atom.styles.onDidAddStyleElement(
(styleElementAddedHandler = jasmine.createSpy(
'styleElementAddedHandler'
))
)
const cssPath = getAbsolutePath(atom.project.getDirectories()[0], 'css.css')
const cssPath = getAbsolutePath(
atom.project.getDirectories()[0],
'css.css'
)
const lengthBefore = document.querySelectorAll('head style').length
atom.themes.requireStylesheet(cssPath)
expect(document.querySelectorAll('head style').length).toBe(lengthBefore + 1)
expect(document.querySelectorAll('head style').length).toBe(
lengthBefore + 1
)
expect(styleElementAddedHandler).toHaveBeenCalled()
const element = document.querySelector('head style[source-path*="css.css"]')
const element = document.querySelector(
'head style[source-path*="css.css"]'
)
expect(element.getAttribute('source-path')).toEqualPath(cssPath)
expect(element.textContent).toBe(fs.readFileSync(cssPath, 'utf8'))
// doesn't append twice
styleElementAddedHandler.reset()
atom.themes.requireStylesheet(cssPath)
expect(document.querySelectorAll('head style').length).toBe(lengthBefore + 1)
expect(document.querySelectorAll('head style').length).toBe(
lengthBefore + 1
)
expect(styleElementAddedHandler).not.toHaveBeenCalled()
document.querySelectorAll('head style[id*="css.css"]').forEach((styleElement) => {
styleElement.remove()
})
document
.querySelectorAll('head style[id*="css.css"]')
.forEach(styleElement => {
styleElement.remove()
})
})
it('synchronously loads and parses less files at the given path and installs a style tag for it in the head', function () {
const lessPath = getAbsolutePath(atom.project.getDirectories()[0], 'sample.less')
const lessPath = getAbsolutePath(
atom.project.getDirectories()[0],
'sample.less'
)
const lengthBefore = document.querySelectorAll('head style').length
atom.themes.requireStylesheet(lessPath)
expect(document.querySelectorAll('head style').length).toBe(lengthBefore + 1)
expect(document.querySelectorAll('head style').length).toBe(
lengthBefore + 1
)
const element = document.querySelector('head style[source-path*="sample.less"]')
const element = document.querySelector(
'head style[source-path*="sample.less"]'
)
expect(element.getAttribute('source-path')).toEqualPath(lessPath)
expect(element.textContent.toLowerCase()).toBe(`\
#header {
@@ -225,24 +280,37 @@ h2 {
color: #4d926f;
}
\
`
)
`)
// doesn't append twice
atom.themes.requireStylesheet(lessPath)
expect(document.querySelectorAll('head style').length).toBe(lengthBefore + 1)
document.querySelectorAll('head style[id*="sample.less"]').forEach((styleElement) => {
styleElement.remove()
})
expect(document.querySelectorAll('head style').length).toBe(
lengthBefore + 1
)
document
.querySelectorAll('head style[id*="sample.less"]')
.forEach(styleElement => {
styleElement.remove()
})
})
it('supports requiring css and less stylesheets without an explicit extension', function () {
atom.themes.requireStylesheet(path.join(__dirname, 'fixtures', 'css'))
expect(document.querySelector('head style[source-path*="css.css"]').getAttribute('source-path'))
.toEqualPath(getAbsolutePath(atom.project.getDirectories()[0], 'css.css'))
expect(
document
.querySelector('head style[source-path*="css.css"]')
.getAttribute('source-path')
).toEqualPath(
getAbsolutePath(atom.project.getDirectories()[0], 'css.css')
)
atom.themes.requireStylesheet(path.join(__dirname, 'fixtures', 'sample'))
expect(document.querySelector('head style[source-path*="sample.less"]').getAttribute('source-path'))
.toEqualPath(getAbsolutePath(atom.project.getDirectories()[0], 'sample.less'))
expect(
document
.querySelector('head style[source-path*="sample.less"]')
.getAttribute('source-path')
).toEqualPath(
getAbsolutePath(atom.project.getDirectories()[0], 'sample.less')
)
document.querySelector('head style[source-path*="css.css"]').remove()
document.querySelector('head style[source-path*="sample.less"]').remove()
@@ -256,7 +324,11 @@ h2 {
expect(getComputedStyle(document.body).fontWeight).toBe('bold')
let styleElementRemovedHandler
atom.styles.onDidRemoveStyleElement(styleElementRemovedHandler = jasmine.createSpy('styleElementRemovedHandler'))
atom.styles.onDidRemoveStyleElement(
(styleElementRemovedHandler = jasmine.createSpy(
'styleElementRemovedHandler'
))
)
disposable.dispose()
@@ -277,46 +349,74 @@ h2 {
it("loads the correct values from the theme's ui-variables file", function () {
let didChangeActiveThemesHandler
atom.themes.onDidChangeActiveThemes(didChangeActiveThemesHandler = jasmine.createSpy())
atom.config.set('core.themes', ['theme-with-ui-variables', 'theme-with-syntax-variables'])
atom.themes.onDidChangeActiveThemes(
(didChangeActiveThemesHandler = jasmine.createSpy())
)
atom.config.set('core.themes', [
'theme-with-ui-variables',
'theme-with-syntax-variables'
])
waitsFor(() => didChangeActiveThemesHandler.callCount > 0)
runs(function () {
// an override loaded in the base css
expect(getComputedStyle(atom.workspace.getElement())['background-color']).toBe('rgb(0, 0, 255)')
expect(
getComputedStyle(atom.workspace.getElement())['background-color']
).toBe('rgb(0, 0, 255)')
// from within the theme itself
expect(getComputedStyle(document.querySelector('atom-text-editor')).paddingTop).toBe('150px')
expect(getComputedStyle(document.querySelector('atom-text-editor')).paddingRight).toBe('150px')
expect(getComputedStyle(document.querySelector('atom-text-editor')).paddingBottom).toBe('150px')
expect(
getComputedStyle(document.querySelector('atom-text-editor'))
.paddingTop
).toBe('150px')
expect(
getComputedStyle(document.querySelector('atom-text-editor'))
.paddingRight
).toBe('150px')
expect(
getComputedStyle(document.querySelector('atom-text-editor'))
.paddingBottom
).toBe('150px')
})
})
describe('when there is a theme with incomplete variables', () =>
it('loads the correct values from the fallback ui-variables', function () {
let didChangeActiveThemesHandler
atom.themes.onDidChangeActiveThemes(didChangeActiveThemesHandler = jasmine.createSpy())
atom.config.set('core.themes', ['theme-with-incomplete-ui-variables', 'theme-with-syntax-variables'])
atom.themes.onDidChangeActiveThemes(
(didChangeActiveThemesHandler = jasmine.createSpy())
)
atom.config.set('core.themes', [
'theme-with-incomplete-ui-variables',
'theme-with-syntax-variables'
])
waitsFor(() => didChangeActiveThemesHandler.callCount > 0)
runs(function () {
// an override loaded in the base css
expect(getComputedStyle(atom.workspace.getElement())['background-color']).toBe('rgb(0, 0, 255)')
expect(
getComputedStyle(atom.workspace.getElement())['background-color']
).toBe('rgb(0, 0, 255)')
// from within the theme itself
expect(getComputedStyle(document.querySelector('atom-text-editor')).backgroundColor).toBe('rgb(0, 152, 255)')
expect(
getComputedStyle(document.querySelector('atom-text-editor'))
.backgroundColor
).toBe('rgb(0, 152, 255)')
})
})
)
}))
})
describe('user stylesheet', function () {
let userStylesheetPath
beforeEach(function () {
userStylesheetPath = path.join(temp.mkdirSync('atom'), 'styles.less')
fs.writeFileSync(userStylesheetPath, 'body {border-style: dotted !important;}')
fs.writeFileSync(
userStylesheetPath,
'body {border-style: dotted !important;}'
)
spyOn(atom.styles, 'getUserStyleSheetPath').andReturn(userStylesheetPath)
})
@@ -331,8 +431,16 @@ h2 {
waitsForPromise(() => atom.themes.activateThemes())
runs(function () {
atom.styles.onDidRemoveStyleElement(styleElementRemovedHandler = jasmine.createSpy('styleElementRemovedHandler'))
atom.styles.onDidAddStyleElement(styleElementAddedHandler = jasmine.createSpy('styleElementAddedHandler'))
atom.styles.onDidRemoveStyleElement(
(styleElementRemovedHandler = jasmine.createSpy(
'styleElementRemovedHandler'
))
)
atom.styles.onDidAddStyleElement(
(styleElementAddedHandler = jasmine.createSpy(
'styleElementAddedHandler'
))
)
spyOn(atom.themes, 'loadUserStylesheet').andCallThrough()
@@ -346,10 +454,14 @@ h2 {
expect(getComputedStyle(document.body).borderStyle).toBe('dashed')
expect(styleElementRemovedHandler).toHaveBeenCalled()
expect(styleElementRemovedHandler.argsForCall[0][0].textContent).toContain('dotted')
expect(
styleElementRemovedHandler.argsForCall[0][0].textContent
).toContain('dotted')
expect(styleElementAddedHandler).toHaveBeenCalled()
expect(styleElementAddedHandler.argsForCall[0][0].textContent).toContain('dashed')
expect(
styleElementAddedHandler.argsForCall[0][0].textContent
).toContain('dashed')
styleElementRemovedHandler.reset()
fs.removeSync(userStylesheetPath)
@@ -359,7 +471,9 @@ h2 {
runs(function () {
expect(styleElementRemovedHandler).toHaveBeenCalled()
expect(styleElementRemovedHandler.argsForCall[0][0].textContent).toContain('dashed')
expect(
styleElementRemovedHandler.argsForCall[0][0].textContent
).toContain('dashed')
expect(getComputedStyle(document.body).borderStyle).toBe('none')
})
})
@@ -372,7 +486,9 @@ h2 {
spyOn(atom.themes.lessCache, 'cssForFile').andCallFake(function () {
throw new Error('EACCES permission denied "styles.less"')
})
atom.notifications.onDidAddNotification(addErrorHandler = jasmine.createSpy())
atom.notifications.onDidAddNotification(
(addErrorHandler = jasmine.createSpy())
)
})
it('creates an error notification and does not add the stylesheet', function () {
@@ -381,21 +497,25 @@ h2 {
const note = addErrorHandler.mostRecentCall.args[0]
expect(note.getType()).toBe('error')
expect(note.getMessage()).toContain('Error loading')
expect(atom.styles.styleElementsBySourcePath[atom.styles.getUserStyleSheetPath()]).toBeUndefined()
expect(
atom.styles.styleElementsBySourcePath[atom.styles.getUserStyleSheetPath()]
).toBeUndefined()
})
})
describe('when there is an error watching the user stylesheet', function () {
let addErrorHandler = null
beforeEach(function () {
const {File} = require('pathwatcher')
const { File } = require('pathwatcher')
spyOn(File.prototype, 'on').andCallFake(function (event) {
if (event.indexOf('contents-changed') > -1) {
throw new Error('Unable to watch path')
}
})
spyOn(atom.themes, 'loadStylesheet').andReturn('')
atom.notifications.onDidAddNotification(addErrorHandler = jasmine.createSpy())
atom.notifications.onDidAddNotification(
(addErrorHandler = jasmine.createSpy())
)
})
it('creates an error notification', function () {
@@ -410,16 +530,25 @@ h2 {
it("adds a notification when a theme's stylesheet is invalid", function () {
const addErrorHandler = jasmine.createSpy()
atom.notifications.onDidAddNotification(addErrorHandler)
expect(() => atom.packages.activatePackage('theme-with-invalid-styles').then(function () {}, function () {})).not.toThrow()
expect(() =>
atom.packages
.activatePackage('theme-with-invalid-styles')
.then(function () {}, function () {})
).not.toThrow()
expect(addErrorHandler.callCount).toBe(2)
expect(addErrorHandler.argsForCall[1][0].message).toContain('Failed to activate the theme-with-invalid-styles theme')
expect(addErrorHandler.argsForCall[1][0].message).toContain(
'Failed to activate the theme-with-invalid-styles theme'
)
})
})
describe('when a non-existent theme is present in the config', function () {
beforeEach(function () {
console.warn.reset()
atom.config.set('core.themes', ['non-existent-dark-ui', 'non-existent-dark-syntax'])
atom.config.set('core.themes', [
'non-existent-dark-ui',
'non-existent-dark-syntax'
])
waitsForPromise(() => atom.themes.activateThemes())
})
@@ -451,7 +580,10 @@ h2 {
describe('when the enabled UI and syntax themes are not bundled with Atom', function () {
beforeEach(function () {
atom.config.set('core.themes', ['installed-dark-ui', 'installed-dark-syntax'])
atom.config.set('core.themes', [
'installed-dark-ui',
'installed-dark-syntax'
])
waitsForPromise(() => atom.themes.activateThemes())
})
@@ -466,7 +598,10 @@ h2 {
describe('when the enabled UI theme is not bundled with Atom', function () {
beforeEach(function () {
atom.config.set('core.themes', ['installed-dark-ui', 'atom-light-syntax'])
atom.config.set('core.themes', [
'installed-dark-ui',
'atom-light-syntax'
])
waitsForPromise(() => atom.themes.activateThemes())
})
@@ -481,7 +616,10 @@ h2 {
describe('when the enabled syntax theme is not bundled with Atom', function () {
beforeEach(function () {
atom.config.set('core.themes', ['atom-light-ui', 'installed-dark-syntax'])
atom.config.set('core.themes', [
'atom-light-ui',
'installed-dark-syntax'
])
waitsForPromise(() => atom.themes.activateThemes())
})

View File

@@ -8,20 +8,28 @@ describe('TitleBar', () => {
themes: atom.themes,
applicationDelegate: atom.applicationDelegate
})
expect(titleBar.element.querySelector('.title').textContent).toBe(document.title)
expect(titleBar.element.querySelector('.title').textContent).toBe(
document.title
)
const paneItem = new FakePaneItem('Title 1')
atom.workspace.getActivePane().activateItem(paneItem)
expect(document.title).toMatch('Title 1')
expect(titleBar.element.querySelector('.title').textContent).toBe(document.title)
expect(titleBar.element.querySelector('.title').textContent).toBe(
document.title
)
paneItem.setTitle('Title 2')
expect(document.title).toMatch('Title 2')
expect(titleBar.element.querySelector('.title').textContent).toBe(document.title)
expect(titleBar.element.querySelector('.title').textContent).toBe(
document.title
)
atom.project.setPaths([temp.mkdirSync('project-1')])
expect(document.title).toMatch('project-1')
expect(titleBar.element.querySelector('.title').textContent).toBe(document.title)
expect(titleBar.element.querySelector('.title').textContent).toBe(
document.title
)
})
it('can update the sheet offset for the current window based on its height', () => {
@@ -46,7 +54,9 @@ class FakePaneItem {
onDidChangeTitle (callback) {
this.didChangeTitleCallback = callback
return {
dispose: () => { this.didChangeTitleCallback = null }
dispose: () => {
this.didChangeTitleCallback = null
}
}
}

View File

@@ -1,4 +1,4 @@
const {CompositeDisposable} = require('atom')
const { CompositeDisposable } = require('atom')
const TooltipManager = require('../src/tooltip-manager')
const Tooltip = require('../src/tooltip')
const _ = require('underscore-plus')
@@ -18,25 +18,30 @@ describe('TooltipManager', () => {
}
beforeEach(function () {
manager = new TooltipManager({keymapManager: atom.keymaps, viewRegistry: atom.views})
manager = new TooltipManager({
keymapManager: atom.keymaps,
viewRegistry: atom.views
})
element = createElement('foo')
})
describe('::add(target, options)', () => {
describe("when the trigger is 'hover' (the default)", () => {
it('creates a tooltip when hovering over the target element', () => {
manager.add(element, {title: 'Title'})
hover(element, () => expect(document.body.querySelector('.tooltip')).toHaveText('Title'))
manager.add(element, { title: 'Title' })
hover(element, () =>
expect(document.body.querySelector('.tooltip')).toHaveText('Title')
)
})
it('displays tooltips immediately when hovering over new elements once a tooltip has been displayed once', () => {
const disposables = new CompositeDisposable()
const element1 = createElement('foo')
disposables.add(manager.add(element1, {title: 'Title'}))
disposables.add(manager.add(element1, { title: 'Title' }))
const element2 = createElement('bar')
disposables.add(manager.add(element2, {title: 'Title'}))
disposables.add(manager.add(element2, { title: 'Title' }))
const element3 = createElement('baz')
disposables.add(manager.add(element3, {title: 'Title'}))
disposables.add(manager.add(element3, { title: 'Title' }))
hover(element1, () => {})
expect(document.body.querySelector('.tooltip')).toBeNull()
@@ -57,12 +62,17 @@ describe('TooltipManager', () => {
})
it('hides the tooltip on keydown events', () => {
const disposable = manager.add(element, { title: 'Title', trigger: 'hover' })
const disposable = manager.add(element, {
title: 'Title',
trigger: 'hover'
})
hover(element, function () {
expect(document.body.querySelector('.tooltip')).not.toBeNull()
window.dispatchEvent(new CustomEvent('keydown', {
bubbles: true
}))
window.dispatchEvent(
new CustomEvent('keydown', {
bubbles: true
})
)
expect(document.body.querySelector('.tooltip')).toBeNull()
disposable.dispose()
})
@@ -71,16 +81,18 @@ describe('TooltipManager', () => {
describe("when the trigger is 'manual'", () =>
it('creates a tooltip immediately and only hides it on dispose', () => {
const disposable = manager.add(element, {title: 'Title', trigger: 'manual'})
const disposable = manager.add(element, {
title: 'Title',
trigger: 'manual'
})
expect(document.body.querySelector('.tooltip')).toHaveText('Title')
disposable.dispose()
expect(document.body.querySelector('.tooltip')).toBeNull()
})
)
}))
describe("when the trigger is 'click'", () =>
it('shows and hides the tooltip when the target element is clicked', () => {
manager.add(element, {title: 'Title', trigger: 'click'})
manager.add(element, { title: 'Title', trigger: 'click' })
expect(document.body.querySelector('.tooltip')).toBeNull()
element.click()
expect(document.body.querySelector('.tooltip')).not.toBeNull()
@@ -102,16 +114,17 @@ describe('TooltipManager', () => {
expect(document.body.querySelector('.tooltip')).not.toBeNull()
element.click()
expect(document.body.querySelector('.tooltip')).toBeNull()
})
)
}))
it('does not hide the tooltip on keyboard input', () => {
manager.add(element, {title: 'Title', trigger: 'click'})
manager.add(element, { title: 'Title', trigger: 'click' })
element.click()
expect(document.body.querySelector('.tooltip')).not.toBeNull()
window.dispatchEvent(new CustomEvent('keydown', {
bubbles: true
}))
window.dispatchEvent(
new CustomEvent('keydown', {
bubbles: true
})
)
expect(document.body.querySelector('.tooltip')).not.toBeNull()
// click again to hide the tooltip because otherwise state leaks
// into other tests.
@@ -120,13 +133,21 @@ describe('TooltipManager', () => {
it('allows a custom item to be specified for the content of the tooltip', () => {
const tooltipElement = document.createElement('div')
manager.add(element, {item: {element: tooltipElement}})
hover(element, () => expect(tooltipElement.closest('.tooltip')).not.toBeNull())
manager.add(element, { item: { element: tooltipElement } })
hover(element, () =>
expect(tooltipElement.closest('.tooltip')).not.toBeNull()
)
})
it('allows a custom class to be specified for the tooltip', () => {
manager.add(element, {title: 'Title', class: 'custom-tooltip-class'})
hover(element, () => expect(document.body.querySelector('.tooltip').classList.contains('custom-tooltip-class')).toBe(true))
manager.add(element, { title: 'Title', class: 'custom-tooltip-class' })
hover(element, () =>
expect(
document.body
.querySelector('.tooltip')
.classList.contains('custom-tooltip-class')
).toBe(true)
)
})
it('allows jQuery elements to be passed as the target', () => {
@@ -139,63 +160,71 @@ describe('TooltipManager', () => {
length: 2,
jquery: 'any-version'
}
const disposable = manager.add(fakeJqueryWrapper, {title: 'Title'})
const disposable = manager.add(fakeJqueryWrapper, { title: 'Title' })
hover(element, () => expect(document.body.querySelector('.tooltip')).toHaveText('Title'))
hover(element, () =>
expect(document.body.querySelector('.tooltip')).toHaveText('Title')
)
expect(document.body.querySelector('.tooltip')).toBeNull()
hover(element2, () => expect(document.body.querySelector('.tooltip')).toHaveText('Title'))
hover(element2, () =>
expect(document.body.querySelector('.tooltip')).toHaveText('Title')
)
expect(document.body.querySelector('.tooltip')).toBeNull()
disposable.dispose()
hover(element, () => expect(document.body.querySelector('.tooltip')).toBeNull())
hover(element2, () => expect(document.body.querySelector('.tooltip')).toBeNull())
hover(element, () =>
expect(document.body.querySelector('.tooltip')).toBeNull()
)
hover(element2, () =>
expect(document.body.querySelector('.tooltip')).toBeNull()
)
})
describe('when a keyBindingCommand is specified', () => {
describe('when a title is specified', () =>
it('appends the key binding corresponding to the command to the title', () => {
atom.keymaps.add('test', {
'.foo': { 'ctrl-x ctrl-y': 'test-command'
},
'.bar': { 'ctrl-x ctrl-z': 'test-command'
}
}
)
'.foo': { 'ctrl-x ctrl-y': 'test-command' },
'.bar': { 'ctrl-x ctrl-z': 'test-command' }
})
manager.add(element, {title: 'Title', keyBindingCommand: 'test-command'})
manager.add(element, {
title: 'Title',
keyBindingCommand: 'test-command'
})
hover(element, function () {
const tooltipElement = document.body.querySelector('.tooltip')
expect(tooltipElement).toHaveText(`Title ${ctrlX} ${ctrlY}`)
})
})
)
}))
describe('when no title is specified', () =>
it('shows the key binding corresponding to the command alone', () => {
atom.keymaps.add('test', {'.foo': {'ctrl-x ctrl-y': 'test-command'}})
atom.keymaps.add('test', {
'.foo': { 'ctrl-x ctrl-y': 'test-command' }
})
manager.add(element, {keyBindingCommand: 'test-command'})
manager.add(element, { keyBindingCommand: 'test-command' })
hover(element, function () {
const tooltipElement = document.body.querySelector('.tooltip')
expect(tooltipElement).toHaveText(`${ctrlX} ${ctrlY}`)
})
})
)
}))
describe('when a keyBindingTarget is specified', () => {
it('looks up the key binding relative to the target', () => {
atom.keymaps.add('test', {
'.bar': { 'ctrl-x ctrl-z': 'test-command'
},
'.foo': { 'ctrl-x ctrl-y': 'test-command'
}
}
)
'.bar': { 'ctrl-x ctrl-z': 'test-command' },
'.foo': { 'ctrl-x ctrl-y': 'test-command' }
})
manager.add(element, {keyBindingCommand: 'test-command', keyBindingTarget: element})
manager.add(element, {
keyBindingCommand: 'test-command',
keyBindingTarget: element
})
hover(element, function () {
const tooltipElement = document.body.querySelector('.tooltip')
@@ -204,7 +233,11 @@ describe('TooltipManager', () => {
})
it('does not display the keybinding if there is nothing mapped to the specified keyBindingCommand', () => {
manager.add(element, {title: 'A Title', keyBindingCommand: 'test-command', keyBindingTarget: element})
manager.add(element, {
title: 'A Title',
keyBindingCommand: 'test-command',
keyBindingTarget: element
})
hover(element, function () {
const tooltipElement = document.body.querySelector('.tooltip')
@@ -216,34 +249,36 @@ describe('TooltipManager', () => {
describe('when .dispose() is called on the returned disposable', () =>
it('no longer displays the tooltip on hover', () => {
const disposable = manager.add(element, {title: 'Title'})
const disposable = manager.add(element, { title: 'Title' })
hover(element, () => expect(document.body.querySelector('.tooltip')).toHaveText('Title'))
hover(element, () =>
expect(document.body.querySelector('.tooltip')).toHaveText('Title')
)
disposable.dispose()
hover(element, () => expect(document.body.querySelector('.tooltip')).toBeNull())
})
)
hover(element, () =>
expect(document.body.querySelector('.tooltip')).toBeNull()
)
}))
describe('when the window is resized', () =>
it('hides the tooltips', () => {
const disposable = manager.add(element, {title: 'Title'})
const disposable = manager.add(element, { title: 'Title' })
hover(element, function () {
expect(document.body.querySelector('.tooltip')).not.toBeNull()
window.dispatchEvent(new CustomEvent('resize'))
expect(document.body.querySelector('.tooltip')).toBeNull()
disposable.dispose()
})
})
)
}))
describe('findTooltips', () => {
it('adds and remove tooltips correctly', () => {
expect(manager.findTooltips(element).length).toBe(0)
const disposable1 = manager.add(element, {title: 'elem1'})
const disposable1 = manager.add(element, { title: 'elem1' })
expect(manager.findTooltips(element).length).toBe(1)
const disposable2 = manager.add(element, {title: 'elem2'})
const disposable2 = manager.add(element, { title: 'elem2' })
expect(manager.findTooltips(element).length).toBe(2)
disposable1.dispose()
expect(manager.findTooltips(element).length).toBe(1)
@@ -252,7 +287,7 @@ describe('TooltipManager', () => {
})
it('lets us hide tooltips programmatically', () => {
const disposable = manager.add(element, {title: 'Title'})
const disposable = manager.add(element, { title: 'Title' })
hover(element, function () {
expect(document.body.querySelector('.tooltip')).not.toBeNull()
manager.findTooltips(element)[0].hide()
@@ -272,11 +307,11 @@ function createElement (className) {
}
function mouseEnter (element) {
element.dispatchEvent(new CustomEvent('mouseenter', {bubbles: false}))
element.dispatchEvent(new CustomEvent('mouseover', {bubbles: true}))
element.dispatchEvent(new CustomEvent('mouseenter', { bubbles: false }))
element.dispatchEvent(new CustomEvent('mouseover', { bubbles: true }))
}
function mouseLeave (element) {
element.dispatchEvent(new CustomEvent('mouseleave', {bubbles: false}))
element.dispatchEvent(new CustomEvent('mouseout', {bubbles: true}))
element.dispatchEvent(new CustomEvent('mouseleave', { bubbles: false }))
element.dispatchEvent(new CustomEvent('mouseout', { bubbles: true }))
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,12 +1,14 @@
/** @babel */
/* eslint-env jasmine */
import {it, fit, ffit, fffit, beforeEach, afterEach} from './async-spec-helpers'
import { it, beforeEach, afterEach } from './async-spec-helpers'
import path from 'path'
import childProcess from 'child_process'
import {updateProcessEnv, shouldGetEnvFromShell} from '../src/update-process-env'
import {
updateProcessEnv,
shouldGetEnvFromShell
} from '../src/update-process-env'
import dedent from 'dedent'
import {EventEmitter} from 'events'
import mockSpawn from 'mock-spawn'
const temp = require('temp').track()
@@ -46,7 +48,13 @@ describe('updateProcessEnv(launchEnv)', function () {
const initialProcessEnv = process.env
await updateProcessEnv({ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT: 'true', PWD: '/the/dir', TERM: 'xterm-something', KEY1: 'value1', KEY2: 'value2'})
await updateProcessEnv({
ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT: 'true',
PWD: '/the/dir',
TERM: 'xterm-something',
KEY1: 'value1',
KEY2: 'value2'
})
expect(process.env).toEqual({
ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT: 'true',
PWD: '/the/dir',
@@ -74,7 +82,12 @@ describe('updateProcessEnv(launchEnv)', function () {
const initialProcessEnv = process.env
await updateProcessEnv({ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT: 'true', PROMPT: '$P$G', KEY1: 'value1', KEY2: 'value2'})
await updateProcessEnv({
ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT: 'true',
PROMPT: '$P$G',
KEY1: 'value1',
KEY2: 'value2'
})
expect(process.env).toEqual({
ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT: 'true',
PROMPT: '$P$G',
@@ -103,13 +116,15 @@ describe('updateProcessEnv(launchEnv)', function () {
await updateProcessEnv({
ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT: 'true',
PSModulePath: 'C:\\Program Files\\WindowsPowerShell\\Modules;C:\\WINDOWS\\system32\\WindowsPowerShell\\v1.0\\Modules\\',
PSModulePath:
'C:\\Program Files\\WindowsPowerShell\\Modules;C:\\WINDOWS\\system32\\WindowsPowerShell\\v1.0\\Modules\\',
KEY1: 'value1',
KEY2: 'value2'
})
expect(process.env).toEqual({
ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT: 'true',
PSModulePath: 'C:\\Program Files\\WindowsPowerShell\\Modules;C:\\WINDOWS\\system32\\WindowsPowerShell\\v1.0\\Modules\\',
PSModulePath:
'C:\\Program Files\\WindowsPowerShell\\Modules;C:\\WINDOWS\\system32\\WindowsPowerShell\\v1.0\\Modules\\',
KEY1: 'value1',
KEY2: 'value2',
NODE_ENV: 'the-node-env',
@@ -133,7 +148,10 @@ describe('updateProcessEnv(launchEnv)', function () {
ATOM_HOME: '/the/atom/home'
}
await updateProcessEnv({ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT: 'true', PWD: '/the/dir'})
await updateProcessEnv({
ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT: 'true',
PWD: '/the/dir'
})
expect(process.env).toEqual({
PWD: '/the/dir',
ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT: 'true',
@@ -142,7 +160,11 @@ describe('updateProcessEnv(launchEnv)', function () {
ATOM_HOME: '/the/atom/home'
})
await updateProcessEnv({ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT: 'true', PWD: '/the/dir', ATOM_HOME: path.join(newAtomHomePath, 'non-existent')})
await updateProcessEnv({
ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT: 'true',
PWD: '/the/dir',
ATOM_HOME: path.join(newAtomHomePath, 'non-existent')
})
expect(process.env).toEqual({
ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT: 'true',
PWD: '/the/dir',
@@ -151,7 +173,11 @@ describe('updateProcessEnv(launchEnv)', function () {
ATOM_HOME: '/the/atom/home'
})
await updateProcessEnv({ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT: 'true', PWD: '/the/dir', ATOM_HOME: newAtomHomePath})
await updateProcessEnv({
ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT: 'true',
PWD: '/the/dir',
ATOM_HOME: newAtomHomePath
})
expect(process.env).toEqual({
ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT: 'true',
PWD: '/the/dir',
@@ -169,7 +195,13 @@ describe('updateProcessEnv(launchEnv)', function () {
ATOM_HOME: '/the/atom/home'
}
await updateProcessEnv({ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT: 'true', PWD: '/the/dir', NODE_ENV: 'the-node-env', NODE_PATH: '/the/node/path', ATOM_HOME: '/the/atom/home'})
await updateProcessEnv({
ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT: 'true',
PWD: '/the/dir',
NODE_ENV: 'the-node-env',
NODE_PATH: '/the/node/path',
ATOM_HOME: '/the/atom/home'
})
expect(process.env).toEqual({
ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT: 'true',
PWD: '/the/dir',
@@ -178,7 +210,12 @@ describe('updateProcessEnv(launchEnv)', function () {
ATOM_HOME: '/the/atom/home'
})
await updateProcessEnv({PWD: '/the/dir', NODE_ENV: 'the-node-env', NODE_PATH: '/the/node/path', ATOM_HOME: '/the/atom/home'})
await updateProcessEnv({
PWD: '/the/dir',
NODE_ENV: 'the-node-env',
NODE_PATH: '/the/node/path',
ATOM_HOME: '/the/atom/home'
})
expect(process.env).toEqual({
ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT: 'true',
PWD: '/the/dir',
@@ -215,16 +252,21 @@ describe('updateProcessEnv(launchEnv)', function () {
describe('when the launch environment does not come from a shell', function () {
describe('on macOS', function () {
it('updates process.env to match the environment in the user\'s login shell', async function () {
it("updates process.env to match the environment in the user's login shell", async function () {
if (process.platform === 'win32') return // TestsThatFailOnWin32
process.platform = 'darwin'
process.env.SHELL = '/my/custom/bash'
spawn.setDefault(spawn.simple(0, dedent`
spawn.setDefault(
spawn.simple(
0,
dedent`
FOO=BAR=BAZ=QUUX
TERM=xterm-something
PATH=/usr/bin:/bin:/usr/sbin:/sbin:/crazy/path
`))
`
)
)
await updateProcessEnv(process.env)
expect(spawn.calls.length).toBe(1)
expect(spawn.calls[0].command).toBe('/my/custom/bash')
@@ -241,16 +283,21 @@ describe('updateProcessEnv(launchEnv)', function () {
})
describe('on linux', function () {
it('updates process.env to match the environment in the user\'s login shell', async function () {
it("updates process.env to match the environment in the user's login shell", async function () {
if (process.platform === 'win32') return // TestsThatFailOnWin32
process.platform = 'linux'
process.env.SHELL = '/my/custom/bash'
spawn.setDefault(spawn.simple(0, dedent`
spawn.setDefault(
spawn.simple(
0,
dedent`
FOO=BAR=BAZ=QUUX
TERM=xterm-something
PATH=/usr/bin:/bin:/usr/sbin:/sbin:/crazy/path
`))
`
)
)
await updateProcessEnv(process.env)
expect(spawn.calls.length).toBe(1)
expect(spawn.calls[0].command).toBe('/my/custom/bash')
@@ -270,11 +317,11 @@ describe('updateProcessEnv(launchEnv)', function () {
it('does not update process.env', async function () {
process.platform = 'win32'
spyOn(childProcess, 'spawn')
process.env = {FOO: 'bar'}
process.env = { FOO: 'bar' }
await updateProcessEnv(process.env)
expect(childProcess.spawn).not.toHaveBeenCalled()
expect(process.env).toEqual({FOO: 'bar'})
expect(process.env).toEqual({ FOO: 'bar' })
})
})
@@ -283,30 +330,52 @@ describe('updateProcessEnv(launchEnv)', function () {
if (process.platform === 'win32') return // TestsThatFailOnWin32
process.platform = 'darwin'
expect(shouldGetEnvFromShell({SHELL: '/bin/sh'})).toBe(true)
expect(shouldGetEnvFromShell({SHELL: '/usr/local/bin/sh'})).toBe(true)
expect(shouldGetEnvFromShell({SHELL: '/bin/bash'})).toBe(true)
expect(shouldGetEnvFromShell({SHELL: '/usr/local/bin/bash'})).toBe(true)
expect(shouldGetEnvFromShell({SHELL: '/bin/zsh'})).toBe(true)
expect(shouldGetEnvFromShell({SHELL: '/usr/local/bin/zsh'})).toBe(true)
expect(shouldGetEnvFromShell({SHELL: '/bin/fish'})).toBe(true)
expect(shouldGetEnvFromShell({SHELL: '/usr/local/bin/fish'})).toBe(true)
expect(shouldGetEnvFromShell({ SHELL: '/bin/sh' })).toBe(true)
expect(shouldGetEnvFromShell({ SHELL: '/usr/local/bin/sh' })).toBe(true)
expect(shouldGetEnvFromShell({ SHELL: '/bin/bash' })).toBe(true)
expect(shouldGetEnvFromShell({ SHELL: '/usr/local/bin/bash' })).toBe(
true
)
expect(shouldGetEnvFromShell({ SHELL: '/bin/zsh' })).toBe(true)
expect(shouldGetEnvFromShell({ SHELL: '/usr/local/bin/zsh' })).toBe(
true
)
expect(shouldGetEnvFromShell({ SHELL: '/bin/fish' })).toBe(true)
expect(shouldGetEnvFromShell({ SHELL: '/usr/local/bin/fish' })).toBe(
true
)
process.platform = 'linux'
expect(shouldGetEnvFromShell({SHELL: '/bin/sh'})).toBe(true)
expect(shouldGetEnvFromShell({SHELL: '/usr/local/bin/sh'})).toBe(true)
expect(shouldGetEnvFromShell({SHELL: '/bin/bash'})).toBe(true)
expect(shouldGetEnvFromShell({SHELL: '/usr/local/bin/bash'})).toBe(true)
expect(shouldGetEnvFromShell({SHELL: '/bin/zsh'})).toBe(true)
expect(shouldGetEnvFromShell({SHELL: '/usr/local/bin/zsh'})).toBe(true)
expect(shouldGetEnvFromShell({SHELL: '/bin/fish'})).toBe(true)
expect(shouldGetEnvFromShell({SHELL: '/usr/local/bin/fish'})).toBe(true)
expect(shouldGetEnvFromShell({ SHELL: '/bin/sh' })).toBe(true)
expect(shouldGetEnvFromShell({ SHELL: '/usr/local/bin/sh' })).toBe(true)
expect(shouldGetEnvFromShell({ SHELL: '/bin/bash' })).toBe(true)
expect(shouldGetEnvFromShell({ SHELL: '/usr/local/bin/bash' })).toBe(
true
)
expect(shouldGetEnvFromShell({ SHELL: '/bin/zsh' })).toBe(true)
expect(shouldGetEnvFromShell({ SHELL: '/usr/local/bin/zsh' })).toBe(
true
)
expect(shouldGetEnvFromShell({ SHELL: '/bin/fish' })).toBe(true)
expect(shouldGetEnvFromShell({ SHELL: '/usr/local/bin/fish' })).toBe(
true
)
})
it('returns false when the environment indicates that Atom was launched from a shell', function () {
process.platform = 'darwin'
expect(shouldGetEnvFromShell({ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT: 'true', SHELL: '/bin/sh'})).toBe(false)
expect(
shouldGetEnvFromShell({
ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT: 'true',
SHELL: '/bin/sh'
})
).toBe(false)
process.platform = 'linux'
expect(shouldGetEnvFromShell({ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT: 'true', SHELL: '/bin/sh'})).toBe(false)
expect(
shouldGetEnvFromShell({
ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT: 'true',
SHELL: '/bin/sh'
})
).toBe(false)
})
it('returns false when the shell is undefined or empty', function () {

View File

@@ -2,7 +2,7 @@
import url from 'url'
import {it} from './async-spec-helpers'
import { it } from './async-spec-helpers'
import URIHandlerRegistry from '../src/uri-handler-registry'
@@ -24,11 +24,17 @@ describe('URIHandlerRegistry', () => {
expect(otherPackageSpy).not.toHaveBeenCalled()
registry.handleURI('atom://test-package/path')
expect(testPackageSpy).toHaveBeenCalledWith(url.parse('atom://test-package/path', true), 'atom://test-package/path')
expect(testPackageSpy).toHaveBeenCalledWith(
url.parse('atom://test-package/path', true),
'atom://test-package/path'
)
expect(otherPackageSpy).not.toHaveBeenCalled()
registry.handleURI('atom://other-package/path')
expect(otherPackageSpy).toHaveBeenCalledWith(url.parse('atom://other-package/path', true), 'atom://other-package/path')
expect(otherPackageSpy).toHaveBeenCalledWith(
url.parse('atom://other-package/path', true),
'atom://other-package/path'
)
})
it('keeps track of the most recent URIs', () => {
@@ -50,9 +56,18 @@ describe('URIHandlerRegistry', () => {
uris.forEach(u => registry.handleURI(u))
expect(changeSpy.callCount).toBe(5)
expect(registry.getRecentlyHandledURIs()).toEqual(uris.map((u, idx) => {
return {id: idx + 1, uri: u, handled: !u.match(/fake/), host: url.parse(u).host}
}).reverse())
expect(registry.getRecentlyHandledURIs()).toEqual(
uris
.map((u, idx) => {
return {
id: idx + 1,
uri: u,
handled: !u.match(/fake/),
host: url.parse(u).host
}
})
.reverse()
)
registry.handleURI('atom://another/url')
expect(changeSpy.callCount).toBe(6)
@@ -63,7 +78,7 @@ describe('URIHandlerRegistry', () => {
})
it('refuses to handle bad URLs', () => {
[
;[
'atom:package/path',
'atom:8080://package/path',
'user:pass@atom://package/path',

View File

@@ -22,8 +22,7 @@ describe('ViewRegistry', () => {
it('returns the given DOM node', () => {
const node = document.createElement('div')
expect(registry.getView(node)).toBe(node)
})
)
}))
describe('when passed an object with an element property', () =>
it("returns the element property if it's an instance of HTMLElement", () => {
@@ -35,8 +34,7 @@ describe('ViewRegistry', () => {
const 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", () => {
@@ -51,8 +49,7 @@ describe('ViewRegistry', () => {
const 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", () =>
@@ -70,7 +67,7 @@ describe('ViewRegistry', () => {
const model = new TestModel()
registry.addViewProvider(TestModel, (model) =>
registry.addViewProvider(TestModel, model =>
new TestView().initialize(model)
)
@@ -82,12 +79,11 @@ describe('ViewRegistry', () => {
const view2 = registry.getView(subclassModel)
expect(view2 instanceof TestView).toBe(true)
expect(view2.model).toBe(subclassModel)
})
)
}))
describe('when a view provider is registered generically, and works with the object', () =>
it('constructs a view element and assigns the model on it', () => {
registry.addViewProvider((model) => {
registry.addViewProvider(model => {
if (model.a === 'b') {
const element = document.createElement('div')
element.className = 'test-element'
@@ -95,18 +91,16 @@ describe('ViewRegistry', () => {
}
})
const view = registry.getView({a: 'b'})
const view = registry.getView({ a: 'b' })
expect(view.className).toBe('test-element')
expect(() => registry.getView({a: 'c'})).toThrow()
})
)
expect(() => registry.getView({ a: 'c' })).toThrow()
}))
describe("when no view provider is registered for the object's constructor", () =>
it('throws an exception', () => {
expect(() => registry.getView({})).toThrow()
})
)
}))
})
})
@@ -120,22 +114,23 @@ describe('ViewRegistry', () => {
}
}
const disposable = registry.addViewProvider(TestModel, (model) =>
const disposable = registry.addViewProvider(TestModel, model =>
new TestView().initialize(model)
)
expect(registry.getView(new TestModel()) instanceof TestView).toBe(true)
disposable.dispose()
expect(() => registry.getView(new TestModel())).toThrow()
})
)
}))
describe('::updateDocument(fn) and ::readDocument(fn)', () => {
let frameRequests = null
beforeEach(() => {
frameRequests = []
spyOn(window, 'requestAnimationFrame').andCallFake(fn => frameRequests.push(fn))
spyOn(window, 'requestAnimationFrame').andCallFake(fn =>
frameRequests.push(fn)
)
})
it('performs all pending writes before all pending reads on the next animation frame', () => {
@@ -201,16 +196,19 @@ describe('ViewRegistry', () => {
let updateCalled = false
let readCalled = false
waitsFor('getNextUpdatePromise to resolve', (done) => {
waitsFor('getNextUpdatePromise to resolve', done => {
registry.getNextUpdatePromise().then(() => {
expect(updateCalled).toBe(true)
expect(readCalled).toBe(true)
done()
})
registry.updateDocument(() => { updateCalled = true })
registry.readDocument(() => { readCalled = true })
registry.updateDocument(() => {
updateCalled = true
})
registry.readDocument(() => {
readCalled = true
})
})
})
)
}))
})

View File

@@ -14,7 +14,10 @@ describe('WindowEventHandler', () => {
return loadSettings
})
atom.project.destroy()
windowEventHandler = new WindowEventHandler({atomEnvironment: atom, applicationDelegate: atom.applicationDelegate})
windowEventHandler = new WindowEventHandler({
atomEnvironment: atom,
applicationDelegate: atom.applicationDelegate
})
windowEventHandler.initialize(window, document)
})
@@ -25,45 +28,44 @@ describe('WindowEventHandler', () => {
describe('when the window is loaded', () =>
it("doesn't have .is-blurred on the body tag", () => {
if (process.platform === 'win32') { return } // Win32TestFailures - can not steal focus
if (process.platform === 'win32') {
return
} // Win32TestFailures - can not steal focus
expect(document.body.className).not.toMatch('is-blurred')
})
)
}))
describe('when the window is blurred', () => {
beforeEach(() => window.dispatchEvent(new CustomEvent('blur')))
afterEach(() => document.body.classList.remove('is-blurred'))
it('adds the .is-blurred class on the body', () => expect(document.body.className).toMatch('is-blurred'))
it('adds the .is-blurred class on the body', () =>
expect(document.body.className).toMatch('is-blurred'))
describe('when the window is focused again', () =>
it('removes the .is-blurred class from the body', () => {
window.dispatchEvent(new CustomEvent('focus'))
expect(document.body.className).not.toMatch('is-blurred')
})
)
}))
})
describe('resize event', () =>
it('calls storeWindowDimensions', () => {
spyOn(atom, 'storeWindowDimensions')
window.dispatchEvent(new CustomEvent('resize'))
expect(atom.storeWindowDimensions).toHaveBeenCalled()
})
)
}))
describe('window:close event', () =>
it('closes the window', () => {
spyOn(atom, 'close')
window.dispatchEvent(new CustomEvent('window:close'))
expect(atom.close).toHaveBeenCalled()
})
)
}))
describe('when a link is clicked', () => {
it('opens the http/https links in an external application', () => {
const {shell} = require('electron')
const { shell } = require('electron')
spyOn(shell, 'openExternal')
const link = document.createElement('a')
@@ -71,7 +73,11 @@ describe('WindowEventHandler', () => {
link.appendChild(linkChild)
link.href = 'http://github.com'
jasmine.attachToDOM(link)
const fakeEvent = {target: linkChild, currentTarget: link, preventDefault: () => {}}
const fakeEvent = {
target: linkChild,
currentTarget: link,
preventDefault: () => {}
}
windowEventHandler.handleLinkClick(fakeEvent)
expect(shell.openExternal).toHaveBeenCalled()
@@ -104,7 +110,11 @@ describe('WindowEventHandler', () => {
link.appendChild(linkChild)
link.href = 'atom://github.com'
jasmine.attachToDOM(link)
const fakeEvent = {target: linkChild, currentTarget: link, preventDefault: () => {}}
const fakeEvent = {
target: linkChild,
currentTarget: link,
preventDefault: () => {}
}
windowEventHandler.handleLinkClick(fakeEvent)
expect(uriHandler.handleURI).toHaveBeenCalled()
@@ -118,12 +128,13 @@ describe('WindowEventHandler', () => {
jasmine.attachToDOM(form)
let defaultPrevented = false
const event = new CustomEvent('submit', {bubbles: true})
event.preventDefault = () => { defaultPrevented = true }
const event = new CustomEvent('submit', { bubbles: true })
event.preventDefault = () => {
defaultPrevented = true
}
form.dispatchEvent(event)
expect(defaultPrevented).toBe(true)
})
)
}))
describe('core:focus-next and core:focus-previous', () => {
describe('when there is no currently focused element', () =>
@@ -138,14 +149,17 @@ describe('WindowEventHandler', () => {
const elements = wrapperDiv.firstChild
jasmine.attachToDOM(elements)
elements.dispatchEvent(new CustomEvent('core:focus-next', {bubbles: true}))
elements.dispatchEvent(
new CustomEvent('core:focus-next', { bubbles: true })
)
expect(document.activeElement.tabIndex).toBe(1)
document.body.focus()
elements.dispatchEvent(new CustomEvent('core:focus-previous', {bubbles: true}))
elements.dispatchEvent(
new CustomEvent('core:focus-previous', { bubbles: true })
)
expect(document.activeElement.tabIndex).toBe(2)
})
)
}))
describe('when a tabindex is set on the currently focused element', () =>
it('focuses the element with the next highest/lowest tabindex, skipping disabled elements', () => {
@@ -166,60 +180,85 @@ describe('WindowEventHandler', () => {
elements.querySelector('[tabindex="1"]').focus()
elements.dispatchEvent(new CustomEvent('core:focus-next', {bubbles: true}))
elements.dispatchEvent(
new CustomEvent('core:focus-next', { bubbles: true })
)
expect(document.activeElement.tabIndex).toBe(2)
elements.dispatchEvent(new CustomEvent('core:focus-next', {bubbles: true}))
elements.dispatchEvent(
new CustomEvent('core:focus-next', { bubbles: true })
)
expect(document.activeElement.tabIndex).toBe(3)
elements.dispatchEvent(new CustomEvent('core:focus-next', {bubbles: true}))
elements.dispatchEvent(
new CustomEvent('core:focus-next', { bubbles: true })
)
expect(document.activeElement.tabIndex).toBe(5)
elements.dispatchEvent(new CustomEvent('core:focus-next', {bubbles: true}))
elements.dispatchEvent(
new CustomEvent('core:focus-next', { bubbles: true })
)
expect(document.activeElement.tabIndex).toBe(7)
elements.dispatchEvent(new CustomEvent('core:focus-next', {bubbles: true}))
elements.dispatchEvent(
new CustomEvent('core:focus-next', { bubbles: true })
)
expect(document.activeElement.tabIndex).toBe(1)
elements.dispatchEvent(new CustomEvent('core:focus-previous', {bubbles: true}))
elements.dispatchEvent(
new CustomEvent('core:focus-previous', { bubbles: true })
)
expect(document.activeElement.tabIndex).toBe(7)
elements.dispatchEvent(new CustomEvent('core:focus-previous', {bubbles: true}))
elements.dispatchEvent(
new CustomEvent('core:focus-previous', { bubbles: true })
)
expect(document.activeElement.tabIndex).toBe(5)
elements.dispatchEvent(new CustomEvent('core:focus-previous', {bubbles: true}))
elements.dispatchEvent(
new CustomEvent('core:focus-previous', { bubbles: true })
)
expect(document.activeElement.tabIndex).toBe(3)
elements.dispatchEvent(new CustomEvent('core:focus-previous', {bubbles: true}))
elements.dispatchEvent(
new CustomEvent('core:focus-previous', { bubbles: true })
)
expect(document.activeElement.tabIndex).toBe(2)
elements.dispatchEvent(new CustomEvent('core:focus-previous', {bubbles: true}))
elements.dispatchEvent(
new CustomEvent('core:focus-previous', { bubbles: true })
)
expect(document.activeElement.tabIndex).toBe(1)
elements.dispatchEvent(new CustomEvent('core:focus-previous', {bubbles: true}))
elements.dispatchEvent(
new CustomEvent('core:focus-previous', { bubbles: true })
)
expect(document.activeElement.tabIndex).toBe(7)
})
)
}))
})
describe('when keydown events occur on the document', () =>
it('dispatches the event via the KeymapManager and CommandRegistry', () => {
const dispatchedCommands = []
atom.commands.onWillDispatch(command => dispatchedCommands.push(command))
atom.commands.add('*', {'foo-command': () => {}})
atom.keymaps.add('source-name', {'*': {'x': 'foo-command'}})
atom.commands.add('*', { 'foo-command': () => {} })
atom.keymaps.add('source-name', { '*': { x: 'foo-command' } })
const event = KeymapManager.buildKeydownEvent('x', {target: document.createElement('div')})
const event = KeymapManager.buildKeydownEvent('x', {
target: document.createElement('div')
})
document.dispatchEvent(event)
expect(dispatchedCommands.length).toBe(1)
expect(dispatchedCommands[0].type).toBe('foo-command')
})
)
}))
describe('native key bindings', () =>
it("correctly dispatches them to active elements with the '.native-key-bindings' class", () => {
const webContentsSpy = jasmine.createSpyObj('webContents', ['copy', 'paste'])
const webContentsSpy = jasmine.createSpyObj('webContents', [
'copy',
'paste'
])
spyOn(atom.applicationDelegate, 'getCurrentWindow').andReturn({
webContents: webContentsSpy,
on: () => {}
@@ -248,6 +287,5 @@ describe('WindowEventHandler', () => {
expect(webContentsSpy.copy).not.toHaveBeenCalled()
expect(webContentsSpy.paste).not.toHaveBeenCalled()
})
)
}))
})

View File

@@ -2,7 +2,7 @@
const TextEditor = require('../src/text-editor')
import {it, fit, ffit, fffit, beforeEach, afterEach} from './async-spec-helpers'
import { it } from './async-spec-helpers'
describe('WorkspaceCenter', () => {
describe('.observeTextEditors()', () => {
@@ -12,20 +12,25 @@ describe('WorkspaceCenter', () => {
const observed = []
const editorAddedBeforeRegisteringObserver = new TextEditor()
const nonEditorItemAddedBeforeRegisteringObserver = document.createElement('div')
const nonEditorItemAddedBeforeRegisteringObserver = document.createElement(
'div'
)
pane.activateItem(editorAddedBeforeRegisteringObserver)
pane.activateItem(nonEditorItemAddedBeforeRegisteringObserver)
workspaceCenter.observeTextEditors(editor => observed.push(editor))
const editorAddedAfterRegisteringObserver = new TextEditor()
const nonEditorItemAddedAfterRegisteringObserver = document.createElement('div')
const nonEditorItemAddedAfterRegisteringObserver = document.createElement(
'div'
)
pane.activateItem(editorAddedAfterRegisteringObserver)
pane.activateItem(nonEditorItemAddedAfterRegisteringObserver)
expect(observed).toEqual(
[editorAddedBeforeRegisteringObserver, editorAddedAfterRegisteringObserver]
)
expect(observed).toEqual([
editorAddedBeforeRegisteringObserver,
editorAddedAfterRegisteringObserver
])
})
})
})

View File

@@ -1,11 +1,11 @@
/** @babel */
const {ipcRenderer} = require('electron')
const { ipcRenderer } = require('electron')
const etch = require('etch')
const path = require('path')
const temp = require('temp').track()
const {Disposable} = require('event-kit')
const {it, fit, ffit, fffit, beforeEach, afterEach} = require('./async-spec-helpers')
const { Disposable } = require('event-kit')
const { it, beforeEach, afterEach } = require('./async-spec-helpers')
const getNextUpdatePromise = () => etch.getScheduler().nextUpdatePromise
@@ -35,19 +35,39 @@ describe('WorkspaceElement', () => {
const dock = atom.workspace.getLeftDock()
dock.show()
jasmine.attachToDOM(atom.workspace.getElement())
expect(atom.workspace.getActivePaneContainer()).toBe(atom.workspace.getCenter())
dock.getActivePane().getElement().focus()
expect(atom.workspace.getActivePaneContainer()).toBe(
atom.workspace.getCenter()
)
dock
.getActivePane()
.getElement()
.focus()
expect(atom.workspace.getActivePaneContainer()).toBe(dock)
})
})
describe('finding the nearest visible pane in a specific direction', () => {
let pane1, pane2, pane3, pane4, pane5, pane6, pane7, pane8, pane9,
leftDockPane, rightDockPane, bottomDockPane, workspace, workspaceElement
let nearestPaneElement,
pane1,
pane2,
pane3,
pane4,
pane5,
pane6,
pane7,
pane8,
leftDockPane,
rightDockPane,
bottomDockPane,
workspace,
workspaceElement
beforeEach(function () {
atom.config.set('core.destroyEmptyPanes', false)
expect(document.hasFocus()).toBe(true, 'Document needs to be focused to run this test')
expect(document.hasFocus()).toBe(
true,
'Document needs to be focused to run this test'
)
workspace = atom.workspace
@@ -77,7 +97,7 @@ describe('WorkspaceElement', () => {
pane6 = pane5.splitRight()
pane8 = pane7.splitRight()
pane9 = pane8.splitRight()
pane8.splitRight()
const leftDock = workspace.getLeftDock()
const rightDock = workspace.getRightDock()
@@ -104,14 +124,20 @@ describe('WorkspaceElement', () => {
describe('finding the nearest pane above', () => {
describe('when there are multiple rows above the pane', () => {
it('returns the pane in the adjacent row above', () => {
nearestPaneElement = workspaceElement.nearestVisiblePaneInDirection('above', pane8)
nearestPaneElement = workspaceElement.nearestVisiblePaneInDirection(
'above',
pane8
)
expect(nearestPaneElement).toBe(pane5.getElement())
})
})
describe('when there are no rows above the pane', () => {
it('returns null', () => {
nearestPaneElement = workspaceElement.nearestVisiblePaneInDirection('above', pane2)
nearestPaneElement = workspaceElement.nearestVisiblePaneInDirection(
'above',
pane2
)
expect(nearestPaneElement).toBeUndefined() // TODO Expect toBeNull()
})
})
@@ -119,7 +145,10 @@ describe('WorkspaceElement', () => {
describe('when the bottom dock contains the pane', () => {
it('returns the pane in the adjacent row above', () => {
workspace.getBottomDock().show()
nearestPaneElement = workspaceElement.nearestVisiblePaneInDirection('above', bottomDockPane)
nearestPaneElement = workspaceElement.nearestVisiblePaneInDirection(
'above',
bottomDockPane
)
expect(nearestPaneElement).toBe(pane7.getElement())
})
})
@@ -128,14 +157,20 @@ describe('WorkspaceElement', () => {
describe('finding the nearest pane below', () => {
describe('when there are multiple rows below the pane', () => {
it('returns the pane in the adjacent row below', () => {
nearestPaneElement = workspaceElement.nearestVisiblePaneInDirection('below', pane2)
nearestPaneElement = workspaceElement.nearestVisiblePaneInDirection(
'below',
pane2
)
expect(nearestPaneElement).toBe(pane5.getElement())
})
})
describe('when there are no rows below the pane', () => {
it('returns null', () => {
nearestPaneElement = workspaceElement.nearestVisiblePaneInDirection('below', pane8)
nearestPaneElement = workspaceElement.nearestVisiblePaneInDirection(
'below',
pane8
)
expect(nearestPaneElement).toBeUndefined() // TODO Expect toBeNull()
})
})
@@ -144,7 +179,10 @@ describe('WorkspaceElement', () => {
describe("when the workspace center's bottommost row contains the pane", () => {
it("returns the pane in the bottom dock's adjacent row below", () => {
workspace.getBottomDock().show()
nearestPaneElement = workspaceElement.nearestVisiblePaneInDirection('below', pane8)
nearestPaneElement = workspaceElement.nearestVisiblePaneInDirection(
'below',
pane8
)
expect(nearestPaneElement).toBe(bottomDockPane.getElement())
})
})
@@ -154,14 +192,20 @@ describe('WorkspaceElement', () => {
describe('finding the nearest pane to the left', () => {
describe('when there are multiple columns to the left of the pane', () => {
it('returns the pane in the adjacent column to the left', () => {
nearestPaneElement = workspaceElement.nearestVisiblePaneInDirection('left', pane6)
nearestPaneElement = workspaceElement.nearestVisiblePaneInDirection(
'left',
pane6
)
expect(nearestPaneElement).toBe(pane5.getElement())
})
})
describe('when there are no columns to the left of the pane', () => {
it('returns null', () => {
nearestPaneElement = workspaceElement.nearestVisiblePaneInDirection('left', pane4)
nearestPaneElement = workspaceElement.nearestVisiblePaneInDirection(
'left',
pane4
)
expect(nearestPaneElement).toBeUndefined() // TODO Expect toBeNull()
})
})
@@ -169,7 +213,10 @@ describe('WorkspaceElement', () => {
describe('when the right dock contains the pane', () => {
it('returns the pane in the adjacent column to the left', () => {
workspace.getRightDock().show()
nearestPaneElement = workspaceElement.nearestVisiblePaneInDirection('left', rightDockPane)
nearestPaneElement = workspaceElement.nearestVisiblePaneInDirection(
'left',
rightDockPane
)
expect(nearestPaneElement).toBe(pane3.getElement())
})
})
@@ -178,7 +225,10 @@ describe('WorkspaceElement', () => {
describe("when the workspace center's leftmost column contains the pane", () => {
it("returns the pane in the left dock's adjacent column to the left", () => {
workspace.getLeftDock().show()
nearestPaneElement = workspaceElement.nearestVisiblePaneInDirection('left', pane4)
nearestPaneElement = workspaceElement.nearestVisiblePaneInDirection(
'left',
pane4
)
expect(nearestPaneElement).toBe(leftDockPane.getElement())
})
})
@@ -187,7 +237,10 @@ describe('WorkspaceElement', () => {
it("returns the pane in the left dock's adjacent column to the left", () => {
workspace.getLeftDock().show()
workspace.getBottomDock().show()
nearestPaneElement = workspaceElement.nearestVisiblePaneInDirection('left', bottomDockPane)
nearestPaneElement = workspaceElement.nearestVisiblePaneInDirection(
'left',
bottomDockPane
)
expect(nearestPaneElement).toBe(leftDockPane.getElement())
})
})
@@ -197,14 +250,20 @@ describe('WorkspaceElement', () => {
describe('finding the nearest pane to the right', () => {
describe('when there are multiple columns to the right of the pane', () => {
it('returns the pane in the adjacent column to the right', () => {
nearestPaneElement = workspaceElement.nearestVisiblePaneInDirection('right', pane4)
nearestPaneElement = workspaceElement.nearestVisiblePaneInDirection(
'right',
pane4
)
expect(nearestPaneElement).toBe(pane5.getElement())
})
})
describe('when there are no columns to the right of the pane', () => {
it('returns null', () => {
nearestPaneElement = workspaceElement.nearestVisiblePaneInDirection('right', pane6)
nearestPaneElement = workspaceElement.nearestVisiblePaneInDirection(
'right',
pane6
)
expect(nearestPaneElement).toBeUndefined() // TODO Expect toBeNull()
})
})
@@ -212,7 +271,10 @@ describe('WorkspaceElement', () => {
describe('when the left dock contains the pane', () => {
it('returns the pane in the adjacent column to the right', () => {
workspace.getLeftDock().show()
nearestPaneElement = workspaceElement.nearestVisiblePaneInDirection('right', leftDockPane)
nearestPaneElement = workspaceElement.nearestVisiblePaneInDirection(
'right',
leftDockPane
)
expect(nearestPaneElement).toBe(pane1.getElement())
})
})
@@ -221,7 +283,10 @@ describe('WorkspaceElement', () => {
describe("when the workspace center's rightmost column contains the pane", () => {
it("returns the pane in the right dock's adjacent column to the right", () => {
workspace.getRightDock().show()
nearestPaneElement = workspaceElement.nearestVisiblePaneInDirection('right', pane6)
nearestPaneElement = workspaceElement.nearestVisiblePaneInDirection(
'right',
pane6
)
expect(nearestPaneElement).toBe(rightDockPane.getElement())
})
})
@@ -230,7 +295,10 @@ describe('WorkspaceElement', () => {
it("returns the pane in the right dock's adjacent column to the right", () => {
workspace.getRightDock().show()
workspace.getBottomDock().show()
nearestPaneElement = workspaceElement.nearestVisiblePaneInDirection('right', bottomDockPane)
nearestPaneElement = workspaceElement.nearestVisiblePaneInDirection(
'right',
bottomDockPane
)
expect(nearestPaneElement).toBe(rightDockPane.getElement())
})
})
@@ -243,7 +311,10 @@ describe('WorkspaceElement', () => {
beforeEach(function () {
atom.config.set('core.destroyEmptyPanes', false)
expect(document.hasFocus()).toBe(true, 'Document needs to be focused to run this test')
expect(document.hasFocus()).toBe(
true,
'Document needs to be focused to run this test'
)
workspace = atom.workspace
expect(workspace.getLeftDock().isVisible()).toBe(false)
@@ -267,16 +338,14 @@ describe('WorkspaceElement', () => {
startingPane.activate()
workspaceElement.focusPaneViewAbove()
expect(document.activeElement).toBe(paneAbove.getElement())
})
)
}))
describe('when there are no rows above the focused pane', () =>
it('keeps the current pane focused', function () {
startingPane.activate()
workspaceElement.focusPaneViewAbove()
expect(document.activeElement).toBe(startingPane.getElement())
})
)
}))
})
describe('::focusPaneViewBelow()', function () {
@@ -286,16 +355,14 @@ describe('WorkspaceElement', () => {
startingPane.activate()
workspaceElement.focusPaneViewBelow()
expect(document.activeElement).toBe(paneBelow.getElement())
})
)
}))
describe('when there are no rows below the focused pane', () =>
it('keeps the current pane focused', function () {
startingPane.activate()
workspaceElement.focusPaneViewBelow()
expect(document.activeElement).toBe(startingPane.getElement())
})
)
}))
})
describe('::focusPaneViewOnLeft()', function () {
@@ -305,16 +372,14 @@ describe('WorkspaceElement', () => {
startingPane.activate()
workspaceElement.focusPaneViewOnLeft()
expect(document.activeElement).toBe(paneOnLeft.getElement())
})
)
}))
describe('when there are no columns to the left of the focused pane', () =>
it('keeps the current pane focused', function () {
startingPane.activate()
workspaceElement.focusPaneViewOnLeft()
expect(document.activeElement).toBe(startingPane.getElement())
})
)
}))
})
describe('::focusPaneViewOnRight()', function () {
@@ -324,16 +389,14 @@ describe('WorkspaceElement', () => {
startingPane.activate()
workspaceElement.focusPaneViewOnRight()
expect(document.activeElement).toBe(paneOnRight.getElement())
})
)
}))
describe('when there are no columns to the right of the focused pane', () =>
it('keeps the current pane focused', function () {
startingPane.activate()
workspaceElement.focusPaneViewOnRight()
expect(document.activeElement).toBe(startingPane.getElement())
})
)
}))
})
describe('::moveActiveItemToPaneAbove(keepOriginal)', function () {
@@ -346,8 +409,7 @@ describe('WorkspaceElement', () => {
workspaceElement.moveActiveItemToPaneAbove()
expect(workspace.paneForItem(item)).toBe(paneAbove)
expect(paneAbove.getActiveItem()).toBe(item)
})
)
}))
describe('when there are no rows above the focused pane', () =>
it('keeps the active pane focused', function () {
@@ -356,8 +418,7 @@ describe('WorkspaceElement', () => {
startingPane.activateItem(item)
workspaceElement.moveActiveItemToPaneAbove()
expect(workspace.paneForItem(item)).toBe(startingPane)
})
)
}))
describe('when `keepOriginal: true` is passed in the params', () =>
it('keeps the item and adds a copy of it to the adjacent pane', function () {
@@ -367,11 +428,10 @@ describe('WorkspaceElement', () => {
const paneAbove = startingPane.splitUp()
startingPane.activate()
startingPane.activateItem(itemA)
workspaceElement.moveActiveItemToPaneAbove({keepOriginal: true})
workspaceElement.moveActiveItemToPaneAbove({ keepOriginal: true })
expect(workspace.paneForItem(itemA)).toBe(startingPane)
expect(paneAbove.getActiveItem()).toBe(itemB)
})
)
}))
})
describe('::moveActiveItemToPaneBelow(keepOriginal)', function () {
@@ -384,8 +444,7 @@ describe('WorkspaceElement', () => {
workspaceElement.moveActiveItemToPaneBelow()
expect(workspace.paneForItem(item)).toBe(paneBelow)
expect(paneBelow.getActiveItem()).toBe(item)
})
)
}))
describe('when there are no rows below the focused pane', () =>
it('keeps the active item in the focused pane', function () {
@@ -394,8 +453,7 @@ describe('WorkspaceElement', () => {
startingPane.activateItem(item)
workspaceElement.moveActiveItemToPaneBelow()
expect(workspace.paneForItem(item)).toBe(startingPane)
})
)
}))
describe('when `keepOriginal: true` is passed in the params', () =>
it('keeps the item and adds a copy of it to the adjacent pane', function () {
@@ -405,11 +463,10 @@ describe('WorkspaceElement', () => {
const paneBelow = startingPane.splitDown()
startingPane.activate()
startingPane.activateItem(itemA)
workspaceElement.moveActiveItemToPaneBelow({keepOriginal: true})
workspaceElement.moveActiveItemToPaneBelow({ keepOriginal: true })
expect(workspace.paneForItem(itemA)).toBe(startingPane)
expect(paneBelow.getActiveItem()).toBe(itemB)
})
)
}))
})
describe('::moveActiveItemToPaneOnLeft(keepOriginal)', function () {
@@ -422,8 +479,7 @@ describe('WorkspaceElement', () => {
workspaceElement.moveActiveItemToPaneOnLeft()
expect(workspace.paneForItem(item)).toBe(paneOnLeft)
expect(paneOnLeft.getActiveItem()).toBe(item)
})
)
}))
describe('when there are no columns to the left of the focused pane', () =>
it('keeps the active item in the focused pane', function () {
@@ -432,8 +488,7 @@ describe('WorkspaceElement', () => {
startingPane.activateItem(item)
workspaceElement.moveActiveItemToPaneOnLeft()
expect(workspace.paneForItem(item)).toBe(startingPane)
})
)
}))
describe('when `keepOriginal: true` is passed in the params', () =>
it('keeps the item and adds a copy of it to the adjacent pane', function () {
@@ -443,11 +498,10 @@ describe('WorkspaceElement', () => {
const paneOnLeft = startingPane.splitLeft()
startingPane.activate()
startingPane.activateItem(itemA)
workspaceElement.moveActiveItemToPaneOnLeft({keepOriginal: true})
workspaceElement.moveActiveItemToPaneOnLeft({ keepOriginal: true })
expect(workspace.paneForItem(itemA)).toBe(startingPane)
expect(paneOnLeft.getActiveItem()).toBe(itemB)
})
)
}))
})
describe('::moveActiveItemToPaneOnRight(keepOriginal)', function () {
@@ -460,8 +514,7 @@ describe('WorkspaceElement', () => {
workspaceElement.moveActiveItemToPaneOnRight()
expect(workspace.paneForItem(item)).toBe(paneOnRight)
expect(paneOnRight.getActiveItem()).toBe(item)
})
)
}))
describe('when there are no columns to the right of the focused pane', () =>
it('keeps the active item in the focused pane', function () {
@@ -470,8 +523,7 @@ describe('WorkspaceElement', () => {
startingPane.activateItem(item)
workspaceElement.moveActiveItemToPaneOnRight()
expect(workspace.paneForItem(item)).toBe(startingPane)
})
)
}))
describe('when `keepOriginal: true` is passed in the params', () =>
it('keeps the item and adds a copy of it to the adjacent pane', function () {
@@ -481,11 +533,10 @@ describe('WorkspaceElement', () => {
const paneOnRight = startingPane.splitRight()
startingPane.activate()
startingPane.activateItem(itemA)
workspaceElement.moveActiveItemToPaneOnRight({keepOriginal: true})
workspaceElement.moveActiveItemToPaneOnRight({ keepOriginal: true })
expect(workspace.paneForItem(itemA)).toBe(startingPane)
expect(paneOnRight.getActiveItem()).toBe(itemB)
})
)
}))
})
describe('::moveActiveItemToNearestPaneInDirection(direction, params)', () => {
@@ -499,10 +550,14 @@ describe('WorkspaceElement', () => {
workspace.getBottomDock().show()
startingPane.activate()
startingPane.activateItem(item)
workspaceElement.moveActiveItemToNearestPaneInDirection('below', {keepOriginal: false})
workspaceElement.moveActiveItemToNearestPaneInDirection('below', {
keepOriginal: false
})
expect(workspace.paneForItem(item)).toBe(startingPane)
workspaceElement.moveActiveItemToNearestPaneInDirection('below', {keepOriginal: true})
workspaceElement.moveActiveItemToNearestPaneInDirection('below', {
keepOriginal: true
})
expect(workspace.paneForItem(item)).toBe(startingPane)
})
})
@@ -516,7 +571,9 @@ describe('WorkspaceElement', () => {
startingPane.activate()
startingPane.activateItem(item)
workspaceElement.focusPaneViewAbove()
workspaceElement.moveActiveItemToNearestPaneInDirection('below', {keepOriginal: true})
workspaceElement.moveActiveItemToNearestPaneInDirection('below', {
keepOriginal: true
})
expect(workspace.paneForItem(item)).toBe(startingPane)
expect(paneBelow.getItems().length).toEqual(0)
})
@@ -538,18 +595,30 @@ describe('WorkspaceElement', () => {
await Promise.all([
atom.workspace.open({
element: document.createElement('div'),
getDefaultLocation() { return 'left' },
getPreferredWidth() { return 150 }
getDefaultLocation () {
return 'left'
},
getPreferredWidth () {
return 150
}
}),
atom.workspace.open({
element: document.createElement('div'),
getDefaultLocation() { return 'right' },
getPreferredWidth() { return 150 }
getDefaultLocation () {
return 'right'
},
getPreferredWidth () {
return 150
}
}),
atom.workspace.open({
element: document.createElement('div'),
getDefaultLocation() { return 'bottom' },
getPreferredHeight() { return 100 }
getDefaultLocation () {
return 'bottom'
},
getPreferredHeight () {
return 100
}
})
])
@@ -567,21 +636,21 @@ describe('WorkspaceElement', () => {
// --- Right Dock ---
// Mouse over where the toggle button would be if the dock were hovered
moveMouse({clientX: 440, clientY: 150})
moveMouse({ clientX: 440, clientY: 150 })
await getNextUpdatePromise()
expectToggleButtonHidden(leftDock)
expectToggleButtonHidden(rightDock)
expectToggleButtonHidden(bottomDock)
// Mouse over the dock
moveMouse({clientX: 460, clientY: 150})
moveMouse({ clientX: 460, clientY: 150 })
await getNextUpdatePromise()
expectToggleButtonHidden(leftDock)
expectToggleButtonVisible(rightDock, 'icon-chevron-right')
expectToggleButtonHidden(bottomDock)
// Mouse over the toggle button
moveMouse({clientX: 440, clientY: 150})
moveMouse({ clientX: 440, clientY: 150 })
await getNextUpdatePromise()
expectToggleButtonHidden(leftDock)
expectToggleButtonVisible(rightDock, 'icon-chevron-right')
@@ -594,10 +663,10 @@ describe('WorkspaceElement', () => {
expectToggleButtonHidden(rightDock)
// Mouse to edge of the window
moveMouse({clientX: 575, clientY: 150})
moveMouse({ clientX: 575, clientY: 150 })
await getNextUpdatePromise()
expectToggleButtonHidden(rightDock)
moveMouse({clientX: 598, clientY: 150})
moveMouse({ clientX: 598, clientY: 150 })
await getNextUpdatePromise()
expectToggleButtonVisible(rightDock, 'icon-chevron-left')
@@ -610,21 +679,21 @@ describe('WorkspaceElement', () => {
// --- Left Dock ---
// Mouse over where the toggle button would be if the dock were hovered
moveMouse({clientX: 160, clientY: 150})
moveMouse({ clientX: 160, clientY: 150 })
await getNextUpdatePromise()
expectToggleButtonHidden(leftDock)
expectToggleButtonHidden(rightDock)
expectToggleButtonHidden(bottomDock)
// Mouse over the dock
moveMouse({clientX: 140, clientY: 150})
moveMouse({ clientX: 140, clientY: 150 })
await getNextUpdatePromise()
expectToggleButtonVisible(leftDock, 'icon-chevron-left')
expectToggleButtonHidden(rightDock)
expectToggleButtonHidden(bottomDock)
// Mouse over the toggle button
moveMouse({clientX: 160, clientY: 150})
moveMouse({ clientX: 160, clientY: 150 })
await getNextUpdatePromise()
expectToggleButtonVisible(leftDock, 'icon-chevron-left')
expectToggleButtonHidden(rightDock)
@@ -637,10 +706,10 @@ describe('WorkspaceElement', () => {
expectToggleButtonHidden(leftDock)
// Mouse to edge of the window
moveMouse({clientX: 25, clientY: 150})
moveMouse({ clientX: 25, clientY: 150 })
await getNextUpdatePromise()
expectToggleButtonHidden(leftDock)
moveMouse({clientX: 2, clientY: 150})
moveMouse({ clientX: 2, clientY: 150 })
await getNextUpdatePromise()
expectToggleButtonVisible(leftDock, 'icon-chevron-right')
@@ -653,21 +722,21 @@ describe('WorkspaceElement', () => {
// --- Bottom Dock ---
// Mouse over where the toggle button would be if the dock were hovered
moveMouse({clientX: 300, clientY: 190})
moveMouse({ clientX: 300, clientY: 190 })
await getNextUpdatePromise()
expectToggleButtonHidden(leftDock)
expectToggleButtonHidden(rightDock)
expectToggleButtonHidden(bottomDock)
// Mouse over the dock
moveMouse({clientX: 300, clientY: 210})
moveMouse({ clientX: 300, clientY: 210 })
await getNextUpdatePromise()
expectToggleButtonHidden(leftDock)
expectToggleButtonHidden(rightDock)
expectToggleButtonVisible(bottomDock, 'icon-chevron-down')
// Mouse over the toggle button
moveMouse({clientX: 300, clientY: 195})
moveMouse({ clientX: 300, clientY: 195 })
await getNextUpdatePromise()
expectToggleButtonHidden(leftDock)
expectToggleButtonHidden(rightDock)
@@ -680,10 +749,10 @@ describe('WorkspaceElement', () => {
expectToggleButtonHidden(bottomDock)
// Mouse to edge of the window
moveMouse({clientX: 300, clientY: 290})
moveMouse({ clientX: 300, clientY: 290 })
await getNextUpdatePromise()
expectToggleButtonHidden(leftDock)
moveMouse({clientX: 300, clientY: 299})
moveMouse({ clientX: 300, clientY: 299 })
await getNextUpdatePromise()
expectToggleButtonVisible(bottomDock, 'icon-chevron-up')
@@ -699,12 +768,16 @@ describe('WorkspaceElement', () => {
advanceClock(100)
}
function expectToggleButtonHidden(dock) {
expect(dock.refs.toggleButton.element).not.toHaveClass('atom-dock-toggle-button-visible')
function expectToggleButtonHidden (dock) {
expect(dock.refs.toggleButton.element).not.toHaveClass(
'atom-dock-toggle-button-visible'
)
}
function expectToggleButtonVisible(dock, iconClass) {
expect(dock.refs.toggleButton.element).toHaveClass('atom-dock-toggle-button-visible')
function expectToggleButtonVisible (dock, iconClass) {
expect(dock.refs.toggleButton.element).toHaveClass(
'atom-dock-toggle-button-visible'
)
expect(dock.refs.toggleButton.refs.iconElement).toHaveClass(iconClass)
}
})
@@ -713,10 +786,12 @@ describe('WorkspaceElement', () => {
it('has a class based on the style of the scrollbar', () => {
let observeCallback
const scrollbarStyle = require('scrollbar-style')
spyOn(scrollbarStyle, 'observePreferredScrollbarStyle').andCallFake(cb => {
observeCallback = cb
return new Disposable(() => {})
})
spyOn(scrollbarStyle, 'observePreferredScrollbarStyle').andCallFake(
cb => {
observeCallback = cb
return new Disposable(() => {})
}
)
const workspaceElement = atom.workspace.getElement()
observeCallback('legacy')
@@ -741,11 +816,15 @@ describe('WorkspaceElement', () => {
it("updates the font-size based on the 'editor.fontSize' config value", async () => {
const initialCharWidth = editor.getDefaultCharWidth()
expect(getComputedStyle(editorElement).fontSize).toBe(atom.config.get('editor.fontSize') + 'px')
expect(getComputedStyle(editorElement).fontSize).toBe(
atom.config.get('editor.fontSize') + 'px'
)
atom.config.set('editor.fontSize', atom.config.get('editor.fontSize') + 5)
await editorElement.component.getNextUpdatePromise()
expect(getComputedStyle(editorElement).fontSize).toBe(atom.config.get('editor.fontSize') + 'px')
expect(getComputedStyle(editorElement).fontSize).toBe(
atom.config.get('editor.fontSize') + 'px'
)
expect(editor.getDefaultCharWidth()).toBeGreaterThan(initialCharWidth)
})
@@ -765,7 +844,9 @@ describe('WorkspaceElement', () => {
const initialLineHeight = editor.getLineHeightInPixels()
atom.config.set('editor.lineHeight', '30px')
await editorElement.component.getNextUpdatePromise()
expect(getComputedStyle(editorElement).lineHeight).toBe(atom.config.get('editor.lineHeight'))
expect(getComputedStyle(editorElement).lineHeight).toBe(
atom.config.get('editor.lineHeight')
)
expect(editor.getLineHeightInPixels()).not.toBe(initialLineHeight)
})
@@ -774,37 +855,47 @@ describe('WorkspaceElement', () => {
atom.config.set('editor.fontSize', 12)
// Zoom out
editorElement.querySelector('span').dispatchEvent(new WheelEvent('mousewheel', {
wheelDeltaY: -10,
ctrlKey: true
}))
editorElement.querySelector('span').dispatchEvent(
new WheelEvent('mousewheel', {
wheelDeltaY: -10,
ctrlKey: true
})
)
expect(atom.config.get('editor.fontSize')).toBe(11)
// Zoom in
editorElement.querySelector('span').dispatchEvent(new WheelEvent('mousewheel', {
wheelDeltaY: 10,
ctrlKey: true
}))
editorElement.querySelector('span').dispatchEvent(
new WheelEvent('mousewheel', {
wheelDeltaY: 10,
ctrlKey: true
})
)
expect(atom.config.get('editor.fontSize')).toBe(12)
// Not on an atom-text-editor
workspaceElement.dispatchEvent(new WheelEvent('mousewheel', {
wheelDeltaY: 10,
ctrlKey: true
}))
workspaceElement.dispatchEvent(
new WheelEvent('mousewheel', {
wheelDeltaY: 10,
ctrlKey: true
})
)
expect(atom.config.get('editor.fontSize')).toBe(12)
// No ctrl key
editorElement.querySelector('span').dispatchEvent(new WheelEvent('mousewheel', {
wheelDeltaY: 10
}))
editorElement.querySelector('span').dispatchEvent(
new WheelEvent('mousewheel', {
wheelDeltaY: 10
})
)
expect(atom.config.get('editor.fontSize')).toBe(12)
atom.config.set('editor.zoomFontWhenCtrlScrolling', false)
editorElement.querySelector('span').dispatchEvent(new WheelEvent('mousewheel', {
wheelDeltaY: 10,
ctrlKey: true
}))
editorElement.querySelector('span').dispatchEvent(
new WheelEvent('mousewheel', {
wheelDeltaY: 10,
ctrlKey: true
})
)
expect(atom.config.get('editor.fontSize')).toBe(12)
})
})
@@ -813,22 +904,40 @@ describe('WorkspaceElement', () => {
it('inserts panel container elements in the correct places in the DOM', () => {
const workspaceElement = atom.workspace.getElement()
const leftContainer = workspaceElement.querySelector('atom-panel-container.left')
const rightContainer = workspaceElement.querySelector('atom-panel-container.right')
const leftContainer = workspaceElement.querySelector(
'atom-panel-container.left'
)
const rightContainer = workspaceElement.querySelector(
'atom-panel-container.right'
)
expect(leftContainer.nextSibling).toBe(workspaceElement.verticalAxis)
expect(rightContainer.previousSibling).toBe(workspaceElement.verticalAxis)
const topContainer = workspaceElement.querySelector('atom-panel-container.top')
const bottomContainer = workspaceElement.querySelector('atom-panel-container.bottom')
const topContainer = workspaceElement.querySelector(
'atom-panel-container.top'
)
const bottomContainer = workspaceElement.querySelector(
'atom-panel-container.bottom'
)
expect(topContainer.nextSibling).toBe(workspaceElement.paneContainer)
expect(bottomContainer.previousSibling).toBe(workspaceElement.paneContainer)
expect(bottomContainer.previousSibling).toBe(
workspaceElement.paneContainer
)
const headerContainer = workspaceElement.querySelector('atom-panel-container.header')
const footerContainer = workspaceElement.querySelector('atom-panel-container.footer')
const headerContainer = workspaceElement.querySelector(
'atom-panel-container.header'
)
const footerContainer = workspaceElement.querySelector(
'atom-panel-container.footer'
)
expect(headerContainer.nextSibling).toBe(workspaceElement.horizontalAxis)
expect(footerContainer.previousSibling).toBe(workspaceElement.horizontalAxis)
expect(footerContainer.previousSibling).toBe(
workspaceElement.horizontalAxis
)
const modalContainer = workspaceElement.querySelector('atom-panel-container.modal')
const modalContainer = workspaceElement.querySelector(
'atom-panel-container.modal'
)
expect(modalContainer.parentNode).toBe(workspaceElement)
})
@@ -838,18 +947,20 @@ describe('WorkspaceElement', () => {
expect(workspaceElement.offsetWidth).toBeGreaterThan(0)
const headerItem = document.createElement('div')
atom.workspace.addHeaderPanel({item: headerItem})
atom.workspace.addHeaderPanel({ item: headerItem })
expect(headerItem.offsetWidth).toEqual(workspaceElement.offsetWidth)
const footerItem = document.createElement('div')
atom.workspace.addFooterPanel({item: footerItem})
atom.workspace.addFooterPanel({ item: footerItem })
expect(footerItem.offsetWidth).toEqual(workspaceElement.offsetWidth)
})
it('shrinks horizontal axis according to header/footer panels height', () => {
const workspaceElement = atom.workspace.getElement()
workspaceElement.style.height = '100px'
const horizontalAxisElement = workspaceElement.querySelector('atom-workspace-axis.horizontal')
const horizontalAxisElement = workspaceElement.querySelector(
'atom-workspace-axis.horizontal'
)
jasmine.attachToDOM(workspaceElement)
const originalHorizontalAxisHeight = horizontalAxisElement.offsetHeight
@@ -858,15 +969,19 @@ describe('WorkspaceElement', () => {
const headerItem = document.createElement('div')
headerItem.style.height = '10px'
atom.workspace.addHeaderPanel({item: headerItem})
atom.workspace.addHeaderPanel({ item: headerItem })
expect(headerItem.offsetHeight).toBeGreaterThan(0)
const footerItem = document.createElement('div')
footerItem.style.height = '15px'
atom.workspace.addFooterPanel({item: footerItem})
atom.workspace.addFooterPanel({ item: footerItem })
expect(footerItem.offsetHeight).toBeGreaterThan(0)
expect(horizontalAxisElement.offsetHeight).toEqual(originalHorizontalAxisHeight - headerItem.offsetHeight - footerItem.offsetHeight)
expect(horizontalAxisElement.offsetHeight).toEqual(
originalHorizontalAxisHeight -
headerItem.offsetHeight -
footerItem.offsetHeight
)
})
})
@@ -895,39 +1010,60 @@ describe('WorkspaceElement', () => {
// No active item. Use first project directory.
atom.commands.dispatch(workspaceElement, 'window:run-package-specs')
expect(ipcRenderer.send).toHaveBeenCalledWith('run-package-specs', path.join(projectPaths[0], 'spec'), {})
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.
const item = document.createElement('div')
atom.workspace.getActivePane().activateItem(item)
atom.commands.dispatch(workspaceElement, 'window:run-package-specs')
expect(ipcRenderer.send).toHaveBeenCalledWith('run-package-specs', path.join(projectPaths[0], 'spec'), {})
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(ipcRenderer.send).toHaveBeenCalledWith('run-package-specs', path.join(projectPaths[0], 'spec'), {})
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(ipcRenderer.send).toHaveBeenCalledWith('run-package-specs', path.join(projectPaths[1], 'spec'), {})
expect(ipcRenderer.send).toHaveBeenCalledWith(
'run-package-specs',
path.join(projectPaths[1], 'spec'),
{}
)
ipcRenderer.send.reset()
})
it("passes additional options to the spec window", () => {
it('passes additional options to the spec window', () => {
const workspaceElement = atom.workspace.getElement()
spyOn(ipcRenderer, 'send')
const projectPath = temp.mkdirSync('dir1-')
atom.project.setPaths([projectPath])
workspaceElement.runPackageSpecs({env: {ATOM_GITHUB_BABEL_ENV: 'coverage'}})
workspaceElement.runPackageSpecs({
env: { ATOM_GITHUB_BABEL_ENV: 'coverage' }
})
expect(ipcRenderer.send).toHaveBeenCalledWith(
'run-package-specs', path.join(projectPath, 'spec'), {env: {ATOM_GITHUB_BABEL_ENV: 'coverage'}})
'run-package-specs',
path.join(projectPath, 'spec'),
{ env: { ATOM_GITHUB_BABEL_ENV: 'coverage' } }
)
})
})
})

File diff suppressed because it is too large Load Diff