UNPKG

dgeni

Version:

Flexible JavaScript documentation generator used by both AngularJS and Angular

459 lines (392 loc) 16.8 kB
const {expect, spy} = require('chai').use(require('chai-spies')); import {Dgeni} from './Dgeni'; import {DocCollection} from './DocCollection'; describe('Dgeni', () => { let dgeni, mockLogger; beforeEach(() => { mockLogger = spy.object(['error', 'warning', 'info', 'debug', 'silly']); dgeni = new Dgeni(); const mockLoggerPackage = dgeni.package('mockLogger'); mockLoggerPackage.factory(function log() { return mockLogger; }); }); describe('constructor', () => { it('should accept an array of packages to load', () => { const package1 = new Dgeni.Package('package1'); const package2 = new Dgeni.Package('package2'); dgeni = new Dgeni([package1, package2]); expect(dgeni.packages.package1).to.equal(package1); expect(dgeni.packages.package2).to.equal(package2); }); it('should complain if the packages parameter is not an array', () => { expect(() => { new Dgeni('bad-param' as any); }).to.throw(); }); }); describe('package()', () => { it('should add the package to the packages property', () => { const testPackage = new Dgeni.Package('test-package'); dgeni.package(testPackage); expect(dgeni.packages['test-package']).to.eql(testPackage); }); it('should create a new package if passed a string', () => { const newPackage = dgeni.package('test-package'); expect(Dgeni.Package.isPackage(newPackage)).to.equal(true); }); it('should throw an error if the not passed an instance of Package or a string name', () => { expect(() => { dgeni.package({}); }).to.throw(); }); it('should complain if two packages have the same name', () => { dgeni.package('test'); expect(() => { dgeni.package('test'); }).to.throw(); }); it('should pass dependencies through to the new package', () => { const newPackage = dgeni.package('test-package', ['dep1', 'dep2']); expect(newPackage.dependencies).to.eql(['dep1', 'dep2']); }); it('should load up package dependencies that are defined inline', function() { const log = []; const a = new Dgeni.Package('a').processor(function aProcessor() { return { $process: (docs: DocCollection) => { log.push('a'); } }; }); const b = new Dgeni.Package('b', [a]); dgeni.package(b); expect(b.dependencies).to.eql([a]); expect(b.namedDependencies).to.eql(['a']); return dgeni.generate().then(() => { expect(log).to.eql(['a']); }); }); it('should not load a dependency that is already loaded', function() { const log = []; // Load package a1, with name 'a' const a1 = new Dgeni.Package('a').processor({ name: 'a', $process: () => { log.push('a1'); } }); dgeni.package(a1); // Load package b with inline depencency on a2, which also has name 'a' // This second 'a' package (i.e. a2) should nt get loaded const a2 = new Dgeni.Package('a').processor({ name: 'a', $process: () => { log.push('a2'); } }); const b = new Dgeni.Package('b', [a2]); dgeni.package(b); expect(b.dependencies).to.eql([a2]); expect(b.namedDependencies).to.eql(['a']); return dgeni.generate().then(() => { expect(log).to.eql(['a1']); }); }); it('should not modify the `dependencies` property of a package', () => { const a = new Dgeni.Package('a').processor({ name: 'a', $process: () => { } }); const b = new Dgeni.Package('b', [a]).processor({ name: 'a', $process: () => { } }); dgeni.package(b); expect(b.dependencies).to.eql([a]); expect(b.namedDependencies).to.eql(['a']); }); }); describe('configureInjector', () => { it('should return the configured injector', () => { const injector = dgeni.configureInjector(); expect(injector.get).to.be.a('function'); }); describe('services', () => { it('should add some basic shared services to the injector', () => { const injector = dgeni.configureInjector(); expect(injector.get('dgeni')).to.be.an('object'); expect(injector.get('log')).to.be.an('object'); expect(injector.get('log').debug).to.be.a('function'); expect(injector.get('getInjectables')).to.be.a('function'); }); it('should set stop on error defaults', () => { let stopOnProcessingError; dgeni.package('testPackage').config(function(dgeni) { stopOnProcessingError = dgeni.stopOnProcessingError; }); dgeni.configureInjector(); expect(stopOnProcessingError).to.equal(true); }); it('should add services to the injector', () => { const log = []; dgeni.package('test-package') .processor(function testProcessor(service1, service2) { return { $process: () => { log.push(service1); log.push(service2); } }; }) .factory(function service1() { return 'service1 value'; }) .factory(function service2(service1) { return service1 + ' service2 value'; }); const injector = dgeni.configureInjector(); injector.get('testProcessor').$process(); expect(log).to.eql(['service1 value', 'service1 value service2 value']); }); it('should add services from packages in the correct package dependency order', () => { const log = []; dgeni.package('test1', ['test2']).factory(function testValue() { return 'test 1'; }); dgeni.package('test2').factory(function testValue() { return 'test 2'; }); dgeni.package('test4', ['test3']) .processor(function test3Processor(testValue) { return { $process: () => { log.push(testValue + '(overridden)'); } }; }); dgeni.package('test3', ['test1', 'test2']) .processor(function test3Processor(testValue) { return { $process: () => { log.push(testValue); } }; }); const injector = dgeni.configureInjector(); injector.get('test3Processor').$process(); expect(log).to.eql(['test 1(overridden)']); }); }); describe('config blocks', () => { it('should run the config functions in the correct package dependency order', () => { const log = []; function testProcessor() { return { $process() { log.push(this.testValue); } }; } dgeni.package('test') .processor(testProcessor); dgeni.package('test1', ['test2']) .config(function(testProcessor) { testProcessor.testValue = 1; }); dgeni.package('test2', ['test']) .config(function(testProcessor) { testProcessor.testValue = 2; }); const injector = dgeni.configureInjector(); injector.get('testProcessor').$process(); expect(log).to.eql([1]); }); it('should provide config blocks with access to the injector', () => { let localInjector; dgeni.package('test') .config(function(injector) { localInjector = injector; }); const injector = dgeni.configureInjector(); expect(injector).to.equal(localInjector); }); }); describe('eventHandlers', () => { it('should add eventHandlers in the correct package dependency order', () => { function handler1() {} function handler2() {} function handler3() {} function handler4() {} dgeni.package('test1', ['test2']).eventHandler('testEvent', () => { return handler1; }); dgeni.package('test2') .eventHandler('testEvent', () => { return handler2; }) .eventHandler('testEvent2', () => { return handler3; }); dgeni.package('test3', ['test1']).eventHandler('testEvent', () => { return handler4; }); dgeni.configureInjector(); expect(dgeni.handlerMap).to.have.property('testEvent').to.eql([handler2, handler1, handler4]); expect(dgeni.handlerMap).to.have.property('testEvent2').to.eql([handler3]); }); }); describe('legacy validation', () => { it('should fail if processor has an invalid property', function(done) { dgeni.package('test') .processor(function testProcessor() { return { $validate: { x: { presence: true } } }; }); dgeni.generate().catch(function(errors) { expect(errors).to.eql([{ processor : 'testProcessor', package : 'test', errors : { x : [ 'X can\'t be blank' ] } }]); done(); }); }); }); describe('processors', () => { it('should order the processors by dependency', () => { const log = []; const a = { $runAfter: ['c'], $process: () => { log.push('a'); } }; const b = { $runAfter: ['c', 'e', 'a'], $process: () => { log.push('b'); } }; const c = { $runBefore: ['e'], $process: () => { log.push('c'); } }; const d = { $runAfter: ['a'], $process: () => { log.push('d'); } }; const e = { $runAfter: [], $process: () => { log.push('e'); } }; dgeni.package('test1') .processor('a', a) .processor('b', b) .processor('c', c) .processor('d', d) .processor('e', e); dgeni.configureInjector(); expect(dgeni.processors).to.eql([c, e, a, b, d]); }); it('should ignore processors that have $enabled set to false', () => { const a = { $process: () => {} }; const b = { $enabled: false, $process: () => {} }; const c = { $process: () => {} }; dgeni.package('test1') .processor('a', a) .processor('b', b) .processor('c', c); dgeni.configureInjector(); expect(dgeni.processors).to.eql([a, c]); }); it('should allow config blocks to change $enabled on a processor', () => { const log = []; const a = { $process: () => { log.push('a'); } }; const b = { $enabled: false, $process: () => { log.push('b'); } }; const c = { $process: () => { log.push('c'); } }; dgeni.package('test1') .processor('a', a) .processor('b', b) .processor('c', c) .config(function(a, b) { a.$enabled = false; b.$enabled = true; }); dgeni.configureInjector(); expect(dgeni.processors).to.eql([b, c]); }); it('should throw an error if the $runAfter dependencies are invalid', () => { dgeni.package('test') .processor(function badRunAfterProcessor() { return { $runAfter: ['tags-processed'] }; }); expect(() => { dgeni.configureInjector(); }).to.throw('Missing dependency: "tags-processed" on "badRunAfterProcessor"'); }); it('should throw an error if the $runBefore dependencies are invalid', () => { dgeni.package('test') .processor(function badRunBeforeProcessor() { return { $runBefore: ['tags-processed'] }; }); expect(() => { dgeni.configureInjector(); }).to.throw('Missing dependency: "tags-processed" on "badRunBeforeProcessor"'); }); it('should throw an error if the processor dependencies are cyclic', () => { dgeni.package('test') .processor(function processor1() { return { $runBefore: ['processor2'] }; }) .processor(function processor2() { return { $runBefore: ['processor1'] }; }); expect(() => { dgeni.configureInjector(); }).to.throw('Dependency Cycle Found: processor1 -> processor2 -> processor1'); }); it('should allow config blocks to change the order of the processors', function(done) { const log = []; dgeni.package('test') .processor(function a() { return { $runBefore: ['b'], $process: () => { log.push('a' ); } }; }) .processor(function b() { return { $runBefore: ['c'], $process: () => { log.push('b' ); } }; }) .processor(function c() { return { $process: () => { log.push('c' ); } }; }) .config(function(a, b, c) { b.$runBefore = []; c.$runBefore = ['b']; }); dgeni.generate([]).then(() => { expect(log).to.eql(['a', 'c', 'b']); done(); }); }); }); }); describe('triggerEvent()', () => { it('should run all the specified event\'s handlers in the correct dependency order', function(done) { const log = []; const handler1 = () => { log.push('handler1'); }; const handler2 = () => { log.push('handler2'); }; const handler3 = () => { log.push('handler3'); }; const handler4 = () => { log.push('handler4'); }; dgeni.package('test1', ['test2']).eventHandler('testEvent', () => { return handler1; }); dgeni.package('test2') .eventHandler('testEvent', () => { return handler2; }) .eventHandler('testEvent2', () => { return handler3; }); dgeni.package('test3', ['test1']).eventHandler('testEvent', () => { return handler4; }); dgeni.configureInjector(); dgeni.triggerEvent('testEvent').finally(() => { expect(log).to.eql(['handler2', 'handler1', 'handler4']); done(); }); }); it('should pass through the call arguments to the handler', function(done) { const handler = spy('handler'); dgeni.package('test1', []).eventHandler('testEvent', () => { return handler; }); dgeni.configureInjector(); dgeni.triggerEvent('testEvent', 'arg1', 'arg2', 'arg3').finally(() => { expect(handler).to.have.been.called.with('testEvent', 'arg1', 'arg2', 'arg3'); done(); }); }); it('should return a promise to event handler results', function(done) { function handler1() { } function handler2() { return true; } function handler3() { return { message: 'info' }; } function handler4() { return Promise.resolve(); } function handler5() { return Promise.resolve(true); } function handler6() { return Promise.resolve({ message: 'info async'}); } dgeni.package('test1', []) .eventHandler('testEvent', () => { return handler1; }) .eventHandler('testEvent', () => { return handler2; }) .eventHandler('testEvent', () => { return handler3; }) .eventHandler('testEvent', () => { return handler4; }) .eventHandler('testEvent', () => { return handler5; }) .eventHandler('testEvent', () => { return handler6; }); dgeni.configureInjector(); dgeni.triggerEvent('testEvent').then(function(results) { expect(results).to.eql([undefined, true, {message: 'info'}, undefined, true, {message: 'info async'}]); done(); }); }); }); describe('generate()', () => { describe('bad-processor', () => { let testPackage; beforeEach(() => { testPackage = dgeni.package('test') .processor(function badProcessor() { return { $process: () => { throw new Error('processor failed'); } }; }); }); describe('stopOnProcessingError', () => { it('should fail if stopOnProcessingError is true and a processor throws an Error', function() { return dgeni.generate() .catch(function(e) { expect(e).to.exist; }); }); it('should not fail but log the error if stopOnProcessingError is false a processor throws an Error', function() { let error; testPackage .config(function(dgeni) { dgeni.stopOnProcessingError = false; }); return dgeni.generate() .catch(function(e) { error = e; }) .finally(() => { expect(error).to.be.undefined; expect(mockLogger.error).to.have.been.called(); }); }); it('should continue to process the subsequent processors after a bad-processor if stopOnProcessingError is false', function() { let called = false; testPackage .config(function(dgeni) { dgeni.stopOnProcessingError = false; }) .processor(function checkProcessor() { return { $runAfter: ['badProcessor'], $process: () => { called = true; } }; }); return dgeni.generate().finally(() => { expect(called).to.eql(true); }); }); }); }); }); });