TextEditor/buffer async problem in specs


#1

I’m trying to add some specs to a simple package, which automatically decompiles binary property list files when opened in Atom, then recompiles them on close. The relevant module code is:

activate: ->
    @subs = atom.workspace.observeTextEditors (editor) ->
      plist = editor.getPath()
      {scopeName} = editor.getGrammar()

      if /\.(plist|strings)$/.test(scopeName) and
            editor.buffer?.getLines()[0]?.startsWith 'bplist00'

            # Decompile from binary to XML for editing.
            {stdout} = exec "plutil -convert xml1 -o - '#{plist}'"
            stdout.on 'data', (XML) -> editor.setText XML

            editor.onDidDestroy -> # Recompile binary from XML.
              exec "plutil -convert binary1 '#{plist}'"

which works fine and has for a while… but then in the specs I’m trying to add, so far I have:

{readFileSync} = require 'fs'
path = require 'path'
plist = 'binary.plist'

describe 'plist-converter', ->
  project = path.join __dirname, 'fixtures'
  beforeEach ->
    atom.project.setPaths [project]

    waitsForPromise ->
          Promise.all [
            atom.packages.activatePackage 'language-property-list'
            atom.packages.activatePackage 'language-objective-c'
          ]
        atom.packages.triggerDeferredActivationHooks()
        waitsForPromise =>
          Promise.all [
            atom.packages.activatePackage 'plist-converter'
            atom.workspace.open(plist).then (@editor) =>
          ]
  describe "When a binary property list file is opened", ->
    it "should be decompiled.", ->
      expect(@editor.buffer.getLines()[0]).not.toMatch /^bplist00/

  describe "When a binary property list file is closed", ->
    beforeEach -> @editor.destroy()

    it "should be recompiled.", ->
          expect(readFileSync "#{project}/#{plist}",'utf8').toMatch /^bplist00/

The second test passes, but the first does not… After a bit of faffing around I managed to get the package activated despite having activationHooks in package.json, once I found out about the undocumented atom.packages.triggerDeferredActivationHooks(). However the test still doesn’t pass. I think the reason is that the test is reading the buffer before it has finished being converted by plutil; how might I remedy this?


#2

I notice that you call atom.packages.triggerDeferredActivationHooks between two waitsForPromise calls. If you want the deferred activation hooks to be triggered after the first two promises resolve and before the last two, you should wrap the trigger call in a runs -> block like this:

    waitsForPromise ->
      Promise.all [
        atom.packages.activatePackage 'language-property-list'
        atom.packages.activatePackage 'language-objective-c'
      ]
    
    runs ->
      atom.packages.triggerDeferredActivationHooks()

    waitsForPromise =>
      Promise.all [
        atom.packages.activatePackage 'plist-converter'
        atom.workspace.open(plist).then (@editor) =>
      ]

This is assuming that you ordered things that way for a specific reason.


#3

Cheers @leedohm,

Well the triggerDeferredActivationHooks are just :grammar-used on those 2 language-packages so I’m not sure if those first 2 promises are therefore duplication?

Either way, the first test still fails with the trigger call inside runs ->

I’m pretty sure the buffer is being read by the test before it is converted (and therefore matching [rather than .not matching] /^bplist00/

In actual use there’s a very slight (few ms) visible flash/delay between opening a binary file and it decompiling the buffer.

I think it’s that that I need to address but not quite sure how; writing the tests is much trickier in this case than the actual package! :smile:


#4

No, they’re not duplication. Activating a package returns a promise that doesn’t resolve until the deferred activation happens, so both steps are necessary. Without the runs block, the non-async code potentially runs before any of the async code.

It’s possible that you need to wait for the buffer to be decompiled, yes.