Skip to content

Commit

Permalink
clean up/rename summarize plugin, and add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
hegemonic committed Jul 27, 2014
1 parent fa53887 commit ca62994
Show file tree
Hide file tree
Showing 3 changed files with 163 additions and 48 deletions.
48 changes: 0 additions & 48 deletions plugins/autosummary.js

This file was deleted.

61 changes: 61 additions & 0 deletions plugins/summarize.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/**
* @overview This plugin creates a summary tag, if missing, from the first sentence in the
* description.
* @module plugins/summarize
* @author Mads Bondo Dydensborg <[email protected]>
*/
'use strict';

exports.handlers = {
/**
* Autogenerate summaries, if missing, from the description, if present.
*/
newDoclet: function(e) {
var endTag;
var tags;
var stack;

// If the summary is missing, grab the first sentence from the description
// and use that.
if (e.doclet && !e.doclet.summary && e.doclet.description) {
// The summary may end with `. ` or with `.<` (a period followed by an HTML tag).
e.doclet.summary = e.doclet.description.split(/\.\s|\.</)[0];
// Append `.` as it was removed in both cases, or is possibly missing.
e.doclet.summary += '.';

// This is an excerpt of something that is possibly HTML.
// Balance it using a stack. Assume it was initially balanced.
tags = e.doclet.summary.match(/<[^>]+>/g) || [];
stack = [];

tags.forEach(function(tag) {
var idx = tag.indexOf('/');

if (idx === -1) {
// start tag -- push onto the stack
stack.push(tag);
} else if (idx === 1) {
// end tag -- pop off of the stack
stack.pop();
}

// otherwise, it's a self-closing tag; don't modify the stack
});

// stack should now contain only the start tags that lack end tags,
// with the most deeply nested start tag at the top
while (stack.length > 0) {
// pop the unmatched tag off the stack
endTag = stack.pop();
// get just the tag name
endTag = endTag.substring(1, endTag.search(/[ >]/));
// append the end tag
e.doclet.summary += '</' + endTag + '>';
}

// and, finally, if the summary starts and ends with a <p> tag, remove it; let the
// template decide whether to wrap the summary in a <p> tag
e.doclet.summary = e.doclet.summary.replace(/^<p>(.*)<\/p>$/i, '$1');
}
}
};
102 changes: 102 additions & 0 deletions plugins/test/specs/summarize.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/*global describe, expect, it */
'use strict';

var summarize = require('../../summarize');

describe('summarize', function() {
it('should export handlers', function() {
expect(summarize.handlers).toBeDefined();
expect(typeof summarize.handlers).toBe('object');
});

it('should export a newDoclet handler', function() {
expect(summarize.handlers.newDoclet).toBeDefined();
expect(typeof summarize.handlers.newDoclet).toBe('function');
});

describe('newDoclet handler', function() {
var handler = summarize.handlers.newDoclet;

it('should not blow up if the doclet is missing', function() {
function noDoclet() {
return handler({});
}

expect(noDoclet).not.toThrow();
});

it('should not change the summary if it is already defined', function() {
var doclet = {
summary: 'This is a summary.',
description: 'Descriptions are good.'
};
handler({ doclet: doclet });

expect(doclet.summary).not.toBe(doclet.description);
});

it('should not do anything if the description is missing', function() {
var doclet = {};
handler({ doclet: doclet });

expect(doclet.summary).not.toBeDefined();
});

it('should use the first sentence as the summary', function() {
var doclet = {
description: 'This sentence is the summary. This sentence is not.'
};
handler({ doclet: doclet });

expect(doclet.summary).toBe('This sentence is the summary.');
});

it('should use the entire description, plus a period, as the summary if the description ' +
'does not contain a period', function() {
var doclet = {
description: 'This is a description'
};
handler({ doclet: doclet });

expect(doclet.summary).toBe('This is a description.');
});

it('should use the entire description as the summary if the description contains only ' +
'one sentence', function() {
var doclet = {
description: 'This is a description.'
};
handler({ doclet: doclet });

expect(doclet.description).toBe('This is a description.');
});

it('should work when an HTML tag immediately follows the first sentence', function() {
var doclet = {
description: 'This sentence is the summary.<small>This sentence is small.</small>'
};
handler({ doclet: doclet });

expect(doclet.summary).toBe('This sentence is the summary.');
});

it('should generate valid HTML if a tag is opened, but not closed, in the summary',
function() {
var doclet = {
description: 'This description has <em>a tag. The tag straddles</em> sentences.'
};
handler({ doclet: doclet });

expect(doclet.summary).toBe('This description has <em>a tag.</em>');
});

it('should not include a <p> tag in the summary', function() {
var doclet = {
description: '<p>This description contains HTML.</p><p>And plenty of it!</p>'
};
handler({ doclet: doclet });

expect(doclet.summary).toBe('This description contains HTML.');
});
});
});

0 comments on commit ca62994

Please sign in to comment.