$ = require 'jquery'
fs = require 'fs'
Project = require 'project'
RootView = require 'root-view'
Buffer = require 'buffer'
Editor = require 'editor'
{View, $$} = require 'space-pen'

describe "RootView", ->
  pathToOpen = null

  beforeEach ->
    project.destroy()
    project.setPath(project.resolve('dir'))
    pathToOpen = project.resolve('a')
    window.rootView = new RootView
    rootView.enableKeymap()
    rootView.open(pathToOpen)
    rootView.focus()

  describe "@deserialize()", ->
    viewState = null

    describe "when the serialized RootView has an unsaved buffer", ->
      it "constructs the view with the same panes", ->
        rootView.open()
        editor1 = rootView.getActiveEditor()
        buffer = editor1.getBuffer()
        editor1.splitRight()
        viewState = rootView.serialize()
        rootView.deactivate()

        window.rootView = RootView.deserialize(viewState)
        rootView.focus()
        expect(rootView.getEditors().length).toBe 2
        expect(rootView.getActiveEditor().getText()).toBe buffer.getText()
        expect(rootView.getTitle()).toBe "untitled – #{project.getPath()}"

    describe "when the serialized RootView has a project", ->
      describe "when there are open editors", ->
        it "constructs the view with the same panes", ->
          editor1 = rootView.getActiveEditor()
          editor2 = editor1.splitRight()
          editor3 = editor2.splitRight()
          editor4 = editor2.splitDown()
          editor2.edit(project.buildEditSessionForPath('b'))
          editor3.edit(project.buildEditSessionForPath('../sample.js'))
          editor3.setCursorScreenPosition([2, 4])
          editor4.edit(project.buildEditSessionForPath('../sample.txt'))
          editor4.setCursorScreenPosition([0, 2])
          rootView.attachToDom()
          editor2.focus()

          viewState = rootView.serialize()
          rootView.deactivate()
          window.rootView = RootView.deserialize(viewState)
          rootView.attachToDom()

          expect(rootView.getEditors().length).toBe 4
          editor1 = rootView.panes.find('.row > .pane .editor:eq(0)').view()
          editor3 = rootView.panes.find('.row > .pane .editor:eq(1)').view()
          editor2 = rootView.panes.find('.row > .column > .pane .editor:eq(0)').view()
          editor4 = rootView.panes.find('.row > .column > .pane .editor:eq(1)').view()

          expect(editor1.getPath()).toBe project.resolve('a')
          expect(editor2.getPath()).toBe project.resolve('b')
          expect(editor3.getPath()).toBe project.resolve('../sample.js')
          expect(editor3.getCursorScreenPosition()).toEqual [2, 4]
          expect(editor4.getPath()).toBe project.resolve('../sample.txt')
          expect(editor4.getCursorScreenPosition()).toEqual [0, 2]

          # ensure adjust pane dimensions is called
          expect(editor1.width()).toBeGreaterThan 0
          expect(editor2.width()).toBeGreaterThan 0
          expect(editor3.width()).toBeGreaterThan 0
          expect(editor4.width()).toBeGreaterThan 0

          # ensure correct editor is focused again
          expect(editor2.isFocused).toBeTruthy()
          expect(editor1.isFocused).toBeFalsy()
          expect(editor3.isFocused).toBeFalsy()
          expect(editor4.isFocused).toBeFalsy()

          expect(rootView.getTitle()).toBe "#{fs.base(editor2.getPath())} – #{project.getPath()}"

      describe "where there are no open editors", ->
        it "constructs the view with no open editors", ->
          rootView.getActiveEditor().remove()
          expect(rootView.getEditors().length).toBe 0

          viewState = rootView.serialize()
          rootView.deactivate()
          window.rootView = RootView.deserialize(viewState)

          rootView.attachToDom()
          expect(rootView.getEditors().length).toBe 0

    describe "when a pane's wrapped view cannot be deserialized", ->
      it "renders an empty pane", ->
        viewState =
          panesViewState:
            deserializer: "Pane",
            wrappedView:
              deserializer: "BogusView"

        rootView.deactivate()
        window.rootView = RootView.deserialize(viewState)
        expect(rootView.find('.pane').length).toBe 1
        expect(rootView.find('.pane').children().length).toBe 0

  describe "focus", ->
    describe "when there is an active editor", ->
      it "hands off focus to the active editor", ->
        rootView.attachToDom()

        rootView.open() # create an editor
        expect(rootView).not.toMatchSelector(':focus')
        expect(rootView.getActiveEditor().isFocused).toBeTruthy()

        rootView.focus()
        expect(rootView).not.toMatchSelector(':focus')
        expect(rootView.getActiveEditor().isFocused).toBeTruthy()

    describe "when there is no active editor", ->
      beforeEach ->
        rootView.getActiveEditor().remove()
        rootView.attachToDom()

      describe "when are visible focusable elements (with a -1 tabindex)", ->
        it "passes focus to the first focusable element", ->
          rootView.horizontal.append $$ ->
            @div "One", id: 'one', tabindex: -1
            @div "Two", id: 'two', tabindex: -1

          rootView.focus()
          expect(rootView).not.toMatchSelector(':focus')
          expect(rootView.find('#one')).toMatchSelector(':focus')
          expect(rootView.find('#two')).not.toMatchSelector(':focus')

      describe "when there are no visible focusable elements", ->
        it "surrenders focus to the body", ->
          expect(document.activeElement).toBe $('body')[0]

  describe "panes", ->
    [pane1, newPaneContent] = []

    beforeEach ->
      rootView.attachToDom()
      rootView.width(800)
      rootView.height(600)
      pane1 = rootView.find('.pane').view()
      pane1.attr('id', 'pane-1')
      newPaneContent = $("<div>New pane content</div>")
      spyOn(newPaneContent, 'focus')

    describe "vertical splits", ->
      describe "when .splitRight(view) is called on a pane", ->
        it "places a new pane to the right of the current pane in a .row div", ->
          expect(rootView.panes.find('.row')).not.toExist()

          pane2 = pane1.splitRight(newPaneContent)
          expect(newPaneContent.focus).toHaveBeenCalled()

          expect(rootView.panes.find('.row')).toExist()
          expect(rootView.panes.find('.row .pane').length).toBe 2
          [leftPane, rightPane] = rootView.panes.find('.row .pane').map -> $(this)
          expect(rightPane[0]).toBe pane2[0]
          expect(leftPane.attr('id')).toBe 'pane-1'
          expect(rightPane.html()).toBe "<div>New pane content</div>"

          expectedColumnWidth = Math.floor(rootView.panes.width() / 2)
          expect(leftPane.outerWidth()).toBe expectedColumnWidth
          expect(rightPane.position().left).toBe expectedColumnWidth
          expect(rightPane.outerWidth()).toBe expectedColumnWidth

          pane2.remove()

          expect(rootView.panes.find('.row')).not.toExist()
          expect(rootView.panes.find('.pane').length).toBe 1
          expect(pane1.outerWidth()).toBe rootView.panes.width()

      describe "when splitLeft(view) is called on a pane", ->
        it "places a new pane to the left of the current pane in a .row div", ->
          expect(rootView.find('.row')).not.toExist()

          pane2 = pane1.splitLeft(newPaneContent)
          expect(newPaneContent.focus).toHaveBeenCalled()

          expect(rootView.find('.row')).toExist()
          expect(rootView.find('.row .pane').length).toBe 2
          [leftPane, rightPane] = rootView.find('.row .pane').map -> $(this)
          expect(leftPane[0]).toBe pane2[0]
          expect(rightPane.attr('id')).toBe 'pane-1'
          expect(leftPane.html()).toBe "<div>New pane content</div>"

          expectedColumnWidth = Math.floor(rootView.panes.width() / 2)
          expect(leftPane.outerWidth()).toBe expectedColumnWidth
          expect(rightPane.position().left).toBe expectedColumnWidth
          expect(rightPane.outerWidth()).toBe expectedColumnWidth

          pane2.remove()

          expect(rootView.panes.find('.row')).not.toExist()
          expect(rootView.panes.find('.pane').length).toBe 1
          expect(pane1.outerWidth()).toBe rootView.panes.width()
          expect(pane1.position().left).toBe 0

    describe "horizontal splits", ->
      describe "when splitUp(view) is called on a pane", ->
        it "places a new pane above the current pane in a .column div", ->
          expect(rootView.find('.column')).not.toExist()

          pane2 = pane1.splitUp(newPaneContent)
          expect(newPaneContent.focus).toHaveBeenCalled()

          expect(rootView.find('.column')).toExist()
          expect(rootView.find('.column .pane').length).toBe 2
          [topPane, bottomPane] = rootView.find('.column .pane').map -> $(this)
          expect(topPane[0]).toBe pane2[0]
          expect(bottomPane.attr('id')).toBe 'pane-1'
          expect(topPane.html()).toBe "<div>New pane content</div>"

          expectedRowHeight = Math.floor(rootView.panes.height() / 2)
          expect(topPane.outerHeight()).toBe expectedRowHeight
          expect(bottomPane.position().top).toBe expectedRowHeight
          expect(bottomPane.outerHeight()).toBe expectedRowHeight

          pane2.remove()

          expect(rootView.panes.find('.column')).not.toExist()
          expect(rootView.panes.find('.pane').length).toBe 1
          expect(pane1.outerHeight()).toBe rootView.panes.height()
          expect(pane1.position().top).toBe 0

      describe "when splitDown(view) is called on a pane", ->
        it "places a new pane below the current pane in a .column div", ->
          expect(rootView.find('.column')).not.toExist()

          pane2 = pane1.splitDown(newPaneContent)
          expect(newPaneContent.focus).toHaveBeenCalled()

          expect(rootView.find('.column')).toExist()
          expect(rootView.find('.column .pane').length).toBe 2
          [topPane, bottomPane] = rootView.find('.column .pane').map -> $(this)
          expect(bottomPane[0]).toBe pane2[0]
          expect(topPane.attr('id')).toBe 'pane-1'
          expect(bottomPane.html()).toBe "<div>New pane content</div>"

          expectedRowHeight = Math.floor(rootView.panes.height() / 2)
          expect(topPane.outerHeight()).toBe expectedRowHeight
          expect(bottomPane.position().top).toBe expectedRowHeight
          expect(bottomPane.outerHeight()).toBe expectedRowHeight

          pane2.remove()

          expect(rootView.panes.find('.column')).not.toExist()
          expect(rootView.panes.find('.pane').length).toBe 1
          expect(pane1.outerHeight()).toBe rootView.panes.height()

    describe "layout of nested vertical and horizontal splits", ->
      it "lays out rows and columns with a consistent width", ->
        pane1.html("1")

        pane1
          .splitLeft("2")
          .splitUp("3")
          .splitLeft("4")
          .splitDown("5")

        row1 = rootView.panes.children(':eq(0)')
        expect(row1.children().length).toBe 2
        column1 = row1.children(':eq(0)').view()
        pane1 = row1.children(':eq(1)').view()
        expect(column1.outerWidth()).toBe Math.round(2/3 * rootView.panes.width())
        expect(column1.outerHeight()).toBe rootView.height()
        expect(pane1.outerWidth()).toBe Math.round(1/3 * rootView.panes.width())
        expect(pane1.outerHeight()).toBe rootView.height()
        expect(Math.round(pane1.position().left)).toBe column1.outerWidth()

        expect(column1.children().length).toBe 2
        row2 = column1.children(':eq(0)').view()
        pane2 = column1.children(':eq(1)').view()
        expect(row2.outerWidth()).toBe column1.outerWidth()
        expect(row2.height()).toBe 2/3 * rootView.panes.height()
        expect(pane2.outerWidth()).toBe column1.outerWidth()
        expect(pane2.outerHeight()).toBe 1/3 * rootView.panes.height()
        expect(pane2.position().top).toBe row2.height()

        expect(row2.children().length).toBe 2
        column3 = row2.children(':eq(0)').view()
        pane3 = row2.children(':eq(1)').view()
        expect(column3.outerWidth()).toBe Math.round(1/3 * rootView.panes.width())
        expect(column3.outerHeight()).toBe row2.outerHeight()
        # the built in rounding seems to be rounding x.5 down, but we need to go up. this sucks.
        expect(Math.round(pane3.trueWidth())).toBe Math.round(1/3 * rootView.panes.width())
        expect(pane3.height()).toBe row2.outerHeight()
        expect(Math.round(pane3.position().left)).toBe column3.width()

        expect(column3.children().length).toBe 2
        pane4 = column3.children(':eq(0)').view()
        pane5 = column3.children(':eq(1)').view()
        expect(pane4.outerWidth()).toBe column3.width()
        expect(pane4.outerHeight()).toBe 1/3 * rootView.panes.height()
        expect(pane5.outerWidth()).toBe column3.width()
        expect(pane5.position().top).toBe pane4.outerHeight()
        expect(pane5.outerHeight()).toBe 1/3 * rootView.panes.height()

        pane5.remove()

        expect(column3.parent()).not.toExist()
        expect(pane2.outerHeight()).toBe Math.floor(1/2 * rootView.panes.height())
        expect(pane3.outerHeight()).toBe Math.floor(1/2 * rootView.panes.height())
        expect(pane4.outerHeight()).toBe Math.floor(1/2 * rootView.panes.height())

        pane4.remove()
        expect(row2.parent()).not.toExist()
        expect(pane1.outerWidth()).toBe Math.floor(1/2 * rootView.panes.width())
        expect(pane2.outerWidth()).toBe Math.floor(1/2 * rootView.panes.width())
        expect(pane3.outerWidth()).toBe Math.floor(1/2 * rootView.panes.width())

        pane3.remove()
        expect(column1.parent()).not.toExist()
        expect(pane2.outerHeight()).toBe rootView.panes.height()

        pane2.remove()
        expect(row1.parent()).not.toExist()
        expect(rootView.panes.children().length).toBe 1
        expect(rootView.panes.children('.pane').length).toBe 1
        expect(pane1.outerWidth()).toBe rootView.panes.width()

    describe ".focusNextPane()", ->
      it "focuses the wrapped view of the pane after the currently focused pane", ->
        class DummyView extends View
          @content: (number) -> @div(number, tabindex: -1)

        view1 = pane1.wrappedView
        view2 = new DummyView(2)
        view3 = new DummyView(3)
        pane2 = pane1.splitDown(view2)
        pane3 = pane2.splitRight(view3)
        rootView.attachToDom()
        view1.focus()

        spyOn(view1, 'focus').andCallThrough()
        spyOn(view2, 'focus').andCallThrough()
        spyOn(view3, 'focus').andCallThrough()

        rootView.focusNextPane()
        expect(view2.focus).toHaveBeenCalled()
        rootView.focusNextPane()
        expect(view3.focus).toHaveBeenCalled()
        rootView.focusNextPane()
        expect(view1.focus).toHaveBeenCalled()

  describe "keymap wiring", ->
    commandHandler = null
    beforeEach ->
      commandHandler = jasmine.createSpy('commandHandler')
      rootView.on('foo-command', commandHandler)

      window.keymap.bindKeys('*', 'x': 'foo-command')

    describe "when a keydown event is triggered on the RootView (not originating from Ace)", ->
      it "triggers matching keybindings for that event", ->
        event = keydownEvent 'x', target: rootView[0]

        rootView.trigger(event)
        expect(commandHandler).toHaveBeenCalled()

    describe ".activeKeybindings()", ->
      originalKeymap = null
      keymap = null
      editor = null

      beforeEach ->
        rootView.attachToDom()
        editor = rootView.getActiveEditor()
        keymap = new (require 'keymap')
        originalKeymap = window.keymap
        window.keymap = keymap

      afterEach ->
        window.keymap = originalKeymap

      it "returns all keybindings available for focused element", ->
        editor.on 'test-event-a', => # nothing

        keymap.bindKeys ".editor",
          "meta-a": "test-event-a"
          "meta-b": "test-event-b"

        keybindings = rootView.activeKeybindings()
        expect(Object.keys(keybindings).length).toBe 2
        expect(keybindings["meta-a"]).toEqual "test-event-a"

  describe "when the path of the active editor changes", ->
    it "changes the title and emits an root-view:active-path-changed event", ->
      pathChangeHandler = jasmine.createSpy 'pathChangeHandler'
      rootView.on 'root-view:active-path-changed', pathChangeHandler

      editor1 = rootView.getActiveEditor()
      expect(rootView.getTitle()).toBe "#{fs.base(editor1.getPath())} – #{project.getPath()}"

      editor2 = rootView.getActiveEditor().splitLeft()

      path = project.resolve('b')
      editor2.edit(project.buildEditSessionForPath(path))
      expect(pathChangeHandler).toHaveBeenCalled()
      expect(rootView.getTitle()).toBe "#{fs.base(editor2.getPath())} – #{project.getPath()}"

      pathChangeHandler.reset()
      editor1.getBuffer().saveAs("/tmp/should-not-be-title.txt")
      expect(pathChangeHandler).not.toHaveBeenCalled()
      expect(rootView.getTitle()).toBe "#{fs.base(editor2.getPath())} – #{project.getPath()}"

    it "sets the project path to the directory of the editor if it was previously unassigned", ->
      project.setPath(undefined)
      window.rootView = new RootView
      rootView.open()
      expect(project.getPath()?).toBeFalsy()
      rootView.getActiveEditor().getBuffer().saveAs('/tmp/ignore-me')
      expect(project.getPath()).toBe '/tmp'

  describe "when editors are focused", ->
    it "triggers 'root-view:active-path-changed' events if the path of the active editor actually changes", ->
      pathChangeHandler = jasmine.createSpy 'pathChangeHandler'
      rootView.on 'root-view:active-path-changed', pathChangeHandler

      editor1 = rootView.getActiveEditor()
      editor2 = rootView.getActiveEditor().splitLeft()

      rootView.open(require.resolve('fixtures/sample.txt'))
      expect(pathChangeHandler).toHaveBeenCalled()
      pathChangeHandler.reset()

      editor1.focus()
      expect(pathChangeHandler).toHaveBeenCalled()
      pathChangeHandler.reset()

      rootView.focus()
      expect(pathChangeHandler).not.toHaveBeenCalled()

      editor2.edit(editor1.activeEditSession.copy())
      editor2.focus()
      expect(pathChangeHandler).not.toHaveBeenCalled()

  describe "when the last editor is removed", ->
    it "updates the title to the project path", ->
      rootView.getEditors()[0].remove()
      expect(rootView.getTitle()).toBe project.getPath()

  describe "font size adjustment", ->
    editor = null
    beforeEach ->
      editor = rootView.getActiveEditor()
      editor.attachToDom()

    it "increases/decreases font size when increase/decrease-font-size events are triggered", ->
      fontSizeBefore = editor.getFontSize()
      rootView.trigger 'window:increase-font-size'
      expect(editor.getFontSize()).toBe fontSizeBefore + 1
      rootView.trigger 'window:increase-font-size'
      expect(editor.getFontSize()).toBe fontSizeBefore + 2
      rootView.trigger 'window:decrease-font-size'
      expect(editor.getFontSize()).toBe fontSizeBefore + 1
      rootView.trigger 'window:decrease-font-size'
      expect(editor.getFontSize()).toBe fontSizeBefore

    it "does not allow the font size to be less than 1", ->
      config.set("editor.fontSize", 1)
      rootView.trigger 'window:decrease-font-size'
      expect(editor.getFontSize()).toBe 1

  describe ".open(path, options)", ->
    describe "when there is no active editor", ->
      beforeEach ->
        rootView.getActiveEditor().destroyActiveEditSession()
        expect(rootView.getActiveEditor()).toBeUndefined()

      describe "when called with no path", ->
        it "opens / returns an edit session for an empty buffer in a new editor", ->
          editSession = rootView.open()
          expect(rootView.getActiveEditor()).toBeDefined()
          expect(rootView.getActiveEditor().getPath()).toBeUndefined()
          expect(editSession).toBe rootView.getActiveEditor().activeEditSession

      describe "when called with a path", ->
        it "opens a buffer with the given path in a new editor", ->
          editSession = rootView.open('b')
          expect(rootView.getActiveEditor()).toBeDefined()
          expect(rootView.getActiveEditor().getPath()).toBe require.resolve('fixtures/dir/b')
          expect(editSession).toBe rootView.getActiveEditor().activeEditSession

    describe "when there is an active editor", ->
      beforeEach ->
        expect(rootView.getActiveEditor()).toBeDefined()

      describe "when called with no path", ->
        it "opens an empty buffer in the active editor", ->
          editSession = rootView.open()
          expect(rootView.getActiveEditor().getPath()).toBeUndefined()
          expect(editSession).toBe rootView.getActiveEditor().activeEditSession

      describe "when called with a path", ->
        [editor1, editor2] = []
        beforeEach ->
          rootView.attachToDom()
          editor1 = rootView.getActiveEditor()
          editor2 = editor1.splitRight()
          rootView.open('b')
          editor2.loadPreviousEditSession()
          editor1.focus()

        describe "when allowActiveEditorChange is false (the default)", ->
          activeEditor = null
          beforeEach ->
            activeEditor = rootView.getActiveEditor()

          describe "when the active editor has an edit session for the given path", ->
            it "re-activates the existing edit session", ->
              expect(activeEditor.getPath()).toBe require.resolve('fixtures/dir/a')
              previousEditSession = activeEditor.activeEditSession

              editSession = rootView.open('b')
              expect(activeEditor.activeEditSession).not.toBe previousEditSession
              expect(editSession).toBe rootView.getActiveEditor().activeEditSession

              editSession = rootView.open('a')
              expect(activeEditor.activeEditSession).toBe previousEditSession
              expect(editSession).toBe previousEditSession

          describe "when the active editor does not have an edit session for the given path", ->
            it "creates a new edit session for the given path in the active editor", ->
              editSession = rootView.open('b')
              expect(activeEditor.editSessions.length).toBe 2
              expect(editSession).toBe rootView.getActiveEditor().activeEditSession

        describe "when the 'allowActiveEditorChange' option is true", ->
          describe "when the active editor has an edit session for the given path", ->
            it "re-activates the existing edit session regardless of whether any other editor also has an edit session for the path", ->
              activeEditor = rootView.getActiveEditor()
              expect(activeEditor.getPath()).toBe require.resolve('fixtures/dir/a')
              previousEditSession = activeEditor.activeEditSession

              editSession = rootView.open('b')
              expect(activeEditor.activeEditSession).not.toBe previousEditSession
              expect(editSession).toBe activeEditor.activeEditSession

              editSession = rootView.open('a', allowActiveEditorChange: true)
              expect(activeEditor.activeEditSession).toBe previousEditSession
              expect(editSession).toBe activeEditor.activeEditSession

          describe "when the active editor does *not* have an edit session for the given path", ->
            describe "when another editor has an edit session for the path", ->
              it "focuses the other editor and activates its edit session for the path", ->
                expect(rootView.getActiveEditor()).toBe editor1
                editSession = rootView.open('b', allowActiveEditorChange: true)
                expect(rootView.getActiveEditor()).toBe editor2
                expect(editor2.getPath()).toBe require.resolve('fixtures/dir/b')
                expect(editSession).toBe rootView.getActiveEditor().activeEditSession

            describe "when no other editor has an edit session for the path either", ->
              it "creates a new edit session for the path on the current active editor", ->
                path = require.resolve('fixtures/sample.js')
                editSession = rootView.open(path, allowActiveEditorChange: true)
                expect(rootView.getActiveEditor()).toBe editor1
                expect(editor1.getPath()).toBe path
                expect(editSession).toBe rootView.getActiveEditor().activeEditSession

  describe ".saveAll()", ->
    it "saves all open editors", ->
      project.setPath('/tmp')
      file1 = '/tmp/atom-temp1.txt'
      file2 = '/tmp/atom-temp2.txt'
      fs.write(file1, "file1")
      fs.write(file2, "file2")
      rootView.open(file1)

      editor1 = rootView.getActiveEditor()
      buffer1 = editor1.activeEditSession.buffer
      expect(buffer1.getText()).toBe("file1")
      expect(buffer1.isModified()).toBe(false)
      buffer1.setText('edited1')
      expect(buffer1.isModified()).toBe(true)

      editor2 = editor1.splitRight()
      editor2.edit(project.buildEditSessionForPath('atom-temp2.txt'))
      buffer2 = editor2.activeEditSession.buffer
      expect(buffer2.getText()).toBe("file2")
      expect(buffer2.isModified()).toBe(false)
      buffer2.setText('edited2')
      expect(buffer2.isModified()).toBe(true)

      rootView.saveAll()

      expect(buffer1.isModified()).toBe(false)
      expect(fs.read(buffer1.getPath())).toBe("edited1")
      expect(buffer2.isModified()).toBe(false)
      expect(fs.read(buffer2.getPath())).toBe("edited2")

  describe "window:toggle-invisibles event", ->
    it "shows/hides invisibles in all open and future editors", ->
      rootView.height(200)
      rootView.attachToDom()
      rightEditor = rootView.getActiveEditor()
      rightEditor.setText(" \t ")
      leftEditor = rightEditor.splitLeft()
      expect(rightEditor.find(".line:first").text()).toBe "    "
      expect(leftEditor.find(".line:first").text()).toBe "    "

      withInvisiblesShowing = "#{rightEditor.invisibles.space}#{rightEditor.invisibles.tab} #{rightEditor.invisibles.space}#{rightEditor.invisibles.eol}"

      rootView.trigger "window:toggle-invisibles"
      expect(rightEditor.find(".line:first").text()).toBe withInvisiblesShowing
      expect(leftEditor.find(".line:first").text()).toBe withInvisiblesShowing

      lowerLeftEditor = leftEditor.splitDown()
      expect(lowerLeftEditor.find(".line:first").text()).toBe withInvisiblesShowing

      rootView.trigger "window:toggle-invisibles"
      expect(rightEditor.find(".line:first").text()).toBe "    "
      expect(leftEditor.find(".line:first").text()).toBe "    "

      lowerRightEditor = rightEditor.splitDown()
      expect(lowerRightEditor.find(".line:first").text()).toBe "    "

  describe ".eachEditor(callback)", ->
    beforeEach ->
      rootView.attachToDom()

    it "invokes the callback for existing editor", ->
      count = 0
      callbackEditor = null
      callback = (editor) ->
        callbackEditor = editor
        count++
      rootView.eachEditor(callback)
      expect(count).toBe 1
      expect(callbackEditor).toBe rootView.getActiveEditor()

    it "invokes the callback for new editor", ->
      count = 0
      callbackEditor = null
      callback = (editor) ->
        callbackEditor = editor
        count++

      rootView.eachEditor(callback)
      count = 0
      callbackEditor = null
      rootView.getActiveEditor().splitRight()
      expect(count).toBe 1
      expect(callbackEditor).toBe rootView.getActiveEditor()

  describe ".eachBuffer(callback)", ->
    beforeEach ->
      rootView.attachToDom()

    it "invokes the callback for existing buffer", ->
      count = 0
      callbackBuffer = null
      callback = (buffer) ->
        callbackBuffer = buffer
        count++
      rootView.eachBuffer(callback)
      expect(count).toBe 1
      expect(callbackBuffer).toBe rootView.getActiveEditor().getBuffer()

    it "invokes the callback for new buffer", ->
      count = 0
      callbackBuffer = null
      callback = (buffer) ->
        callbackBuffer = buffer
        count++

      rootView.eachBuffer(callback)
      count = 0
      callbackBuffer = null
      rootView.open(require.resolve('fixtures/sample.txt'))
      expect(count).toBe 1
      expect(callbackBuffer).toBe rootView.getActiveEditor().getBuffer()
