[Pkg-javascript-commits] [node-tap] 45/186: mocha-like BDD DSL unit test

Bastien Roucariès rouca at moszumanska.debian.org
Fri Dec 1 16:40:42 UTC 2017


This is an automated email from the git hooks/post-receive script.

rouca pushed a commit to branch master
in repository node-tap.

commit 1517575716e3c4fe2bd50c76551105a5e607bc17
Author: isaacs <i at izs.me>
Date:   Sun Oct 29 22:27:57 2017 -0700

    mocha-like BDD DSL unit test
---
 lib/mocha.js           | 167 ++++++++++++++++++++++++++-----------------------
 lib/test.js            |  15 ++---
 test/test/mochalike.js |   3 +-
 unit/mocha.js          | 125 ++++++++++++++++++++++++++++++++++++
 4 files changed, 222 insertions(+), 88 deletions(-)

diff --git a/lib/mocha.js b/lib/mocha.js
index c93c2a6..60e179c 100644
--- a/lib/mocha.js
+++ b/lib/mocha.js
@@ -1,133 +1,119 @@
 'use strict'
-exports.it = exports.specify = it
-exports.context = exports.describe = describe
-exports.before = before
-exports.after = after
-exports.beforeEach = beforeEach
-exports.afterEach = afterEach
-
-exports.global = function () {
-  Object.keys(exports).forEach(function (g) {
-    global[g] = exports[g]
-  })
-}
-
 const t = require('./tap.js')
 t.jobs = 1
 const tapStack = [ t ]
 let level = 0
 const suiteStack = []
 
-function describe (name, fn) {
-  new Suite(name, fn)
-}
+const describe = (name, fn, opt) =>
+  new Suite(name, fn, opt)
 
-function Suite (name, fn) {
-  this.parent = suiteStack[ suiteStack.length - 1 ]
-  if (typeof name === 'function')
-    fn = name, name = null
-  if (fn && fn.name && !name)
-    name = fn.name
-  this.todo = !fn
-  this.fn = fn
-  this.name = name
-  this.after = []
-  this.test = null
+class Suite {
+  constructor (name, fn, opt) {
+    this.parent = suiteStack[ suiteStack.length - 1 ]
+    if (typeof name === 'function')
+      fn = name, name = null
+    if (fn && fn.name && !name)
+      name = fn.name
+    this.options = opt || {}
+    this.options.todo = this.options.todo || !fn
+    this.fn = fn
+    this.name = name
+    this.after = []
+    this.test = null
 
-  this.run()
-}
+    this.run()
+  }
 
-Suite.prototype.run = function () {
-  const t = tapStack[ tapStack.length - 1 ]
-  t.test(this.name, { todo: this.todo }, function (tt) {
-    this.test = tt
-    tapStack.push(tt)
-    suiteStack.push(this)
-    const ret = this.fn()
-    this.runAfter()
-    suiteStack.pop()
-    return ret
-  }.bind(this))
-}
+  run () {
+    const t = tapStack[ tapStack.length - 1 ]
+    t.test(this.name, this.options, tt => {
+      this.test = tt
+      tapStack.push(tt)
+      suiteStack.push(this)
+      const ret = this.fn()
+      this.runAfter()
+      suiteStack.pop()
+      return ret
+    })
+  }
 
-Suite.prototype.runAfter = function () {
-  this.after.forEach(function (namefn) {
-    const name = namefn[0]
-    const fn = namefn[1]
-    before(name, fn)
-  })
-  let t
-  do {
-    t = tapStack.pop()
-  } while (t && t !== this.test)
-  if (this.test && !this.test.results)
-    t.end()
+  runAfter () {
+    this.after.forEach(a =>
+      before(a[0], a[1], a[2]))
+    let t
+    do {
+      t = tapStack.pop()
+    } while (t && t !== this.test)
+    if (this.test && !this.test.results)
+      t.end()
+  }
 }
 
-function before (name, fn) {
+const before = (name, fn, options) => {
   if (typeof name === 'function')
     fn = name, name = null
   if (fn && fn.name && !name)
     name = fn.name
+  options = options || {}
   const todo = !fn
+  options.todo = options.todo || todo
+  options.silent = true
   const suite = suiteStack[ suiteStack.length - 1 ]
+  if (!suite)
+    throw new Error('cannot call "before" outside of describe()')
   const t = tapStack[ tapStack.length - 1 ]
   if (!name)
     name = ''
-  t.test(name, { todo: todo, silent: true }, function (tt) {
+
+  const done = tt => er => er ? tt.threw(er) : tt.end()
+  t.test(name, options, tt => {
     const ret = fn.call(suite, done(tt))
     if (!ret && fn.length === 0)
       tt.end()
     else
       return ret
   })
-
-  function done (tt) { return function (er) {
-    if (er)
-      tt.threw(er)
-    else
-      tt.end()
-  }}
 }
 
-function it (name, fn) {
+const it = (name, fn, options) => {
   if (typeof name === 'function')
     fn = name, name = null
   if (fn && fn.name && !name)
     name = fn.name
+  options = options || {}
   const todo = !fn
   const suite = suiteStack[ suiteStack.length - 1 ]
   const t = tapStack[ tapStack.length - 1 ]
   if (!name)
     name = ''
-  t.test(name, { todo: todo, tapMochaTest: true }, function (tt) {
+
+  const done = tt => er => er ? tt.threw(er) : tt.end()
+  options.todo = options.todo || todo
+  options.tapMochaTest = true
+  t.test(name, options, tt => {
     const ret = fn.call(tt, done(tt))
     if (ret && ret.then)
       return ret
     else if (fn.length === 0)
       tt.end()
   })
-
-  function done (tt) { return function (er) {
-    if (er)
-      tt.threw(er)
-    else
-      tt.end()
-  }}
 }
 
-function after (name, fn) {
+it.skip = (name, fn) => it(name, fn, { skip: true })
+it.todo = (name, fn) => it(name, fn, { todo: true })
+
+function after (name, fn, options) {
   const suite = suiteStack[ suiteStack.length - 1 ]
   if (!suite)
     throw new Error('cannot call "after" outside of describe()')
-  if (fn)
-    suite.after.push([name, fn])
-  else
-    suite.after.push([name])
+  suite.after.push([name, fn, options])
 }
 
 function moment (when, fn) {
   const t = tapStack[ tapStack.length - 1 ]
+  // need function because 'this' tells us which tap object
+  // has the tapMochaTest thing in its options object
   t[when](function (cb) {
     if (!this.options.tapMochaTest)
       return cb()
@@ -140,10 +126,33 @@ function moment (when, fn) {
   })
 }
 
-function beforeEach (fn) {
+const beforeEach = fn =>
   moment('beforeEach', fn)
-}
 
-function afterEach (fn) {
+const afterEach = fn =>
   moment('afterEach', fn)
+
+exports.it = exports.specify = it
+exports.context = exports.describe = describe
+exports.before = before
+exports.after = after
+exports.beforeEach = beforeEach
+exports.afterEach = afterEach
+
+let saved
+exports.global = _ => {
+  if (!saved)
+    saved = new Map()
+
+  Object.keys(exports).filter(g => g !== 'global').forEach(g => {
+    if (!saved.has(g))
+      saved.set(g, global[g])
+    global[g] = exports[g]
+  })
 }
+
+exports.deglobal = _ =>
+  Object.keys(exports).filter(g => g !== 'global').forEach(g => {
+    if (saved && saved.has(g))
+      global[g] = saved.get(g)
+  })
diff --git a/lib/test.js b/lib/test.js
index 47c5e18..d92938a 100644
--- a/lib/test.js
+++ b/lib/test.js
@@ -301,9 +301,8 @@ class Test extends Base {
       if (this.bailedOut)
         this.onbufferedend(p)
       else
-        this.runBeforeEach(p,
-          p.main.bind(p,
-            this.onbufferedend.bind(this, p)))
+        this.runBeforeEach(p, () =>
+          p.main(() => this.onbufferedend(p)))
     }
 
     this.debug('done processing', this.queue, this.occupied)
@@ -325,10 +324,9 @@ class Test extends Base {
         return this.onindentedend(p)
       this.debug(' > subtest indented')
       p.pipe(this.parser, { end: false })
-      this.runBeforeEach(p,
-        this.writeSubComment.bind(this, p,
-          p.main.bind(p,
-            this.onindentedend.bind(this, p))))
+      this.runBeforeEach(p, () =>
+        this.writeSubComment(p, () =>
+          p.main(() => this.onindentedend(p))))
     } else if (p.readyToProcess) {
       this.debug(' > subtest buffered, finished')
       // finished!  do the thing!
@@ -475,6 +473,9 @@ class Test extends Base {
 
     extra = extra || {}
 
+    if (extra.expectFail)
+      ok = !ok
+
     if (this.assertAt) {
       extra.at = this.assertAt
       this.assertAt = null
diff --git a/test/test/mochalike.js b/test/test/mochalike.js
index 76f777f..f93e755 100644
--- a/test/test/mochalike.js
+++ b/test/test/mochalike.js
@@ -1,6 +1,5 @@
 if (typeof describe !== 'function') {
-  var t = require('../..')
-  t.mochaGlobals()
+  require('../../lib/mocha.js').global()
 }
 
 /* global describe, it */
diff --git a/unit/mocha.js b/unit/mocha.js
new file mode 100644
index 0000000..203be75
--- /dev/null
+++ b/unit/mocha.js
@@ -0,0 +1,125 @@
+'use strict'
+const mocha = require('../lib/mocha.js')
+const assert = require('assert')
+
+mocha.describe('globals', () => {
+  let beforeEaches = 0
+  mocha.beforeEach(() => beforeEaches++)
+
+  mocha.beforeEach(cb => setTimeout(cb))
+  mocha.beforeEach(() => new Promise(r => r()))
+
+  let afterEaches = 0
+  mocha.afterEach(() => afterEaches++)
+
+  // test that afterEach is happening correct number
+  // of times.
+  let eachExpect = 0
+  mocha.afterEach(() => new Promise(res => {
+    eachExpect ++
+    assert.equal(beforeEaches, eachExpect, 'before')
+    assert.equal(afterEaches, eachExpect, 'after')
+    res()
+  }))
+
+  mocha.it('has no describe', () =>
+    assert.equal(global.describe, undefined))
+
+  mocha.it('is ok running deglobal() first', () => {
+    mocha.deglobal()
+    assert.equal(global.describe, undefined)
+  })
+
+  mocha.it('has describe after call', () => {
+    mocha.global()
+    mocha.global()
+    assert.equal(global.describe, mocha.describe)
+  })
+
+  mocha.it('has no describe after deglobal', () => {
+    deglobal()
+    assert.equal(global.describe, undefined)
+  })
+
+  mocha.it('escape to tap', function () {
+    const t = this
+    t.test('should not get a beforeEach', t =>
+      t.test('or an after each', t => {
+        t.pass('this is fine')
+        t.end()
+      }))
+  })
+
+  // at this point, beforeEach has been called
+  // 1 more time than afterEach
+  mocha.it('called beforeEach/afterEach', () =>
+     new Promise((resolve) => {
+       assert.equal(beforeEaches, eachExpect + 1)
+       assert.equal(afterEaches, eachExpect)
+       resolve()
+     }))
+})
+
+assert.throws(_ => mocha.after(),
+  'cannot call "after" outside of describe()')
+assert.throws(_ => mocha.before(),
+  'cannot call "before" outside of describe()')
+
+let calledAfter = false
+let calledBefore = false
+mocha.describe(function after_and_before () {
+  mocha.before((cb) => {
+    assert.equal(calledBefore, false)
+    calledBefore = true
+    setTimeout(cb)
+  })
+  mocha.before('named before', () => new Promise(r => {
+    assert.equal(calledBefore, true)
+    r()
+  }))
+
+  mocha.after(() => {
+    assert.equal(calledAfter, false)
+    calledAfter = true
+  })
+
+  mocha.after('named after', () => {
+    assert.equal(calledAfter, true)
+  })
+
+  mocha.after(function named_after () {
+    assert.equal(calledAfter, true)
+  })
+
+  mocha.it(function this_is_fine () {})
+  mocha.it(() => {})
+  mocha.it(cb => cb())
+})
+mocha.describe('after after', function () {
+  this.test.plan(1)
+  mocha.it('should have called after fn', () =>
+    assert.equal(calledAfter, true))
+})
+
+mocha.describe('todo, skip, and failure', () => {
+  let calledTodoFn = false
+  let calledSkipFn = false
+  const it = mocha.it
+  mocha.describe('expect todo and skip', function () {
+    /* istanbul ignore next */
+    it.todo('expected todo', () => calledTodoFn = true)
+    /* istanbul ignore next */
+    it.skip('expected skip', () => calledSkipFn = true)
+  }, { silent: true })
+  it('expected fail from cb(er)', cb => {
+    cb(new Error('expected failure'))
+  }, { expectFail: true })
+  it('did not call skip/todo functions', () => {
+    assert.equal(calledTodoFn, false)
+    assert.equal(calledSkipFn, false)
+  })
+})
+
+mocha.describe('expected before failure', () =>
+  mocha.before('expect failure', (cb) =>
+    cb(new Error('expected')), { expectFail : true }))

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/node-tap.git



More information about the Pkg-javascript-commits mailing list