Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor + add more tests (Part 1) #847

Merged
merged 6 commits into from
Jul 13, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions lib/core/__tests__/__fixtures__/insertTOC.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
---
id: pokemon-commands
title: Pokemon Commands
---

## Commands

<AUTOGENERATED_TABLE_OF_CONTENTS>

---

## Reference

### `pokemon-run`

Alias: `run`.

### `pokemon-fight`

Alias: `fight`

### `pokemon-bag`

Alias: `bag`

### `pokemon-rename`

Alias: `rename`
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`with custom heading levels 1`] = `
exports[`getTOC with custom heading levels 1`] = `
Array [
Object {
"children": Array [
Expand Down Expand Up @@ -105,7 +105,7 @@ Array [
]
`;

exports[`with defaults 1`] = `
exports[`getTOC with defaults 1`] = `
Array [
Object {
"children": Array [
Expand Down Expand Up @@ -185,3 +185,57 @@ Array [
},
]
`;

exports[`insertTOC AUTOGENERATED_TABLE_OF_CONTENTS does not exist 1`] = `
"## foo
### foo
### foo 1
## foo 1
## foo 2
### foo
#### 4th level headings
All 4th level headings should not be shown by default

## bar
### bar
#### bar
4th level heading should be ignored by default, but is should be always taken
into account, when generating slugs
### \`bar\`
#### \`bar\`
## bar
### bar
#### bar
## bar
"
`;

exports[`insertTOC AUTOGENERATED_TABLE_OF_CONTENTS exists 1`] = `
"
## Commands

- [\`pokemon-run\`](#pokemon-run)
- [\`pokemon-fight\`](#pokemon-fight)
- [\`pokemon-bag\`](#pokemon-bag)
- [\`pokemon-rename\`](#pokemon-rename)

---

## Reference

### \`pokemon-run\`

Alias: \`run\`.

### \`pokemon-fight\`

Alias: \`fight\`

### \`pokemon-bag\`

Alias: \`bag\`

### \`pokemon-rename\`

Alias: \`rename\`"
`;
33 changes: 0 additions & 33 deletions lib/core/__tests__/getTOC.test.js

This file was deleted.

60 changes: 60 additions & 0 deletions lib/core/__tests__/toc.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/**
* Copyright (c) 2017-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

const path = require('path');
const readFileSync = require('fs').readFileSync;
const {getTOC, insertTOC} = require('../toc');
const {extractMetadata} = require('../../server/metadataUtils');

const getTOCmd = readFileSync(
path.join(__dirname, '__fixtures__', 'getTOC.md'),
'utf8'
);

const insertTOCmd = readFileSync(
path.join(__dirname, '__fixtures__', 'insertTOC.md'),
'utf8'
);

describe('getTOC', () => {
test('with defaults', () => {
const headings = getTOC(getTOCmd);
const headingsJson = JSON.stringify(headings);

expect(headings).toMatchSnapshot();
expect(headingsJson).toContain('bar-8'); // maximum unique bar index is 8
expect(headingsJson).not.toContain('4th level headings');
});

test('with custom heading levels', () => {
const headings = getTOC(getTOCmd, 'h2', ['h3', 'h4']);
const headingsJson = JSON.stringify(headings);

expect(headings).toMatchSnapshot();
expect(headingsJson).toContain('bar-8'); // maximum unique bar index is 8
expect(headingsJson).toContain('4th level headings');
});
});

describe('insertTOC', () => {
test('null or undefined content', () => {
expect(insertTOC(null)).toBeNull();
expect(insertTOC(undefined)).toBeUndefined();
});

test('AUTOGENERATED_TABLE_OF_CONTENTS does not exist', () => {
const rawContent = extractMetadata(getTOCmd).rawContent;
expect(insertTOC(rawContent)).toMatchSnapshot();
expect(insertTOC(rawContent)).toEqual(rawContent);
});

test('AUTOGENERATED_TABLE_OF_CONTENTS exists', () => {
const rawContent = extractMetadata(insertTOCmd).rawContent;
expect(insertTOC(rawContent)).toMatchSnapshot();
expect(insertTOC(rawContent)).not.toEqual(rawContent);
});
});
2 changes: 1 addition & 1 deletion lib/core/nav/OnPageNav.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
const React = require('react');

const siteConfig = require(`${process.cwd()}/siteConfig.js`);
const getTOC = require('../getTOC');
const {getTOC} = require('../toc');

const Link = ({hashLink, content}) => (
<a
Expand Down
31 changes: 23 additions & 8 deletions lib/core/getTOC.js → lib/core/toc.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const Remarkable = require('remarkable');
const mdToc = require('markdown-toc');
const toSlug = require('./toSlug');

const tagToLevel = tag => Number(tag.slice(1));
const TABLE_OF_CONTENTS_TOKEN = '<AUTOGENERATED_TABLE_OF_CONTENTS>';

/**
* Returns a table of content from the headings
Expand All @@ -18,16 +18,15 @@ const tagToLevel = tag => Number(tag.slice(1));
* Array of heading objects with `hashLink`, `content` and `children` fields
*
*/
module.exports = (content, headingTags = 'h2', subHeadingTags = 'h3') => {
function getTOC(content, headingTags = 'h2', subHeadingTags = 'h3') {
const tagToLevel = tag => Number(tag.slice(1));
const headingLevels = [].concat(headingTags).map(tagToLevel);
const subHeadingLevels = subHeadingTags
? [].concat(subHeadingTags).map(tagToLevel)
: [];
const allowedHeadingLevels = headingLevels.concat(subHeadingLevels);

const md = new Remarkable();
const headings = mdToc(content).json;

const toc = [];
const context = {};
let current;
Expand All @@ -36,26 +35,42 @@ module.exports = (content, headingTags = 'h2', subHeadingTags = 'h3') => {
// we need always generate slugs to ensure, that we will have consistent
// slug indexes for headings with the same names
const hashLink = toSlug(heading.content, context);

if (!allowedHeadingLevels.includes(heading.lvl)) {
return;
}

const rawContent = mdToc.titleize(heading.content);
const entry = {
hashLink,
rawContent,
content: md.renderInline(rawContent),
children: [],
};

if (headingLevels.includes(heading.lvl)) {
toc.push(entry);
current = entry;
} else if (current) {
current.children.push(entry);
}
});

return toc;
}

// takes the content of a doc article and returns the content with a table of
// contents inserted
function insertTOC(rawContent) {
if (!rawContent || rawContent.indexOf(TABLE_OF_CONTENTS_TOKEN) === -1) {
return rawContent;
}
const filterRe = /^`[^`]*`/;
const headers = getTOC(rawContent, 'h3', null);
const tableOfContents = headers
.filter(header => filterRe.test(header.rawContent))
.map(header => ` - [${header.rawContent}](#${header.hashLink})`)
.join('\n');
return rawContent.replace(TABLE_OF_CONTENTS_TOKEN, tableOfContents);
}

module.exports = {
getTOC,
insertTOC,
};
1 change: 0 additions & 1 deletion lib/core/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ function getPath(path, cleanUrl = false) {
? path.replace(/\/index.html$/, '')
: removeExtension(path);
}

return path;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,11 @@
* LICENSE file in the root directory of this source tree.
*/

const {
blogRouting,
docsRouting,
dotRouting,
feedRouting,
noExtRouting,
pageRouting,
sitemapRouting,
} = require('../routing');
const routing = require('../routing');

describe('Blog routing', () => {
const blogRegex = blogRouting('/');
const blogRegex2 = blogRouting('/react/');
const blogRegex = routing.blog('/');
const blogRegex2 = routing.blog('/react/');

test('valid blog', () => {
expect('/blog/test.html').toMatch(blogRegex);
Expand All @@ -43,8 +35,8 @@ describe('Blog routing', () => {
});

describe('Docs routing', () => {
const docsRegex = docsRouting('/');
const docsRegex2 = docsRouting('/reason/');
const docsRegex = routing.docs('/');
const docsRegex2 = routing.docs('/reason/');

test('valid docs', () => {
expect('/docs/en/test.html').toMatch(docsRegex);
Expand All @@ -70,7 +62,7 @@ describe('Docs routing', () => {
});

describe('Dot routing', () => {
const dotRegex = dotRouting();
const dotRegex = routing.dotfiles();

test('valid url with dot after last slash', () => {
expect('/docs/en/test.23').toMatch(dotRegex);
Expand All @@ -96,8 +88,8 @@ describe('Dot routing', () => {
});

describe('Feed routing', () => {
const feedRegex = feedRouting('/');
const feedRegex2 = feedRouting('/reason/');
const feedRegex = routing.feed('/');
const feedRegex2 = routing.feed('/reason/');

test('valid feed url', () => {
expect('/blog/atom.xml').toMatch(feedRegex);
Expand Down Expand Up @@ -126,7 +118,7 @@ describe('Feed routing', () => {
});

describe('Extension-less url routing', () => {
const noExtRegex = noExtRouting();
const noExtRegex = routing.noExtension();

test('valid no extension url', () => {
expect('/test').toMatch(noExtRegex);
Expand All @@ -146,8 +138,8 @@ describe('Extension-less url routing', () => {
});

describe('Page routing', () => {
const pageRegex = pageRouting('/');
const pageRegex2 = pageRouting('/reason/');
const pageRegex = routing.page('/');
const pageRegex2 = routing.page('/reason/');

test('valid page url', () => {
expect('/index.html').toMatch(pageRegex);
Expand All @@ -173,8 +165,8 @@ describe('Page routing', () => {
});

describe('Sitemap routing', () => {
const sitemapRegex = sitemapRouting('/');
const sitemapRegex2 = sitemapRouting('/reason/');
const sitemapRegex = routing.sitemap('/');
const sitemapRegex2 = routing.sitemap('/reason/');

test('valid sitemap url', () => {
expect('/sitemap.xml').toMatch(sitemapRegex);
Expand Down
Loading