build(docs-infra): throw error if using `title` on code snippets (#26514)

Since #26396, the `title` property is ignored and `header` should be
used instead for specifying a code snippet's header.

This commit ensures that we don't accidentally set `title` have it be
silently ignored.

PR Close #26514
This commit is contained in:
George Kalpakas 2018-10-17 17:35:20 +03:00 committed by Miško Hevery
parent d557f1d9de
commit 0add00a743
4 changed files with 94 additions and 11 deletions

View File

@ -31,9 +31,15 @@ module.exports = function exampleInlineTagDef(parseArgString, createDocMessage,
const sourceCode = getExampleRegion(doc, relativePath, regionName); const sourceCode = getExampleRegion(doc, relativePath, regionName);
const attributes = []; const attributes = [];
if (relativePath) attributes.push(` path="${relativePath}"`);
if (regionName) attributes.push(` region="${regionName}"`);
if (header) attributes.push(` header="${header}"`); if (header) attributes.push(` header="${header}"`);
if (linenums !== undefined) attributes.push(` linenums="${linenums}"`); if (linenums !== undefined) attributes.push(` linenums="${linenums}"`);
// Preserve the no-longer-supported `title` attribute,
// in order to throw an appropriate error in `renderExamples` later.
if (tagArgs.title) attributes.push(` title="${tagArgs.title}"`);
return '<code-example' + attributes.join('') + '>\n' + sourceCode + '\n</code-example>'; return '<code-example' + attributes.join('') + '>\n' + sourceCode + '\n</code-example>';
} }
}; };

View File

@ -42,27 +42,40 @@ describe('example inline-tag-def', function() {
}); });
it('should contain the whole contents from the example file if no region is specified', () => { it('should contain the whole contents from the example file if no region is specified', () => {
expect(handler({}, 'example', 'test/url')).toEqual('<code-example>\nwhole file\n</code-example>'); expect(handler({}, 'example', 'test/url')).toEqual('<code-example path="test/url">\nwhole file\n</code-example>');
}); });
it('should contain the region contents from the example file if a region is specified', () => { it('should contain the region contents from the example file if a region is specified', () => {
expect(handler({}, 'example', 'test/url region-1')).toEqual('<code-example>\nregion 1 contents\n</code-example>'); expect(handler({}, 'example', 'test/url region-1')).toEqual(
'<code-example path="test/url" region="region-1">\nregion 1 contents\n</code-example>');
}); });
it('should add a header if specified', () => { it('should add a header if specified', () => {
expect(handler({}, 'example', 'test/url region-1 \'Some Header\'')).toEqual('<code-example header="Some Header">\nregion 1 contents\n</code-example>'); expect(handler({}, 'example', 'test/url region-1 \'Some Header\'')).toEqual(
expect(handler({}, 'example', 'test/url region-1 Some Header')).toEqual('<code-example header="Some Header">\nregion 1 contents\n</code-example>'); '<code-example path="test/url" region="region-1" header="Some Header">\nregion 1 contents\n</code-example>');
expect(handler({}, 'example', 'test/url region-1 Some Header')).toEqual(
'<code-example path="test/url" region="region-1" header="Some Header">\nregion 1 contents\n</code-example>');
}); });
it('should contain the whole contents from the example file if an empty ("") region is specified', () => { it('should contain the whole contents from the example file if an empty ("") region is specified', () => {
expect(handler({}, 'example', 'test/url \'\'')).toEqual('<code-example>\nwhole file\n</code-example>'); expect(handler({}, 'example', 'test/url \'\'')).toEqual(
expect(handler({}, 'example', 'test/url \'\' Some Header')).toEqual('<code-example header="Some Header">\nwhole file\n</code-example>'); '<code-example path="test/url">\nwhole file\n</code-example>');
expect(handler({}, 'example', 'test/url \'\' Some Header')).toEqual(
'<code-example path="test/url" header="Some Header">\nwhole file\n</code-example>');
}); });
it('should add in linenum attribute if specified', () => { it('should add in linenum attribute if specified', () => {
expect(handler({}, 'example', 'test/url --linenums=\'false\'')).toEqual('<code-example linenums="false">\nwhole file\n</code-example>'); expect(handler({}, 'example', 'test/url --linenums=\'false\'')).toEqual(
expect(handler({}, 'example', 'test/url --linenums=\'true\'')).toEqual('<code-example linenums="true">\nwhole file\n</code-example>'); '<code-example path="test/url" linenums="false">\nwhole file\n</code-example>');
expect(handler({}, 'example', 'test/url --linenums=\'15\'')).toEqual('<code-example linenums="15">\nwhole file\n</code-example>'); expect(handler({}, 'example', 'test/url --linenums=\'true\'')).toEqual(
'<code-example path="test/url" linenums="true">\nwhole file\n</code-example>');
expect(handler({}, 'example', 'test/url --linenums=\'15\'')).toEqual(
'<code-example path="test/url" linenums="15">\nwhole file\n</code-example>');
});
it('should preserve the title if specified', () => {
expect(handler({}, 'example', 'test/url title="Some Title"')).toEqual(
'<code-example path="test/url" title="Some Title">\nwhole file\n</code-example>');
}); });
}); });
}); });

View File

@ -10,6 +10,8 @@ module.exports = function renderExamples(getExampleRegion, log, createDocMessage
$runBefore: ['writing-files'], $runBefore: ['writing-files'],
ignoreBrokenExamples: false, ignoreBrokenExamples: false,
$process: function(docs) { $process: function(docs) {
const titleVsHeaderErrors = [];
docs.forEach(doc => { docs.forEach(doc => {
if (doc.renderedContent) { if (doc.renderedContent) {
// We match either `code-example` or `code-pane` elements that have a path attribute // We match either `code-example` or `code-pane` elements that have a path attribute
@ -17,6 +19,14 @@ module.exports = function renderExamples(getExampleRegion, log, createDocMessage
/<(code-example|code-pane)([^>]*)>[^<]*<\/([^>]+)>/g, /<(code-example|code-pane)([^>]*)>[^<]*<\/([^>]+)>/g,
(original, openingTag, attributes, closingTag) => { (original, openingTag, attributes, closingTag) => {
const attrMap = parseAttributes(attributes); const attrMap = parseAttributes(attributes);
if (attrMap.hasOwnProperty('title')) {
titleVsHeaderErrors.push(createDocMessage(
`Using the "title" attribute for specifying a ${openingTag} header is no longer supported. ` +
`Use the "header" attribute instead.\n<${openingTag}${attributes}>`, doc));
return;
}
if (attrMap.path) { if (attrMap.path) {
try { try {
if (closingTag !== openingTag) { if (closingTag !== openingTag) {
@ -59,6 +69,11 @@ module.exports = function renderExamples(getExampleRegion, log, createDocMessage
}); });
} }
}); });
if (titleVsHeaderErrors.length) {
titleVsHeaderErrors.forEach(err => log.error(err));
throw new Error('Some code snippets use the `title` attribute instead of `header`.');
}
} }
}; };
}; };

View File

@ -84,10 +84,10 @@ describe('renderExamples processor', () => {
it('should cope with spaces and double quotes inside attribute values', () => { it('should cope with spaces and double quotes inside attribute values', () => {
const docs = [ const docs = [
{ renderedContent: `<${CODE_TAG} title='a "quoted" value' path="test/url"></${CODE_TAG}>`} { renderedContent: `<${CODE_TAG} header='a "quoted" value' path="test/url"></${CODE_TAG}>`}
]; ];
processor.$process(docs); processor.$process(docs);
expect(docs[0].renderedContent).toEqual(`<${CODE_TAG} title="a &quot;quoted&quot; value" path="test/url">\nwhole file\n</${CODE_TAG}>`); expect(docs[0].renderedContent).toEqual(`<${CODE_TAG} header="a &quot;quoted&quot; value" path="test/url">\nwhole file\n</${CODE_TAG}>`);
}); });
it('should throw an exception if the code-example tag is not closed correctly', () => { it('should throw an exception if the code-example tag is not closed correctly', () => {
@ -117,6 +117,55 @@ describe('renderExamples processor', () => {
'Missing example file... relativePath: "missing/url". - doc\n' + 'Missing example file... relativePath: "missing/url". - doc\n' +
'Example files can be found in the following relative paths: "examples" - doc'); 'Example files can be found in the following relative paths: "examples" - doc');
}); });
it('should throw an exception if any code-example tag has a `title` attribute', () => {
const docs = [
{
name: 'Document A',
renderedContent: `
Example 1: <${CODE_TAG} path="test/url" header="This is a header "></${CODE_TAG}>
Example 2: <${CODE_TAG} path="test/url" title="This is a title 2"></${CODE_TAG}>
`,
},
{
name: 'Document B',
renderedContent: `
Example 3: <${CODE_TAG} path="test/url" title="This is a title 3"></${CODE_TAG}>
Example 4: <${CODE_TAG} path="test/url" header="This is a header 4"></${CODE_TAG}>
`,
},
];
expect(() => processor.$process(docs)).toThrowError(
'Some code snippets use the `title` attribute instead of `header`.');
expect(log.error).toHaveBeenCalledTimes(2);
expect(log.error).toHaveBeenCalledWith(
`Using the "title" attribute for specifying a ${CODE_TAG} header is no longer supported. ` +
'Use the "header" attribute instead.\n' +
`<${CODE_TAG} path="test/url" title="This is a title 2"> - doc "Document A"`);
expect(log.error).toHaveBeenCalledWith(
`Using the "title" attribute for specifying a ${CODE_TAG} header is no longer supported. ` +
'Use the "header" attribute instead.\n' +
`<${CODE_TAG} path="test/url" title="This is a title 3"> - doc "Document B"`);
});
it('should throw an exception for `title` attribute even if `ignoreBrokenExamples` is set to true', () => {
processor.ignoreBrokenExamples = true;
const docs = [
{ renderedContent: `<${CODE_TAG} path="test/url" title="This is a title"></${CODE_TAG}>` },
];
expect(() => processor.$process(docs)).toThrowError(
'Some code snippets use the `title` attribute instead of `header`.');
});
it('should throw an exception for `title` attribute even if there is no `path` attribute', () => {
const docs = [
{ renderedContent: `<${CODE_TAG} title="This is a title">Hard-coded contents.</${CODE_TAG}>` },
];
expect(() => processor.$process(docs)).toThrowError(
'Some code snippets use the `title` attribute instead of `header`.');
});
}) })
); );
}); });