mirror of
https://github.com/atom/atom.git
synced 2026-04-06 03:02:13 -04:00
This, along with using a temporary directory as the ATOM_HOME, will make sure that tests won't share any state with one another, possibly increasing the level of resiliency of the suite.
419 lines
18 KiB
JavaScript
419 lines
18 KiB
JavaScript
/** @babel */
|
|
|
|
import season from 'season'
|
|
import dedent from 'dedent'
|
|
import electron from 'electron'
|
|
import fs from 'fs-plus'
|
|
import path from 'path'
|
|
import temp from 'temp'
|
|
import AtomApplication from '../../src/main-process/atom-application'
|
|
import parseCommandLine from '../../src/main-process/parse-command-line'
|
|
|
|
const ATOM_RESOURCE_PATH = path.resolve(__dirname, '..', '..')
|
|
|
|
describe('AtomApplication', function () {
|
|
this.timeout(20000)
|
|
|
|
let originalAtomHome, atomApplicationsToDestroy
|
|
|
|
beforeEach(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'))
|
|
season.writeFileSync(path.join(process.env.ATOM_HOME, 'config.cson'), {
|
|
'*': {welcome: {showOnStartup: false}}
|
|
})
|
|
atomApplicationsToDestroy = []
|
|
})
|
|
|
|
afterEach(async function () {
|
|
process.env.ATOM_HOME = originalAtomHome
|
|
for (let atomApplication of atomApplicationsToDestroy) {
|
|
await atomApplication.destroy()
|
|
}
|
|
await clearElectronSession()
|
|
})
|
|
|
|
describe('launch', function () {
|
|
it('can open to a specific line number of a file', async function () {
|
|
const filePath = path.join(makeTempDir(), 'new-file')
|
|
fs.writeFileSync(filePath, '1\n2\n3\n4\n')
|
|
const atomApplication = buildAtomApplication()
|
|
|
|
const window = atomApplication.launch(parseCommandLine([filePath + ':3']))
|
|
await window.loadedPromise
|
|
|
|
const cursorRow = await evalInWebContents(window.browserWindow.webContents, function (sendBackToMainProcess) {
|
|
atom.workspace.onDidChangeActivePaneItem(function (textEditor) {
|
|
sendBackToMainProcess(textEditor.getCursorBufferPosition().row)
|
|
})
|
|
})
|
|
|
|
assert.equal(cursorRow, 2)
|
|
})
|
|
|
|
it('can open to a specific line and column of a file', async function () {
|
|
const filePath = path.join(makeTempDir(), 'new-file')
|
|
fs.writeFileSync(filePath, '1\n2\n3\n4\n')
|
|
const atomApplication = buildAtomApplication()
|
|
|
|
const window = atomApplication.launch(parseCommandLine([filePath + ':2:2']))
|
|
await window.loadedPromise
|
|
|
|
const cursorPosition = await evalInWebContents(window.browserWindow.webContents, function (sendBackToMainProcess) {
|
|
atom.workspace.onDidChangeActivePaneItem(function (textEditor) {
|
|
sendBackToMainProcess(textEditor.getCursorBufferPosition())
|
|
})
|
|
})
|
|
|
|
assert.deepEqual(cursorPosition, {row: 1, column: 1})
|
|
})
|
|
|
|
it('removes all trailing whitespace and colons from the specified path', async function () {
|
|
let filePath = path.join(makeTempDir(), 'new-file')
|
|
fs.writeFileSync(filePath, '1\n2\n3\n4\n')
|
|
const atomApplication = buildAtomApplication()
|
|
|
|
const window = atomApplication.launch(parseCommandLine([filePath + ':: ']))
|
|
await window.loadedPromise
|
|
|
|
const openedPath = await evalInWebContents(window.browserWindow.webContents, function (sendBackToMainProcess) {
|
|
atom.workspace.onDidChangeActivePaneItem(function (textEditor) {
|
|
sendBackToMainProcess(textEditor.getPath())
|
|
})
|
|
})
|
|
|
|
assert.equal(openedPath, filePath)
|
|
})
|
|
|
|
it('positions new windows at an offset distance from the previous window', async function () {
|
|
const atomApplication = buildAtomApplication()
|
|
|
|
const window1 = atomApplication.launch(parseCommandLine([]))
|
|
await window1.loadedPromise
|
|
window1.browserWindow.setBounds({width: 400, height: 400, x: 0, y: 0})
|
|
|
|
const window2 = atomApplication.launch(parseCommandLine([]))
|
|
await window2.loadedPromise
|
|
|
|
window1Dimensions = window1.getDimensions()
|
|
window2Dimensions = window2.getDimensions()
|
|
assert.isAbove(window2Dimensions.x, window1Dimensions.x)
|
|
assert.isAbove(window2Dimensions.y, window1Dimensions.y)
|
|
})
|
|
|
|
it('reuses existing windows when opening paths, but not directories', async function () {
|
|
const dirAPath = makeTempDir("a")
|
|
const dirBPath = makeTempDir("b")
|
|
const dirCPath = makeTempDir("c")
|
|
const existingDirCFilePath = path.join(dirCPath, 'existing-file')
|
|
fs.writeFileSync(existingDirCFilePath, 'this is an existing file')
|
|
|
|
const atomApplication = buildAtomApplication()
|
|
const window1 = atomApplication.launch(parseCommandLine([path.join(dirAPath, 'new-file')]))
|
|
await window1.loadedPromise
|
|
|
|
let activeEditorPath
|
|
activeEditorPath = await evalInWebContents(window1.browserWindow.webContents, function (sendBackToMainProcess) {
|
|
atom.workspace.onDidChangeActivePaneItem(function (textEditor) {
|
|
sendBackToMainProcess(textEditor.getPath())
|
|
})
|
|
})
|
|
assert.equal(activeEditorPath, path.join(dirAPath, 'new-file'))
|
|
|
|
// Reuses the window when opening *files*, even if they're in a different directory
|
|
// Does not change the project paths when doing so.
|
|
const reusedWindow = atomApplication.launch(parseCommandLine([existingDirCFilePath]))
|
|
assert.equal(reusedWindow, window1)
|
|
assert.deepEqual(atomApplication.windows, [window1])
|
|
activeEditorPath = await evalInWebContents(window1.browserWindow.webContents, function (sendBackToMainProcess) {
|
|
atom.workspace.onDidChangeActivePaneItem(function (textEditor) {
|
|
sendBackToMainProcess(textEditor.getPath())
|
|
})
|
|
})
|
|
assert.equal(activeEditorPath, existingDirCFilePath)
|
|
assert.deepEqual(await getTreeViewRootDirectories(window1), [dirAPath])
|
|
|
|
// Opens new windows when opening directories
|
|
const window2 = atomApplication.launch(parseCommandLine([dirCPath]))
|
|
assert.notEqual(window2, window1)
|
|
await window2.loadedPromise
|
|
assert.deepEqual(await getTreeViewRootDirectories(window2), [dirCPath])
|
|
})
|
|
|
|
it('adds folders to existing windows when the --add option is used', async function () {
|
|
const dirAPath = makeTempDir("a")
|
|
const dirBPath = makeTempDir("b")
|
|
const dirCPath = makeTempDir("c")
|
|
const existingDirCFilePath = path.join(dirCPath, 'existing-file')
|
|
fs.writeFileSync(existingDirCFilePath, 'this is an existing file')
|
|
|
|
const atomApplication = buildAtomApplication()
|
|
const window1 = atomApplication.launch(parseCommandLine([path.join(dirAPath, 'new-file')]))
|
|
await window1.loadedPromise
|
|
|
|
let activeEditorPath
|
|
activeEditorPath = await evalInWebContents(window1.browserWindow.webContents, function (sendBackToMainProcess) {
|
|
atom.workspace.onDidChangeActivePaneItem(function (textEditor) {
|
|
sendBackToMainProcess(textEditor.getPath())
|
|
})
|
|
})
|
|
assert.equal(activeEditorPath, path.join(dirAPath, 'new-file'))
|
|
|
|
// When opening *files* with --add, reuses an existing window and adds
|
|
// parent directory to the project
|
|
let reusedWindow = atomApplication.launch(parseCommandLine([existingDirCFilePath, '--add']))
|
|
assert.equal(reusedWindow, window1)
|
|
assert.deepEqual(atomApplication.windows, [window1])
|
|
activeEditorPath = await evalInWebContents(window1.browserWindow.webContents, function (sendBackToMainProcess) {
|
|
atom.workspace.onDidChangeActivePaneItem(function (textEditor) {
|
|
sendBackToMainProcess(textEditor.getPath())
|
|
})
|
|
})
|
|
assert.equal(activeEditorPath, existingDirCFilePath)
|
|
assert.deepEqual(await getTreeViewRootDirectories(window1), [dirAPath, dirCPath])
|
|
|
|
// When opening *directories* with add reuses an existing window and adds
|
|
// the directory to the project
|
|
reusedWindow = atomApplication.launch(parseCommandLine([dirBPath, '-a']))
|
|
assert.equal(reusedWindow, window1)
|
|
assert.deepEqual(atomApplication.windows, [window1])
|
|
assert.deepEqual(await getTreeViewRootDirectories(window1), [dirAPath, dirCPath, dirBPath])
|
|
})
|
|
|
|
it('persists window state based on the project directories', async function () {
|
|
const tempDirPath = makeTempDir()
|
|
const atomApplication = buildAtomApplication()
|
|
const window1 = atomApplication.launch(parseCommandLine([path.join(tempDirPath, 'new-file')]))
|
|
await evalInWebContents(window1.browserWindow.webContents, function (sendBackToMainProcess) {
|
|
atom.workspace.onDidChangeActivePaneItem(function (textEditor) {
|
|
textEditor.insertText('Hello World!')
|
|
sendBackToMainProcess(null)
|
|
})
|
|
})
|
|
window1.close()
|
|
await window1.closedPromise
|
|
|
|
const window2 = atomApplication.launch(parseCommandLine([path.join(tempDirPath)]))
|
|
const window2Text = await evalInWebContents(window2.browserWindow.webContents, function (sendBackToMainProcess) {
|
|
atom.workspace.onDidChangeActivePaneItem(function (textEditor) {
|
|
sendBackToMainProcess(textEditor.getText())
|
|
})
|
|
})
|
|
|
|
assert.equal(window2Text, 'Hello World!')
|
|
})
|
|
|
|
it('shows all directories in the tree view when multiple directory paths are passed to Atom', async function () {
|
|
const dirAPath = makeTempDir("a")
|
|
const dirBPath = makeTempDir("b")
|
|
const dirBSubdirPath = path.join(dirBPath, 'c')
|
|
fs.mkdirSync(dirBSubdirPath)
|
|
|
|
const atomApplication = buildAtomApplication()
|
|
const window1 = atomApplication.launch(parseCommandLine([dirAPath, dirBPath]))
|
|
await window1.loadedPromise
|
|
|
|
await new Promise(function (resolve) {
|
|
setTimeout(resolve, 1000)
|
|
})
|
|
|
|
let treeViewPaths = await evalInWebContents(window1.browserWindow.webContents, function (sendBackToMainProcess) {
|
|
sendBackToMainProcess(
|
|
Array
|
|
.from(document.querySelectorAll('.tree-view .project-root > .header .name'))
|
|
.map(element => element.dataset.path)
|
|
)
|
|
})
|
|
assert.deepEqual(treeViewPaths, [dirAPath, dirBPath])
|
|
})
|
|
|
|
it('reuses windows with no project paths to open directories', async function () {
|
|
const tempDirPath = makeTempDir()
|
|
const atomApplication = buildAtomApplication()
|
|
const window1 = atomApplication.launch(parseCommandLine([]))
|
|
await window1.loadedPromise
|
|
|
|
const reusedWindow = atomApplication.launch(parseCommandLine([tempDirPath]))
|
|
assert.equal(reusedWindow, window1)
|
|
assert.deepEqual(await getTreeViewRootDirectories(window1), [tempDirPath])
|
|
})
|
|
|
|
it('opens a new window with a single untitled buffer when launched with no path, even if windows already exist', async function () {
|
|
const atomApplication = buildAtomApplication()
|
|
const window1 = atomApplication.launch(parseCommandLine([]))
|
|
await window1.loadedPromise
|
|
const window1EditorTitle = await evalInWebContents(window1.browserWindow.webContents, function (sendBackToMainProcess) {
|
|
sendBackToMainProcess(atom.workspace.getActiveTextEditor().getTitle())
|
|
})
|
|
assert.equal(window1EditorTitle, 'untitled')
|
|
|
|
const window2 = atomApplication.launch(parseCommandLine([]))
|
|
await window2.loadedPromise
|
|
const window2EditorTitle = await evalInWebContents(window1.browserWindow.webContents, function (sendBackToMainProcess) {
|
|
sendBackToMainProcess(atom.workspace.getActiveTextEditor().getTitle())
|
|
})
|
|
assert.equal(window2EditorTitle, 'untitled')
|
|
|
|
assert.deepEqual(atomApplication.windows, [window1, window2])
|
|
})
|
|
|
|
it('does not open an empty editor when opened with no path if the core.openEmptyEditorOnStart config setting is false', async function () {
|
|
const configPath = path.join(process.env.ATOM_HOME, 'config.cson')
|
|
const config = season.readFileSync(configPath)
|
|
if (!config['*'].core) config['*'].core = {}
|
|
config['*'].core.openEmptyEditorOnStart = false
|
|
season.writeFileSync(configPath, config)
|
|
|
|
const atomApplication = buildAtomApplication()
|
|
const window1 = atomApplication.launch(parseCommandLine([]))
|
|
await window1.loadedPromise
|
|
|
|
// wait a bit just to make sure we don't pass due to querying the render process before it loads
|
|
await getTimeoutPromise(1000)
|
|
|
|
const itemCount = await evalInWebContents(window1.browserWindow.webContents, function (sendBackToMainProcess) {
|
|
sendBackToMainProcess(atom.workspace.getActivePane().getItems().length)
|
|
})
|
|
assert.equal(itemCount, 0)
|
|
})
|
|
|
|
it('opens an empty text editor and loads its parent directory in the tree-view when launched with a new file path', async function () {
|
|
const atomApplication = buildAtomApplication()
|
|
const newFilePath = path.join(makeTempDir(), 'new-file')
|
|
const window = atomApplication.launch(parseCommandLine([newFilePath]))
|
|
await window.loadedPromise
|
|
const {editorTitle, editorText} = await evalInWebContents(window.browserWindow.webContents, function (sendBackToMainProcess) {
|
|
atom.workspace.onDidChangeActivePaneItem(function (editor) {
|
|
sendBackToMainProcess({editorTitle: editor.getTitle(), editorText: editor.getText()})
|
|
})
|
|
})
|
|
assert.equal(editorTitle, path.basename(newFilePath))
|
|
assert.equal(editorText, '')
|
|
assert.deepEqual(await getTreeViewRootDirectories(window), [path.dirname(newFilePath)])
|
|
})
|
|
|
|
it('opens an empty text editor and loads its parent directory in the tree-view when launched with a new file path in a remote directory', async function () {
|
|
// Disable the tree-view because it will try to enumerate the contents of
|
|
// the remote directory and, since it doesn't exist, throw an error.
|
|
const configPath = path.join(process.env.ATOM_HOME, 'config.cson')
|
|
const config = season.readFileSync(configPath)
|
|
if (!config['*'].core) config['*'].core = {}
|
|
config['*'].core.disabledPackages = ['tree-view']
|
|
season.writeFileSync(configPath, config)
|
|
|
|
const atomApplication = buildAtomApplication()
|
|
const newRemoteFilePath = 'remote://server:3437/some/directory/path'
|
|
const window = atomApplication.launch(parseCommandLine([newRemoteFilePath]))
|
|
await window.loadedPromise
|
|
const {projectPaths, editorTitle, editorText} = await evalInWebContents(window.browserWindow.webContents, function (sendBackToMainProcess) {
|
|
atom.workspace.onDidChangeActivePaneItem(function (editor) {
|
|
sendBackToMainProcess({
|
|
projectPaths: atom.project.getPaths(),
|
|
editorTitle: editor.getTitle(),
|
|
editorText: editor.getText()
|
|
})
|
|
})
|
|
})
|
|
assert.deepEqual(projectPaths, [newRemoteFilePath])
|
|
assert.equal(editorTitle, path.basename(newRemoteFilePath))
|
|
assert.equal(editorText, '')
|
|
})
|
|
|
|
it('reopens any previously opened windows when launched with no path', async function () {
|
|
const atomApplication1 = buildAtomApplication()
|
|
const app1Window1 = atomApplication1.launch(parseCommandLine([makeTempDir()]))
|
|
await app1Window1.loadedPromise
|
|
const app1Window2 = atomApplication1.launch(parseCommandLine([makeTempDir()]))
|
|
await app1Window2.loadedPromise
|
|
|
|
const atomApplication2 = buildAtomApplication()
|
|
const [app2Window1, app2Window2] = atomApplication2.launch(parseCommandLine([]))
|
|
await app2Window1.loadedPromise
|
|
await app2Window2.loadedPromise
|
|
assert.deepEqual(await getTreeViewRootDirectories(app2Window1), await getTreeViewRootDirectories(app1Window1))
|
|
assert.deepEqual(await getTreeViewRootDirectories(app2Window2), await getTreeViewRootDirectories(app1Window2))
|
|
})
|
|
|
|
it('does not reopen any previously opened windows when launched with no path and `core.restorePreviousWindowsOnStart` is false', async function () {
|
|
const atomApplication1 = buildAtomApplication()
|
|
const app1Window1 = atomApplication1.launch(parseCommandLine([makeTempDir()]))
|
|
await app1Window1.loadedPromise
|
|
const app1Window2 = atomApplication1.launch(parseCommandLine([makeTempDir()]))
|
|
await app1Window2.loadedPromise
|
|
|
|
const configPath = path.join(process.env.ATOM_HOME, 'config.cson')
|
|
const config = season.readFileSync(configPath)
|
|
if (!config['*'].core) config['*'].core = {}
|
|
config['*'].core.restorePreviousWindowsOnStart = false
|
|
season.writeFileSync(configPath, config)
|
|
|
|
const atomApplication2 = buildAtomApplication()
|
|
const app2Window = atomApplication2.launch(parseCommandLine([]))
|
|
await app2Window.loadedPromise
|
|
assert.deepEqual(await getTreeViewRootDirectories(app2Window), [])
|
|
})
|
|
})
|
|
|
|
function buildAtomApplication () {
|
|
const atomApplication = new AtomApplication({
|
|
resourcePath: ATOM_RESOURCE_PATH,
|
|
atomHomeDirPath: process.env.ATOM_HOME
|
|
})
|
|
atomApplicationsToDestroy.push(atomApplication)
|
|
return atomApplication
|
|
}
|
|
|
|
function makeTempDir (name) {
|
|
return fs.realpathSync(temp.mkdirSync(name))
|
|
}
|
|
|
|
let channelIdCounter = 0
|
|
function evalInWebContents (webContents, source) {
|
|
const channelId = 'eval-result-' + channelIdCounter++
|
|
return new Promise(function (resolve) {
|
|
electron.ipcMain.on(channelId, receiveResult)
|
|
|
|
function receiveResult (event, result) {
|
|
electron.ipcMain.removeListener('eval-result', receiveResult)
|
|
resolve(result)
|
|
}
|
|
|
|
webContents.executeJavaScript(dedent`
|
|
function sendBackToMainProcess (result) {
|
|
require('electron').ipcRenderer.send('${channelId}', result)
|
|
}
|
|
(${source})(sendBackToMainProcess)
|
|
`)
|
|
})
|
|
}
|
|
|
|
function getTreeViewRootDirectories (atomWindow) {
|
|
return evalInWebContents(atomWindow.browserWindow.webContents, function (sendBackToMainProcess) {
|
|
sendBackToMainProcess(
|
|
Array
|
|
.from(document.querySelectorAll('.tree-view .project-root > .header .name'))
|
|
.map(element => element.dataset.path)
|
|
)
|
|
})
|
|
}
|
|
|
|
function getTimeoutPromise (timeout) {
|
|
return new Promise(function (resolve) {
|
|
global.setTimeout(resolve, timeout)
|
|
})
|
|
}
|
|
|
|
function clearElectronSession () {
|
|
return new Promise(function (resolve) {
|
|
electron.session.defaultSession.clearStorageData(function () {
|
|
// Resolve promise on next tick, otherwise the process stalls. This
|
|
// might be a bug in Electron, but it's probably fixed on the newer
|
|
// versions.
|
|
process.nextTick(resolve)
|
|
})
|
|
})
|
|
}
|
|
})
|