mirror of
https://github.com/facebook/docusaurus.git
synced 2025-05-18 03:26:57 +02:00
test: improve test coverage (#6387)
* test: improve test coverage * fix * use posixPath
This commit is contained in:
parent
a9810db1cc
commit
62223ee556
24 changed files with 463 additions and 60 deletions
|
@ -1,4 +1,3 @@
|
||||||
__fixtures__
|
|
||||||
build
|
build
|
||||||
coverage
|
coverage
|
||||||
examples/
|
examples/
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
/* stylelint-disable docusaurus/copyright-header, declaration-block-no-duplicate-custom-properties */
|
||||||
:root {
|
:root {
|
||||||
--color-primary: red;
|
--color-primary: red;
|
||||||
--color-secondary: green;
|
--color-secondary: green;
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
/* stylelint-disable docusaurus/copyright-header, declaration-block-no-duplicate-custom-properties */
|
||||||
:root {
|
:root {
|
||||||
--color-primary: red;
|
--color-primary: red;
|
||||||
--color-primary: red;
|
--color-primary: red;
|
||||||
|
@ -5,3 +6,8 @@
|
||||||
--color-primary: blue;
|
--color-primary: blue;
|
||||||
--color-header: gray;
|
--color-header: gray;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.non-root {
|
||||||
|
--color-primary: red;
|
||||||
|
--color-primary: red;
|
||||||
|
}
|
||||||
|
|
|
@ -1,16 +1,23 @@
|
||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`remove-overridden-custom-properties overridden custom properties should be removed 1`] = `
|
exports[`remove-overridden-custom-properties overridden custom properties should be removed 1`] = `
|
||||||
":root {
|
"/* stylelint-disable docusaurus/copyright-header, declaration-block-no-duplicate-custom-properties */
|
||||||
|
:root {
|
||||||
--color-secondary: green;
|
--color-secondary: green;
|
||||||
--color-primary: blue;
|
--color-primary: blue;
|
||||||
--color-header: gray;
|
--color-header: gray;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.non-root {
|
||||||
|
--color-primary: red;
|
||||||
|
--color-primary: red;
|
||||||
|
}
|
||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`remove-overridden-custom-properties overridden custom properties with \`!important\` rule should not be removed 1`] = `
|
exports[`remove-overridden-custom-properties overridden custom properties with \`!important\` rule should not be removed 1`] = `
|
||||||
":root {
|
"/* stylelint-disable docusaurus/copyright-header, declaration-block-no-duplicate-custom-properties */
|
||||||
|
:root {
|
||||||
--color-primary: blue;
|
--color-primary: blue;
|
||||||
--color-header: gray !important;
|
--color-header: gray !important;
|
||||||
--color-secondary: yellow !important;
|
--color-secondary: yellow !important;
|
||||||
|
|
|
@ -22,8 +22,9 @@ module.exports = function creator() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const sameProperties =
|
const sameProperties = decl.parent.nodes.filter(
|
||||||
decl.parent.nodes.filter((n) => n.prop === decl.prop) || [];
|
(n) => n.prop === decl.prop,
|
||||||
|
);
|
||||||
const hasImportantProperties = sameProperties.some((p) =>
|
const hasImportantProperties = sameProperties.some((p) =>
|
||||||
Object.prototype.hasOwnProperty.call(p, 'important'),
|
Object.prototype.hasOwnProperty.call(p, 'important'),
|
||||||
);
|
);
|
||||||
|
|
|
@ -260,6 +260,25 @@ describe('collectRedirects', () => {
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('should allow returning string / undefined', () => {
|
||||||
|
expect(
|
||||||
|
collectRedirects(
|
||||||
|
createTestPluginContext(
|
||||||
|
{
|
||||||
|
createRedirects: (routePath) => {
|
||||||
|
if (routePath === '/') {
|
||||||
|
return `${routePath}foo`;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
['/', '/testpath', '/otherPath.html'],
|
||||||
|
),
|
||||||
|
undefined,
|
||||||
|
),
|
||||||
|
).toEqual([{from: '/foo', to: '/'}]);
|
||||||
|
});
|
||||||
|
|
||||||
test('should throw if redirect creator creates invalid redirects', () => {
|
test('should throw if redirect creator creates invalid redirects', () => {
|
||||||
expect(() =>
|
expect(() =>
|
||||||
collectRedirects(
|
collectRedirects(
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
---
|
---
|
||||||
slug: /hey/my super path/héllô
|
slug: /hey/my super path/héllô
|
||||||
title: Complex Slug
|
title: Complex Slug
|
||||||
date: 2020-08-16
|
# This date is not YAML date, but we can still use it.
|
||||||
|
date: 2020/08/16
|
||||||
---
|
---
|
||||||
|
|
||||||
complex url slug
|
complex url slug
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`blogFeed atom shows feed item for each post 1`] = `
|
exports[`blogFeed atom shows feed item for each post 1`] = `
|
||||||
"<?xml version=\\"1.0\\" encoding=\\"utf-8\\"?>
|
Array [
|
||||||
|
Array [
|
||||||
|
"build/blog/atom.xml",
|
||||||
|
"<?xml version=\\"1.0\\" encoding=\\"utf-8\\"?>
|
||||||
<feed xmlns=\\"http://www.w3.org/2005/Atom\\">
|
<feed xmlns=\\"http://www.w3.org/2005/Atom\\">
|
||||||
<id>https://docusaurus.io/myBaseUrl/blog</id>
|
<id>https://docusaurus.io/myBaseUrl/blog</id>
|
||||||
<title>Hello Blog</title>
|
<title>Hello Blog</title>
|
||||||
|
@ -83,11 +86,16 @@ exports[`blogFeed atom shows feed item for each post 1`] = `
|
||||||
<name>Sébastien Lorber (translated)</name>
|
<name>Sébastien Lorber (translated)</name>
|
||||||
</author>
|
</author>
|
||||||
</entry>
|
</entry>
|
||||||
</feed>"
|
</feed>",
|
||||||
|
],
|
||||||
|
]
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`blogFeed json shows feed item for each post 1`] = `
|
exports[`blogFeed json shows feed item for each post 1`] = `
|
||||||
"{
|
Array [
|
||||||
|
Array [
|
||||||
|
"build/blog/feed.json",
|
||||||
|
"{
|
||||||
\\"version\\": \\"https://jsonfeed.org/version/1\\",
|
\\"version\\": \\"https://jsonfeed.org/version/1\\",
|
||||||
\\"title\\": \\"Hello Blog\\",
|
\\"title\\": \\"Hello Blog\\",
|
||||||
\\"home_page_url\\": \\"https://docusaurus.io/myBaseUrl/blog\\",
|
\\"home_page_url\\": \\"https://docusaurus.io/myBaseUrl/blog\\",
|
||||||
|
@ -163,11 +171,16 @@ exports[`blogFeed json shows feed item for each post 1`] = `
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}"
|
}",
|
||||||
|
],
|
||||||
|
]
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`blogFeed rss shows feed item for each post 1`] = `
|
exports[`blogFeed rss shows feed item for each post 1`] = `
|
||||||
"<?xml version=\\"1.0\\" encoding=\\"utf-8\\"?>
|
Array [
|
||||||
|
Array [
|
||||||
|
"build/blog/rss.xml",
|
||||||
|
"<?xml version=\\"1.0\\" encoding=\\"utf-8\\"?>
|
||||||
<rss version=\\"2.0\\" xmlns:dc=\\"http://purl.org/dc/elements/1.1/\\" xmlns:content=\\"http://purl.org/rss/1.0/modules/content/\\">
|
<rss version=\\"2.0\\" xmlns:dc=\\"http://purl.org/dc/elements/1.1/\\" xmlns:content=\\"http://purl.org/rss/1.0/modules/content/\\">
|
||||||
<channel>
|
<channel>
|
||||||
<title>Hello Blog</title>
|
<title>Hello Blog</title>
|
||||||
|
@ -240,5 +253,7 @@ exports[`blogFeed rss shows feed item for each post 1`] = `
|
||||||
<content:encoded><![CDATA[<p>Happy birthday! (translated)</p>]]></content:encoded>
|
<content:encoded><![CDATA[<p>Happy birthday! (translated)</p>]]></content:encoded>
|
||||||
</item>
|
</item>
|
||||||
</channel>
|
</channel>
|
||||||
</rss>"
|
</rss>",
|
||||||
|
],
|
||||||
|
]
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -5,7 +5,24 @@
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {parseBlogFileName} from '../blogUtils';
|
import {truncate, parseBlogFileName} from '../blogUtils';
|
||||||
|
|
||||||
|
describe('truncate', () => {
|
||||||
|
test('truncates texts', () => {
|
||||||
|
expect(
|
||||||
|
truncate('aaa\n<!-- truncate -->\nbbb\nccc', /<!-- truncate -->/),
|
||||||
|
).toEqual('aaa\n');
|
||||||
|
expect(
|
||||||
|
truncate('\n<!-- truncate -->\nbbb\nccc', /<!-- truncate -->/),
|
||||||
|
).toEqual('\n');
|
||||||
|
});
|
||||||
|
test('leaves texts without markers', () => {
|
||||||
|
expect(truncate('aaa\nbbb\nccc', /<!-- truncate -->/)).toEqual(
|
||||||
|
'aaa\nbbb\nccc',
|
||||||
|
);
|
||||||
|
expect(truncate('', /<!-- truncate -->/)).toEqual('');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('parseBlogFileName', () => {
|
describe('parseBlogFileName', () => {
|
||||||
test('parse file', () => {
|
test('parse file', () => {
|
||||||
|
|
|
@ -6,12 +6,12 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import {generateBlogFeed} from '../feed';
|
import fs from 'fs-extra';
|
||||||
|
import {createBlogFeedFiles} from '../feed';
|
||||||
import type {LoadContext, I18n} from '@docusaurus/types';
|
import type {LoadContext, I18n} from '@docusaurus/types';
|
||||||
import type {BlogContentPaths} from '../types';
|
import type {BlogContentPaths} from '../types';
|
||||||
import {DEFAULT_OPTIONS} from '../pluginOptionSchema';
|
import {DEFAULT_OPTIONS} from '../pluginOptionSchema';
|
||||||
import {generateBlogPosts} from '../blogUtils';
|
import {generateBlogPosts} from '../blogUtils';
|
||||||
import type {Feed} from 'feed';
|
|
||||||
import type {PluginOptions} from '@docusaurus/plugin-content-blog';
|
import type {PluginOptions} from '@docusaurus/plugin-content-blog';
|
||||||
|
|
||||||
const DefaultI18N: I18n = {
|
const DefaultI18N: I18n = {
|
||||||
|
@ -36,23 +36,26 @@ function getBlogContentPaths(siteDir: string): BlogContentPaths {
|
||||||
async function testGenerateFeeds(
|
async function testGenerateFeeds(
|
||||||
context: LoadContext,
|
context: LoadContext,
|
||||||
options: PluginOptions,
|
options: PluginOptions,
|
||||||
): Promise<Feed | null> {
|
): Promise<void> {
|
||||||
const blogPosts = await generateBlogPosts(
|
const blogPosts = await generateBlogPosts(
|
||||||
getBlogContentPaths(context.siteDir),
|
getBlogContentPaths(context.siteDir),
|
||||||
context,
|
context,
|
||||||
options,
|
options,
|
||||||
);
|
);
|
||||||
|
|
||||||
return generateBlogFeed({
|
await createBlogFeedFiles({
|
||||||
blogPosts,
|
blogPosts,
|
||||||
options,
|
options,
|
||||||
siteConfig: context.siteConfig,
|
siteConfig: context.siteConfig,
|
||||||
|
outDir: 'build',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('blogFeed', () => {
|
describe('blogFeed', () => {
|
||||||
(['atom', 'rss', 'json'] as const).forEach((feedType) => {
|
(['atom', 'rss', 'json'] as const).forEach((feedType) => {
|
||||||
describe(`${feedType}`, () => {
|
describe(`${feedType}`, () => {
|
||||||
|
const fsMock = jest.spyOn(fs, 'outputFile').mockImplementation(() => {});
|
||||||
|
|
||||||
test('should not show feed without posts', async () => {
|
test('should not show feed without posts', async () => {
|
||||||
const siteDir = __dirname;
|
const siteDir = __dirname;
|
||||||
const siteConfig = {
|
const siteConfig = {
|
||||||
|
@ -62,7 +65,7 @@ describe('blogFeed', () => {
|
||||||
favicon: 'image/favicon.ico',
|
favicon: 'image/favicon.ico',
|
||||||
};
|
};
|
||||||
|
|
||||||
const feed = await testGenerateFeeds(
|
await testGenerateFeeds(
|
||||||
{
|
{
|
||||||
siteDir,
|
siteDir,
|
||||||
siteConfig,
|
siteConfig,
|
||||||
|
@ -83,7 +86,8 @@ describe('blogFeed', () => {
|
||||||
} as PluginOptions,
|
} as PluginOptions,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(feed).toEqual(null);
|
expect(fsMock).toBeCalledTimes(0);
|
||||||
|
fsMock.mockClear();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('shows feed item for each post', async () => {
|
test('shows feed item for each post', async () => {
|
||||||
|
@ -96,7 +100,7 @@ describe('blogFeed', () => {
|
||||||
favicon: 'image/favicon.ico',
|
favicon: 'image/favicon.ico',
|
||||||
};
|
};
|
||||||
|
|
||||||
const feed = await testGenerateFeeds(
|
await testGenerateFeeds(
|
||||||
{
|
{
|
||||||
siteDir,
|
siteDir,
|
||||||
siteConfig,
|
siteConfig,
|
||||||
|
@ -119,22 +123,8 @@ describe('blogFeed', () => {
|
||||||
} as PluginOptions,
|
} as PluginOptions,
|
||||||
);
|
);
|
||||||
|
|
||||||
let feedContent = '';
|
expect(fsMock.mock.calls).toMatchSnapshot();
|
||||||
switch (feedType) {
|
fsMock.mockClear();
|
||||||
case 'rss':
|
|
||||||
feedContent = feed.rss2();
|
|
||||||
break;
|
|
||||||
case 'json':
|
|
||||||
feedContent = feed.json1();
|
|
||||||
break;
|
|
||||||
case 'atom':
|
|
||||||
feedContent = feed.atom1();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(feedContent).toMatchSnapshot();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -204,7 +204,7 @@ describe('loadBlog', () => {
|
||||||
date: new Date('2020-08-16'),
|
date: new Date('2020-08-16'),
|
||||||
formattedDate: 'August 16, 2020',
|
formattedDate: 'August 16, 2020',
|
||||||
frontMatter: {
|
frontMatter: {
|
||||||
date: new Date('2020-08-16'),
|
date: '2020/08/16',
|
||||||
slug: '/hey/my super path/héllô',
|
slug: '/hey/my super path/héllô',
|
||||||
title: 'Complex Slug',
|
title: 'Complex Slug',
|
||||||
},
|
},
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
import {Feed, type Author as FeedAuthor, type Item as FeedItem} from 'feed';
|
import {Feed, type Author as FeedAuthor, type Item as FeedItem} from 'feed';
|
||||||
import type {BlogPost} from './types';
|
import type {BlogPost} from './types';
|
||||||
import {normalizeUrl, mdxToHtml} from '@docusaurus/utils';
|
import {normalizeUrl, mdxToHtml, posixPath} from '@docusaurus/utils';
|
||||||
import type {DocusaurusConfig} from '@docusaurus/types';
|
import type {DocusaurusConfig} from '@docusaurus/types';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import fs from 'fs-extra';
|
import fs from 'fs-extra';
|
||||||
|
@ -30,7 +30,7 @@ function mdxToFeedContent(mdxContent: string): string | undefined {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function generateBlogFeed({
|
async function generateBlogFeed({
|
||||||
blogPosts,
|
blogPosts,
|
||||||
options,
|
options,
|
||||||
siteConfig,
|
siteConfig,
|
||||||
|
@ -47,9 +47,7 @@ export async function generateBlogFeed({
|
||||||
const {url: siteUrl, baseUrl, title, favicon} = siteConfig;
|
const {url: siteUrl, baseUrl, title, favicon} = siteConfig;
|
||||||
const blogBaseUrl = normalizeUrl([siteUrl, baseUrl, routeBasePath]);
|
const blogBaseUrl = normalizeUrl([siteUrl, baseUrl, routeBasePath]);
|
||||||
|
|
||||||
const updated =
|
const updated = blogPosts[0] && blogPosts[0].metadata.date;
|
||||||
(blogPosts[0] && blogPosts[0].metadata.date) ||
|
|
||||||
new Date('2015-10-25T16:29:00.000-07:00'); // weird legacy magic date
|
|
||||||
|
|
||||||
const feed = new Feed({
|
const feed = new Feed({
|
||||||
id: blogBaseUrl,
|
id: blogBaseUrl,
|
||||||
|
@ -118,7 +116,10 @@ async function createBlogFeedFile({
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
try {
|
try {
|
||||||
await fs.outputFile(path.join(generatePath, feedPath), feedContent);
|
await fs.outputFile(
|
||||||
|
posixPath(path.join(generatePath, feedPath)),
|
||||||
|
feedContent,
|
||||||
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw new Error(`Generating ${feedType} feed failed: ${err}.`);
|
throw new Error(`Generating ${feedType} feed failed: ${err}.`);
|
||||||
}
|
}
|
||||||
|
|
|
@ -201,7 +201,9 @@ describe('DefaultSidebarItemsGenerator', () => {
|
||||||
source: 'guide1.md',
|
source: 'guide1.md',
|
||||||
sourceDirName: '02-Guides',
|
sourceDirName: '02-Guides',
|
||||||
sidebarPosition: 1,
|
sidebarPosition: 1,
|
||||||
frontMatter: {},
|
frontMatter: {
|
||||||
|
sidebar_class_name: 'foo',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'nested-guide',
|
id: 'nested-guide',
|
||||||
|
@ -250,7 +252,7 @@ describe('DefaultSidebarItemsGenerator', () => {
|
||||||
id: 'guides-index',
|
id: 'guides-index',
|
||||||
},
|
},
|
||||||
items: [
|
items: [
|
||||||
{type: 'doc', id: 'guide1'},
|
{type: 'doc', id: 'guide1', className: 'foo'},
|
||||||
{
|
{
|
||||||
type: 'category',
|
type: 'category',
|
||||||
label: 'SubGuides (metadata file label)',
|
label: 'SubGuides (metadata file label)',
|
||||||
|
@ -278,12 +280,17 @@ describe('DefaultSidebarItemsGenerator', () => {
|
||||||
'subfolder/subsubfolder/subsubsubfolder2/_category_.yml': {
|
'subfolder/subsubfolder/subsubsubfolder2/_category_.yml': {
|
||||||
position: 2,
|
position: 2,
|
||||||
label: 'subsubsubfolder2 (_category_.yml label)',
|
label: 'subsubsubfolder2 (_category_.yml label)',
|
||||||
|
className: 'bar',
|
||||||
},
|
},
|
||||||
'subfolder/subsubfolder/subsubsubfolder3/_category_.json': {
|
'subfolder/subsubfolder/subsubsubfolder3/_category_.json': {
|
||||||
position: 1,
|
position: 1,
|
||||||
label: 'subsubsubfolder3 (_category_.json label)',
|
label: 'subsubsubfolder3 (_category_.json label)',
|
||||||
collapsible: false,
|
collapsible: false,
|
||||||
collapsed: false,
|
collapsed: false,
|
||||||
|
link: {
|
||||||
|
type: 'doc',
|
||||||
|
id: 'doc1', // This is a "fully-qualified" ID that can't be found locally
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -367,6 +374,10 @@ describe('DefaultSidebarItemsGenerator', () => {
|
||||||
label: 'subsubsubfolder3 (_category_.json label)',
|
label: 'subsubsubfolder3 (_category_.json label)',
|
||||||
collapsed: false,
|
collapsed: false,
|
||||||
collapsible: false,
|
collapsible: false,
|
||||||
|
link: {
|
||||||
|
id: 'doc1',
|
||||||
|
type: 'doc',
|
||||||
|
},
|
||||||
items: [
|
items: [
|
||||||
{type: 'doc', id: 'doc8'},
|
{type: 'doc', id: 'doc8'},
|
||||||
{type: 'doc', id: 'doc7'},
|
{type: 'doc', id: 'doc7'},
|
||||||
|
@ -377,6 +388,7 @@ describe('DefaultSidebarItemsGenerator', () => {
|
||||||
label: 'subsubsubfolder2 (_category_.yml label)',
|
label: 'subsubsubfolder2 (_category_.yml label)',
|
||||||
collapsed: true,
|
collapsed: true,
|
||||||
collapsible: true,
|
collapsible: true,
|
||||||
|
className: 'bar',
|
||||||
items: [{type: 'doc', id: 'doc6'}],
|
items: [{type: 'doc', id: 'doc6'}],
|
||||||
},
|
},
|
||||||
{type: 'doc', id: 'doc1'},
|
{type: 'doc', id: 'doc1'},
|
||||||
|
|
|
@ -102,8 +102,8 @@ describe('processSidebars', () => {
|
||||||
slug: 'category-generated-index-slug',
|
slug: 'category-generated-index-slug',
|
||||||
permalink: 'category-generated-index-permalink',
|
permalink: 'category-generated-index-permalink',
|
||||||
},
|
},
|
||||||
collapsed: false,
|
collapsed: true, // A suspicious bad config that will be normalized
|
||||||
collapsible: true,
|
collapsible: false,
|
||||||
items: [
|
items: [
|
||||||
{type: 'doc', id: 'doc2'},
|
{type: 'doc', id: 'doc2'},
|
||||||
{type: 'autogenerated', dirName: 'dir1'},
|
{type: 'autogenerated', dirName: 'dir1'},
|
||||||
|
@ -172,7 +172,7 @@ describe('processSidebars', () => {
|
||||||
permalink: 'category-generated-index-permalink',
|
permalink: 'category-generated-index-permalink',
|
||||||
},
|
},
|
||||||
collapsed: false,
|
collapsed: false,
|
||||||
collapsible: true,
|
collapsible: false,
|
||||||
items: [{type: 'doc', id: 'doc2'}, ...StaticGeneratedSidebarSlice],
|
items: [{type: 'doc', id: 'doc2'}, ...StaticGeneratedSidebarSlice],
|
||||||
},
|
},
|
||||||
{type: 'link', href: 'https://facebook.com', label: 'FB'},
|
{type: 'link', href: 'https://facebook.com', label: 'FB'},
|
||||||
|
|
|
@ -96,6 +96,14 @@ describe('createSidebarsUtils', () => {
|
||||||
];
|
];
|
||||||
|
|
||||||
const sidebar4: Sidebar = [
|
const sidebar4: Sidebar = [
|
||||||
|
{
|
||||||
|
type: 'category',
|
||||||
|
items: [
|
||||||
|
{type: 'link', href: 'https://facebook.com'},
|
||||||
|
{type: 'link', href: 'https://reactjs.org'},
|
||||||
|
{type: 'link', href: 'https://docusaurus.io'},
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
type: 'category',
|
type: 'category',
|
||||||
collapsed: false,
|
collapsed: false,
|
||||||
|
|
|
@ -5,7 +5,11 @@
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {parseCodeBlockTitle} from '../codeBlockUtils';
|
import {
|
||||||
|
parseCodeBlockTitle,
|
||||||
|
parseLanguage,
|
||||||
|
parseLines,
|
||||||
|
} from '../codeBlockUtils';
|
||||||
|
|
||||||
describe('parseCodeBlockTitle', () => {
|
describe('parseCodeBlockTitle', () => {
|
||||||
test('should parse double quote delimited title', () => {
|
test('should parse double quote delimited title', () => {
|
||||||
|
@ -52,3 +56,227 @@ describe('parseCodeBlockTitle', () => {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('parseLanguage', () => {
|
||||||
|
test('behaves correctly', () => {
|
||||||
|
expect(parseLanguage('language-foo xxx yyy')).toEqual('foo');
|
||||||
|
expect(parseLanguage('xxxxx language-foo yyy')).toEqual('foo');
|
||||||
|
expect(parseLanguage('xx-language-foo yyyy')).toBeUndefined();
|
||||||
|
expect(parseLanguage('xxx yyy zzz')).toBeUndefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('parseLines', () => {
|
||||||
|
test('does not parse content with metastring', () => {
|
||||||
|
expect(parseLines('aaaaa\nbbbbb', '{1}', 'js')).toMatchInlineSnapshot(`
|
||||||
|
Object {
|
||||||
|
"code": "aaaaa
|
||||||
|
bbbbb",
|
||||||
|
"highlightLines": Array [
|
||||||
|
0,
|
||||||
|
],
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
expect(
|
||||||
|
parseLines(
|
||||||
|
`// highlight-next-line
|
||||||
|
aaaaa
|
||||||
|
bbbbb`,
|
||||||
|
'{1}',
|
||||||
|
'js',
|
||||||
|
),
|
||||||
|
).toMatchInlineSnapshot(`
|
||||||
|
Object {
|
||||||
|
"code": "// highlight-next-line
|
||||||
|
aaaaa
|
||||||
|
bbbbb",
|
||||||
|
"highlightLines": Array [
|
||||||
|
0,
|
||||||
|
],
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
expect(
|
||||||
|
parseLines(
|
||||||
|
`aaaaa
|
||||||
|
bbbbb`,
|
||||||
|
'{1}',
|
||||||
|
),
|
||||||
|
).toMatchInlineSnapshot(`
|
||||||
|
Object {
|
||||||
|
"code": "aaaaa
|
||||||
|
bbbbb",
|
||||||
|
"highlightLines": Array [
|
||||||
|
0,
|
||||||
|
],
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
test('does not parse content with no language', () => {
|
||||||
|
expect(
|
||||||
|
parseLines(
|
||||||
|
`// highlight-next-line
|
||||||
|
aaaaa
|
||||||
|
bbbbb`,
|
||||||
|
'',
|
||||||
|
undefined,
|
||||||
|
),
|
||||||
|
).toMatchInlineSnapshot(`
|
||||||
|
Object {
|
||||||
|
"code": "// highlight-next-line
|
||||||
|
aaaaa
|
||||||
|
bbbbb",
|
||||||
|
"highlightLines": Array [],
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
test('removes lines correctly', () => {
|
||||||
|
expect(
|
||||||
|
parseLines(
|
||||||
|
`// highlight-next-line
|
||||||
|
aaaaa
|
||||||
|
bbbbb`,
|
||||||
|
'',
|
||||||
|
'js',
|
||||||
|
),
|
||||||
|
).toMatchInlineSnapshot(`
|
||||||
|
Object {
|
||||||
|
"code": "aaaaa
|
||||||
|
bbbbb",
|
||||||
|
"highlightLines": Array [
|
||||||
|
0,
|
||||||
|
],
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
expect(
|
||||||
|
parseLines(
|
||||||
|
`// highlight-start
|
||||||
|
aaaaa
|
||||||
|
// highlight-end
|
||||||
|
bbbbb`,
|
||||||
|
'',
|
||||||
|
'js',
|
||||||
|
),
|
||||||
|
).toMatchInlineSnapshot(`
|
||||||
|
Object {
|
||||||
|
"code": "aaaaa
|
||||||
|
bbbbb",
|
||||||
|
"highlightLines": Array [
|
||||||
|
0,
|
||||||
|
],
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
expect(
|
||||||
|
parseLines(
|
||||||
|
`// highlight-start
|
||||||
|
// highlight-next-line
|
||||||
|
aaaaa
|
||||||
|
bbbbbbb
|
||||||
|
// highlight-next-line
|
||||||
|
// highlight-end
|
||||||
|
bbbbb`,
|
||||||
|
'',
|
||||||
|
'js',
|
||||||
|
),
|
||||||
|
).toMatchInlineSnapshot(`
|
||||||
|
Object {
|
||||||
|
"code": "aaaaa
|
||||||
|
bbbbbbb
|
||||||
|
bbbbb",
|
||||||
|
"highlightLines": Array [
|
||||||
|
0,
|
||||||
|
2,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
],
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
test('respects language', () => {
|
||||||
|
expect(
|
||||||
|
parseLines(
|
||||||
|
`# highlight-next-line
|
||||||
|
aaaaa
|
||||||
|
bbbbb`,
|
||||||
|
'',
|
||||||
|
'js',
|
||||||
|
),
|
||||||
|
).toMatchInlineSnapshot(`
|
||||||
|
Object {
|
||||||
|
"code": "# highlight-next-line
|
||||||
|
aaaaa
|
||||||
|
bbbbb",
|
||||||
|
"highlightLines": Array [],
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
expect(
|
||||||
|
parseLines(
|
||||||
|
`/* highlight-next-line */
|
||||||
|
aaaaa
|
||||||
|
bbbbb`,
|
||||||
|
'',
|
||||||
|
'py',
|
||||||
|
),
|
||||||
|
).toMatchInlineSnapshot(`
|
||||||
|
Object {
|
||||||
|
"code": "/* highlight-next-line */
|
||||||
|
aaaaa
|
||||||
|
bbbbb",
|
||||||
|
"highlightLines": Array [],
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
expect(
|
||||||
|
parseLines(
|
||||||
|
`// highlight-next-line
|
||||||
|
aaaa
|
||||||
|
/* highlight-next-line */
|
||||||
|
bbbbb
|
||||||
|
# highlight-next-line
|
||||||
|
ccccc
|
||||||
|
<!-- highlight-next-line -->
|
||||||
|
dddd`,
|
||||||
|
'',
|
||||||
|
'py',
|
||||||
|
),
|
||||||
|
).toMatchInlineSnapshot(`
|
||||||
|
Object {
|
||||||
|
"code": "// highlight-next-line
|
||||||
|
aaaa
|
||||||
|
/* highlight-next-line */
|
||||||
|
bbbbb
|
||||||
|
ccccc
|
||||||
|
<!-- highlight-next-line -->
|
||||||
|
dddd",
|
||||||
|
"highlightLines": Array [
|
||||||
|
4,
|
||||||
|
],
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
expect(
|
||||||
|
parseLines(
|
||||||
|
`// highlight-next-line
|
||||||
|
aaaa
|
||||||
|
/* highlight-next-line */
|
||||||
|
bbbbb
|
||||||
|
# highlight-next-line
|
||||||
|
ccccc
|
||||||
|
<!-- highlight-next-line -->
|
||||||
|
dddd`,
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
),
|
||||||
|
).toMatchInlineSnapshot(`
|
||||||
|
Object {
|
||||||
|
"code": "aaaa
|
||||||
|
bbbbb
|
||||||
|
ccccc
|
||||||
|
dddd",
|
||||||
|
"highlightLines": Array [
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
3,
|
||||||
|
],
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
@ -5,7 +5,27 @@
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {uniq} from '../jsUtils';
|
import {uniq, duplicates} from '../jsUtils';
|
||||||
|
|
||||||
|
describe('duplicates', () => {
|
||||||
|
test('gets duplicate values', () => {
|
||||||
|
expect(duplicates(['a', 'b', 'c', 'd'])).toEqual([]);
|
||||||
|
expect(duplicates(['a', 'b', 'b', 'b'])).toEqual(['b', 'b']);
|
||||||
|
expect(duplicates(['c', 'b', 'b', 'c'])).toEqual(['b', 'c']);
|
||||||
|
expect(duplicates([{a: 1}, {a: 1}, {a: 1}])).toEqual([]);
|
||||||
|
});
|
||||||
|
test('accepts custom comparator', () => {
|
||||||
|
expect(duplicates([{a: 1}, {a: 1}, {a: 1}], (a, b) => a.a === b.a)).toEqual(
|
||||||
|
[{a: 1}, {a: 1}],
|
||||||
|
);
|
||||||
|
expect(duplicates(['a', 'b', 'c', 'd'], (a, b) => a !== b)).toEqual([
|
||||||
|
'a',
|
||||||
|
'b',
|
||||||
|
'c',
|
||||||
|
'd',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('uniq', () => {
|
describe('uniq', () => {
|
||||||
test('remove duplicate primitives', () => {
|
test('remove duplicate primitives', () => {
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {isRegexpStringMatch} from '../regexpUtils';
|
||||||
|
|
||||||
|
describe('isRegexpStringMatch', () => {
|
||||||
|
test('behaves correctly', () => {
|
||||||
|
expect(isRegexpStringMatch(undefined, 'foo')).toEqual(false);
|
||||||
|
expect(isRegexpStringMatch('bar', undefined)).toEqual(false);
|
||||||
|
expect(isRegexpStringMatch('foo', 'bar')).toEqual(false);
|
||||||
|
expect(isRegexpStringMatch('foo', 'foo')).toEqual(true);
|
||||||
|
expect(isRegexpStringMatch('fooooooooooo', 'foo')).toEqual(false);
|
||||||
|
expect(isRegexpStringMatch('foo', 'fooooooo')).toEqual(true);
|
||||||
|
expect(isRegexpStringMatch('f.*o', 'fggo')).toEqual(true);
|
||||||
|
expect(isRegexpStringMatch('FOO', 'foo')).toEqual(true);
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,14 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {docVersionSearchTag} from '../searchUtils';
|
||||||
|
|
||||||
|
describe('docVersionSearchTag', () => {
|
||||||
|
test('behaves correctly', () => {
|
||||||
|
expect(docVersionSearchTag('foo', 'bar')).toEqual('docs-foo-bar');
|
||||||
|
});
|
||||||
|
});
|
|
@ -6,7 +6,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility to convert an optional string into a Regex case sensitive and global
|
* Utility to convert an optional string into a Regex case insensitive and global
|
||||||
*/
|
*/
|
||||||
export function isRegexpStringMatch(
|
export function isRegexpStringMatch(
|
||||||
regexAsString?: string,
|
regexAsString?: string,
|
||||||
|
|
|
@ -14,13 +14,26 @@ import {
|
||||||
|
|
||||||
describe('codeTranslationLocalesToTry', () => {
|
describe('codeTranslationLocalesToTry', () => {
|
||||||
test('should return appropriate locale lists', () => {
|
test('should return appropriate locale lists', () => {
|
||||||
expect(codeTranslationLocalesToTry('fr')).toEqual(['fr', 'fr-FR']);
|
expect(codeTranslationLocalesToTry('fr')).toEqual([
|
||||||
|
'fr',
|
||||||
|
'fr-FR',
|
||||||
|
'fr-Latn',
|
||||||
|
]);
|
||||||
expect(codeTranslationLocalesToTry('fr-FR')).toEqual(['fr-FR', 'fr']);
|
expect(codeTranslationLocalesToTry('fr-FR')).toEqual(['fr-FR', 'fr']);
|
||||||
// Note: "pt" is expanded into "pt-BR", not "pt-PT", as "pt-BR" is more widely used!
|
// Note: "pt" is expanded into "pt-BR", not "pt-PT", as "pt-BR" is more widely used!
|
||||||
// See https://github.com/facebook/docusaurus/pull/4536#issuecomment-810088783
|
// See https://github.com/facebook/docusaurus/pull/4536#issuecomment-810088783
|
||||||
expect(codeTranslationLocalesToTry('pt')).toEqual(['pt', 'pt-BR']);
|
expect(codeTranslationLocalesToTry('pt')).toEqual([
|
||||||
|
'pt',
|
||||||
|
'pt-BR',
|
||||||
|
'pt-Latn',
|
||||||
|
]);
|
||||||
expect(codeTranslationLocalesToTry('pt-BR')).toEqual(['pt-BR', 'pt']);
|
expect(codeTranslationLocalesToTry('pt-BR')).toEqual(['pt-BR', 'pt']);
|
||||||
expect(codeTranslationLocalesToTry('pt-PT')).toEqual(['pt-PT', 'pt']);
|
expect(codeTranslationLocalesToTry('pt-PT')).toEqual(['pt-PT', 'pt']);
|
||||||
|
expect(codeTranslationLocalesToTry('zh')).toEqual([
|
||||||
|
'zh',
|
||||||
|
'zh-CN',
|
||||||
|
'zh-Hans',
|
||||||
|
]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -123,4 +136,17 @@ describe('readDefaultCodeTranslationMessages', () => {
|
||||||
}),
|
}),
|
||||||
).resolves.toEqual(await readAsJSON('en'));
|
).resolves.toEqual(await readAsJSON('en'));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('default locale', async () => {
|
||||||
|
await expect(
|
||||||
|
readDefaultCodeTranslationMessages({
|
||||||
|
locale: 'zh',
|
||||||
|
name: 'plugin-pwa',
|
||||||
|
}),
|
||||||
|
).resolves.toEqual({
|
||||||
|
'theme.PwaReloadPopup.closeButtonAriaLabel': '关闭',
|
||||||
|
'theme.PwaReloadPopup.info': '有可用的新版本',
|
||||||
|
'theme.PwaReloadPopup.refreshButtonText': '刷新',
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -14,16 +14,17 @@ function getDefaultLocalesDirPath(): string {
|
||||||
|
|
||||||
// Return an ordered list of locales we should try
|
// Return an ordered list of locales we should try
|
||||||
export function codeTranslationLocalesToTry(locale: string): string[] {
|
export function codeTranslationLocalesToTry(locale: string): string[] {
|
||||||
const intlLocale = Intl.Locale ? new Intl.Locale(locale) : undefined;
|
const intlLocale = new Intl.Locale(locale);
|
||||||
if (!intlLocale) {
|
|
||||||
return [locale];
|
|
||||||
}
|
|
||||||
// if locale is just a simple language like "pt", we want to fallback to pt-BR (not pt-PT!)
|
// if locale is just a simple language like "pt", we want to fallback to pt-BR (not pt-PT!)
|
||||||
// see https://github.com/facebook/docusaurus/pull/4536#issuecomment-810088783
|
// see https://github.com/facebook/docusaurus/pull/4536#issuecomment-810088783
|
||||||
if (intlLocale.language === locale) {
|
if (intlLocale.language === locale) {
|
||||||
const maximizedLocale = intlLocale.maximize(); // pt-Latn-BR`
|
const maximizedLocale = intlLocale.maximize(); // pt-Latn-BR`
|
||||||
// ["pt","pt-BR"]
|
// ["pt","pt-BR"]; ["zh", "zh-Hans"]
|
||||||
return [locale, `${maximizedLocale.language}-${maximizedLocale.region}`];
|
return [
|
||||||
|
locale,
|
||||||
|
`${maximizedLocale.language}-${maximizedLocale.region}`,
|
||||||
|
`${maximizedLocale.language}-${maximizedLocale.script}`,
|
||||||
|
];
|
||||||
}
|
}
|
||||||
// if locale is like "pt-BR", we want to fallback to "pt"
|
// if locale is like "pt-BR", we want to fallback to "pt"
|
||||||
else {
|
else {
|
||||||
|
|
|
@ -35,6 +35,8 @@ describe('normalizeLocation', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('untouched pathnames', () => {
|
test('untouched pathnames', () => {
|
||||||
|
const replaceMock = jest.spyOn(String.prototype, 'replace');
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
normalizeLocation({
|
normalizeLocation({
|
||||||
pathname: '/docs/introduction',
|
pathname: '/docs/introduction',
|
||||||
|
@ -47,6 +49,20 @@ describe('normalizeLocation', () => {
|
||||||
hash: '#features',
|
hash: '#features',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// For the sake of testing memoization
|
||||||
|
expect(
|
||||||
|
normalizeLocation({
|
||||||
|
pathname: '/docs/introduction',
|
||||||
|
search: '',
|
||||||
|
hash: '#features',
|
||||||
|
}),
|
||||||
|
).toEqual({
|
||||||
|
pathname: '/docs/introduction',
|
||||||
|
search: '',
|
||||||
|
hash: '#features',
|
||||||
|
});
|
||||||
|
expect(replaceMock).toBeCalledTimes(1);
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
normalizeLocation({
|
normalizeLocation({
|
||||||
pathname: '/docs/introduction/foo.html',
|
pathname: '/docs/introduction/foo.html',
|
||||||
|
|
|
@ -23,7 +23,7 @@ export default function loadPresets(context: LoadContext): {
|
||||||
// declares the dependency on these presets.
|
// declares the dependency on these presets.
|
||||||
const presetRequire = createRequire(context.siteConfigPath);
|
const presetRequire = createRequire(context.siteConfigPath);
|
||||||
|
|
||||||
const presets: PresetConfig[] = (context.siteConfig || {}).presets || [];
|
const presets: PresetConfig[] = context.siteConfig.presets || [];
|
||||||
const unflatPlugins: PluginConfig[][] = [];
|
const unflatPlugins: PluginConfig[][] = [];
|
||||||
const unflatThemes: PluginConfig[][] = [];
|
const unflatThemes: PluginConfig[][] = [];
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue