Merge pull request #14853 from atom/aw-filewatcher

Filesystem watcher API
This commit is contained in:
Ash Wilson
2017-08-08 14:04:39 -04:00
committed by GitHub
32 changed files with 1840 additions and 27 deletions

View File

@@ -20,6 +20,11 @@ export function afterEach (fn) {
['it', 'fit', 'ffit', 'fffit'].forEach(function (name) {
module.exports[name] = function (description, fn) {
if (fn === undefined) {
global[name](description)
return
}
global[name](description, function () {
const result = fn()
if (result instanceof Promise) {
@@ -29,7 +34,7 @@ export function afterEach (fn) {
}
})
export async function conditionPromise (condition) {
export async function conditionPromise (condition) {
const startTime = Date.now()
while (true) {
@@ -40,7 +45,7 @@ export async function conditionPromise (condition) {
}
if (Date.now() - startTime > 5000) {
throw new Error("Timed out waiting on condition")
throw new Error('Timed out waiting on condition')
}
}
}
@@ -72,3 +77,27 @@ export function emitterEventPromise (emitter, event, timeout = 15000) {
})
})
}
export function promisify (original) {
return function (...args) {
return new Promise((resolve, reject) => {
args.push((err, ...results) => {
if (err) {
reject(err)
} else {
resolve(...results)
}
})
return original(...args)
})
}
}
export function promisifySome (obj, fnNames) {
const result = {}
for (const fnName of fnNames) {
result[fnName] = promisify(obj[fnName])
}
return result
}

View File

@@ -6,7 +6,8 @@ StorageFolder = require '../src/storage-folder'
describe "AtomEnvironment", ->
afterEach ->
temp.cleanupSync()
try
temp.cleanupSync()
describe 'window sizing methods', ->
describe '::getPosition and ::setPosition', ->

View File

@@ -86,7 +86,11 @@ describe("AtomPaths", () => {
afterEach(() => {
delete process.env.ATOM_HOME
fs.removeSync(electronUserDataPath)
temp.cleanupSync()
try {
temp.cleanupSync()
} catch (e) {
// Ignore
}
app.setPath('userData', defaultElectronUserDataPath)
})

View File

@@ -19,7 +19,8 @@ describe "Babel transpiler support", ->
afterEach ->
CompileCache.setCacheDirectory(originalCacheDir)
temp.cleanupSync()
try
temp.cleanupSync()
describe 'when a .js file starts with /** @babel */;', ->
it "transpiles it using babel", ->

View File

@@ -21,7 +21,8 @@ describe "CommandInstaller on #darwin", ->
spyOn(CommandInstaller::, 'getInstallDirectory').andReturn(installationPath)
afterEach ->
temp.cleanupSync()
try
temp.cleanupSync()
it "shows an error dialog when installing commands interactively fails", ->
appDelegate = jasmine.createSpyObj("appDelegate", ["confirm"])

View File

@@ -23,7 +23,8 @@ describe 'CompileCache', ->
afterEach ->
CompileCache.setAtomHomeDirectory(process.env.ATOM_HOME)
CSON.setCacheDir(CompileCache.getCacheDirectory())
temp.cleanupSync()
try
temp.cleanupSync()
describe 'addPathToCache(filePath, atomHome)', ->
describe 'when the given file is plain javascript', ->

View File

@@ -10,7 +10,8 @@ describe "DefaultDirectoryProvider", ->
tmp = temp.mkdirSync('atom-spec-default-dir-provider')
afterEach ->
temp.cleanupSync()
try
temp.cleanupSync()
describe ".directoryForURISync(uri)", ->
it "returns a Directory with a path that matches the uri", ->

View File

@@ -12,7 +12,8 @@ describe "GitRepositoryProvider", ->
provider = new GitRepositoryProvider(atom.project, atom.config, atom.confirm)
afterEach ->
temp.cleanupSync()
try
temp.cleanupSync()
describe ".repositoryForDirectory(directory)", ->
describe "when specified a Directory with a Git repository", ->

View File

@@ -24,7 +24,8 @@ describe "the `grammars` global", ->
afterEach ->
atom.packages.deactivatePackages()
atom.packages.unloadPackages()
temp.cleanupSync()
try
temp.cleanupSync()
describe ".selectGrammar(filePath)", ->
it "always returns a grammar", ->

View File

@@ -508,7 +508,8 @@ describe('AtomApplication', function () {
}
function makeTempDir (name) {
return fs.realpathSync(require('temp').mkdirSync(name))
const temp = require('temp').track()
return fs.realpathSync(temp.mkdirSync(name))
}
let channelIdCounter = 0

View File

@@ -16,7 +16,11 @@ describe("FileRecoveryService", () => {
})
afterEach(() => {
temp.cleanupSync()
try {
temp.cleanupSync()
} catch (e) {
// Ignore
}
})
describe("when no crash happens during a save", () => {

View File

@@ -9,7 +9,8 @@ describe 'ModuleCache', ->
spyOn(Module, '_findPath').andCallThrough()
afterEach ->
temp.cleanupSync()
try
temp.cleanupSync()
it 'resolves Electron module paths without hitting the filesystem', ->
builtins = ModuleCache.cache.builtins

View File

@@ -0,0 +1,362 @@
/** @babel */
import {it, beforeEach} from './async-spec-helpers'
import path from 'path'
import {Emitter} from 'event-kit'
import {NativeWatcherRegistry} from '../src/native-watcher-registry'
function findRootDirectory () {
let current = process.cwd()
while (true) {
let next = path.resolve(current, '..')
if (next === current) {
return next
} else {
current = next
}
}
}
const ROOT = findRootDirectory()
function absolute (...parts) {
const candidate = path.join(...parts)
return path.isAbsolute(candidate) ? candidate : path.join(ROOT, candidate)
}
function parts (fullPath) {
return fullPath.split(path.sep).filter(part => part.length > 0)
}
class MockWatcher {
constructor (normalizedPath) {
this.normalizedPath = normalizedPath
this.native = null
}
getNormalizedPathPromise () {
return Promise.resolve(this.normalizedPath)
}
attachToNative (native, nativePath) {
if (this.normalizedPath.startsWith(nativePath)) {
if (this.native) {
this.native.attached = this.native.attached.filter(each => each !== this)
}
this.native = native
this.native.attached.push(this)
}
}
}
class MockNative {
constructor (name) {
this.name = name
this.attached = []
this.disposed = false
this.stopped = false
this.emitter = new Emitter()
}
reattachTo (newNative, nativePath) {
for (const watcher of this.attached) {
watcher.attachToNative(newNative, nativePath)
}
}
onWillStop (callback) {
return this.emitter.on('will-stop', callback)
}
dispose () {
this.disposed = true
}
stop () {
this.stopped = true
this.emitter.emit('will-stop')
}
}
describe('NativeWatcherRegistry', function () {
let createNative, registry
beforeEach(function () {
registry = new NativeWatcherRegistry(normalizedPath => createNative(normalizedPath))
})
it('attaches a Watcher to a newly created NativeWatcher for a new directory', async function () {
const watcher = new MockWatcher(absolute('some', 'path'))
const NATIVE = new MockNative('created')
createNative = () => NATIVE
await registry.attach(watcher)
expect(watcher.native).toBe(NATIVE)
})
it('reuses an existing NativeWatcher on the same directory', async function () {
const EXISTING = new MockNative('existing')
const existingPath = absolute('existing', 'path')
let firstTime = true
createNative = () => {
if (firstTime) {
firstTime = false
return EXISTING
}
return new MockNative('nope')
}
await registry.attach(new MockWatcher(existingPath))
const watcher = new MockWatcher(existingPath)
await registry.attach(watcher)
expect(watcher.native).toBe(EXISTING)
})
it('attaches to an existing NativeWatcher on a parent directory', async function () {
const EXISTING = new MockNative('existing')
const parentDir = absolute('existing', 'path')
const subDir = path.join(parentDir, 'sub', 'directory')
let firstTime = true
createNative = () => {
if (firstTime) {
firstTime = false
return EXISTING
}
return new MockNative('nope')
}
await registry.attach(new MockWatcher(parentDir))
const watcher = new MockWatcher(subDir)
await registry.attach(watcher)
expect(watcher.native).toBe(EXISTING)
})
it('adopts Watchers from NativeWatchers on child directories', async function () {
const parentDir = absolute('existing', 'path')
const childDir0 = path.join(parentDir, 'child', 'directory', 'zero')
const childDir1 = path.join(parentDir, 'child', 'directory', 'one')
const otherDir = absolute('another', 'path')
const CHILD0 = new MockNative('existing0')
const CHILD1 = new MockNative('existing1')
const OTHER = new MockNative('existing2')
const PARENT = new MockNative('parent')
createNative = dir => {
if (dir === childDir0) {
return CHILD0
} else if (dir === childDir1) {
return CHILD1
} else if (dir === otherDir) {
return OTHER
} else if (dir === parentDir) {
return PARENT
} else {
throw new Error(`Unexpected path: ${dir}`)
}
}
const watcher0 = new MockWatcher(childDir0)
await registry.attach(watcher0)
const watcher1 = new MockWatcher(childDir1)
await registry.attach(watcher1)
const watcher2 = new MockWatcher(otherDir)
await registry.attach(watcher2)
expect(watcher0.native).toBe(CHILD0)
expect(watcher1.native).toBe(CHILD1)
expect(watcher2.native).toBe(OTHER)
// Consolidate all three watchers beneath the same native watcher on the parent directory
const watcher = new MockWatcher(parentDir)
await registry.attach(watcher)
expect(watcher.native).toBe(PARENT)
expect(watcher0.native).toBe(PARENT)
expect(CHILD0.stopped).toBe(true)
expect(CHILD0.disposed).toBe(true)
expect(watcher1.native).toBe(PARENT)
expect(CHILD1.stopped).toBe(true)
expect(CHILD1.disposed).toBe(true)
expect(watcher2.native).toBe(OTHER)
expect(OTHER.stopped).toBe(false)
expect(OTHER.disposed).toBe(false)
})
describe('removing NativeWatchers', function () {
it('happens when they stop', async function () {
const STOPPED = new MockNative('stopped')
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)
createNative = dir => {
if (dir === stoppedPath) {
return STOPPED
} else if (dir === runningPath) {
return RUNNING
} else {
throw new Error(`Unexpected path: ${dir}`)
}
}
const stoppedWatcher = new MockWatcher(stoppedPath)
await registry.attach(stoppedWatcher)
const runningWatcher = new MockWatcher(runningPath)
await registry.attach(runningWatcher)
STOPPED.stop()
const runningNode = registry.tree.root.lookup(runningPathParts).when({
parent: node => node,
missing: () => false,
children: () => false
})
expect(runningNode).toBeTruthy()
expect(runningNode.getNativeWatcher()).toBe(RUNNING)
const stoppedNode = registry.tree.root.lookup(stoppedPathParts).when({
parent: () => false,
missing: () => true,
children: () => false
})
expect(stoppedNode).toBe(true)
})
it('reassigns new child watchers when a parent watcher is stopped', async function () {
const CHILD0 = new MockNative('child0')
const CHILD1 = new MockNative('child1')
const PARENT = new MockNative('parent')
const parentDir = absolute('parent')
const childDir0 = path.join(parentDir, 'child0')
const childDir1 = path.join(parentDir, 'child1')
createNative = dir => {
if (dir === parentDir) {
return PARENT
} else if (dir === childDir0) {
return CHILD0
} else if (dir === childDir1) {
return CHILD1
} else {
throw new Error(`Unexpected directory ${dir}`)
}
}
const parentWatcher = new MockWatcher(parentDir)
const childWatcher0 = new MockWatcher(childDir0)
const childWatcher1 = new MockWatcher(childDir1)
await registry.attach(parentWatcher)
await Promise.all([
registry.attach(childWatcher0),
registry.attach(childWatcher1)
])
// All three watchers should share the parent watcher's native watcher.
expect(parentWatcher.native).toBe(PARENT)
expect(childWatcher0.native).toBe(PARENT)
expect(childWatcher1.native).toBe(PARENT)
// Stopping the parent should detach and recreate the child watchers.
PARENT.stop()
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(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)
})
it('consolidates children when splitting a parent watcher', async function () {
const CHILD0 = new MockNative('child0')
const PARENT = new MockNative('parent')
const parentDir = absolute('parent')
const childDir0 = path.join(parentDir, 'child0')
const childDir1 = path.join(parentDir, 'child0', 'child1')
createNative = dir => {
if (dir === parentDir) {
return PARENT
} else if (dir === childDir0) {
return CHILD0
} else {
throw new Error(`Unexpected directory ${dir}`)
}
}
const parentWatcher = new MockWatcher(parentDir)
const childWatcher0 = new MockWatcher(childDir0)
const childWatcher1 = new MockWatcher(childDir1)
await registry.attach(parentWatcher)
await Promise.all([
registry.attach(childWatcher0),
registry.attach(childWatcher1)
])
// All three watchers should share the parent watcher's native watcher.
expect(parentWatcher.native).toBe(PARENT)
expect(childWatcher0.native).toBe(PARENT)
expect(childWatcher1.native).toBe(PARENT)
// Stopping the parent should detach and create the child watchers. Both child watchers should
// share the same native watcher.
PARENT.stop()
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(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)
})
})
})

View File

@@ -17,7 +17,8 @@ describe "PackageManager", ->
spyOn(ModuleCache, 'add')
afterEach ->
temp.cleanupSync()
try
temp.cleanupSync()
describe "::getApmPath()", ->
it "returns the path to the apm command", ->

186
spec/path-watcher-spec.js Normal file
View File

@@ -0,0 +1,186 @@
/** @babel */
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'
tempCb.track()
const fs = promisifySome(fsCb, ['writeFile', 'mkdir', 'symlink', 'appendFile', 'realpath'])
const temp = promisifySome(tempCb, ['mkdir'])
describe('watchPath', function () {
let subs
beforeEach(function () {
subs = new CompositeDisposable()
})
afterEach(async function () {
subs.dispose()
await stopAllWatchers()
})
function waitForChanges (watcher, ...fileNames) {
const waiting = new Set(fileNames)
let fired = false
const relevantEvents = []
return new Promise(resolve => {
const sub = watcher.onDidChange(events => {
for (const event of events) {
if (waiting.delete(event.path)) {
relevantEvents.push(event)
}
}
if (!fired && waiting.size === 0) {
fired = true
resolve(relevantEvents)
sub.dispose()
}
})
})
}
describe('watchPath()', function () {
it('resolves getStartPromise() when the watcher begins listening', async function () {
const rootDir = await temp.mkdir('atom-fsmanager-test-')
const watcher = watchPath(rootDir, {}, () => {})
await watcher.getStartPromise()
})
it('reuses an existing native watcher and resolves getStartPromise immediately if attached to a running watcher', async function () {
const rootDir = await temp.mkdir('atom-fsmanager-test-')
const watcher0 = watchPath(rootDir, {}, () => {})
await watcher0.getStartPromise()
const watcher1 = watchPath(rootDir, {}, () => {})
await watcher1.getStartPromise()
expect(watcher0.native).toBe(watcher1.native)
})
it("reuses existing native watchers even while they're still starting", async function () {
const rootDir = await temp.mkdir('atom-fsmanager-test-')
const watcher0 = watchPath(rootDir, {}, () => {})
await watcher0.getAttachedPromise()
expect(watcher0.native.isRunning()).toBe(false)
const watcher1 = watchPath(rootDir, {}, () => {})
await watcher1.getAttachedPromise()
expect(watcher0.native).toBe(watcher1.native)
await Promise.all([watcher0.getStartPromise(), watcher1.getStartPromise()])
})
it("doesn't attach new watchers to a native watcher that's stopping", async function () {
const rootDir = await temp.mkdir('atom-fsmanager-test-')
const watcher0 = watchPath(rootDir, {}, () => {})
await watcher0.getStartPromise()
const native0 = watcher0.native
watcher0.dispose()
const watcher1 = watchPath(rootDir, {}, () => {})
expect(watcher1.native).not.toBe(native0)
})
it('reuses an existing native watcher on a parent directory and filters events', async function () {
const rootDir = await temp.mkdir('atom-fsmanager-test-').then(fs.realpath)
const rootFile = path.join(rootDir, 'rootfile.txt')
const subDir = path.join(rootDir, 'subdir')
const subFile = path.join(subDir, 'subfile.txt')
await fs.mkdir(subDir)
// Keep the watchers alive with an undisposed subscription
const rootWatcher = watchPath(rootDir, {}, () => {})
const childWatcher = watchPath(subDir, {}, () => {})
await Promise.all([
rootWatcher.getStartPromise(),
childWatcher.getStartPromise()
])
expect(rootWatcher.native).toBe(childWatcher.native)
expect(rootWatcher.native.isRunning()).toBe(true)
const firstChanges = Promise.all([
waitForChanges(rootWatcher, subFile),
waitForChanges(childWatcher, subFile)
])
await fs.writeFile(subFile, 'subfile\n', {encoding: 'utf8'})
await firstChanges
const nextRootEvent = waitForChanges(rootWatcher, rootFile)
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)
// Create the directory tree
const rootFile = path.join(parentDir, 'rootfile.txt')
const subDir0 = path.join(parentDir, 'subdir0')
const subFile0 = path.join(subDir0, 'subfile0.txt')
const subDir1 = path.join(parentDir, 'subdir1')
const subFile1 = path.join(subDir1, 'subfile1.txt')
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'})
])
// Begin the child watchers and keep them alive
const subWatcher0 = watchPath(subDir0, {}, () => {})
const subWatcherChanges0 = waitForChanges(subWatcher0, subFile0)
const subWatcher1 = watchPath(subDir1, {}, () => {})
const subWatcherChanges1 = waitForChanges(subWatcher1, subFile1)
await Promise.all(
[subWatcher0, subWatcher1].map(watcher => watcher.getStartPromise())
)
expect(subWatcher0.native).not.toBe(subWatcher1.native)
// Create the parent watcher
const parentWatcher = watchPath(parentDir, {}, () => {})
const parentWatcherChanges = waitForChanges(parentWatcher, rootFile, subFile0, subFile1)
await parentWatcher.getStartPromise()
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'})
])
await Promise.all([
subWatcherChanges0,
subWatcherChanges1,
parentWatcherChanges
])
})
})
})

View File

@@ -4,6 +4,7 @@ Project = require '../src/project'
fs = require 'fs-plus'
path = require 'path'
{Directory} = require 'pathwatcher'
{stopAllWatchers} = require '../src/path-watcher'
GitRepository = require '../src/git-repository'
describe "Project", ->
@@ -13,9 +14,6 @@ describe "Project", ->
# Wait for project's service consumers to be asynchronously added
waits(1)
afterEach ->
temp.cleanupSync()
describe "serialization", ->
deserializedProject = null
@@ -548,6 +546,59 @@ describe "Project", ->
atom.project.removePath(ftpURI)
expect(atom.project.getPaths()).toEqual []
describe ".onDidChangeFiles()", ->
sub = []
events = []
checkCallback = ->
beforeEach ->
sub = atom.project.onDidChangeFiles (incoming) ->
events.push incoming...
checkCallback()
afterEach ->
sub.dispose()
waitForEvents = (paths) ->
remaining = new Set(fs.realpathSync(p) for p in paths)
new Promise (resolve, reject) ->
checkCallback = ->
remaining.delete(event.path) for event in events
resolve() if remaining.size is 0
expire = ->
checkCallback = ->
console.error "Paths not seen:", Array.from(remaining)
reject(new Error('Expired before all expected events were delivered.'))
checkCallback()
setTimeout expire, 2000
it "reports filesystem changes within project paths", ->
dirOne = temp.mkdirSync('atom-spec-project-one')
fileOne = path.join(dirOne, 'file-one.txt')
fileTwo = path.join(dirOne, 'file-two.txt')
dirTwo = temp.mkdirSync('atom-spec-project-two')
fileThree = path.join(dirTwo, 'file-three.txt')
# Ensure that all preexisting watchers are stopped
waitsForPromise -> stopAllWatchers()
runs -> atom.project.setPaths([dirOne])
waitsForPromise -> atom.project.watchersByPath[dirOne].getStartPromise()
runs ->
expect(atom.project.watchersByPath[dirTwo]).toEqual undefined
fs.writeFileSync fileThree, "three\n"
fs.writeFileSync fileTwo, "two\n"
fs.writeFileSync fileOne, "one\n"
waitsForPromise -> waitForEvents [fileOne, fileTwo]
runs ->
expect(events.some (event) -> event.path is fileThree).toBeFalsy()
describe ".onDidAddBuffer()", ->
it "invokes the callback with added text buffers", ->
buffers = []

View File

@@ -37,7 +37,8 @@ describe "Windows Squirrel Update", ->
WinShell.folderBackgroundContextMenu = new FakeShellOption()
afterEach ->
temp.cleanupSync()
try
temp.cleanupSync()
it "quits the app on all squirrel events", ->
app = quit: jasmine.createSpy('quit')

View File

@@ -15,7 +15,11 @@ describe('StyleManager', () => {
})
afterEach(() => {
temp.cleanupSync()
try {
temp.cleanupSync()
} catch (e) {
// Do nothing
}
})
describe('::addStyleSheet(source, params)', () => {

View File

@@ -9,7 +9,8 @@ describe "atom.themes", ->
afterEach ->
atom.themes.deactivateThemes()
temp.cleanupSync()
try
temp.cleanupSync()
describe "theme getters and setters", ->
beforeEach ->

View File

@@ -1,5 +1,5 @@
const TitleBar = require('../src/title-bar')
const temp = require('temp')
const temp = require('temp').track()
describe('TitleBar', () => {
it('updates its title when document.title changes', () => {

View File

@@ -28,7 +28,11 @@ describe('updateProcessEnv(launchEnv)', function () {
}
process.env = originalProcessEnv
process.platform = originalProcessPlatform
temp.cleanupSync()
try {
temp.cleanupSync()
} catch (e) {
// Do nothing
}
})
describe('when the launch environment appears to come from a shell', function () {

View File

@@ -9,7 +9,13 @@ const {Disposable} = require('event-kit')
const {it, fit, ffit, fffit, beforeEach, afterEach} = require('./async-spec-helpers')
describe('WorkspaceElement', () => {
afterEach(() => { temp.cleanupSync() })
afterEach(() => {
try {
temp.cleanupSync()
} catch (e) {
// Do nothing
}
})
describe('when the workspace element is focused', () => {
it('transfers focus to the active pane', () => {

View File

@@ -25,7 +25,13 @@ describe('Workspace', () => {
waitsForPromise(() => atom.workspace.itemLocationStore.clear())
})
afterEach(() => temp.cleanupSync())
afterEach(() => {
try {
temp.cleanupSync()
} catch (e) {
// Do nothing
}
})
function simulateReload() {
waitsForPromise(() => {