2012-08-28 02:36:36 +04:00
|
|
|
$ = require 'jquery'
|
2013-05-17 02:00:55 +04:00
|
|
|
{$$} = require 'space-pen'
|
2013-04-03 22:01:01 +04:00
|
|
|
fsUtils = require 'fs-utils'
|
2013-06-13 03:20:40 +04:00
|
|
|
path = require 'path'
|
2013-03-04 23:12:20 +04:00
|
|
|
{less} = require 'less'
|
2013-05-17 01:13:25 +04:00
|
|
|
WindowEventHandler = require 'window-event-handler'
|
2012-08-28 02:36:36 +04:00
|
|
|
|
|
|
|
describe "Window", ->
|
2013-05-17 01:13:25 +04:00
|
|
|
[projectPath, windowEventHandler] = []
|
2013-01-08 02:26:53 +04:00
|
|
|
|
2012-08-28 02:36:36 +04:00
|
|
|
beforeEach ->
|
2013-06-12 02:48:11 +04:00
|
|
|
atom.getLoadSettings().initialPath = project.getPath()
|
2013-08-30 05:40:02 +04:00
|
|
|
project.destroy()
|
2013-05-17 01:13:25 +04:00
|
|
|
windowEventHandler = new WindowEventHandler()
|
2013-04-10 01:47:06 +04:00
|
|
|
window.deserializeEditorWindow()
|
2013-02-20 23:30:39 +04:00
|
|
|
projectPath = project.getPath()
|
2012-08-28 02:36:36 +04:00
|
|
|
|
|
|
|
afterEach ->
|
2013-05-17 01:13:25 +04:00
|
|
|
windowEventHandler.unsubscribe()
|
2012-08-28 02:36:36 +04:00
|
|
|
$(window).off 'beforeunload'
|
|
|
|
|
2013-02-06 00:03:56 +04:00
|
|
|
describe "when the window is loaded", ->
|
2013-02-05 04:02:09 +04:00
|
|
|
it "doesn't have .is-blurred on the body tag", ->
|
2013-02-06 00:03:56 +04:00
|
|
|
expect($("body")).not.toHaveClass("is-blurred")
|
2013-02-02 03:40:53 +04:00
|
|
|
|
2013-02-06 00:59:26 +04:00
|
|
|
describe "when the window is blurred", ->
|
2013-02-06 00:03:56 +04:00
|
|
|
beforeEach ->
|
|
|
|
$(window).trigger 'blur'
|
|
|
|
|
|
|
|
afterEach ->
|
|
|
|
$('body').removeClass('is-blurred')
|
|
|
|
|
|
|
|
it "adds the .is-blurred class on the body", ->
|
|
|
|
expect($("body")).toHaveClass("is-blurred")
|
|
|
|
|
|
|
|
describe "when the window is focused again", ->
|
|
|
|
it "removes the .is-blurred class from the body", ->
|
|
|
|
$(window).trigger 'focus'
|
|
|
|
expect($("body")).not.toHaveClass("is-blurred")
|
2013-02-02 03:40:53 +04:00
|
|
|
|
2013-03-13 02:54:07 +04:00
|
|
|
describe "window:close event", ->
|
2013-06-27 17:31:07 +04:00
|
|
|
it "closes the window", ->
|
2013-08-28 21:42:56 +04:00
|
|
|
spyOn(atom, 'close')
|
2013-06-27 17:31:07 +04:00
|
|
|
$(window).trigger 'window:close'
|
2013-08-28 21:42:56 +04:00
|
|
|
expect(atom.close).toHaveBeenCalled()
|
2013-01-08 01:15:33 +04:00
|
|
|
|
2013-06-27 17:31:07 +04:00
|
|
|
it "emits the beforeunload event", ->
|
|
|
|
$(window).off 'beforeunload'
|
|
|
|
beforeunload = jasmine.createSpy('beforeunload').andReturn(false)
|
|
|
|
$(window).on 'beforeunload', beforeunload
|
|
|
|
|
|
|
|
$(window).trigger 'window:close'
|
|
|
|
expect(beforeunload).toHaveBeenCalled()
|
|
|
|
|
|
|
|
describe "beforeunload event", ->
|
2013-03-13 02:54:07 +04:00
|
|
|
describe "when pane items are are modified", ->
|
2013-06-27 17:31:07 +04:00
|
|
|
it "prompts user to save and and calls rootView.confirmClose", ->
|
|
|
|
spyOn(rootView, 'confirmClose').andCallThrough()
|
|
|
|
spyOn(atom, "confirmSync").andReturn(2)
|
|
|
|
editSession = rootView.open("sample.js")
|
|
|
|
editSession.insertText("I look different, I feel different.")
|
|
|
|
$(window).trigger 'beforeunload'
|
|
|
|
expect(rootView.confirmClose).toHaveBeenCalled()
|
|
|
|
expect(atom.confirmSync).toHaveBeenCalled()
|
|
|
|
|
|
|
|
it "prompts user to save and handler returns true if don't save", ->
|
2013-06-27 11:07:38 +04:00
|
|
|
spyOn(atom, "confirmSync").andReturn(2)
|
2013-03-13 02:43:37 +04:00
|
|
|
editSession = rootView.open("sample.js")
|
|
|
|
editSession.insertText("I look different, I feel different.")
|
2013-06-27 17:31:07 +04:00
|
|
|
expect(window.onbeforeunload(new Event('beforeunload'))).toBeTruthy()
|
2013-06-27 11:07:38 +04:00
|
|
|
expect(atom.confirmSync).toHaveBeenCalled()
|
2013-03-13 02:43:37 +04:00
|
|
|
|
2013-06-27 17:31:07 +04:00
|
|
|
it "prompts user to save and handler returns false if dialog is canceled", ->
|
2013-06-27 11:07:38 +04:00
|
|
|
spyOn(atom, "confirmSync").andReturn(1)
|
2013-03-13 02:43:37 +04:00
|
|
|
editSession = rootView.open("sample.js")
|
|
|
|
editSession.insertText("I look different, I feel different.")
|
2013-06-27 17:31:07 +04:00
|
|
|
expect(window.onbeforeunload(new Event('beforeunload'))).toBeFalsy()
|
2013-06-27 11:07:38 +04:00
|
|
|
expect(atom.confirmSync).toHaveBeenCalled()
|
2013-01-08 01:15:33 +04:00
|
|
|
|
2012-08-28 02:36:36 +04:00
|
|
|
describe "requireStylesheet(path)", ->
|
2013-03-06 05:03:28 +04:00
|
|
|
it "synchronously loads css at the given path and installs a style tag for it in the head", ->
|
|
|
|
cssPath = project.resolve('css.css')
|
2012-08-28 02:36:36 +04:00
|
|
|
lengthBefore = $('head style').length
|
|
|
|
|
2013-03-06 05:03:28 +04:00
|
|
|
requireStylesheet(cssPath)
|
|
|
|
expect($('head style').length).toBe lengthBefore + 1
|
2012-08-28 02:36:36 +04:00
|
|
|
|
2013-03-06 05:03:28 +04:00
|
|
|
element = $('head style[id*="css.css"]')
|
|
|
|
expect(element.attr('id')).toBe cssPath
|
2013-04-03 22:01:01 +04:00
|
|
|
expect(element.text()).toBe fsUtils.read(cssPath)
|
2012-08-28 02:36:36 +04:00
|
|
|
|
|
|
|
# doesn't append twice
|
2013-03-06 05:03:28 +04:00
|
|
|
requireStylesheet(cssPath)
|
2012-08-28 02:36:36 +04:00
|
|
|
expect($('head style').length).toBe lengthBefore + 1
|
|
|
|
|
2013-03-06 05:03:28 +04:00
|
|
|
$('head style[id*="css.css"]').remove()
|
|
|
|
|
2013-03-26 07:34:51 +04:00
|
|
|
it "synchronously loads and parses less files at the given path and installs a style tag for it in the head", ->
|
2013-03-06 05:03:28 +04:00
|
|
|
lessPath = project.resolve('sample.less')
|
2012-08-28 02:36:36 +04:00
|
|
|
lengthBefore = $('head style').length
|
2013-03-06 05:03:28 +04:00
|
|
|
requireStylesheet(lessPath)
|
2012-08-28 02:36:36 +04:00
|
|
|
expect($('head style').length).toBe lengthBefore + 1
|
|
|
|
|
2013-03-06 05:03:28 +04:00
|
|
|
element = $('head style[id*="sample.less"]')
|
|
|
|
expect(element.attr('id')).toBe lessPath
|
|
|
|
expect(element.text()).toBe """
|
|
|
|
#header {
|
|
|
|
color: #4d926f;
|
|
|
|
}
|
|
|
|
h2 {
|
|
|
|
color: #4d926f;
|
|
|
|
}
|
2012-08-28 02:36:36 +04:00
|
|
|
|
2013-03-06 05:03:28 +04:00
|
|
|
"""
|
2012-08-28 02:36:36 +04:00
|
|
|
|
|
|
|
# doesn't append twice
|
2013-03-06 05:03:28 +04:00
|
|
|
requireStylesheet(lessPath)
|
2012-08-28 02:36:36 +04:00
|
|
|
expect($('head style').length).toBe lengthBefore + 1
|
2013-03-06 05:03:28 +04:00
|
|
|
$('head style[id*="sample.less"]').remove()
|
2012-08-28 02:36:36 +04:00
|
|
|
|
2013-03-26 07:34:51 +04:00
|
|
|
it "supports requiring css and less stylesheets without an explicit extension", ->
|
|
|
|
requireStylesheet 'fixtures/css'
|
|
|
|
expect($('head style[id*="css.css"]').attr('id')).toBe project.resolve('css.css')
|
|
|
|
requireStylesheet 'fixtures/sample'
|
|
|
|
expect($('head style[id*="sample.less"]').attr('id')).toBe project.resolve('sample.less')
|
|
|
|
|
|
|
|
$('head style[id*="css.css"]').remove()
|
|
|
|
$('head style[id*="sample.less"]').remove()
|
|
|
|
|
|
|
|
describe ".removeStylesheet(path)", ->
|
2012-12-27 05:35:19 +04:00
|
|
|
it "removes styling applied by given stylesheet path", ->
|
2013-06-13 03:20:40 +04:00
|
|
|
cssPath = require.resolve(path.join("fixtures", "css.css"))
|
2012-12-27 05:35:19 +04:00
|
|
|
|
|
|
|
expect($(document.body).css('font-weight')).not.toBe("bold")
|
|
|
|
requireStylesheet(cssPath)
|
|
|
|
expect($(document.body).css('font-weight')).toBe("bold")
|
|
|
|
removeStylesheet(cssPath)
|
|
|
|
expect($(document.body).css('font-weight')).not.toBe("bold")
|
|
|
|
|
2013-04-11 03:27:35 +04:00
|
|
|
describe ".unloadEditorWindow()", ->
|
2013-03-22 04:23:37 +04:00
|
|
|
it "saves the serialized state of the window so it can be deserialized after reload", ->
|
2013-05-23 03:44:09 +04:00
|
|
|
rootViewState = rootView.serialize()
|
|
|
|
syntaxState = syntax.serialize()
|
2013-02-20 00:12:29 +04:00
|
|
|
|
2013-04-11 03:27:35 +04:00
|
|
|
window.unloadEditorWindow()
|
2013-02-20 00:12:29 +04:00
|
|
|
|
2013-06-21 02:57:34 +04:00
|
|
|
expect(atom.getWindowState().getObject('rootView')).toEqual rootViewState.toObject()
|
2013-06-21 00:10:36 +04:00
|
|
|
expect(atom.getWindowState().getObject('syntax')).toEqual syntaxState
|
|
|
|
expect(atom.saveWindowState).toHaveBeenCalled()
|
2013-03-22 04:23:37 +04:00
|
|
|
|
2012-08-28 02:36:36 +04:00
|
|
|
it "unsubscribes from all buffers", ->
|
2012-08-31 02:19:38 +04:00
|
|
|
rootView.open('sample.js')
|
2013-02-25 22:36:24 +04:00
|
|
|
buffer = rootView.getActivePaneItem().buffer
|
|
|
|
rootView.getActivePane().splitRight()
|
|
|
|
expect(window.rootView.find('.editor').length).toBe 2
|
2012-08-28 02:36:36 +04:00
|
|
|
|
2013-04-11 03:27:35 +04:00
|
|
|
window.unloadEditorWindow()
|
2012-08-28 02:36:36 +04:00
|
|
|
|
2013-02-25 22:36:24 +04:00
|
|
|
expect(buffer.subscriptionCount()).toBe 0
|
2013-02-20 22:18:19 +04:00
|
|
|
|
2013-03-07 04:06:17 +04:00
|
|
|
describe ".deserialize(state)", ->
|
|
|
|
class Foo
|
|
|
|
@deserialize: ({name}) -> new Foo(name)
|
|
|
|
constructor: (@name) ->
|
|
|
|
|
|
|
|
beforeEach ->
|
|
|
|
registerDeserializer(Foo)
|
|
|
|
|
|
|
|
afterEach ->
|
|
|
|
unregisterDeserializer(Foo)
|
|
|
|
|
|
|
|
it "calls deserialize on the deserializer for the given state object, or returns undefined if one can't be found", ->
|
2013-08-29 20:18:52 +04:00
|
|
|
spyOn(console, 'warn')
|
2013-03-07 04:06:17 +04:00
|
|
|
object = deserialize({ deserializer: 'Foo', name: 'Bar' })
|
|
|
|
expect(object.name).toBe 'Bar'
|
|
|
|
expect(deserialize({ deserializer: 'Bogus' })).toBeUndefined()
|
|
|
|
|
|
|
|
describe "when the deserializer has a version", ->
|
|
|
|
beforeEach ->
|
|
|
|
Foo.version = 2
|
|
|
|
|
|
|
|
describe "when the deserialized state has a matching version", ->
|
|
|
|
it "attempts to deserialize the state", ->
|
|
|
|
object = deserialize({ deserializer: 'Foo', version: 2, name: 'Bar' })
|
|
|
|
expect(object.name).toBe 'Bar'
|
|
|
|
|
|
|
|
describe "when the deserialized state has a non-matching version", ->
|
|
|
|
it "returns undefined", ->
|
|
|
|
expect(deserialize({ deserializer: 'Foo', version: 3, name: 'Bar' })).toBeUndefined()
|
|
|
|
expect(deserialize({ deserializer: 'Foo', version: 1, name: 'Bar' })).toBeUndefined()
|
|
|
|
expect(deserialize({ deserializer: 'Foo', name: 'Bar' })).toBeUndefined()
|
2013-04-08 16:17:40 +04:00
|
|
|
|
|
|
|
describe "drag and drop", ->
|
|
|
|
buildDragEvent = (type, files) ->
|
|
|
|
dataTransfer =
|
|
|
|
files: files
|
|
|
|
data: {}
|
|
|
|
setData: (key, value) -> @data[key] = value
|
|
|
|
getData: (key) -> @data[key]
|
|
|
|
|
|
|
|
event = $.Event(type)
|
|
|
|
event.originalEvent = { dataTransfer }
|
|
|
|
event.preventDefault = ->
|
|
|
|
event.stopPropagation = ->
|
|
|
|
event
|
|
|
|
|
|
|
|
describe "when a file is dragged to window", ->
|
|
|
|
it "opens it", ->
|
|
|
|
spyOn(atom, "open")
|
|
|
|
event = buildDragEvent("drop", [ {path: "/fake1"}, {path: "/fake2"} ])
|
|
|
|
window.onDrop(event)
|
2013-08-21 02:24:30 +04:00
|
|
|
expect(atom.open.callCount).toBe 1
|
|
|
|
expect(atom.open.argsForCall[0][0]).toEqual pathsToOpen: ['/fake1', '/fake2']
|
2013-04-08 16:17:40 +04:00
|
|
|
|
|
|
|
describe "when a non-file is dragged to window", ->
|
|
|
|
it "does nothing", ->
|
|
|
|
spyOn(atom, "open")
|
|
|
|
event = buildDragEvent("drop", [])
|
|
|
|
window.onDrop(event)
|
|
|
|
expect(atom.open).not.toHaveBeenCalled()
|
2013-05-02 08:35:29 +04:00
|
|
|
|
|
|
|
describe "when a link is clicked", ->
|
|
|
|
it "opens the http/https links in an external application", ->
|
2013-05-20 06:05:19 +04:00
|
|
|
shell = require 'shell'
|
|
|
|
spyOn(shell, 'openExternal')
|
2013-05-02 08:35:29 +04:00
|
|
|
|
|
|
|
$("<a href='http://github.com'>the website</a>").appendTo(document.body).click().remove()
|
2013-05-20 06:05:19 +04:00
|
|
|
expect(shell.openExternal).toHaveBeenCalled()
|
2013-05-23 10:50:21 +04:00
|
|
|
expect(shell.openExternal.argsForCall[0][0]).toBe "http://github.com"
|
2013-05-02 08:35:29 +04:00
|
|
|
|
2013-05-20 06:05:19 +04:00
|
|
|
shell.openExternal.reset()
|
2013-05-02 08:35:29 +04:00
|
|
|
$("<a href='https://github.com'>the website</a>").appendTo(document.body).click().remove()
|
2013-05-20 06:05:19 +04:00
|
|
|
expect(shell.openExternal).toHaveBeenCalled()
|
2013-05-23 10:50:21 +04:00
|
|
|
expect(shell.openExternal.argsForCall[0][0]).toBe "https://github.com"
|
2013-05-02 08:35:29 +04:00
|
|
|
|
2013-05-20 06:05:19 +04:00
|
|
|
shell.openExternal.reset()
|
2013-05-02 08:35:29 +04:00
|
|
|
$("<a href=''>the website</a>").appendTo(document.body).click().remove()
|
2013-05-20 06:05:19 +04:00
|
|
|
expect(shell.openExternal).not.toHaveBeenCalled()
|
2013-05-02 08:35:29 +04:00
|
|
|
|
2013-05-20 06:05:19 +04:00
|
|
|
shell.openExternal.reset()
|
2013-05-02 08:35:29 +04:00
|
|
|
$("<a href='#scroll-me'>link</a>").appendTo(document.body).click().remove()
|
2013-05-20 06:05:19 +04:00
|
|
|
expect(shell.openExternal).not.toHaveBeenCalled()
|
2013-05-17 02:00:55 +04:00
|
|
|
|
|
|
|
describe "core:focus-next and core:focus-previous", ->
|
|
|
|
describe "when there is no currently focused element", ->
|
|
|
|
it "focuses the element with the lowest/highest tabindex", ->
|
|
|
|
elements = $$ ->
|
|
|
|
@div =>
|
|
|
|
@button tabindex: 2
|
|
|
|
@input tabindex: 1
|
|
|
|
|
|
|
|
elements.attachToDom()
|
|
|
|
|
|
|
|
elements.trigger "core:focus-next"
|
|
|
|
expect(elements.find("[tabindex=1]:focus")).toExist()
|
|
|
|
|
|
|
|
$(":focus").blur()
|
|
|
|
|
|
|
|
elements.trigger "core:focus-previous"
|
|
|
|
expect(elements.find("[tabindex=2]:focus")).toExist()
|
|
|
|
|
|
|
|
describe "when a tabindex is set on the currently focused element", ->
|
|
|
|
it "focuses the element with the next highest tabindex", ->
|
|
|
|
elements = $$ ->
|
|
|
|
@div =>
|
|
|
|
@input tabindex: 1
|
|
|
|
@button tabindex: 2
|
|
|
|
@button tabindex: 5
|
|
|
|
@input tabindex: -1
|
|
|
|
@input tabindex: 3
|
|
|
|
@button tabindex: 7
|
|
|
|
|
|
|
|
elements.attachToDom()
|
|
|
|
elements.find("[tabindex=1]").focus()
|
|
|
|
|
|
|
|
elements.trigger "core:focus-next"
|
|
|
|
expect(elements.find("[tabindex=2]:focus")).toExist()
|
|
|
|
|
|
|
|
elements.trigger "core:focus-next"
|
|
|
|
expect(elements.find("[tabindex=3]:focus")).toExist()
|
|
|
|
|
|
|
|
elements.focus().trigger "core:focus-next"
|
|
|
|
expect(elements.find("[tabindex=5]:focus")).toExist()
|
|
|
|
|
|
|
|
elements.focus().trigger "core:focus-next"
|
|
|
|
expect(elements.find("[tabindex=7]:focus")).toExist()
|
|
|
|
|
|
|
|
elements.focus().trigger "core:focus-next"
|
|
|
|
expect(elements.find("[tabindex=1]:focus")).toExist()
|
|
|
|
|
|
|
|
elements.trigger "core:focus-previous"
|
|
|
|
expect(elements.find("[tabindex=7]:focus")).toExist()
|
|
|
|
|
|
|
|
elements.trigger "core:focus-previous"
|
|
|
|
expect(elements.find("[tabindex=5]:focus")).toExist()
|
|
|
|
|
|
|
|
elements.focus().trigger "core:focus-previous"
|
|
|
|
expect(elements.find("[tabindex=3]:focus")).toExist()
|
|
|
|
|
|
|
|
elements.focus().trigger "core:focus-previous"
|
|
|
|
expect(elements.find("[tabindex=2]:focus")).toExist()
|
|
|
|
|
|
|
|
elements.focus().trigger "core:focus-previous"
|
|
|
|
expect(elements.find("[tabindex=1]:focus")).toExist()
|
|
|
|
|
|
|
|
it "skips disabled elements", ->
|
|
|
|
elements = $$ ->
|
|
|
|
@div =>
|
|
|
|
@input tabindex: 1
|
|
|
|
@button tabindex: 2, disabled: 'disabled'
|
|
|
|
@input tabindex: 3
|
|
|
|
|
|
|
|
elements.attachToDom()
|
|
|
|
elements.find("[tabindex=1]").focus()
|
|
|
|
|
|
|
|
elements.trigger "core:focus-next"
|
|
|
|
expect(elements.find("[tabindex=3]:focus")).toExist()
|
|
|
|
|
|
|
|
elements.trigger "core:focus-previous"
|
|
|
|
expect(elements.find("[tabindex=1]:focus")).toExist()
|