refactor(v2): docs plugin refactor (#3245)

* safe refactorings

* safe refactors

* add code to read versions more generically

* refactor docs plugin

* refactors

* stable docs refactor

* progress on refactor

* stable docs refactor

* stable docs refactor

* stable docs refactor

* attempt to fix admonition :(

* configureWebpack docs: better typing

* more refactors

* rename cli

* refactor docs metadata processing => move to pure function

* stable docs refactor

* stable docs refactor

* named exports

* basic sidebars refactor

* add getElementsAround utils

* refactor sidebar + ordering/navigation logic

* stable retrocompatible refactor

* add proper versions metadata tests

* fix docs metadata tests

* fix docs tests

* fix test due to absolute path

* fix webpack tests

* refactor linkify + add broken markdown links warning

* fix DOM warning due to forwarding legacy prop to div element

* add todo
This commit is contained in:
Sébastien Lorber 2020-08-17 17:50:22 +02:00 committed by GitHub
parent d17df954b5
commit a4c8a7f55b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
54 changed files with 3219 additions and 2724 deletions

View file

@ -0,0 +1,5 @@
{
"version-1.0.1/docs": {
"Test": ["version-withSlugs/rootAbsoluteSlug"]
}
}

View file

@ -6,14 +6,14 @@
*/
import path from 'path';
import {docsVersion} from '../version';
import {cliDocsVersionCommand} from '../cli';
import {PathOptions} from '../types';
import fs from 'fs-extra';
import {
getVersionedDocsDir,
getVersionsJSONFile,
getVersionedSidebarsDir,
} from '../env';
getVersionedDocsDirPath,
getVersionsFilePath,
getVersionedSidebarsDirPath,
} from '../versions';
import {DEFAULT_PLUGIN_ID} from '@docusaurus/core/lib/constants';
const fixtureDir = path.join(__dirname, '__fixtures__');
@ -28,17 +28,32 @@ describe('docsVersion', () => {
test('no version tag provided', () => {
expect(() =>
docsVersion(null, simpleSiteDir, DEFAULT_PLUGIN_ID, DEFAULT_OPTIONS),
cliDocsVersionCommand(
null,
simpleSiteDir,
DEFAULT_PLUGIN_ID,
DEFAULT_OPTIONS,
),
).toThrowErrorMatchingInlineSnapshot(
`"[docs] No version tag specified!. Pass the version you wish to create as an argument. Ex: 1.0.0"`,
);
expect(() =>
docsVersion(undefined, simpleSiteDir, DEFAULT_PLUGIN_ID, DEFAULT_OPTIONS),
cliDocsVersionCommand(
undefined,
simpleSiteDir,
DEFAULT_PLUGIN_ID,
DEFAULT_OPTIONS,
),
).toThrowErrorMatchingInlineSnapshot(
`"[docs] No version tag specified!. Pass the version you wish to create as an argument. Ex: 1.0.0"`,
);
expect(() =>
docsVersion('', simpleSiteDir, DEFAULT_PLUGIN_ID, DEFAULT_OPTIONS),
cliDocsVersionCommand(
'',
simpleSiteDir,
DEFAULT_PLUGIN_ID,
DEFAULT_OPTIONS,
),
).toThrowErrorMatchingInlineSnapshot(
`"[docs] No version tag specified!. Pass the version you wish to create as an argument. Ex: 1.0.0"`,
);
@ -46,12 +61,17 @@ describe('docsVersion', () => {
test('version tag should not have slash', () => {
expect(() =>
docsVersion('foo/bar', simpleSiteDir, DEFAULT_PLUGIN_ID, DEFAULT_OPTIONS),
cliDocsVersionCommand(
'foo/bar',
simpleSiteDir,
DEFAULT_PLUGIN_ID,
DEFAULT_OPTIONS,
),
).toThrowErrorMatchingInlineSnapshot(
`"[docs] Invalid version tag specified! Do not include slash (/) or (\\\\). Try something like: 1.0.0"`,
);
expect(() =>
docsVersion(
cliDocsVersionCommand(
'foo\\bar',
simpleSiteDir,
DEFAULT_PLUGIN_ID,
@ -64,7 +84,7 @@ describe('docsVersion', () => {
test('version tag should not be too long', () => {
expect(() =>
docsVersion(
cliDocsVersionCommand(
'a'.repeat(255),
simpleSiteDir,
DEFAULT_PLUGIN_ID,
@ -77,12 +97,22 @@ describe('docsVersion', () => {
test('version tag should not be a dot or two dots', () => {
expect(() =>
docsVersion('..', simpleSiteDir, DEFAULT_PLUGIN_ID, DEFAULT_OPTIONS),
cliDocsVersionCommand(
'..',
simpleSiteDir,
DEFAULT_PLUGIN_ID,
DEFAULT_OPTIONS,
),
).toThrowErrorMatchingInlineSnapshot(
`"[docs] Invalid version tag specified! Do not name your version \\".\\" or \\"..\\". Try something like: 1.0.0"`,
);
expect(() =>
docsVersion('.', simpleSiteDir, DEFAULT_PLUGIN_ID, DEFAULT_OPTIONS),
cliDocsVersionCommand(
'.',
simpleSiteDir,
DEFAULT_PLUGIN_ID,
DEFAULT_OPTIONS,
),
).toThrowErrorMatchingInlineSnapshot(
`"[docs] Invalid version tag specified! Do not name your version \\".\\" or \\"..\\". Try something like: 1.0.0"`,
);
@ -90,7 +120,7 @@ describe('docsVersion', () => {
test('version tag should be a valid pathname', () => {
expect(() =>
docsVersion(
cliDocsVersionCommand(
'<foo|bar>',
simpleSiteDir,
DEFAULT_PLUGIN_ID,
@ -100,7 +130,7 @@ describe('docsVersion', () => {
`"[docs] Invalid version tag specified! Please ensure its a valid pathname too. Try something like: 1.0.0"`,
);
expect(() =>
docsVersion(
cliDocsVersionCommand(
'foo\x00bar',
simpleSiteDir,
DEFAULT_PLUGIN_ID,
@ -110,7 +140,12 @@ describe('docsVersion', () => {
`"[docs] Invalid version tag specified! Please ensure its a valid pathname too. Try something like: 1.0.0"`,
);
expect(() =>
docsVersion('foo:bar', simpleSiteDir, DEFAULT_PLUGIN_ID, DEFAULT_OPTIONS),
cliDocsVersionCommand(
'foo:bar',
simpleSiteDir,
DEFAULT_PLUGIN_ID,
DEFAULT_OPTIONS,
),
).toThrowErrorMatchingInlineSnapshot(
`"[docs] Invalid version tag specified! Please ensure its a valid pathname too. Try something like: 1.0.0"`,
);
@ -118,7 +153,7 @@ describe('docsVersion', () => {
test('version tag already exist', () => {
expect(() =>
docsVersion(
cliDocsVersionCommand(
'1.0.0',
versionedSiteDir,
DEFAULT_PLUGIN_ID,
@ -132,7 +167,12 @@ describe('docsVersion', () => {
test('no docs file to version', () => {
const emptySiteDir = path.join(fixtureDir, 'empty-site');
expect(() =>
docsVersion('1.0.0', emptySiteDir, DEFAULT_PLUGIN_ID, DEFAULT_OPTIONS),
cliDocsVersionCommand(
'1.0.0',
emptySiteDir,
DEFAULT_PLUGIN_ID,
DEFAULT_OPTIONS,
),
).toThrowErrorMatchingInlineSnapshot(
`"[docs] There is no docs to version !"`,
);
@ -159,23 +199,23 @@ describe('docsVersion', () => {
path: 'docs',
sidebarPath: path.join(simpleSiteDir, 'sidebars.json'),
};
docsVersion('1.0.0', simpleSiteDir, DEFAULT_PLUGIN_ID, options);
cliDocsVersionCommand('1.0.0', simpleSiteDir, DEFAULT_PLUGIN_ID, options);
expect(copyMock).toHaveBeenCalledWith(
path.join(simpleSiteDir, options.path),
path.join(
getVersionedDocsDir(simpleSiteDir, DEFAULT_PLUGIN_ID),
getVersionedDocsDirPath(simpleSiteDir, DEFAULT_PLUGIN_ID),
'version-1.0.0',
),
);
expect(versionedSidebar).toMatchSnapshot();
expect(versionedSidebarPath).toEqual(
path.join(
getVersionedSidebarsDir(simpleSiteDir, DEFAULT_PLUGIN_ID),
getVersionedSidebarsDirPath(simpleSiteDir, DEFAULT_PLUGIN_ID),
'version-1.0.0-sidebars.json',
),
);
expect(versionsPath).toEqual(
getVersionsJSONFile(simpleSiteDir, DEFAULT_PLUGIN_ID),
getVersionsFilePath(simpleSiteDir, DEFAULT_PLUGIN_ID),
);
expect(versions).toEqual(['1.0.0']);
expect(consoleMock).toHaveBeenCalledWith('[docs] Version 1.0.0 created!');
@ -207,23 +247,28 @@ describe('docsVersion', () => {
path: 'docs',
sidebarPath: path.join(versionedSiteDir, 'sidebars.json'),
};
docsVersion('2.0.0', versionedSiteDir, DEFAULT_PLUGIN_ID, options);
cliDocsVersionCommand(
'2.0.0',
versionedSiteDir,
DEFAULT_PLUGIN_ID,
options,
);
expect(copyMock).toHaveBeenCalledWith(
path.join(versionedSiteDir, options.path),
path.join(
getVersionedDocsDir(versionedSiteDir, DEFAULT_PLUGIN_ID),
getVersionedDocsDirPath(versionedSiteDir, DEFAULT_PLUGIN_ID),
'version-2.0.0',
),
);
expect(versionedSidebar).toMatchSnapshot();
expect(versionedSidebarPath).toEqual(
path.join(
getVersionedSidebarsDir(versionedSiteDir, DEFAULT_PLUGIN_ID),
getVersionedSidebarsDirPath(versionedSiteDir, DEFAULT_PLUGIN_ID),
'version-2.0.0-sidebars.json',
),
);
expect(versionsPath).toEqual(
getVersionsJSONFile(versionedSiteDir, DEFAULT_PLUGIN_ID),
getVersionsFilePath(versionedSiteDir, DEFAULT_PLUGIN_ID),
);
expect(versions).toEqual(['2.0.0', '1.0.1', '1.0.0', 'withSlugs']);
expect(consoleMock).toHaveBeenCalledWith('[docs] Version 2.0.0 created!');
@ -257,23 +302,23 @@ describe('docsVersion', () => {
path: 'community',
sidebarPath: path.join(versionedSiteDir, 'community_sidebars.json'),
};
docsVersion('2.0.0', versionedSiteDir, pluginId, options);
cliDocsVersionCommand('2.0.0', versionedSiteDir, pluginId, options);
expect(copyMock).toHaveBeenCalledWith(
path.join(versionedSiteDir, options.path),
path.join(
getVersionedDocsDir(versionedSiteDir, pluginId),
getVersionedDocsDirPath(versionedSiteDir, pluginId),
'version-2.0.0',
),
);
expect(versionedSidebar).toMatchSnapshot();
expect(versionedSidebarPath).toEqual(
path.join(
getVersionedSidebarsDir(versionedSiteDir, pluginId),
getVersionedSidebarsDirPath(versionedSiteDir, pluginId),
'version-2.0.0-sidebars.json',
),
);
expect(versionsPath).toEqual(
getVersionsJSONFile(versionedSiteDir, pluginId),
getVersionsFilePath(versionedSiteDir, pluginId),
);
expect(versions).toEqual(['2.0.0', '1.0.0']);
expect(consoleMock).toHaveBeenCalledWith(

View file

@ -1,361 +0,0 @@
/**
* 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 {
ActivePlugin,
getActivePlugin,
getLatestVersion,
getActiveDocContext,
getActiveVersion,
getDocVersionSuggestions,
} from '../../client/docsClientUtils';
import {GlobalPluginData, GlobalVersion} from '../../types';
import {shuffle} from 'lodash';
describe('docsClientUtils', () => {
test('getActivePlugin', () => {
const data: Record<string, GlobalPluginData> = {
pluginIosId: {
path: '/ios',
latestVersionName: 'xyz',
versions: [],
},
pluginAndroidId: {
path: '/android',
latestVersionName: 'xyz',
versions: [],
},
};
expect(getActivePlugin(data, '/')).toEqual(undefined);
expect(getActivePlugin(data, '/xyz')).toEqual(undefined);
const activePluginIos: ActivePlugin = {
pluginId: 'pluginIosId',
pluginData: data.pluginIosId,
};
expect(getActivePlugin(data, '/ios')).toEqual(activePluginIos);
expect(getActivePlugin(data, '/ios/')).toEqual(activePluginIos);
expect(getActivePlugin(data, '/ios/abc/def')).toEqual(activePluginIos);
const activePluginAndroid: ActivePlugin = {
pluginId: 'pluginAndroidId',
pluginData: data.pluginAndroidId,
};
expect(getActivePlugin(data, '/android')).toEqual(activePluginAndroid);
expect(getActivePlugin(data, '/android/')).toEqual(activePluginAndroid);
expect(getActivePlugin(data, '/android/ijk')).toEqual(activePluginAndroid);
});
test('getLatestVersion', () => {
const versions: GlobalVersion[] = [
{
name: 'version1',
path: '/???',
docs: [],
mainDocId: '???',
},
{
name: 'version2',
path: '/???',
docs: [],
mainDocId: '???',
},
{
name: 'version3',
path: '/???',
docs: [],
mainDocId: '???',
},
];
expect(
getLatestVersion({
path: '???',
latestVersionName: 'does not exist',
versions,
}),
).toEqual(undefined);
expect(
getLatestVersion({
path: '???',
latestVersionName: 'version1',
versions,
})?.name,
).toEqual('version1');
expect(
getLatestVersion({
path: '???',
latestVersionName: 'version2',
versions,
})?.name,
).toEqual('version2');
expect(
getLatestVersion({
path: '???',
latestVersionName: 'version3',
versions,
})?.name,
).toEqual('version3');
});
test('getActiveVersion', () => {
const data: GlobalPluginData = {
path: 'docs',
latestVersionName: 'version2',
versions: [
{
name: 'next',
path: '/docs/next',
docs: [],
mainDocId: '???',
},
{
name: 'version2',
path: '/docs',
docs: [],
mainDocId: '???',
},
{
name: 'version1',
path: '/docs/version1',
docs: [],
mainDocId: '???',
},
],
};
expect(getActiveVersion(data, '/docs/next')?.name).toEqual('next');
expect(getActiveVersion(data, '/docs/next/')?.name).toEqual('next');
expect(getActiveVersion(data, '/docs/next/someDoc')?.name).toEqual('next');
expect(getActiveVersion(data, '/docs')?.name).toEqual('version2');
expect(getActiveVersion(data, '/docs/')?.name).toEqual('version2');
expect(getActiveVersion(data, '/docs/someDoc')?.name).toEqual('version2');
expect(getActiveVersion(data, '/docs/version1')?.name).toEqual('version1');
expect(getActiveVersion(data, '/docs/version1')?.name).toEqual('version1');
expect(getActiveVersion(data, '/docs/version1/someDoc')?.name).toEqual(
'version1',
);
});
test('getActiveDocContext', () => {
const versionNext: GlobalVersion = {
name: 'next',
path: '/docs/next',
mainDocId: 'doc1',
docs: [
{
id: 'doc1',
path: '/docs/next/',
},
{
id: 'doc2',
path: '/docs/next/doc2',
},
],
};
const version2: GlobalVersion = {
name: 'version2',
path: '/docs',
mainDocId: 'doc1',
docs: [
{
id: 'doc1',
path: '/docs/',
},
{
id: 'doc2',
path: '/docs/doc2',
},
],
};
const version1: GlobalVersion = {
name: 'version1',
path: '/docs/version1',
mainDocId: 'doc1',
docs: [
{
id: 'doc1',
path: '/docs/version1/',
},
],
};
// shuffle, because order shouldn't matter
const versions: GlobalVersion[] = shuffle([
versionNext,
version2,
version1,
]);
const data: GlobalPluginData = {
path: 'docs',
latestVersionName: 'version2',
versions,
};
expect(getActiveDocContext(data, '/doesNotExist')).toEqual({
activeVersion: undefined,
activeDoc: undefined,
alternateDocVersions: {},
});
expect(getActiveDocContext(data, '/docs/next/doesNotExist')).toEqual({
activeVersion: versionNext,
activeDoc: undefined,
alternateDocVersions: {},
});
expect(getActiveDocContext(data, '/docs/next')).toEqual({
activeVersion: versionNext,
activeDoc: versionNext.docs[0],
alternateDocVersions: {
next: versionNext.docs[0],
version2: version2.docs[0],
version1: version1.docs[0],
},
});
expect(getActiveDocContext(data, '/docs/next/doc2')).toEqual({
activeVersion: versionNext,
activeDoc: versionNext.docs[1],
alternateDocVersions: {
next: versionNext.docs[1],
version2: version2.docs[1],
version1: undefined,
},
});
expect(getActiveDocContext(data, '/docs/')).toEqual({
activeVersion: version2,
activeDoc: version2.docs[0],
alternateDocVersions: {
next: versionNext.docs[0],
version2: version2.docs[0],
version1: version1.docs[0],
},
});
expect(getActiveDocContext(data, '/docs/doc2')).toEqual({
activeVersion: version2,
activeDoc: version2.docs[1],
alternateDocVersions: {
next: versionNext.docs[1],
version2: version2.docs[1],
version1: undefined,
},
});
expect(getActiveDocContext(data, '/docs/version1')).toEqual({
activeVersion: version1,
activeDoc: version1.docs[0],
alternateDocVersions: {
next: versionNext.docs[0],
version2: version2.docs[0],
version1: version1.docs[0],
},
});
expect(getActiveDocContext(data, '/docs/version1/doc2')).toEqual({
activeVersion: version1,
activeDoc: undefined,
alternateDocVersions: {},
});
});
test('getDocVersionSuggestions', () => {
const versionNext: GlobalVersion = {
name: 'next',
path: '/docs/next',
mainDocId: 'doc1',
docs: [
{
id: 'doc1',
path: '/docs/next/',
},
{
id: 'doc2',
path: '/docs/next/doc2',
},
],
};
const version2: GlobalVersion = {
name: 'version2',
path: '/docs',
mainDocId: 'doc1',
docs: [
{
id: 'doc1',
path: '/docs/',
},
{
id: 'doc2',
path: '/docs/doc2',
},
],
};
const version1: GlobalVersion = {
name: 'version1',
path: '/docs/version1',
mainDocId: 'doc1',
docs: [
{
id: 'doc1',
path: '/docs/version1/',
},
],
};
// shuffle, because order shouldn't matter
const versions: GlobalVersion[] = shuffle([
versionNext,
version2,
version1,
]);
const data: GlobalPluginData = {
path: 'docs',
latestVersionName: 'version2',
versions,
};
expect(getDocVersionSuggestions(data, '/doesNotExist')).toEqual({
latestDocSuggestion: undefined,
latestVersionSuggestion: version2,
});
expect(getDocVersionSuggestions(data, '/docs/next')).toEqual({
latestDocSuggestion: version2.docs[0],
latestVersionSuggestion: version2,
});
expect(getDocVersionSuggestions(data, '/docs/next/doc2')).toEqual({
latestDocSuggestion: version2.docs[1],
latestVersionSuggestion: version2,
});
// nothing to suggest, we are already on latest version
expect(getDocVersionSuggestions(data, '/docs/')).toEqual({
latestDocSuggestion: undefined,
latestVersionSuggestion: undefined,
});
expect(getDocVersionSuggestions(data, '/docs/doc2')).toEqual({
latestDocSuggestion: undefined,
latestVersionSuggestion: undefined,
});
expect(getDocVersionSuggestions(data, '/docs/version1/')).toEqual({
latestDocSuggestion: version2.docs[0],
latestVersionSuggestion: version2,
});
expect(getDocVersionSuggestions(data, '/docs/version1/doc2')).toEqual({
latestDocSuggestion: undefined, // because /docs/version1/doc2 does not exist
latestVersionSuggestion: version2,
});
});
});

View file

@ -0,0 +1,529 @@
/**
* 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 path from 'path';
import {loadContext} from '@docusaurus/core/src/server/index';
import {processDocMetadata, readVersionDocs, readDocFile} from '../docs';
import {readVersionsMetadata} from '../versions';
import {
DocFile,
DocMetadataBase,
MetadataOptions,
VersionMetadata,
} from '../types';
import {LoadContext} from '@docusaurus/types';
import {DEFAULT_PLUGIN_ID} from '@docusaurus/core/lib/constants';
import {DEFAULT_OPTIONS} from '../options';
import {Optional} from 'utility-types';
const fixtureDir = path.join(__dirname, '__fixtures__');
const createFakeDocFile = ({
source,
frontmatter = {},
markdown = 'some markdown content',
}: {
source: string;
frontmatter?: Record<string, string>;
markdown?: string;
}): DocFile => {
const content = `---
${Object.entries(frontmatter)
.map(([key, value]) => `${key}: ${value}`)
.join('\n')}
---
${markdown}
`;
return {
source,
content,
lastUpdate: {},
};
};
function createTestUtils({
siteDir,
context,
versionMetadata,
options,
}: {
siteDir: string;
context: LoadContext;
versionMetadata: VersionMetadata;
options: MetadataOptions;
}) {
async function readDoc(docFileSource: string) {
return readDocFile(versionMetadata.docsDirPath, docFileSource, options);
}
function processDocFile(docFile: DocFile) {
return processDocMetadata({
docFile,
versionMetadata,
options,
context,
});
}
async function testMeta(
docFileSource: string,
expectedMetadata: Optional<
DocMetadataBase,
'source' | 'lastUpdatedBy' | 'lastUpdatedAt' | 'sidebar_label' | 'editUrl'
>,
) {
const docFile = await readDoc(docFileSource);
const metadata = await processDocMetadata({
docFile,
versionMetadata,
context,
options,
});
expect(metadata).toEqual({
lastUpdatedBy: undefined,
lastUpdatedAt: undefined,
sidebar_label: undefined,
editUrl: undefined,
source: path.join(
'@site',
path.relative(siteDir, versionMetadata.docsDirPath),
docFileSource,
),
...expectedMetadata,
});
}
async function testSlug(docFileSource: string, expectedPermalink: string) {
const docFile = await readDoc(docFileSource);
const metadata = await processDocMetadata({
docFile,
versionMetadata,
context,
options,
});
expect(metadata.permalink).toEqual(expectedPermalink);
}
return {processDocFile, testMeta, testSlug};
}
describe('simple site', () => {
const siteDir = path.join(fixtureDir, 'simple-site');
const context = loadContext(siteDir);
const options = {
id: DEFAULT_PLUGIN_ID,
...DEFAULT_OPTIONS,
};
const versionsMetadata = readVersionsMetadata({
context,
options: {
id: DEFAULT_PLUGIN_ID,
...DEFAULT_OPTIONS,
},
});
expect(versionsMetadata.length).toEqual(1);
const [currentVersion] = versionsMetadata;
const defaultTestUtils = createTestUtils({
siteDir,
context,
options,
versionMetadata: currentVersion,
});
test('readVersionDocs', async () => {
const docs = await readVersionDocs(currentVersion, options);
expect(docs.map((doc) => doc.source)).toMatchObject([
'hello.md',
'ipsum.md',
'lorem.md',
'rootAbsoluteSlug.md',
'rootRelativeSlug.md',
'rootResolvedSlug.md',
'rootTryToEscapeSlug.md',
'foo/bar.md',
'foo/baz.md',
'slugs/absoluteSlug.md',
'slugs/relativeSlug.md',
'slugs/resolvedSlug.md',
'slugs/tryToEscapeSlug.md',
]);
});
test('normal docs', async () => {
await defaultTestUtils.testMeta(path.join('foo', 'bar.md'), {
version: 'current',
id: 'foo/bar',
unversionedId: 'foo/bar',
isDocsHomePage: false,
permalink: '/docs/foo/bar',
slug: '/foo/bar',
title: 'Bar',
description: 'This is custom description',
});
await defaultTestUtils.testMeta(path.join('hello.md'), {
version: 'current',
id: 'hello',
unversionedId: 'hello',
isDocsHomePage: false,
permalink: '/docs/hello',
slug: '/hello',
title: 'Hello, World !',
description: `Hi, Endilie here :)`,
});
});
test('homePageId doc', async () => {
const testUtilsLocal = createTestUtils({
siteDir,
context,
options: {...options, homePageId: 'hello'},
versionMetadata: currentVersion,
});
await testUtilsLocal.testMeta(path.join('hello.md'), {
version: 'current',
id: 'hello',
unversionedId: 'hello',
isDocsHomePage: true,
permalink: '/docs/',
slug: '/',
title: 'Hello, World !',
description: `Hi, Endilie here :)`,
});
});
test('homePageId doc nested', async () => {
const testUtilsLocal = createTestUtils({
siteDir,
context,
options: {...options, homePageId: 'foo/bar'},
versionMetadata: currentVersion,
});
await testUtilsLocal.testMeta(path.join('foo', 'bar.md'), {
version: 'current',
id: 'foo/bar',
unversionedId: 'foo/bar',
isDocsHomePage: true,
permalink: '/docs/',
slug: '/',
title: 'Bar',
description: 'This is custom description',
});
});
test('docs with editUrl', async () => {
const testUtilsLocal = createTestUtils({
siteDir,
context,
options: {
...options,
editUrl: 'https://github.com/facebook/docusaurus/edit/master/website',
},
versionMetadata: currentVersion,
});
await testUtilsLocal.testMeta(path.join('foo', 'baz.md'), {
version: 'current',
id: 'foo/baz',
unversionedId: 'foo/baz',
isDocsHomePage: false,
permalink: '/docs/foo/bazSlug.html',
slug: '/foo/bazSlug.html',
title: 'baz',
editUrl:
'https://github.com/facebook/docusaurus/edit/master/website/docs/foo/baz.md',
description: 'Images',
});
});
test('docs with custom editUrl & unrelated frontmatter', async () => {
await defaultTestUtils.testMeta('lorem.md', {
version: 'current',
id: 'lorem',
unversionedId: 'lorem',
isDocsHomePage: false,
permalink: '/docs/lorem',
slug: '/lorem',
title: 'lorem',
editUrl: 'https://github.com/customUrl/docs/lorem.md',
description: 'Lorem ipsum.',
});
});
test('docs with last update time and author', async () => {
const testUtilsLocal = createTestUtils({
siteDir,
context,
options: {
...options,
showLastUpdateAuthor: true,
showLastUpdateTime: true,
},
versionMetadata: currentVersion,
});
await testUtilsLocal.testMeta('lorem.md', {
version: 'current',
id: 'lorem',
unversionedId: 'lorem',
isDocsHomePage: false,
permalink: '/docs/lorem',
slug: '/lorem',
title: 'lorem',
editUrl: 'https://github.com/customUrl/docs/lorem.md',
description: 'Lorem ipsum.',
lastUpdatedAt: 1539502055,
lastUpdatedBy: 'Author',
});
});
test('docs with slugs', async () => {
await defaultTestUtils.testSlug(
path.join('rootRelativeSlug.md'),
'/docs/rootRelativeSlug',
);
await defaultTestUtils.testSlug(
path.join('rootAbsoluteSlug.md'),
'/docs/rootAbsoluteSlug',
);
await defaultTestUtils.testSlug(
path.join('rootResolvedSlug.md'),
'/docs/hey/rootResolvedSlug',
);
await defaultTestUtils.testSlug(
path.join('rootTryToEscapeSlug.md'),
'/docs/rootTryToEscapeSlug',
);
await defaultTestUtils.testSlug(
path.join('slugs', 'absoluteSlug.md'),
'/docs/absoluteSlug',
);
await defaultTestUtils.testSlug(
path.join('slugs', 'relativeSlug.md'),
'/docs/slugs/relativeSlug',
);
await defaultTestUtils.testSlug(
path.join('slugs', 'resolvedSlug.md'),
'/docs/slugs/hey/resolvedSlug',
);
await defaultTestUtils.testSlug(
path.join('slugs', 'tryToEscapeSlug.md'),
'/docs/tryToEscapeSlug',
);
});
test('docs with invalid id', () => {
expect(() => {
defaultTestUtils.processDocFile(
createFakeDocFile({
source: 'some/fake/path',
frontmatter: {
id: 'Hello/world',
},
}),
);
}).toThrowErrorMatchingInlineSnapshot(
`"Document id [Hello/world] cannot include \\"/\\"."`,
);
});
test('docs with slug on doc home', async () => {
const testUtilsLocal = createTestUtils({
siteDir,
context,
options: {
...options,
homePageId: 'homePageId',
},
versionMetadata: currentVersion,
});
expect(() => {
testUtilsLocal.processDocFile(
createFakeDocFile({
source: 'homePageId',
frontmatter: {
slug: '/x/y',
},
}),
);
}).toThrowErrorMatchingInlineSnapshot(
`"The docs homepage (homePageId=homePageId) is not allowed to have a frontmatter slug=/x/y => you have to chooser either homePageId or slug, not both"`,
);
});
});
describe('versioned site', () => {
const siteDir = path.join(fixtureDir, 'versioned-site');
const context = loadContext(siteDir);
const options = {
id: DEFAULT_PLUGIN_ID,
...DEFAULT_OPTIONS,
};
const versionsMetadata = readVersionsMetadata({
context,
options: {
id: DEFAULT_PLUGIN_ID,
...DEFAULT_OPTIONS,
},
});
expect(versionsMetadata.length).toEqual(4);
const [
currentVersion,
version101,
version100,
versionWithSlugs,
] = versionsMetadata;
const currentVersionTestUtils = createTestUtils({
siteDir,
context,
options,
versionMetadata: currentVersion,
});
const version101TestUtils = createTestUtils({
siteDir,
context,
options,
versionMetadata: version101,
});
const version100TestUtils = createTestUtils({
siteDir,
context,
options,
versionMetadata: version100,
});
const versionWithSlugsTestUtils = createTestUtils({
siteDir,
context,
options,
versionMetadata: versionWithSlugs,
});
test('next docs', async () => {
await currentVersionTestUtils.testMeta(path.join('foo', 'bar.md'), {
id: 'foo/bar',
unversionedId: 'foo/bar',
isDocsHomePage: false,
permalink: '/docs/next/foo/barSlug',
slug: '/foo/barSlug',
title: 'bar',
description: 'This is next version of bar.',
version: 'current',
});
await currentVersionTestUtils.testMeta(path.join('hello.md'), {
id: 'hello',
unversionedId: 'hello',
isDocsHomePage: false,
permalink: '/docs/next/hello',
slug: '/hello',
title: 'hello',
description: 'Hello next !',
version: 'current',
});
});
test('versioned docs', async () => {
await version100TestUtils.testMeta(path.join('foo', 'bar.md'), {
id: 'version-1.0.0/foo/bar',
unversionedId: 'foo/bar',
isDocsHomePage: false,
permalink: '/docs/1.0.0/foo/barSlug',
slug: '/foo/barSlug',
title: 'bar',
description: 'Bar 1.0.0 !',
version: '1.0.0',
});
await version100TestUtils.testMeta(path.join('hello.md'), {
id: 'version-1.0.0/hello',
unversionedId: 'hello',
isDocsHomePage: false,
permalink: '/docs/1.0.0/hello',
slug: '/hello',
title: 'hello',
description: 'Hello 1.0.0 !',
version: '1.0.0',
});
await version101TestUtils.testMeta(path.join('foo', 'bar.md'), {
id: 'version-1.0.1/foo/bar',
unversionedId: 'foo/bar',
isDocsHomePage: false,
permalink: '/docs/foo/bar',
slug: '/foo/bar',
title: 'bar',
description: 'Bar 1.0.1 !',
version: '1.0.1',
});
await version101TestUtils.testMeta(path.join('hello.md'), {
id: 'version-1.0.1/hello',
unversionedId: 'hello',
isDocsHomePage: false,
permalink: '/docs/hello',
slug: '/hello',
title: 'hello',
description: 'Hello 1.0.1 !',
version: '1.0.1',
});
});
test('next doc slugs', async () => {
await currentVersionTestUtils.testSlug(
path.join('slugs', 'absoluteSlug.md'),
'/docs/next/absoluteSlug',
);
await currentVersionTestUtils.testSlug(
path.join('slugs', 'relativeSlug.md'),
'/docs/next/slugs/relativeSlug',
);
await currentVersionTestUtils.testSlug(
path.join('slugs', 'resolvedSlug.md'),
'/docs/next/slugs/hey/resolvedSlug',
);
await currentVersionTestUtils.testSlug(
path.join('slugs', 'tryToEscapeSlug.md'),
'/docs/next/tryToEscapeSlug',
);
});
test('versioned doc slugs', async () => {
await versionWithSlugsTestUtils.testSlug(
path.join('rootAbsoluteSlug.md'),
'/docs/withSlugs/rootAbsoluteSlug',
);
await versionWithSlugsTestUtils.testSlug(
path.join('rootRelativeSlug.md'),
'/docs/withSlugs/rootRelativeSlug',
);
await versionWithSlugsTestUtils.testSlug(
path.join('rootResolvedSlug.md'),
'/docs/withSlugs/hey/rootResolvedSlug',
);
await versionWithSlugsTestUtils.testSlug(
path.join('rootTryToEscapeSlug.md'),
'/docs/withSlugs/rootTryToEscapeSlug',
);
await versionWithSlugsTestUtils.testSlug(
path.join('slugs', 'absoluteSlug.md'),
'/docs/withSlugs/absoluteSlug',
);
await versionWithSlugsTestUtils.testSlug(
path.join('slugs', 'relativeSlug.md'),
'/docs/withSlugs/slugs/relativeSlug',
);
await versionWithSlugsTestUtils.testSlug(
path.join('slugs', 'resolvedSlug.md'),
'/docs/withSlugs/slugs/hey/resolvedSlug',
);
await versionWithSlugsTestUtils.testSlug(
path.join('slugs', 'tryToEscapeSlug.md'),
'/docs/withSlugs/tryToEscapeSlug',
);
});
});

View file

@ -1,58 +0,0 @@
/**
* 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 path from 'path';
import loadEnv from '../env';
import {DEFAULT_PLUGIN_ID} from '@docusaurus/core/lib/constants';
describe('loadEnv', () => {
test('website with versioning disabled', () => {
const siteDir = path.join(__dirname, '__fixtures__', 'simple-site');
const env = loadEnv(siteDir, DEFAULT_PLUGIN_ID);
expect(env.versioning.enabled).toBe(false);
expect(env.versioning.versions).toStrictEqual([]);
});
test('website with versioning enabled', () => {
const siteDir = path.join(__dirname, '__fixtures__', 'versioned-site');
const env = loadEnv(siteDir, DEFAULT_PLUGIN_ID);
expect(env.versioning.enabled).toBe(true);
expect(env.versioning.latestVersion).toBe('1.0.1');
expect(env.versioning.versions).toStrictEqual([
'1.0.1',
'1.0.0',
'withSlugs',
]);
});
test('website with versioning enabled, 2nd docs plugin instance', () => {
const siteDir = path.join(__dirname, '__fixtures__', 'versioned-site');
const env = loadEnv(siteDir, 'community');
expect(env.versioning.enabled).toBe(true);
expect(env.versioning.latestVersion).toBe('1.0.0');
expect(env.versioning.versions).toStrictEqual(['1.0.0']);
});
test('website with versioning but disabled', () => {
const siteDir = path.join(__dirname, '__fixtures__', 'versioned-site');
const env = loadEnv(siteDir, DEFAULT_PLUGIN_ID, {disableVersioning: true});
expect(env.versioning.enabled).toBe(false);
expect(env.versioning.versions).toStrictEqual([]);
});
test('website with invalid versions.json file', () => {
const siteDir = path.join(__dirname, '__fixtures__', 'versioned-site');
const mock = jest.spyOn(JSON, 'parse').mockImplementationOnce(() => {
return {
invalid: 'json',
};
});
const env = loadEnv(siteDir, DEFAULT_PLUGIN_ID);
expect(env.versioning.enabled).toBe(false);
mock.mockRestore();
});
});

View file

@ -6,12 +6,12 @@
*/
import path from 'path';
import {validate} from 'webpack';
import {isMatch} from 'picomatch';
import commander from 'commander';
import {kebabCase} from 'lodash';
import fs from 'fs-extra';
import pluginContentDocs from '../index';
import loadEnv from '../env';
import {loadContext} from '@docusaurus/core/src/server/index';
import {applyConfigureWebpack} from '@docusaurus/core/src/webpack/utils';
import {RouteConfig} from '@docusaurus/types';
@ -19,9 +19,26 @@ import {posixPath} from '@docusaurus/utils';
import {sortConfig} from '@docusaurus/core/src/server/plugins';
import {DEFAULT_PLUGIN_ID} from '@docusaurus/core/lib/constants';
import * as version from '../version';
import {PluginOptionSchema} from '../pluginOptionSchema';
import * as cliDocs from '../cli';
import {OptionsSchema} from '../options';
import {normalizePluginOptions} from '@docusaurus/utils-validation';
import {DocMetadata, LoadedVersion} from '../types';
import {toSidebarsProp} from '../props';
// @ts-expect-error: TODO typedefs missing?
import {validate} from 'webpack';
function findDocById(version: LoadedVersion, unversionedId: string) {
return version.docs.find((item) => item.unversionedId === unversionedId);
}
const defaultDocMetadata: Partial<DocMetadata> = {
next: undefined,
previous: undefined,
editUrl: undefined,
lastUpdatedAt: undefined,
lastUpdatedBy: undefined,
sidebar_label: undefined,
};
const createFakeActions = (contentDir: string) => {
const routeConfigs: RouteConfig[] = [];
@ -41,20 +58,34 @@ const createFakeActions = (contentDir: string) => {
},
};
// query by prefix, because files have a hash at the end
// so it's not convenient to query by full filename
const getCreatedDataByPrefix = (prefix: string) => {
const entry = Object.entries(dataContainer).find(([key]) =>
key.startsWith(prefix),
);
if (!entry) {
throw new Error(`No created entry found for prefix=[${prefix}]
Entries created:
- ${Object.keys(dataContainer).join('\n- ')}
`);
}
return JSON.parse(entry[1] as string);
};
// Extra fns useful for tests!
const utils = {
getGlobalData: () => globalDataContainer,
getRouteConfigs: () => routeConfigs,
// query by prefix, because files have a hash at the end
// so it's not convenient to query by full filename
getCreatedDataByPrefix: (prefix: string) => {
const entry = Object.entries(dataContainer).find(([key]) =>
key.startsWith(prefix),
checkVersionMetadataPropCreated: (version: LoadedVersion) => {
const versionMetadataProp = getCreatedDataByPrefix(
`version-${kebabCase(version.versionName)}-metadata-prop`,
);
expect(versionMetadataProp.docsSidebars).toEqual(toSidebarsProp(version));
expect(versionMetadataProp.permalinkToSidebar).toEqual(
version.permalinkToSidebar,
);
if (!entry) {
throw new Error(`No entry found for prefix=${prefix}`);
}
return JSON.parse(entry[1] as string);
},
expectSnapshot: () => {
@ -79,11 +110,11 @@ test('site with wrong sidebar file', async () => {
const sidebarPath = path.join(siteDir, 'wrong-sidebars.json');
const plugin = pluginContentDocs(
context,
normalizePluginOptions(PluginOptionSchema, {
normalizePluginOptions(OptionsSchema, {
sidebarPath,
}),
);
await expect(plugin.loadContent()).rejects.toThrowErrorMatchingSnapshot();
await expect(plugin.loadContent!()).rejects.toThrowErrorMatchingSnapshot();
});
describe('empty/no docs website', () => {
@ -94,33 +125,26 @@ describe('empty/no docs website', () => {
await fs.ensureDir(path.join(siteDir, 'docs'));
const plugin = pluginContentDocs(
context,
normalizePluginOptions(PluginOptionSchema, {}),
normalizePluginOptions(OptionsSchema, {}),
);
await expect(
plugin.loadContent!(),
).rejects.toThrowErrorMatchingInlineSnapshot(
`"Docs version current has no docs! At least one doc should exist at path=[docs]"`,
);
const content = await plugin.loadContent();
const {docsMetadata, docsSidebars} = content;
expect(docsMetadata).toMatchInlineSnapshot(`Object {}`);
expect(docsSidebars).toMatchInlineSnapshot(`Object {}`);
const pluginContentDir = path.join(context.generatedFilesDir, plugin.name);
const {actions, utils} = createFakeActions(pluginContentDir);
await plugin.contentLoaded({
content,
actions,
});
expect(utils.getRouteConfigs()).toEqual([]);
});
test('docs folder does not exist', async () => {
const plugin = pluginContentDocs(
context,
normalizePluginOptions(PluginOptionSchema, {
path: '/path/does/not/exist/',
}),
expect(() =>
pluginContentDocs(
context,
normalizePluginOptions(OptionsSchema, {
path: '/path/does/not/exist/',
}),
),
).toThrowErrorMatchingInlineSnapshot(
`"The docs folder does not exist for version [current]. A docs folder is expected to be found at /path/does/not/exist"`,
);
const content = await plugin.loadContent();
expect(content).toBeNull();
});
});
@ -128,11 +152,10 @@ describe('simple website', () => {
const siteDir = path.join(__dirname, '__fixtures__', 'simple-site');
const context = loadContext(siteDir);
const sidebarPath = path.join(siteDir, 'sidebars.json');
const pluginPath = 'docs';
const plugin = pluginContentDocs(
context,
normalizePluginOptions(PluginOptionSchema, {
path: pluginPath,
normalizePluginOptions(OptionsSchema, {
path: 'docs',
sidebarPath,
homePageId: 'hello',
}),
@ -140,27 +163,31 @@ describe('simple website', () => {
const pluginContentDir = path.join(context.generatedFilesDir, plugin.name);
test('extendCli - docsVersion', () => {
const mock = jest.spyOn(version, 'docsVersion').mockImplementation();
const mock = jest
.spyOn(cliDocs, 'cliDocsVersionCommand')
.mockImplementation();
const cli = new commander.Command();
plugin.extendCli(cli);
// @ts-expect-error: TODO annoying type incompatibility
plugin.extendCli!(cli);
cli.parse(['node', 'test', 'docs:version', '1.0.0']);
expect(mock).toHaveBeenCalledTimes(1);
expect(mock).toHaveBeenCalledWith('1.0.0', siteDir, DEFAULT_PLUGIN_ID, {
path: pluginPath,
path: 'docs',
sidebarPath,
});
mock.mockRestore();
});
test('getPathToWatch', () => {
const pathToWatch = plugin.getPathsToWatch();
const pathToWatch = plugin.getPathsToWatch!();
const matchPattern = pathToWatch.map((filepath) =>
posixPath(path.relative(siteDir, filepath)),
);
expect(matchPattern).not.toEqual([]);
expect(matchPattern).toMatchInlineSnapshot(`
Array [
"docs/**/*.{md,mdx}",
"sidebars.json",
"docs/**/*.{md,mdx}",
]
`);
expect(isMatch('docs/hello.md', matchPattern)).toEqual(true);
@ -192,15 +219,13 @@ describe('simple website', () => {
});
test('content', async () => {
const content = await plugin.loadContent();
const {
docsMetadata,
docsSidebars,
versionToSidebars,
permalinkToSidebar,
} = content;
expect(versionToSidebars).toEqual({});
expect(docsMetadata.hello).toEqual({
const content = await plugin.loadContent!();
expect(content.loadedVersions.length).toEqual(1);
const [currentVersion] = content.loadedVersions;
expect(findDocById(currentVersion, 'hello')).toEqual({
...defaultDocMetadata,
version: 'current',
id: 'hello',
unversionedId: 'hello',
isDocsHomePage: true,
@ -211,12 +236,18 @@ describe('simple website', () => {
permalink: '/docs/foo/bazSlug.html',
},
sidebar: 'docs',
source: path.join('@site', pluginPath, 'hello.md'),
source: path.join(
'@site',
path.relative(siteDir, currentVersion.docsDirPath),
'hello.md',
),
title: 'Hello, World !',
description: 'Hi, Endilie here :)',
});
expect(docsMetadata['foo/bar']).toEqual({
expect(findDocById(currentVersion, 'foo/bar')).toEqual({
...defaultDocMetadata,
version: 'current',
id: 'foo/bar',
unversionedId: 'foo/bar',
isDocsHomePage: false,
@ -227,26 +258,30 @@ describe('simple website', () => {
permalink: '/docs/foo/bar',
slug: '/foo/bar',
sidebar: 'docs',
source: path.join('@site', pluginPath, 'foo', 'bar.md'),
source: path.join(
'@site',
path.relative(siteDir, currentVersion.docsDirPath),
'foo',
'bar.md',
),
title: 'Bar',
description: 'This is custom description',
});
expect(docsSidebars).toMatchSnapshot();
expect(currentVersion.sidebars).toMatchSnapshot();
const {actions, utils} = createFakeActions(pluginContentDir);
await plugin.contentLoaded({
await plugin.contentLoaded!({
content,
actions,
allContent: {},
});
// There is only one nested docs route for simple site
const baseMetadata = utils.getCreatedDataByPrefix('docs-route-');
expect(baseMetadata.docsSidebars).toEqual(docsSidebars);
expect(baseMetadata.permalinkToSidebar).toEqual(permalinkToSidebar);
utils.checkVersionMetadataPropCreated(currentVersion);
utils.expectSnapshot();
expect(utils.getGlobalData()).toMatchSnapshot();
});
});
@ -258,25 +293,24 @@ describe('versioned website', () => {
const routeBasePath = 'docs';
const plugin = pluginContentDocs(
context,
normalizePluginOptions(PluginOptionSchema, {
normalizePluginOptions(OptionsSchema, {
routeBasePath,
sidebarPath,
homePageId: 'hello',
}),
);
const env = loadEnv(siteDir, DEFAULT_PLUGIN_ID);
const {docsDir: versionedDir} = env.versioning;
const pluginContentDir = path.join(context.generatedFilesDir, plugin.name);
test('isVersioned', () => {
expect(env.versioning.enabled).toEqual(true);
});
test('extendCli - docsVersion', () => {
const mock = jest.spyOn(version, 'docsVersion').mockImplementation();
const mock = jest
.spyOn(cliDocs, 'cliDocsVersionCommand')
.mockImplementation();
const cli = new commander.Command();
plugin.extendCli(cli);
// @ts-expect-error: TODO annoying type incompatibility
plugin.extendCli!(cli);
cli.parse(['node', 'test', 'docs:version', '2.0.0']);
expect(mock).toHaveBeenCalledTimes(1);
expect(mock).toHaveBeenCalledWith('2.0.0', siteDir, DEFAULT_PLUGIN_ID, {
path: routeBasePath,
sidebarPath,
@ -285,21 +319,21 @@ describe('versioned website', () => {
});
test('getPathToWatch', () => {
const pathToWatch = plugin.getPathsToWatch();
const pathToWatch = plugin.getPathsToWatch!();
const matchPattern = pathToWatch.map((filepath) =>
posixPath(path.relative(siteDir, filepath)),
);
expect(matchPattern).not.toEqual([]);
expect(matchPattern).toMatchInlineSnapshot(`
Array [
"sidebars.json",
"docs/**/*.{md,mdx}",
"versioned_sidebars/version-1.0.1-sidebars.json",
"versioned_sidebars/version-1.0.0-sidebars.json",
"versioned_sidebars/version-withSlugs-sidebars.json",
"versioned_docs/version-1.0.1/**/*.{md,mdx}",
"versioned_sidebars/version-1.0.0-sidebars.json",
"versioned_docs/version-1.0.0/**/*.{md,mdx}",
"versioned_sidebars/version-withSlugs-sidebars.json",
"versioned_docs/version-withSlugs/**/*.{md,mdx}",
"sidebars.json",
]
`);
expect(isMatch('docs/hello.md', matchPattern)).toEqual(true);
@ -335,50 +369,65 @@ describe('versioned website', () => {
});
test('content', async () => {
const content = await plugin.loadContent();
const {
docsMetadata,
docsSidebars,
versionToSidebars,
permalinkToSidebar,
} = content;
const content = await plugin.loadContent!();
expect(content.loadedVersions.length).toEqual(4);
const [
currentVersion,
version101,
version100,
versionWithSlugs,
] = content.loadedVersions;
// foo/baz.md only exists in version -1.0.0
expect(docsMetadata['foo/baz']).toBeUndefined();
expect(docsMetadata['version-1.0.1/foo/baz']).toBeUndefined();
expect(docsMetadata['foo/bar']).toEqual({
expect(findDocById(currentVersion, 'foo/baz')).toBeUndefined();
expect(findDocById(version101, 'foo/baz')).toBeUndefined();
expect(findDocById(versionWithSlugs, 'foo/baz')).toBeUndefined();
expect(findDocById(currentVersion, 'foo/bar')).toEqual({
...defaultDocMetadata,
id: 'foo/bar',
unversionedId: 'foo/bar',
isDocsHomePage: false,
permalink: '/docs/next/foo/barSlug',
slug: '/foo/barSlug',
source: path.join('@site', routeBasePath, 'foo', 'bar.md'),
source: path.join(
'@site',
path.relative(siteDir, currentVersion.docsDirPath),
'foo',
'bar.md',
),
title: 'bar',
description: 'This is next version of bar.',
version: 'next',
version: 'current',
sidebar: 'docs',
next: {
title: 'hello',
permalink: '/docs/next/',
},
});
expect(docsMetadata.hello).toEqual({
expect(findDocById(currentVersion, 'hello')).toEqual({
...defaultDocMetadata,
id: 'hello',
unversionedId: 'hello',
isDocsHomePage: true,
permalink: '/docs/next/',
slug: '/',
source: path.join('@site', routeBasePath, 'hello.md'),
source: path.join(
'@site',
path.relative(siteDir, currentVersion.docsDirPath),
'hello.md',
),
title: 'hello',
description: 'Hello next !',
version: 'next',
version: 'current',
sidebar: 'docs',
previous: {
title: 'bar',
permalink: '/docs/next/foo/barSlug',
},
});
expect(docsMetadata['version-1.0.1/hello']).toEqual({
expect(findDocById(version101, 'hello')).toEqual({
...defaultDocMetadata,
id: 'version-1.0.1/hello',
unversionedId: 'hello',
isDocsHomePage: true,
@ -386,8 +435,7 @@ describe('versioned website', () => {
slug: '/',
source: path.join(
'@site',
path.relative(siteDir, versionedDir),
'version-1.0.1',
path.relative(siteDir, version101.docsDirPath),
'hello.md',
),
title: 'hello',
@ -399,7 +447,8 @@ describe('versioned website', () => {
permalink: '/docs/foo/bar',
},
});
expect(docsMetadata['version-1.0.0/foo/baz']).toEqual({
expect(findDocById(version100, 'foo/baz')).toEqual({
...defaultDocMetadata,
id: 'version-1.0.0/foo/baz',
unversionedId: 'foo/baz',
isDocsHomePage: false,
@ -407,8 +456,7 @@ describe('versioned website', () => {
slug: '/foo/baz',
source: path.join(
'@site',
path.relative(siteDir, versionedDir),
'version-1.0.0',
path.relative(siteDir, version100.docsDirPath),
'foo',
'baz.md',
),
@ -427,47 +475,24 @@ describe('versioned website', () => {
},
});
expect(docsSidebars).toMatchSnapshot('all sidebars');
expect(versionToSidebars).toMatchSnapshot(
'sidebars needed for each version',
expect(currentVersion.sidebars).toMatchSnapshot('current version sidebars');
expect(version101.sidebars).toMatchSnapshot('101 version sidebars');
expect(version100.sidebars).toMatchSnapshot('100 version sidebars');
expect(versionWithSlugs.sidebars).toMatchSnapshot(
'withSlugs version sidebars',
);
const {actions, utils} = createFakeActions(pluginContentDir);
await plugin.contentLoaded({
await plugin.contentLoaded!({
content,
actions,
allContent: {},
});
// The created base metadata for each nested docs route is smartly chunked/ splitted across version
const latestVersionBaseMetadata = utils.getCreatedDataByPrefix(
'docs-route-',
);
expect(latestVersionBaseMetadata).toMatchSnapshot(
'base metadata for latest version',
);
expect(latestVersionBaseMetadata.docsSidebars).not.toEqual(docsSidebars);
expect(latestVersionBaseMetadata.permalinkToSidebar).not.toEqual(
permalinkToSidebar,
);
const nextVersionBaseMetadata = utils.getCreatedDataByPrefix(
'docs-next-route-',
);
expect(nextVersionBaseMetadata).toMatchSnapshot(
'base metadata for next version',
);
expect(nextVersionBaseMetadata.docsSidebars).not.toEqual(docsSidebars);
expect(nextVersionBaseMetadata.permalinkToSidebar).not.toEqual(
permalinkToSidebar,
);
const firstVersionBaseMetadata = utils.getCreatedDataByPrefix(
'docs-1-0-0-route-',
);
expect(firstVersionBaseMetadata).toMatchSnapshot(
'base metadata for first version',
);
expect(nextVersionBaseMetadata.docsSidebars).not.toEqual(docsSidebars);
expect(nextVersionBaseMetadata.permalinkToSidebar).not.toEqual(
permalinkToSidebar,
);
utils.checkVersionMetadataPropCreated(currentVersion);
utils.checkVersionMetadataPropCreated(version101);
utils.checkVersionMetadataPropCreated(version100);
utils.checkVersionMetadataPropCreated(versionWithSlugs);
utils.expectSnapshot();
});
@ -481,26 +506,24 @@ describe('versioned website (community)', () => {
const pluginId = 'community';
const plugin = pluginContentDocs(
context,
normalizePluginOptions(PluginOptionSchema, {
normalizePluginOptions(OptionsSchema, {
id: 'community',
path: 'community',
routeBasePath,
sidebarPath,
}),
);
const env = loadEnv(siteDir, pluginId);
const {docsDir: versionedDir} = env.versioning;
const pluginContentDir = path.join(context.generatedFilesDir, plugin.name);
test('isVersioned', () => {
expect(env.versioning.enabled).toEqual(true);
});
test('extendCli - docsVersion', () => {
const mock = jest.spyOn(version, 'docsVersion').mockImplementation();
const mock = jest
.spyOn(cliDocs, 'cliDocsVersionCommand')
.mockImplementation();
const cli = new commander.Command();
plugin.extendCli(cli);
// @ts-expect-error: TODO annoying type incompatibility
plugin.extendCli!(cli);
cli.parse(['node', 'test', `docs:version:${pluginId}`, '2.0.0']);
expect(mock).toHaveBeenCalledTimes(1);
expect(mock).toHaveBeenCalledWith('2.0.0', siteDir, pluginId, {
path: routeBasePath,
sidebarPath,
@ -509,17 +532,17 @@ describe('versioned website (community)', () => {
});
test('getPathToWatch', () => {
const pathToWatch = plugin.getPathsToWatch();
const pathToWatch = plugin.getPathsToWatch!();
const matchPattern = pathToWatch.map((filepath) =>
posixPath(path.relative(siteDir, filepath)),
);
expect(matchPattern).not.toEqual([]);
expect(matchPattern).toMatchInlineSnapshot(`
Array [
"community_sidebars.json",
"community/**/*.{md,mdx}",
"community_versioned_sidebars/version-1.0.0-sidebars.json",
"community_versioned_docs/version-1.0.0/**/*.{md,mdx}",
"community_sidebars.json",
]
`);
expect(isMatch('community/team.md', matchPattern)).toEqual(true);
@ -545,27 +568,29 @@ describe('versioned website (community)', () => {
});
test('content', async () => {
const content = await plugin.loadContent();
const {
docsMetadata,
docsSidebars,
versionToSidebars,
permalinkToSidebar,
} = content;
const content = await plugin.loadContent!();
expect(content.loadedVersions.length).toEqual(2);
const [currentVersion, version100] = content.loadedVersions;
expect(docsMetadata.team).toEqual({
expect(findDocById(currentVersion, 'team')).toEqual({
...defaultDocMetadata,
id: 'team',
unversionedId: 'team',
isDocsHomePage: false,
permalink: '/community/next/team',
slug: '/team',
source: path.join('@site', routeBasePath, 'team.md'),
source: path.join(
'@site',
path.relative(siteDir, currentVersion.docsDirPath),
'team.md',
),
title: 'team',
description: 'Team current version',
version: 'next',
version: 'current',
sidebar: 'community',
});
expect(docsMetadata['version-1.0.0/team']).toEqual({
expect(findDocById(version100, 'team')).toEqual({
...defaultDocMetadata,
id: 'version-1.0.0/team',
unversionedId: 'team',
isDocsHomePage: false,
@ -573,8 +598,7 @@ describe('versioned website (community)', () => {
slug: '/team',
source: path.join(
'@site',
path.relative(siteDir, versionedDir),
'version-1.0.0',
path.relative(siteDir, version100.docsDirPath),
'team.md',
),
title: 'team',
@ -583,38 +607,18 @@ describe('versioned website (community)', () => {
sidebar: 'version-1.0.0/community',
});
expect(docsSidebars).toMatchSnapshot('all sidebars');
expect(versionToSidebars).toMatchSnapshot(
'sidebars needed for each version',
);
expect(currentVersion.sidebars).toMatchSnapshot('current version sidebars');
expect(version100.sidebars).toMatchSnapshot('100 version sidebars');
const {actions, utils} = createFakeActions(pluginContentDir);
await plugin.contentLoaded({
await plugin.contentLoaded!({
content,
actions,
allContent: {},
});
// The created base metadata for each nested docs route is smartly chunked/ splitted across version
const latestVersionBaseMetadata = utils.getCreatedDataByPrefix(
'community-route-',
);
expect(latestVersionBaseMetadata).toMatchSnapshot(
'base metadata for latest version',
);
expect(latestVersionBaseMetadata.docsSidebars).not.toEqual(docsSidebars);
expect(latestVersionBaseMetadata.permalinkToSidebar).not.toEqual(
permalinkToSidebar,
);
const nextVersionBaseMetadata = utils.getCreatedDataByPrefix(
'community-next-route-',
);
expect(nextVersionBaseMetadata).toMatchSnapshot(
'base metadata for next version',
);
expect(nextVersionBaseMetadata.docsSidebars).not.toEqual(docsSidebars);
expect(nextVersionBaseMetadata.permalinkToSidebar).not.toEqual(
permalinkToSidebar,
);
utils.checkVersionMetadataPropCreated(currentVersion);
utils.checkVersionMetadataPropCreated(version100);
utils.expectSnapshot();
});

View file

@ -9,7 +9,7 @@ import fs from 'fs';
import path from 'path';
import shell from 'shelljs';
import lastUpdate from '../lastUpdate';
import {getFileLastUpdate} from '../lastUpdate';
describe('lastUpdate', () => {
const existingFilePath = path.join(
@ -17,7 +17,7 @@ describe('lastUpdate', () => {
'__fixtures__/simple-site/docs/hello.md',
);
test('existing test file in repository with Git timestamp', async () => {
const lastUpdateData = await lastUpdate(existingFilePath);
const lastUpdateData = await getFileLastUpdate(existingFilePath);
expect(lastUpdateData).not.toBeNull();
const {author, timestamp} = lastUpdateData;
@ -36,29 +36,29 @@ describe('lastUpdate', () => {
'__fixtures__',
'.nonExisting',
);
expect(await lastUpdate(nonExistingFilePath)).toBeNull();
expect(await getFileLastUpdate(nonExistingFilePath)).toBeNull();
expect(consoleMock).toHaveBeenCalledTimes(1);
expect(consoleMock).toHaveBeenCalledWith(
new Error(
`Command failed with exit code 128: git log -1 --format=%ct, %an ${nonExistingFilePath}`,
),
);
expect(await lastUpdate(null)).toBeNull();
expect(await lastUpdate(undefined)).toBeNull();
expect(await getFileLastUpdate(null)).toBeNull();
expect(await getFileLastUpdate(undefined)).toBeNull();
consoleMock.mockRestore();
});
test('temporary created file that has no git timestamp', async () => {
const tempFilePath = path.join(__dirname, '__fixtures__', '.temp');
fs.writeFileSync(tempFilePath, 'Lorem ipsum :)');
expect(await lastUpdate(tempFilePath)).toBeNull();
expect(await getFileLastUpdate(tempFilePath)).toBeNull();
fs.unlinkSync(tempFilePath);
});
test('Git does not exist', async () => {
const mock = jest.spyOn(shell, 'which').mockImplementationOnce(() => null);
const consoleMock = jest.spyOn(console, 'warn').mockImplementation();
const lastUpdateData = await lastUpdate(existingFilePath);
const lastUpdateData = await getFileLastUpdate(existingFilePath);
expect(lastUpdateData).toBeNull();
expect(consoleMock).toHaveBeenLastCalledWith(
'Sorry, the docs plugin last update options require Git.',

View file

@ -1,464 +0,0 @@
/**
* 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 path from 'path';
import {loadContext} from '@docusaurus/core/src/server/index';
import processMetadata from '../metadata';
import loadEnv from '../env';
import {MetadataRaw, Env, MetadataOptions} from '../types';
import {LoadContext} from '@docusaurus/types';
import {DEFAULT_PLUGIN_ID} from '@docusaurus/core/lib/constants';
const fixtureDir = path.join(__dirname, '__fixtures__');
function createTestHelpers({
siteDir,
context,
env,
options,
}: {
siteDir: string;
context: LoadContext;
env: Env;
options: MetadataOptions;
}) {
async function testMeta(
refDir: string,
source: string,
expectedMetadata: Omit<MetadataRaw, 'source'>,
) {
const metadata = await processMetadata({
source,
refDir,
context,
options,
env,
});
expect(metadata).toEqual({
...expectedMetadata,
source: path.join('@site', path.relative(siteDir, refDir), source),
});
}
async function testSlug(
refDir: string,
source: string,
expectedPermalink: string,
) {
const metadata = await processMetadata({
source,
refDir,
context,
options,
env,
});
expect(metadata.permalink).toEqual(expectedPermalink);
}
return {testMeta, testSlug};
}
describe('simple site', () => {
const siteDir = path.join(fixtureDir, 'simple-site');
const context = loadContext(siteDir);
const routeBasePath = 'docs';
const docsDir = path.resolve(siteDir, routeBasePath);
const env = loadEnv(siteDir, DEFAULT_PLUGIN_ID);
const options = {routeBasePath};
const {testMeta, testSlug} = createTestHelpers({
siteDir,
context,
options,
env,
});
test('normal docs', async () => {
await testMeta(docsDir, path.join('foo', 'bar.md'), {
id: 'foo/bar',
unversionedId: 'foo/bar',
isDocsHomePage: false,
permalink: '/docs/foo/bar',
slug: '/foo/bar',
title: 'Bar',
description: 'This is custom description',
});
await testMeta(docsDir, path.join('hello.md'), {
id: 'hello',
unversionedId: 'hello',
isDocsHomePage: false,
permalink: '/docs/hello',
slug: '/hello',
title: 'Hello, World !',
description: `Hi, Endilie here :)`,
});
});
test('homePageId doc', async () => {
const {testMeta: testMetaLocal} = createTestHelpers({
siteDir,
options: {
routeBasePath,
homePageId: 'hello',
},
context,
env,
});
await testMetaLocal(docsDir, path.join('hello.md'), {
id: 'hello',
unversionedId: 'hello',
isDocsHomePage: true,
permalink: '/docs/',
slug: '/',
title: 'Hello, World !',
description: `Hi, Endilie here :)`,
});
});
test('homePageId doc nested', async () => {
const {testMeta: testMetaLocal} = createTestHelpers({
siteDir,
options: {
routeBasePath,
homePageId: 'foo/bar',
},
context,
env,
});
await testMetaLocal(docsDir, path.join('foo', 'bar.md'), {
id: 'foo/bar',
unversionedId: 'foo/bar',
isDocsHomePage: true,
permalink: '/docs/',
slug: '/',
title: 'Bar',
description: 'This is custom description',
});
});
test('docs with editUrl', async () => {
const {testMeta: testMetaLocal} = createTestHelpers({
siteDir,
options: {
routeBasePath,
editUrl: 'https://github.com/facebook/docusaurus/edit/master/website',
},
context,
env,
});
await testMetaLocal(docsDir, path.join('foo', 'baz.md'), {
id: 'foo/baz',
unversionedId: 'foo/baz',
isDocsHomePage: false,
permalink: '/docs/foo/bazSlug.html',
slug: '/foo/bazSlug.html',
title: 'baz',
editUrl:
'https://github.com/facebook/docusaurus/edit/master/website/docs/foo/baz.md',
description: 'Images',
});
});
test('docs with custom editUrl & unrelated frontmatter', async () => {
await testMeta(docsDir, 'lorem.md', {
id: 'lorem',
unversionedId: 'lorem',
isDocsHomePage: false,
permalink: '/docs/lorem',
slug: '/lorem',
title: 'lorem',
editUrl: 'https://github.com/customUrl/docs/lorem.md',
description: 'Lorem ipsum.',
});
});
test('docs with last update time and author', async () => {
const {testMeta: testMetaLocal} = createTestHelpers({
siteDir,
options: {
routeBasePath,
showLastUpdateAuthor: true,
showLastUpdateTime: true,
},
context,
env,
});
await testMetaLocal(docsDir, 'lorem.md', {
id: 'lorem',
unversionedId: 'lorem',
isDocsHomePage: false,
permalink: '/docs/lorem',
slug: '/lorem',
title: 'lorem',
editUrl: 'https://github.com/customUrl/docs/lorem.md',
description: 'Lorem ipsum.',
lastUpdatedAt: 1539502055,
lastUpdatedBy: 'Author',
});
});
test('docs with null custom_edit_url', async () => {
const {testMeta: testMetaLocal} = createTestHelpers({
siteDir,
options: {
routeBasePath,
showLastUpdateAuthor: true,
showLastUpdateTime: true,
},
context,
env,
});
await testMetaLocal(docsDir, 'ipsum.md', {
id: 'ipsum',
unversionedId: 'ipsum',
isDocsHomePage: false,
permalink: '/docs/ipsum',
slug: '/ipsum',
title: 'ipsum',
editUrl: null,
description: 'Lorem ipsum.',
lastUpdatedAt: 1539502055,
lastUpdatedBy: 'Author',
});
});
test('docs with slugs', async () => {
await testSlug(
docsDir,
path.join('rootRelativeSlug.md'),
'/docs/rootRelativeSlug',
);
await testSlug(
docsDir,
path.join('rootAbsoluteSlug.md'),
'/docs/rootAbsoluteSlug',
);
await testSlug(
docsDir,
path.join('rootResolvedSlug.md'),
'/docs/hey/rootResolvedSlug',
);
await testSlug(
docsDir,
path.join('rootTryToEscapeSlug.md'),
'/docs/rootTryToEscapeSlug',
);
await testSlug(
docsDir,
path.join('slugs', 'absoluteSlug.md'),
'/docs/absoluteSlug',
);
await testSlug(
docsDir,
path.join('slugs', 'relativeSlug.md'),
'/docs/slugs/relativeSlug',
);
await testSlug(
docsDir,
path.join('slugs', 'resolvedSlug.md'),
'/docs/slugs/hey/resolvedSlug',
);
await testSlug(
docsDir,
path.join('slugs', 'tryToEscapeSlug.md'),
'/docs/tryToEscapeSlug',
);
});
test('docs with invalid id', async () => {
const badSiteDir = path.join(fixtureDir, 'bad-id-site');
await expect(
processMetadata({
source: 'invalid-id.md',
refDir: path.join(badSiteDir, 'docs'),
context,
options: {
routeBasePath,
},
env,
}),
).rejects.toThrowErrorMatchingInlineSnapshot(
`"Document id cannot include \\"/\\"."`,
);
});
test('docs with slug on doc home', async () => {
const badSiteDir = path.join(fixtureDir, 'bad-slug-on-doc-home-site');
await expect(
processMetadata({
source: 'docWithSlug.md',
refDir: path.join(badSiteDir, 'docs'),
context,
options: {
routeBasePath,
homePageId: 'docWithSlug',
},
env,
}),
).rejects.toThrowErrorMatchingInlineSnapshot(
`"The docs homepage (homePageId=docWithSlug) is not allowed to have a frontmatter slug=docWithSlug.html => you have to chooser either homePageId or slug, not both"`,
);
});
});
describe('versioned site', () => {
const siteDir = path.join(fixtureDir, 'versioned-site');
const context = loadContext(siteDir);
const routeBasePath = 'docs';
const docsDir = path.resolve(siteDir, routeBasePath);
const env = loadEnv(siteDir, DEFAULT_PLUGIN_ID);
const {docsDir: versionedDir} = env.versioning;
const options = {routeBasePath};
const {testMeta, testSlug} = createTestHelpers({
siteDir,
context,
options,
env,
});
test('next docs', async () => {
await testMeta(docsDir, path.join('foo', 'bar.md'), {
id: 'foo/bar',
unversionedId: 'foo/bar',
isDocsHomePage: false,
permalink: '/docs/next/foo/barSlug',
slug: '/foo/barSlug',
title: 'bar',
description: 'This is next version of bar.',
version: 'next',
});
await testMeta(docsDir, path.join('hello.md'), {
id: 'hello',
unversionedId: 'hello',
isDocsHomePage: false,
permalink: '/docs/next/hello',
slug: '/hello',
title: 'hello',
description: 'Hello next !',
version: 'next',
});
});
test('versioned docs', async () => {
await testMeta(versionedDir, path.join('version-1.0.0', 'foo', 'bar.md'), {
id: 'version-1.0.0/foo/bar',
unversionedId: 'foo/bar',
isDocsHomePage: false,
permalink: '/docs/1.0.0/foo/barSlug',
slug: '/foo/barSlug',
title: 'bar',
description: 'Bar 1.0.0 !',
version: '1.0.0',
});
await testMeta(versionedDir, path.join('version-1.0.0', 'hello.md'), {
id: 'version-1.0.0/hello',
unversionedId: 'hello',
isDocsHomePage: false,
permalink: '/docs/1.0.0/hello',
slug: '/hello',
title: 'hello',
description: 'Hello 1.0.0 !',
version: '1.0.0',
});
await testMeta(versionedDir, path.join('version-1.0.1', 'foo', 'bar.md'), {
id: 'version-1.0.1/foo/bar',
unversionedId: 'foo/bar',
isDocsHomePage: false,
permalink: '/docs/foo/bar',
slug: '/foo/bar',
title: 'bar',
description: 'Bar 1.0.1 !',
version: '1.0.1',
});
await testMeta(versionedDir, path.join('version-1.0.1', 'hello.md'), {
id: 'version-1.0.1/hello',
unversionedId: 'hello',
isDocsHomePage: false,
permalink: '/docs/hello',
slug: '/hello',
title: 'hello',
description: 'Hello 1.0.1 !',
version: '1.0.1',
});
});
test('next doc slugs', async () => {
await testSlug(
docsDir,
path.join('slugs', 'absoluteSlug.md'),
'/docs/next/absoluteSlug',
);
await testSlug(
docsDir,
path.join('slugs', 'relativeSlug.md'),
'/docs/next/slugs/relativeSlug',
);
await testSlug(
docsDir,
path.join('slugs', 'resolvedSlug.md'),
'/docs/next/slugs/hey/resolvedSlug',
);
await testSlug(
docsDir,
path.join('slugs', 'tryToEscapeSlug.md'),
'/docs/next/tryToEscapeSlug',
);
});
test('versioned doc slugs', async () => {
await testSlug(
versionedDir,
path.join('version-withSlugs', 'rootAbsoluteSlug.md'),
'/docs/withSlugs/rootAbsoluteSlug',
);
await testSlug(
versionedDir,
path.join('version-withSlugs', 'rootRelativeSlug.md'),
'/docs/withSlugs/rootRelativeSlug',
);
await testSlug(
versionedDir,
path.join('version-withSlugs', 'rootResolvedSlug.md'),
'/docs/withSlugs/hey/rootResolvedSlug',
);
await testSlug(
versionedDir,
path.join('version-withSlugs', 'rootTryToEscapeSlug.md'),
'/docs/withSlugs/rootTryToEscapeSlug',
);
await testSlug(
versionedDir,
path.join('version-withSlugs', 'slugs', 'absoluteSlug.md'),
'/docs/withSlugs/absoluteSlug',
);
await testSlug(
versionedDir,
path.join('version-withSlugs', 'slugs', 'relativeSlug.md'),
'/docs/withSlugs/slugs/relativeSlug',
);
await testSlug(
versionedDir,
path.join('version-withSlugs', 'slugs', 'resolvedSlug.md'),
'/docs/withSlugs/slugs/hey/resolvedSlug',
);
await testSlug(
versionedDir,
path.join('version-withSlugs', 'slugs', 'tryToEscapeSlug.md'),
'/docs/withSlugs/tryToEscapeSlug',
);
});
});

View file

@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
import {PluginOptionSchema, DEFAULT_OPTIONS} from '../pluginOptionSchema';
import {OptionsSchema, DEFAULT_OPTIONS} from '../options';
import {normalizePluginOptions} from '@docusaurus/utils-validation';
// the type of remark/rehype plugins is function
@ -14,7 +14,7 @@ const markdownPluginsObjectStub = {};
describe('normalizeDocsPluginOptions', () => {
test('should return default options for undefined user options', async () => {
const {value, error} = await PluginOptionSchema.validate({});
const {value, error} = await OptionsSchema.validate({});
expect(value).toEqual(DEFAULT_OPTIONS);
expect(error).toBe(undefined);
});
@ -34,9 +34,10 @@ describe('normalizeDocsPluginOptions', () => {
showLastUpdateAuthor: true,
admonitions: {},
excludeNextVersionDocs: true,
includeCurrentVersion: false,
disableVersioning: true,
};
const {value, error} = await PluginOptionSchema.validate(userOptions);
const {value, error} = await OptionsSchema.validate(userOptions);
expect(value).toEqual(userOptions);
expect(error).toBe(undefined);
});
@ -50,14 +51,14 @@ describe('normalizeDocsPluginOptions', () => {
[markdownPluginsFunctionStub, {option1: '42'}],
],
};
const {value, error} = await PluginOptionSchema.validate(userOptions);
const {value, error} = await OptionsSchema.validate(userOptions);
expect(value).toEqual(userOptions);
expect(error).toBe(undefined);
});
test('should reject invalid remark plugin options', () => {
expect(() => {
normalizePluginOptions(PluginOptionSchema, {
normalizePluginOptions(OptionsSchema, {
remarkPlugins: [[{option1: '42'}, markdownPluginsFunctionStub]],
});
}).toThrowErrorMatchingInlineSnapshot(
@ -67,7 +68,7 @@ describe('normalizeDocsPluginOptions', () => {
test('should reject invalid rehype plugin options', () => {
expect(() => {
normalizePluginOptions(PluginOptionSchema, {
normalizePluginOptions(OptionsSchema, {
rehypePlugins: [
[
markdownPluginsFunctionStub,
@ -83,7 +84,7 @@ describe('normalizeDocsPluginOptions', () => {
test('should reject bad path inputs', () => {
expect(() => {
normalizePluginOptions(PluginOptionSchema, {
normalizePluginOptions(OptionsSchema, {
path: 2,
});
}).toThrowErrorMatchingInlineSnapshot(`"\\"path\\" must be a string"`);
@ -91,7 +92,7 @@ describe('normalizeDocsPluginOptions', () => {
test('should reject bad include inputs', () => {
expect(() => {
normalizePluginOptions(PluginOptionSchema, {
normalizePluginOptions(OptionsSchema, {
include: '**/*.{md,mdx}',
});
}).toThrowErrorMatchingInlineSnapshot(`"\\"include\\" must be an array"`);
@ -99,7 +100,7 @@ describe('normalizeDocsPluginOptions', () => {
test('should reject bad showLastUpdateTime inputs', () => {
expect(() => {
normalizePluginOptions(PluginOptionSchema, {
normalizePluginOptions(OptionsSchema, {
showLastUpdateTime: 'true',
});
}).toThrowErrorMatchingInlineSnapshot(
@ -109,7 +110,7 @@ describe('normalizeDocsPluginOptions', () => {
test('should reject bad remarkPlugins input', () => {
expect(() => {
normalizePluginOptions(PluginOptionSchema, {
normalizePluginOptions(OptionsSchema, {
remarkPlugins: 'remark-math',
});
}).toThrowErrorMatchingInlineSnapshot(

View file

@ -1,240 +0,0 @@
/**
* 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 createOrder from '../order';
describe('createOrder', () => {
test('multiple sidebars with subcategory', () => {
const result = createOrder({
docs: [
{
type: 'category',
label: 'Category1',
items: [
{
type: 'category',
label: 'Subcategory 1',
items: [{type: 'doc', id: 'doc1'}],
},
{
type: 'category',
label: 'Subcategory 2',
items: [{type: 'doc', id: 'doc2'}],
},
],
},
{
type: 'category',
label: 'Category2',
items: [
{type: 'doc', id: 'doc3'},
{type: 'doc', id: 'doc4'},
],
},
],
otherDocs: [
{
type: 'category',
label: 'Category1',
items: [{type: 'doc', id: 'doc5'}],
},
],
});
expect(result).toEqual({
doc1: {
next: 'doc2',
previous: undefined,
sidebar: 'docs',
},
doc2: {
next: 'doc3',
previous: 'doc1',
sidebar: 'docs',
},
doc3: {
next: 'doc4',
previous: 'doc2',
sidebar: 'docs',
},
doc4: {
next: undefined,
previous: 'doc3',
sidebar: 'docs',
},
doc5: {
next: undefined,
previous: undefined,
sidebar: 'otherDocs',
},
});
});
test('multiple sidebars without subcategory', () => {
const result = createOrder({
docs: [
{
type: 'category',
label: 'Category1',
items: [
{type: 'doc', id: 'doc1'},
{type: 'doc', id: 'doc2'},
],
},
{
type: 'category',
label: 'Category2',
items: [
{type: 'doc', id: 'doc3'},
{type: 'doc', id: 'doc4'},
],
},
],
otherDocs: [
{
type: 'category',
label: 'Category1',
items: [{type: 'doc', id: 'doc5'}],
},
],
});
expect(result).toEqual({
doc1: {
next: 'doc2',
previous: undefined,
sidebar: 'docs',
},
doc2: {
next: 'doc3',
previous: 'doc1',
sidebar: 'docs',
},
doc3: {
next: 'doc4',
previous: 'doc2',
sidebar: 'docs',
},
doc4: {
next: undefined,
previous: 'doc3',
sidebar: 'docs',
},
doc5: {
next: undefined,
previous: undefined,
sidebar: 'otherDocs',
},
});
});
test('versioned sidebars', () => {
const result = createOrder({
docs: [
{
type: 'category',
label: 'Category1',
items: [{type: 'doc', id: 'doc1'}],
},
],
'version-1.2.3-docs': [
{
type: 'category',
label: 'Category1',
items: [{type: 'doc', id: 'version-1.2.3-doc2'}],
},
{
type: 'category',
label: 'Category2',
items: [{type: 'doc', id: 'version-1.2.3-doc1'}],
},
],
});
expect(result).toEqual({
doc1: {
next: undefined,
previous: undefined,
sidebar: 'docs',
},
'version-1.2.3-doc1': {
next: undefined,
previous: 'version-1.2.3-doc2',
sidebar: 'version-1.2.3-docs',
},
'version-1.2.3-doc2': {
next: 'version-1.2.3-doc1',
previous: undefined,
sidebar: 'version-1.2.3-docs',
},
});
});
test('multiple sidebars with subcategories, refs and external links', () => {
const result = createOrder({
docs: [
{
type: 'category',
label: 'Category1',
items: [
{
type: 'category',
label: 'Subcategory 1',
items: [{type: 'link', href: '//example.com', label: 'bar'}],
},
{
type: 'category',
label: 'Subcategory 2',
items: [{type: 'doc', id: 'doc2'}],
},
{
type: 'category',
label: 'Subcategory 1',
items: [{type: 'link', href: '//example2.com', label: 'baz'}],
},
],
},
{
type: 'category',
label: 'Category2',
items: [
{type: 'doc', id: 'doc3'},
{type: 'ref', id: 'doc4'},
],
},
],
otherDocs: [
{
type: 'category',
label: 'Category1',
items: [{type: 'doc', id: 'doc5'}],
},
],
});
expect(result).toEqual({
doc2: {
next: 'doc3',
previous: undefined,
sidebar: 'docs',
},
doc3: {
next: undefined,
previous: 'doc2',
sidebar: 'docs',
},
doc5: {
next: undefined,
previous: undefined,
sidebar: 'otherDocs',
},
});
});
test('edge cases', () => {
expect(createOrder({})).toEqual({});
expect(createOrder(undefined)).toEqual({});
expect(() => createOrder(null)).toThrowErrorMatchingInlineSnapshot(
`"Cannot convert undefined or null to object"`,
);
});
});

View file

@ -6,7 +6,13 @@
*/
import path from 'path';
import loadSidebars from '../sidebars';
import {
loadSidebars,
collectSidebarDocItems,
collectSidebarsDocIds,
createSidebarsUtils,
} from '../sidebars';
import {Sidebar, Sidebars} from '../types';
/* eslint-disable global-require, import/no-dynamic-require */
@ -14,13 +20,13 @@ describe('loadSidebars', () => {
const fixtureDir = path.join(__dirname, '__fixtures__', 'sidebars');
test('sidebars with known sidebar item type', async () => {
const sidebarPath = path.join(fixtureDir, 'sidebars.json');
const result = loadSidebars([sidebarPath]);
const result = loadSidebars(sidebarPath);
expect(result).toMatchSnapshot();
});
test('sidebars with deep level of category', async () => {
const sidebarPath = path.join(fixtureDir, 'sidebars-category.js');
const result = loadSidebars([sidebarPath]);
const result = loadSidebars(sidebarPath);
expect(result).toMatchSnapshot();
});
@ -30,8 +36,8 @@ describe('loadSidebars', () => {
fixtureDir,
'sidebars-category-shorthand.js',
);
const sidebar1 = loadSidebars([sidebarPath1]);
const sidebar2 = loadSidebars([sidebarPath2]);
const sidebar1 = loadSidebars(sidebarPath1);
const sidebar2 = loadSidebars(sidebarPath2);
expect(sidebar1).toEqual(sidebar2);
});
@ -40,9 +46,7 @@ describe('loadSidebars', () => {
fixtureDir,
'sidebars-category-wrong-items.json',
);
expect(() =>
loadSidebars([sidebarPath]),
).toThrowErrorMatchingInlineSnapshot(
expect(() => loadSidebars(sidebarPath)).toThrowErrorMatchingInlineSnapshot(
`"Error loading {\\"type\\":\\"category\\",\\"label\\":\\"Category Label\\",\\"items\\":\\"doc1\\"}. \\"items\\" must be an array."`,
);
});
@ -52,9 +56,7 @@ describe('loadSidebars', () => {
fixtureDir,
'sidebars-category-wrong-label.json',
);
expect(() =>
loadSidebars([sidebarPath]),
).toThrowErrorMatchingInlineSnapshot(
expect(() => loadSidebars(sidebarPath)).toThrowErrorMatchingInlineSnapshot(
`"Error loading {\\"type\\":\\"category\\",\\"label\\":true,\\"items\\":[\\"doc1\\"]}. \\"label\\" must be a string."`,
);
});
@ -64,9 +66,7 @@ describe('loadSidebars', () => {
fixtureDir,
'sidebars-doc-id-not-string.json',
);
expect(() =>
loadSidebars([sidebarPath]),
).toThrowErrorMatchingInlineSnapshot(
expect(() => loadSidebars(sidebarPath)).toThrowErrorMatchingInlineSnapshot(
`"Error loading {\\"type\\":\\"doc\\",\\"id\\":[\\"doc1\\"]}. \\"id\\" must be a string."`,
);
});
@ -76,60 +76,75 @@ describe('loadSidebars', () => {
fixtureDir,
'sidebars-first-level-not-category.js',
);
const result = loadSidebars([sidebarPath]);
const result = loadSidebars(sidebarPath);
expect(result).toMatchSnapshot();
});
test('sidebars link', async () => {
const sidebarPath = path.join(fixtureDir, 'sidebars-link.json');
const result = loadSidebars([sidebarPath]);
const result = loadSidebars(sidebarPath);
expect(result).toMatchSnapshot();
});
test('sidebars link wrong label', async () => {
const sidebarPath = path.join(fixtureDir, 'sidebars-link-wrong-label.json');
expect(() =>
loadSidebars([sidebarPath]),
).toThrowErrorMatchingInlineSnapshot(
expect(() => loadSidebars(sidebarPath)).toThrowErrorMatchingInlineSnapshot(
`"Error loading {\\"type\\":\\"link\\",\\"label\\":false,\\"href\\":\\"https://github.com\\"}. \\"label\\" must be a string."`,
);
});
test('sidebars link wrong href', async () => {
const sidebarPath = path.join(fixtureDir, 'sidebars-link-wrong-href.json');
expect(() =>
loadSidebars([sidebarPath]),
).toThrowErrorMatchingInlineSnapshot(
expect(() => loadSidebars(sidebarPath)).toThrowErrorMatchingInlineSnapshot(
`"Error loading {\\"type\\":\\"link\\",\\"label\\":\\"GitHub\\",\\"href\\":[\\"example.com\\"]}. \\"href\\" must be a string."`,
);
});
test('sidebars with unknown sidebar item type', async () => {
const sidebarPath = path.join(fixtureDir, 'sidebars-unknown-type.json');
expect(() =>
loadSidebars([sidebarPath]),
).toThrowErrorMatchingInlineSnapshot(
expect(() => loadSidebars(sidebarPath)).toThrowErrorMatchingInlineSnapshot(
`"Unknown sidebar item type [superman]. Sidebar item={\\"type\\":\\"superman\\"} "`,
);
});
test('sidebars with known sidebar item type but wrong field', async () => {
const sidebarPath = path.join(fixtureDir, 'sidebars-wrong-field.json');
expect(() =>
loadSidebars([sidebarPath]),
).toThrowErrorMatchingInlineSnapshot(
expect(() => loadSidebars(sidebarPath)).toThrowErrorMatchingInlineSnapshot(
`"Unknown sidebar item keys: href. Item: {\\"type\\":\\"category\\",\\"label\\":\\"category\\",\\"href\\":\\"https://github.com\\"}"`,
);
});
test('no sidebars', () => {
const result = loadSidebars(null);
expect(result).toEqual({});
test('unexisting path', () => {
expect(() => loadSidebars('badpath')).toThrowErrorMatchingInlineSnapshot(
`"No sidebar file exist at path: badpath"`,
);
});
test('undefined path', () => {
expect(() =>
loadSidebars(
// @ts-expect-error: bad arg
undefined,
),
).toThrowErrorMatchingInlineSnapshot(
`"sidebarFilePath not provided: undefined"`,
);
});
test('null path', () => {
expect(() =>
loadSidebars(
// @ts-expect-error: bad arg
null,
),
).toThrowErrorMatchingInlineSnapshot(
`"sidebarFilePath not provided: null"`,
);
});
test('sidebars with category.collapsed property', async () => {
const sidebarPath = path.join(fixtureDir, 'sidebars-collapsed.json');
const result = loadSidebars([sidebarPath]);
const result = loadSidebars(sidebarPath);
expect(result).toMatchSnapshot();
});
@ -138,7 +153,177 @@ describe('loadSidebars', () => {
fixtureDir,
'sidebars-collapsed-first-level.json',
);
const result = loadSidebars([sidebarPath]);
const result = loadSidebars(sidebarPath);
expect(result).toMatchSnapshot();
});
});
describe('collectSidebarDocItems', () => {
test('can collect recursively', async () => {
const sidebar: Sidebar = [
{
type: 'category',
collapsed: false,
label: 'Category1',
items: [
{
type: 'category',
collapsed: false,
label: 'Subcategory 1',
items: [{type: 'doc', id: 'doc1'}],
},
{
type: 'category',
collapsed: false,
label: 'Subcategory 2',
items: [
{type: 'doc', id: 'doc2'},
{
type: 'category',
collapsed: false,
label: 'Sub sub category 1',
items: [{type: 'doc', id: 'doc3'}],
},
],
},
],
},
{
type: 'category',
collapsed: false,
label: 'Category2',
items: [
{type: 'doc', id: 'doc4'},
{type: 'doc', id: 'doc5'},
],
},
];
expect(collectSidebarDocItems(sidebar).map((doc) => doc.id)).toEqual([
'doc1',
'doc2',
'doc3',
'doc4',
'doc5',
]);
});
});
describe('collectSidebarsDocItems', () => {
test('can collect sidebars doc items', async () => {
const sidebar1: Sidebar = [
{
type: 'category',
collapsed: false,
label: 'Category1',
items: [
{
type: 'category',
collapsed: false,
label: 'Subcategory 1',
items: [{type: 'doc', id: 'doc1'}],
},
{type: 'doc', id: 'doc2'},
],
},
];
const sidebar2: Sidebar = [
{
type: 'category',
collapsed: false,
label: 'Category2',
items: [
{type: 'doc', id: 'doc3'},
{type: 'doc', id: 'doc4'},
],
},
];
const sidebar3: Sidebar = [
{type: 'doc', id: 'doc5'},
{type: 'doc', id: 'doc6'},
];
expect(collectSidebarsDocIds({sidebar1, sidebar2, sidebar3})).toEqual({
sidebar1: ['doc1', 'doc2'],
sidebar2: ['doc3', 'doc4'],
sidebar3: ['doc5', 'doc6'],
});
});
});
describe('createSidebarsUtils', () => {
const sidebar1: Sidebar = [
{
type: 'category',
collapsed: false,
label: 'Category1',
items: [
{
type: 'category',
collapsed: false,
label: 'Subcategory 1',
items: [{type: 'doc', id: 'doc1'}],
},
{type: 'doc', id: 'doc2'},
],
},
];
const sidebar2: Sidebar = [
{
type: 'category',
collapsed: false,
label: 'Category2',
items: [
{type: 'doc', id: 'doc3'},
{type: 'doc', id: 'doc4'},
],
},
];
const sidebars: Sidebars = {sidebar1, sidebar2};
const {
getFirstDocIdOfFirstSidebar,
getSidebarNameByDocId,
getDocNavigation,
} = createSidebarsUtils(sidebars);
test('getSidebarNameByDocId', async () => {
expect(getFirstDocIdOfFirstSidebar()).toEqual('doc1');
});
test('getSidebarNameByDocId', async () => {
expect(getSidebarNameByDocId('doc1')).toEqual('sidebar1');
expect(getSidebarNameByDocId('doc2')).toEqual('sidebar1');
expect(getSidebarNameByDocId('doc3')).toEqual('sidebar2');
expect(getSidebarNameByDocId('doc4')).toEqual('sidebar2');
expect(getSidebarNameByDocId('doc5')).toEqual(undefined);
expect(getSidebarNameByDocId('doc6')).toEqual(undefined);
});
test('getDocNavigation', async () => {
expect(getDocNavigation('doc1')).toEqual({
sidebarName: 'sidebar1',
previousId: undefined,
nextId: 'doc2',
});
expect(getDocNavigation('doc2')).toEqual({
sidebarName: 'sidebar1',
previousId: 'doc1',
nextId: undefined,
});
expect(getDocNavigation('doc3')).toEqual({
sidebarName: 'sidebar2',
previousId: undefined,
nextId: 'doc4',
});
expect(getDocNavigation('doc4')).toEqual({
sidebarName: 'sidebar2',
previousId: 'doc3',
nextId: undefined,
});
});
});

View file

@ -0,0 +1,345 @@
/**
* 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 path from 'path';
import {
getVersionsFilePath,
getVersionedDocsDirPath,
getVersionedSidebarsDirPath,
readVersionsMetadata,
} from '../versions';
import {DEFAULT_OPTIONS} from '../options';
import {DEFAULT_PLUGIN_ID} from '@docusaurus/core/lib/constants';
import {VersionMetadata} from '../types';
describe('version paths', () => {
test('getVersionedDocsDirPath', () => {
expect(getVersionsFilePath('someSiteDir', DEFAULT_PLUGIN_ID)).toBe(
'someSiteDir/versions.json',
);
expect(getVersionsFilePath('otherSite/dir', 'pluginId')).toBe(
'otherSite/dir/pluginId_versions.json',
);
});
test('getVersionedDocsDirPath', () => {
expect(getVersionedDocsDirPath('someSiteDir', DEFAULT_PLUGIN_ID)).toBe(
'someSiteDir/versioned_docs',
);
expect(getVersionedDocsDirPath('otherSite/dir', 'pluginId')).toBe(
'otherSite/dir/pluginId_versioned_docs',
);
});
test('getVersionedSidebarsDirPath', () => {
expect(getVersionedSidebarsDirPath('someSiteDir', DEFAULT_PLUGIN_ID)).toBe(
'someSiteDir/versioned_sidebars',
);
expect(getVersionedSidebarsDirPath('otherSite/dir', 'pluginId')).toBe(
'otherSite/dir/pluginId_versioned_sidebars',
);
});
});
describe('simple site', () => {
const simpleSiteDir = path.resolve(
path.join(__dirname, '__fixtures__', 'simple-site'),
);
const defaultOptions = {
id: DEFAULT_PLUGIN_ID,
...DEFAULT_OPTIONS,
};
const defaultContext = {
siteDir: simpleSiteDir,
baseUrl: '/',
};
const vCurrent: VersionMetadata = {
docsDirPath: path.join(simpleSiteDir, 'docs'),
isLast: true,
routePriority: -1,
sidebarFilePath: path.join(simpleSiteDir, 'sidebars.json'),
versionLabel: 'Next',
versionName: 'current',
versionPath: '/docs',
};
test('readVersionsMetadata simple site', () => {
const versionsMetadata = readVersionsMetadata({
options: defaultOptions,
context: defaultContext,
});
expect(versionsMetadata).toEqual([vCurrent]);
});
test('readVersionsMetadata simple site with base url', () => {
const versionsMetadata = readVersionsMetadata({
options: defaultOptions,
context: {
...defaultContext,
baseUrl: '/myBaseUrl',
},
});
expect(versionsMetadata).toEqual([
{
...vCurrent,
versionPath: '/myBaseUrl/docs',
},
]);
});
test('readVersionsMetadata simple site with base url', () => {
expect(() =>
readVersionsMetadata({
options: {...defaultOptions, disableVersioning: true},
context: defaultContext,
}),
).toThrowErrorMatchingInlineSnapshot(
`"Docs: using disableVersioning=true option on a non-versioned site does not make sense"`,
);
});
test('readVersionsMetadata simple site with base url', () => {
expect(() =>
readVersionsMetadata({
options: {...defaultOptions, includeCurrentVersion: false},
context: defaultContext,
}),
).toThrowErrorMatchingInlineSnapshot(
`"It is not possible to use docs without any version. Please check the configuration of these options: includeCurrentVersion=false disableVersioning=false"`,
);
});
});
describe('versioned site, pluginId=default', () => {
const versionedSiteDir = path.resolve(
path.join(__dirname, '__fixtures__', 'versioned-site'),
);
const defaultOptions = {
id: DEFAULT_PLUGIN_ID,
...DEFAULT_OPTIONS,
};
const defaultContext = {
siteDir: versionedSiteDir,
baseUrl: '/',
};
const vCurrent: VersionMetadata = {
docsDirPath: path.join(versionedSiteDir, 'docs'),
isLast: false,
routePriority: undefined,
sidebarFilePath: path.join(versionedSiteDir, 'sidebars.json'),
versionLabel: 'Next',
versionName: 'current',
versionPath: '/docs/next',
};
const v101: VersionMetadata = {
docsDirPath: path.join(versionedSiteDir, 'versioned_docs/version-1.0.1'),
isLast: true,
routePriority: -1,
sidebarFilePath: path.join(
versionedSiteDir,
'versioned_sidebars/version-1.0.1-sidebars.json',
),
versionLabel: '1.0.1',
versionName: '1.0.1',
versionPath: '/docs',
};
const v100: VersionMetadata = {
docsDirPath: path.join(versionedSiteDir, 'versioned_docs/version-1.0.0'),
isLast: false,
routePriority: undefined,
sidebarFilePath: path.join(
versionedSiteDir,
'versioned_sidebars/version-1.0.0-sidebars.json',
),
versionLabel: '1.0.0',
versionName: '1.0.0',
versionPath: '/docs/1.0.0',
};
const vwithSlugs: VersionMetadata = {
docsDirPath: path.join(
versionedSiteDir,
'versioned_docs/version-withSlugs',
),
isLast: false,
routePriority: undefined,
sidebarFilePath: path.join(
versionedSiteDir,
'versioned_sidebars/version-withSlugs-sidebars.json',
),
versionLabel: 'withSlugs',
versionName: 'withSlugs',
versionPath: '/docs/withSlugs',
};
test('readVersionsMetadata versioned site', () => {
const versionsMetadata = readVersionsMetadata({
options: defaultOptions,
context: defaultContext,
});
expect(versionsMetadata).toEqual([vCurrent, v101, v100, vwithSlugs]);
});
test('readVersionsMetadata versioned site with includeCurrentVersion=false', () => {
const versionsMetadata = readVersionsMetadata({
options: {...defaultOptions, includeCurrentVersion: false},
context: defaultContext,
});
expect(versionsMetadata).toEqual([
// vCurrent removed
v101,
v100,
vwithSlugs,
]);
});
test('readVersionsMetadata versioned site with disableVersioning', () => {
const versionsMetadata = readVersionsMetadata({
options: {...defaultOptions, disableVersioning: true},
context: defaultContext,
});
expect(versionsMetadata).toEqual([
{...vCurrent, isLast: true, routePriority: -1, versionPath: '/docs'},
]);
});
test('readVersionsMetadata versioned site with all versions disabled', () => {
expect(() =>
readVersionsMetadata({
options: {
...defaultOptions,
includeCurrentVersion: false,
disableVersioning: true,
},
context: defaultContext,
}),
).toThrowErrorMatchingInlineSnapshot(
`"It is not possible to use docs without any version. Please check the configuration of these options: includeCurrentVersion=false disableVersioning=true"`,
);
});
test('readVersionsMetadata versioned site with invalid versions.json file', () => {
const mock = jest.spyOn(JSON, 'parse').mockImplementationOnce(() => {
return {
invalid: 'json',
};
});
expect(() => {
readVersionsMetadata({
options: defaultOptions,
context: defaultContext,
});
}).toThrowErrorMatchingInlineSnapshot(
`"The versions file should contain an array of versions! Found content={\\"invalid\\":\\"json\\"}"`,
);
mock.mockRestore();
});
});
describe('versioned site, pluginId=community', () => {
const versionedSiteDir = path.resolve(
path.join(__dirname, '__fixtures__', 'versioned-site'),
);
const defaultOptions = {
...DEFAULT_OPTIONS,
id: 'community',
path: 'community',
routeBasePath: 'communityBasePath',
};
const defaultContext = {
siteDir: versionedSiteDir,
baseUrl: '/',
};
const vCurrent: VersionMetadata = {
docsDirPath: path.join(versionedSiteDir, 'community'),
isLast: false,
routePriority: undefined,
sidebarFilePath: path.join(versionedSiteDir, 'sidebars.json'),
versionLabel: 'Next',
versionName: 'current',
versionPath: '/communityBasePath/next',
};
const v100: VersionMetadata = {
docsDirPath: path.join(
versionedSiteDir,
'community_versioned_docs/version-1.0.0',
),
isLast: true,
routePriority: -1,
sidebarFilePath: path.join(
versionedSiteDir,
'community_versioned_sidebars/version-1.0.0-sidebars.json',
),
versionLabel: '1.0.0',
versionName: '1.0.0',
versionPath: '/communityBasePath',
};
test('readVersionsMetadata versioned site (community)', () => {
const versionsMetadata = readVersionsMetadata({
options: defaultOptions,
context: defaultContext,
});
expect(versionsMetadata).toEqual([vCurrent, v100]);
});
test('readVersionsMetadata versioned site (community) with includeCurrentVersion=false', () => {
const versionsMetadata = readVersionsMetadata({
options: {...defaultOptions, includeCurrentVersion: false},
context: defaultContext,
});
expect(versionsMetadata).toEqual([
// vCurrent removed
v100,
]);
});
test('readVersionsMetadata versioned site (community) with disableVersioning', () => {
const versionsMetadata = readVersionsMetadata({
options: {...defaultOptions, disableVersioning: true},
context: defaultContext,
});
expect(versionsMetadata).toEqual([
{
...vCurrent,
isLast: true,
routePriority: -1,
versionPath: '/communityBasePath',
},
]);
});
test('readVersionsMetadata versioned site (community) with all versions disabled', () => {
expect(() =>
readVersionsMetadata({
options: {
...defaultOptions,
includeCurrentVersion: false,
disableVersioning: true,
},
context: defaultContext,
}),
).toThrowErrorMatchingInlineSnapshot(
`"It is not possible to use docs without any version. Please check the configuration of these options: includeCurrentVersion=false disableVersioning=true"`,
);
});
});