Эх сурвалжийг харах

feature(extensionLoading): add support to legacy extensions in the new extension mechanism

Old extensions that register themselves in `showdown.extensions` can be loaded and validated using the new extension loading mechanism.
However, a warn is issued, alerting users and developers that the extension should be updated to use the new mechanism

BREAKING CHANGE: Deprecates `showdown.extensions` property. To migrate, you should use the new method `showdown.extension(<ext name>, <extension>)` to register the extension.
Estevao Soares dos Santos 10 жил өмнө
parent
commit
4ebd0caa27

+ 2 - 1
Gruntfile.js

@@ -108,10 +108,11 @@ module.exports = function (grunt) {
 
   require('load-grunt-tasks')(grunt);
 
+  grunt.registerTask('concatenate', ['concat']);
   grunt.registerTask('lint', ['jshint', 'jscs']);
   grunt.registerTask('test', ['lint', 'concat', 'simplemocha']);
   grunt.registerTask('test-without-building', ['simplemocha']);
-  grunt.registerTask('build', ['lint', 'test', 'uglify']);
+  grunt.registerTask('build', ['test', 'uglify']);
   grunt.registerTask('prep-release', ['build', 'changelog']);
 
   // Default task(s).

+ 148 - 90
dist/showdown.js

@@ -1,4 +1,4 @@
-;/*! showdown 31-05-2015 */
+;/*! showdown 07-06-2015 */
 (function(){
 /**
  * Created by Tivie on 06-01-2015.
@@ -115,10 +115,16 @@ showdown.extension = function (name, ext) {
 
     // Setter
   } else {
+    // Expand extension if it's wrapped in a function
     if (typeof ext === 'function') {
       ext = ext();
     }
 
+    // Ensure extension is an array
+    if (!showdown.helper.isArray(ext)) {
+      ext = [ext];
+    }
+
     var validExtension = validate(ext, name);
 
     if (validExtension.valid) {
@@ -157,83 +163,90 @@ showdown.resetExtensions = function () {
 
 /**
  * Validate extension
- * @param {object} ext
+ * @param {array} extension
  * @param {string} name
  * @returns {{valid: boolean, error: string}}
  */
-function validate(ext, name) {
+function validate(extension, name) {
   'use strict';
 
-  var baseMsg = (name) ? 'Error in ' + name + ' extension: ' : 'Error in unnamed extension',
+  var errMsg = (name) ? 'Error in ' + name + ' extension->' : 'Error in unnamed extension',
     ret = {
       valid: true,
-      error: baseMsg
+      error: ''
     };
 
-  if (typeof ext !== 'object') {
-    ret.valid = false;
-    ret.error = baseMsg + 'it must be an object, but ' + typeof ext + ' given';
-    return ret;
+  if (!showdown.helper.isArray(extension)) {
+    extension = [extension];
   }
 
-  if (!showdown.helper.isString(ext.type)) {
-    ret.valid = false;
-    ret.error = baseMsg + 'property "type" must be a string, but ' + typeof ext.type + ' given';
-    return ret;
-  }
+  for (var i = 0; i < extension.length; ++i) {
+    var baseMsg = errMsg + 'sub-extension ' + i + ': ',
+        ext = extension[i];
+    if (typeof ext !== 'object') {
+      ret.valid = false;
+      ret.error = baseMsg + 'must be an object, but ' + typeof ext + ' given';
+      return ret;
+    }
 
-  var type = ext.type = ext.type.toLowerCase();
+    if (!showdown.helper.isString(ext.type)) {
+      ret.valid = false;
+      ret.error = baseMsg + 'property "type" must be a string, but ' + typeof ext.type + ' given';
+      return ret;
+    }
 
-  // normalize extension type
-  if (type === 'language') {
-    type = ext.type = 'lang';
-  }
+    var type = ext.type = ext.type.toLowerCase();
 
-  if (type === 'html') {
-    type = ext.type = 'output';
-  }
+    // normalize extension type
+    if (type === 'language') {
+      type = ext.type = 'lang';
+    }
 
-  if (type !== 'lang' && type !== 'output') {
-    ret.valid = false;
-    ret.error = baseMsg + 'type ' + type + ' is not recognized. Valid values: "lang" or "output"';
-    return ret;
-  }
+    if (type === 'html') {
+      type = ext.type = 'output';
+    }
 
-  if (ext.filter) {
-    if (typeof ext.filter !== 'function') {
+    if (type !== 'lang' && type !== 'output') {
       ret.valid = false;
-      ret.error = baseMsg + '"filter" must be a function, but ' + typeof ext.filter + ' given';
+      ret.error = baseMsg + 'type ' + type + ' is not recognized. Valid values: "lang" or "output"';
       return ret;
     }
 
-  } else if (ext.regex) {
-    if (showdown.helper.isString(ext.regex)) {
-      ext.regex = new RegExp(ext.regex, 'g');
-    }
-    if (!ext.regex instanceof RegExp) {
+    if (ext.filter) {
+      if (typeof ext.filter !== 'function') {
+        ret.valid = false;
+        ret.error = baseMsg + '"filter" must be a function, but ' + typeof ext.filter + ' given';
+        return ret;
+      }
+
+    } else if (ext.regex) {
+      if (showdown.helper.isString(ext.regex)) {
+        ext.regex = new RegExp(ext.regex, 'g');
+      }
+      if (!ext.regex instanceof RegExp) {
+        ret.valid = false;
+        ret.error = baseMsg + '"regex" property must either be a string or a RegExp object, but ' +
+          typeof ext.regex + ' given';
+        return ret;
+      }
+      if (showdown.helper.isUndefined(ext.replace)) {
+        ret.valid = false;
+        ret.error = baseMsg + '"regex" extensions must implement a replace string or function';
+        return ret;
+      }
+
+    } else {
       ret.valid = false;
-      ret.error = baseMsg + '"regex" property must either be a string or a RegExp object, but ' +
-        typeof ext.regex + ' given';
+      ret.error = baseMsg + 'extensions must define either a "regex" property or a "filter" method';
       return ret;
     }
-    if (showdown.helper.isUndefined(ext.replace)) {
+
+    if (showdown.helper.isUndefined(ext.filter) && showdown.helper.isUndefined(ext.regex)) {
       ret.valid = false;
-      ret.error = baseMsg + '"regex" extensions must implement a replace string or function';
+      ret.error = baseMsg + 'output extensions must define a filter property';
       return ret;
     }
-
-  } else {
-    ret.valid = false;
-    ret.error = baseMsg + 'extensions must define either a "regex" property or a "filter" method';
-    return ret;
-  }
-
-  if (showdown.helper.isUndefined(ext.filter) && showdown.helper.isUndefined(ext.regex)) {
-    ret.valid = false;
-    ret.error = baseMsg + 'output extensions must define a filter property';
-    return ret;
   }
-
   return ret;
 }
 
@@ -455,6 +468,9 @@ showdown.Converter = function (converterOptions) {
           options[opt] = converterOptions[opt];
         }
       }
+    } else {
+      throw Error('Converter expects the passed parameter to be an object, but ' + typeof converterOptions +
+      ' was passed instead.');
     }
 
     if (options.extensions) {
@@ -473,11 +489,12 @@ showdown.Converter = function (converterOptions) {
     if (showdown.helper.isString(ext)) {
       ext = showdown.helper.stdExtName(ext);
 
-      // TODO LEGACY SUPPORT CODE
-      if (!showdown.helper.isUndefined(showdown.extensions[ext]) && showdown.extensions[ext]) {
-        console.warn(ext + ' is an old extension that uses a deprecated loading method.' +
+      // LEGACY_SUPPORT CODE
+      if (showdown.extensions[ext]) {
+        console.warn('DEPRECATION WARNING: ' + ext + ' is an old extension that uses a deprecated loading method.' +
           'Please inform the developer that the extension should be updated!');
-        ext = showdown.extensions[ext];
+        legacyExtensionLoading(showdown.extensions[ext], ext);
+        return;
       // END LEGACY SUPPORT CODE
 
       } else if (!showdown.helper.isUndefined(extensions[ext])) {
@@ -486,26 +503,66 @@ showdown.Converter = function (converterOptions) {
       } else {
         throw Error('Extension "' + ext + '" could not be loaded. It was either not found or is not a valid extension.');
       }
-    } else if (typeof ext === 'function') {
+    }
+
+    if (typeof ext === 'function') {
       ext = ext();
     }
 
+    if (!showdown.helper.isArray(ext)) {
+      ext = [ext];
+    }
+
     if (!showdown.validateExtension(ext)) {
       return;
     }
 
-    switch (ext.type) {
-      case 'lang':
-        langExtensions.push(ext);
-        break;
+    for (var i = 0; i < ext.length; ++i) {
+      switch (ext[i].type) {
+        case 'lang':
+          langExtensions.push(ext[i]);
+          break;
+
+        case 'output':
+          outputModifiers.push(ext[i]);
+          break;
+
+        default:
+          // should never reach here
+          throw Error('Extension loader error: Type unrecognized!!!');
+      }
+    }
+  }
+
+  /**
+   * LEGACY_SUPPORT
+   * @param {*} ext
+   * @param {string} name
+   */
+  function legacyExtensionLoading(ext, name) {
+    if (typeof ext === 'function') {
+      ext = ext(new showdown.Converter());
+    }
+    if (!showdown.helper.isArray(ext)) {
+      ext = [ext];
+    }
+    var valid = validate(ext, name);
 
-      case 'output':
-        outputModifiers.push(ext);
-        break;
+    if (!valid.valid) {
+      throw Error(valid.error);
+    }
 
-      default:
-        // should never reach here
-        throw Error('Extension loader error: Type unrecognized!!!');
+    for (var i = 0; i < ext.length; ++i) {
+      switch (ext[i].type) {
+        case 'lang':
+          langExtensions.push(ext[i]);
+          break;
+        case 'output':
+          outputModifiers.push(ext[i]);
+          break;
+        default:// should never reach here
+          throw Error('Extension loader error: Type unrecognized!!!');
+      }
     }
   }
 
@@ -576,7 +633,6 @@ showdown.Converter = function (converterOptions) {
     showdown.helper.forEach(outputModifiers, function (ext) {
       text = showdown.subParser('runExtension')(ext, text, options, globals);
     });
-    text = parsers.outputModifiers(text, options, globals);
 
     return text;
   };
@@ -616,20 +672,34 @@ showdown.Converter = function (converterOptions) {
   };
 
   /**
-   * Remove an extension from THIS converter
-   * @param {{}} extension
+   * Use a global registered extension with THIS converter
+   * @param {string} extensionName Name of the previously registered extension
+   */
+  this.useExtension = function (extensionName) {
+    _parseExtension(extensionName);
+  };
+
+  /**
+   * Remove an extension from THIS converter.
+   * Note: This is a costly operation. It's better to initialize a new converter
+   * and specify the extensions you wish to use
+   * @param {Array} extension
    */
   this.removeExtension = function (extension) {
-    for (var i = 0; i < langExtensions.length; ++i) {
-      if (langExtensions[i] === extension) {
-        langExtensions[i].splice(i, 1);
-        return;
-      }
+    if (!showdown.helper.isArray(extension)) {
+      extension = [extension];
     }
-    for (var ii = 0; ii < outputModifiers.length; ++i) {
-      if (outputModifiers[ii] === extension) {
-        outputModifiers[ii].splice(i, 1);
-        return;
+    for (var a = 0; a < extension.length; ++a) {
+      var ext = extension[a];
+      for (var i = 0; i < langExtensions.length; ++i) {
+        if (langExtensions[i] === ext) {
+          langExtensions[i].splice(i, 1);
+        }
+      }
+      for (var ii = 0; ii < outputModifiers.length; ++i) {
+        if (outputModifiers[ii] === ext) {
+          outputModifiers[ii].splice(i, 1);
+        }
       }
     }
   };
@@ -1700,18 +1770,6 @@ showdown.subParser('outdent', function (text) {
   return text;
 });
 
-/**
- * Run language extensions
- */
-showdown.subParser('outputModifiers', function (text, config, globals) {
-  'use strict';
-
-  showdown.helper.forEach(globals.outputModifiers, function (ext) {
-    text = showdown.subParser('runExtension')(ext, text);
-  });
-  return text;
-});
-
 /**
  *
  */

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 0
dist/showdown.js.map


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 1 - 1
dist/showdown.min.js


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 0
dist/showdown.min.js.map


+ 1 - 1
package.json

@@ -30,7 +30,7 @@
     "url": "https://github.com/showdownjs/showdown.git",
     "web": "https://github.com/showdownjs/showdown"
   },
-  "license": "BSD",
+  "license": "BSD-2-Clause",
   "main": "./dist/showdown.js",
   "scripts": {
     "test": "grunt test"

+ 81 - 27
src/converter.js

@@ -97,11 +97,12 @@ showdown.Converter = function (converterOptions) {
     if (showdown.helper.isString(ext)) {
       ext = showdown.helper.stdExtName(ext);
 
-      // TODO LEGACY SUPPORT CODE
-      if (!showdown.helper.isUndefined(showdown.extensions[ext]) && showdown.extensions[ext]) {
-        console.warn(ext + ' is an old extension that uses a deprecated loading method.' +
+      // LEGACY_SUPPORT CODE
+      if (showdown.extensions[ext]) {
+        console.warn('DEPRECATION WARNING: ' + ext + ' is an old extension that uses a deprecated loading method.' +
           'Please inform the developer that the extension should be updated!');
-        ext = showdown.extensions[ext];
+        legacyExtensionLoading(showdown.extensions[ext], ext);
+        return;
       // END LEGACY SUPPORT CODE
 
       } else if (!showdown.helper.isUndefined(extensions[ext])) {
@@ -110,26 +111,66 @@ showdown.Converter = function (converterOptions) {
       } else {
         throw Error('Extension "' + ext + '" could not be loaded. It was either not found or is not a valid extension.');
       }
-    } else if (typeof ext === 'function') {
+    }
+
+    if (typeof ext === 'function') {
       ext = ext();
     }
 
+    if (!showdown.helper.isArray(ext)) {
+      ext = [ext];
+    }
+
     if (!showdown.validateExtension(ext)) {
       return;
     }
 
-    switch (ext.type) {
-      case 'lang':
-        langExtensions.push(ext);
-        break;
+    for (var i = 0; i < ext.length; ++i) {
+      switch (ext[i].type) {
+        case 'lang':
+          langExtensions.push(ext[i]);
+          break;
 
-      case 'output':
-        outputModifiers.push(ext);
-        break;
+        case 'output':
+          outputModifiers.push(ext[i]);
+          break;
 
-      default:
-        // should never reach here
-        throw Error('Extension loader error: Type unrecognized!!!');
+        default:
+          // should never reach here
+          throw Error('Extension loader error: Type unrecognized!!!');
+      }
+    }
+  }
+
+  /**
+   * LEGACY_SUPPORT
+   * @param {*} ext
+   * @param {string} name
+   */
+  function legacyExtensionLoading(ext, name) {
+    if (typeof ext === 'function') {
+      ext = ext(new showdown.Converter());
+    }
+    if (!showdown.helper.isArray(ext)) {
+      ext = [ext];
+    }
+    var valid = validate(ext, name);
+
+    if (!valid.valid) {
+      throw Error(valid.error);
+    }
+
+    for (var i = 0; i < ext.length; ++i) {
+      switch (ext[i].type) {
+        case 'lang':
+          langExtensions.push(ext[i]);
+          break;
+        case 'output':
+          outputModifiers.push(ext[i]);
+          break;
+        default:// should never reach here
+          throw Error('Extension loader error: Type unrecognized!!!');
+      }
     }
   }
 
@@ -200,7 +241,6 @@ showdown.Converter = function (converterOptions) {
     showdown.helper.forEach(outputModifiers, function (ext) {
       text = showdown.subParser('runExtension')(ext, text, options, globals);
     });
-    text = parsers.outputModifiers(text, options, globals);
 
     return text;
   };
@@ -240,20 +280,34 @@ showdown.Converter = function (converterOptions) {
   };
 
   /**
-   * Remove an extension from THIS converter
-   * @param {{}} extension
+   * Use a global registered extension with THIS converter
+   * @param {string} extensionName Name of the previously registered extension
+   */
+  this.useExtension = function (extensionName) {
+    _parseExtension(extensionName);
+  };
+
+  /**
+   * Remove an extension from THIS converter.
+   * Note: This is a costly operation. It's better to initialize a new converter
+   * and specify the extensions you wish to use
+   * @param {Array} extension
    */
   this.removeExtension = function (extension) {
-    for (var i = 0; i < langExtensions.length; ++i) {
-      if (langExtensions[i] === extension) {
-        langExtensions[i].splice(i, 1);
-        return;
-      }
+    if (!showdown.helper.isArray(extension)) {
+      extension = [extension];
     }
-    for (var ii = 0; ii < outputModifiers.length; ++i) {
-      if (outputModifiers[ii] === extension) {
-        outputModifiers[ii].splice(i, 1);
-        return;
+    for (var a = 0; a < extension.length; ++a) {
+      var ext = extension[a];
+      for (var i = 0; i < langExtensions.length; ++i) {
+        if (langExtensions[i] === ext) {
+          langExtensions[i].splice(i, 1);
+        }
+      }
+      for (var ii = 0; ii < outputModifiers.length; ++i) {
+        if (outputModifiers[ii] === ext) {
+          outputModifiers[ii].splice(i, 1);
+        }
       }
     }
   };

+ 63 - 50
src/showdown.js

@@ -113,10 +113,16 @@ showdown.extension = function (name, ext) {
 
     // Setter
   } else {
+    // Expand extension if it's wrapped in a function
     if (typeof ext === 'function') {
       ext = ext();
     }
 
+    // Ensure extension is an array
+    if (!showdown.helper.isArray(ext)) {
+      ext = [ext];
+    }
+
     var validExtension = validate(ext, name);
 
     if (validExtension.valid) {
@@ -155,83 +161,90 @@ showdown.resetExtensions = function () {
 
 /**
  * Validate extension
- * @param {object} ext
+ * @param {array} extension
  * @param {string} name
  * @returns {{valid: boolean, error: string}}
  */
-function validate(ext, name) {
+function validate(extension, name) {
   'use strict';
 
-  var baseMsg = (name) ? 'Error in ' + name + ' extension: ' : 'Error in unnamed extension',
+  var errMsg = (name) ? 'Error in ' + name + ' extension->' : 'Error in unnamed extension',
     ret = {
       valid: true,
-      error: baseMsg
+      error: ''
     };
 
-  if (typeof ext !== 'object') {
-    ret.valid = false;
-    ret.error = baseMsg + 'it must be an object, but ' + typeof ext + ' given';
-    return ret;
+  if (!showdown.helper.isArray(extension)) {
+    extension = [extension];
   }
 
-  if (!showdown.helper.isString(ext.type)) {
-    ret.valid = false;
-    ret.error = baseMsg + 'property "type" must be a string, but ' + typeof ext.type + ' given';
-    return ret;
-  }
+  for (var i = 0; i < extension.length; ++i) {
+    var baseMsg = errMsg + 'sub-extension ' + i + ': ',
+        ext = extension[i];
+    if (typeof ext !== 'object') {
+      ret.valid = false;
+      ret.error = baseMsg + 'must be an object, but ' + typeof ext + ' given';
+      return ret;
+    }
 
-  var type = ext.type = ext.type.toLowerCase();
+    if (!showdown.helper.isString(ext.type)) {
+      ret.valid = false;
+      ret.error = baseMsg + 'property "type" must be a string, but ' + typeof ext.type + ' given';
+      return ret;
+    }
 
-  // normalize extension type
-  if (type === 'language') {
-    type = ext.type = 'lang';
-  }
+    var type = ext.type = ext.type.toLowerCase();
 
-  if (type === 'html') {
-    type = ext.type = 'output';
-  }
+    // normalize extension type
+    if (type === 'language') {
+      type = ext.type = 'lang';
+    }
 
-  if (type !== 'lang' && type !== 'output') {
-    ret.valid = false;
-    ret.error = baseMsg + 'type ' + type + ' is not recognized. Valid values: "lang" or "output"';
-    return ret;
-  }
+    if (type === 'html') {
+      type = ext.type = 'output';
+    }
 
-  if (ext.filter) {
-    if (typeof ext.filter !== 'function') {
+    if (type !== 'lang' && type !== 'output') {
       ret.valid = false;
-      ret.error = baseMsg + '"filter" must be a function, but ' + typeof ext.filter + ' given';
+      ret.error = baseMsg + 'type ' + type + ' is not recognized. Valid values: "lang" or "output"';
       return ret;
     }
 
-  } else if (ext.regex) {
-    if (showdown.helper.isString(ext.regex)) {
-      ext.regex = new RegExp(ext.regex, 'g');
-    }
-    if (!ext.regex instanceof RegExp) {
+    if (ext.filter) {
+      if (typeof ext.filter !== 'function') {
+        ret.valid = false;
+        ret.error = baseMsg + '"filter" must be a function, but ' + typeof ext.filter + ' given';
+        return ret;
+      }
+
+    } else if (ext.regex) {
+      if (showdown.helper.isString(ext.regex)) {
+        ext.regex = new RegExp(ext.regex, 'g');
+      }
+      if (!ext.regex instanceof RegExp) {
+        ret.valid = false;
+        ret.error = baseMsg + '"regex" property must either be a string or a RegExp object, but ' +
+          typeof ext.regex + ' given';
+        return ret;
+      }
+      if (showdown.helper.isUndefined(ext.replace)) {
+        ret.valid = false;
+        ret.error = baseMsg + '"regex" extensions must implement a replace string or function';
+        return ret;
+      }
+
+    } else {
       ret.valid = false;
-      ret.error = baseMsg + '"regex" property must either be a string or a RegExp object, but ' +
-        typeof ext.regex + ' given';
+      ret.error = baseMsg + 'extensions must define either a "regex" property or a "filter" method';
       return ret;
     }
-    if (showdown.helper.isUndefined(ext.replace)) {
+
+    if (showdown.helper.isUndefined(ext.filter) && showdown.helper.isUndefined(ext.regex)) {
       ret.valid = false;
-      ret.error = baseMsg + '"regex" extensions must implement a replace string or function';
+      ret.error = baseMsg + 'output extensions must define a filter property';
       return ret;
     }
-
-  } else {
-    ret.valid = false;
-    ret.error = baseMsg + 'extensions must define either a "regex" property or a "filter" method';
-    return ret;
   }
-
-  if (showdown.helper.isUndefined(ext.filter) && showdown.helper.isUndefined(ext.regex)) {
-    ret.valid = false;
-    ret.error = baseMsg + 'output extensions must define a filter property';
-    return ret;
-  }
-
   return ret;
 }
 

+ 0 - 11
src/subParsers/outputModifiers.js

@@ -1,11 +0,0 @@
-/**
- * Run language extensions
- */
-showdown.subParser('outputModifiers', function (text, config, globals) {
-  'use strict';
-
-  showdown.helper.forEach(globals.outputModifiers, function (ext) {
-    text = showdown.subParser('runExtension')(ext, text);
-  });
-  return text;
-});

+ 24 - 0
test/node/legacyExtensionSupport.js

@@ -0,0 +1,24 @@
+/**
+ * Created by Estevao on 06-06-2015.
+ */
+require('source-map-support').install();
+var expect = require('chai').expect,
+    showdown = require('../../dist/showdown.js');
+
+describe('showdown legacy extension support', function () {
+  'use strict';
+  var extObjMock =
+      {
+        type: 'lang',
+        filter: function () {}
+      },
+      extFunc = function () {
+        return [extObjMock];
+      };
+
+  it('accept extensions loaded by the old mechanism', function () {
+    showdown.extensions.bazinga = extFunc;
+    var cnv = new showdown.Converter({extensions: ['bazinga']});
+    expect(cnv.getAllExtensions().language).to.eql([extObjMock]);
+  });
+});

+ 2 - 2
test/node/showdown.Converter.js

@@ -49,11 +49,11 @@ describe('showdown.Converter', function () {
       converter.getAllExtensions().language.should.contain(extObjMock);
     });
 
-    it('addExtension() should add a previous registered extension in showdown', function () {
+    it('useExtension() should use a previous registered extension in showdown', function () {
       showdown.extension('foo', extObjMock);
       var converter = new showdown.Converter();
 
-      converter.addExtension('foo');
+      converter.useExtension('foo');
       converter.getAllExtensions().language.should.contain(extObjMock);
       showdown.resetExtensions();
     });

+ 6 - 6
test/node/showdown.js

@@ -16,7 +16,7 @@ describe('showdown.options', function () {
   });
 });
 
-describe('showdown.extension', function () {
+describe('showdown.extension()', function () {
   'use strict';
 
   var extObjMock = {
@@ -28,15 +28,15 @@ describe('showdown.extension', function () {
       };
 
   describe('should register', function () {
-    it('should register an extension object', function () {
+    it('an extension object', function () {
       showdown.extension('foo', extObjMock);
-      showdown.extension('foo').should.equal(extObjMock);
+      showdown.extension('foo').should.eql([extObjMock]);
       showdown.resetExtensions();
     });
 
-    it('should register an extension function', function () {
+    it('an extension function', function () {
       showdown.extension('foo', extObjFunc);
-      showdown.extension('foo').should.equal(extObjMock);
+      showdown.extension('foo').should.eql([extObjMock]);
       showdown.resetExtensions();
     });
   });
@@ -78,6 +78,6 @@ describe('showdown.getAllExtensions()', function () {
 
   it('should return all extensions', function () {
     showdown.extension('bar', extObjMock);
-    showdown.getAllExtensions().should.eql({bar: extObjMock});
+    showdown.getAllExtensions().should.eql({bar: [extObjMock]});
   });
 });

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно