Cannot get active text editor in tests


#1

Hello,
I have been learning how to write packages from the atom flight manual, which explains how to edit the template code to display the wordcount of the current text editor. Due to the changes made, some of the tests were failing. I want to rewrite those tests so that they pass, but I can’t seem to get the text editor element from the tests.

I tried using the getActiveTextEditor() method, and also the atom.workspace.open(filePath) to open the file directly, but neither of those worked.

the main package file:

MyPackageView = require './my-package-view'
{CompositeDisposable} = require 'atom'

module.exports = MyPackage =
  myPackageView: null
  modalPanel: null
  subscriptions: null

  activate: (state) ->
    @myPackageView = new MyPackageView(state.myPackageViewState)
    @modalPanel = atom.workspace.addModalPanel(item: @myPackageView.getElement(), visible: false)

    # Events subscribed to in atom's system can be easily cleaned up with a CompositeDisposable
    @subscriptions = new CompositeDisposable

    # Register command that toggles this view
    @subscriptions.add atom.commands.add 'atom-workspace', 'my-package:toggle': => @toggle()

  deactivate: ->
    @modalPanel.destroy()
    @subscriptions.dispose()
    @myPackageView.destroy()

  serialize: ->
    myPackageViewState: @myPackageView.serialize()

  toggle: ->
    console.log "editor"
    if @modalPanel.isVisible()
      @modalPanel.hide()
    else
      editor = atom.workspace.getActiveTextEditor()
      words = editor.getText().split(/\s+/).length
      @myPackageView.setCount(words)
      @modalPanel.show()

The spec file:

MyPackage = require '../lib/my-package'

# Use the command `window:run-package-specs` (cmd-alt-ctrl-p) to run specs.
#
# To run a specific `it` or `describe` block add an `f` to the front (e.g. `fit`
# or `fdescribe`). Remove the `f` to unfocus the block.

describe "MyPackage", ->
  [workspaceElement, activationPromise] = []

  beforeEach ->
    workspaceElement = atom.views.getView(atom.workspace)
    activationPromise = atom.packages.activatePackage('my-package')

  describe "when the my-package:toggle event is triggered", ->
    it "hides and shows the modal panel", ->
      # Before the activation event the view is not on the DOM, and no panel
      # has been created

      expect(workspaceElement.querySelector('.my-package')).not.toExist()

      # This is an activation event, triggering it will cause the package to be
      # activated.
      atom.commands.dispatch workspaceElement, 'my-package:toggle'

      expect(atom.packages.isPackageActive('my-package')).toBe true

      waitsForPromise ->
        activationPromise

      runs ->
        expect(workspaceElement.querySelector('.my-package')).toExist()

        myPackageElement = workspaceElement.querySelector('.my-package')
        expect(myPackageElement).toExist()

        myPackagePanel = atom.workspace.panelForItem(myPackageElement)
        expect(myPackagePanel.isVisible()).toBe true
        atom.commands.dispatch workspaceElement, 'my-package:toggle'
        expect(myPackagePanel.isVisible()).toBe false

#2

I also have the same issue. To be exact, the error seems to be that atom.workspace.getActiveTextEditor() returns undefined; calling getText() on undefined naturally fails. The package tutorial merely has a TODO at the moment.

I’ve been searching Github for open source code that already performs these tests but haven’t had much luck yet.

I’m beginning to think that the use of Jasmine Spies to stub out the external calls and return mock data might be the way forward. Note that the Atom docs link to Jasmine version 1.3, which doesn’t seem too recent – I don’t know what version is actually available mind.


#3

An update: I’ve got the tests working using the Jasmine Spies I mentioned above. I still need to figure out if this is the right way, mind. :slightly_smiling:

In the beforeEach block I add a new test editor by calling buildTextEditor(), and I set some example content for test purposes. Finally, I create a spy that returns this test editor when atom.workspace.getActiveTextEditor() is called:

  beforeEach ->
    workspaceElement = atom.views.getView(atom.workspace)
    activationPromise = atom.packages.activatePackage('word-count-tutorial')
    editor = atom.workspace.buildTextEditor()
    editor.setText('Some text')
    spyOn(atom.workspace, 'getActiveTextEditor').andReturn(editor)

The only test that fails for me is the one on the view. I suppose I shouldn’t expect life to be easy. :wink:


#4

According to the spec file in the original post, I don’t see where the spec actually opens a new editor. If you don’t open an editor, then there won’t be an active text editor to get and atom.workspace.getActiveTextEditor will return undefined.

Here’s how I open a text editor and get the editor object in my specs:

https://github.com/lee-dohm/tabs-to-spaces/blob/master/spec/tabs-to-spaces-spec.coffee#L20-L22

You have to declare editor in a space that is visible to the rest of the specs and if you have anything that needs to be executed in the same scope as the waitsForPromise it has to be wrapped in a runs. But once you have that in place, everything should just work. No need for spies or anything else.

You can also take a look at the specs for the whitespace package. They were designed as kind of a basic example of good specs.


#5

Using spies to return a mock value seems a bit like cheating to me. The main problem here seems to be to get a new text file to open from within the spec so that it is set as the activeTextEditor, as @lee-dohm said.
I tried opening a new text file using the workspace.open(filePath) method from the spec, which failed. The method works just fine if I use it in the main package file though. I can’t for the life of me figure what the hell is wrong with this code!


#6

Did you wait for the Promise to resolve?


#7

Fair enough.

Taking inspiration from what @leedohm wrote above, we can open an editor. The open() function returns a Promise, so we need to wait for that to resolve:

  [workspaceElement, activationPromise, editor] = []

  beforeEach ->
    workspaceElement = atom.views.getView(atom.workspace)
    activationPromise = atom.packages.activatePackage('word-count-tutorial')
    waitsForPromise ->
      atom.workspace.open().then (e) ->
        editor = e

I don’t do anything with the editor just yet, but I presume this could be used for setting up tests of the functionality, e.g. adding 10 words to the editor to ensure the words are counted correctly!