mirror of
https://github.com/facebook/docusaurus.git
synced 2025-04-29 18:27:56 +02:00
test: improve test coverage; properly test core client APIs (#6905)
* test: improve test coverage * fix
This commit is contained in:
parent
76cb012209
commit
d85cee576d
41 changed files with 1400 additions and 753 deletions
|
@ -36,18 +36,18 @@ export default {
|
|||
// Jest can't resolve CSS or asset imports
|
||||
'^.+\\.(css|jpe?g|png|svg)$': '<rootDir>/jest/emptyModule.js',
|
||||
|
||||
// TODO we need to allow Jest to resolve core Webpack aliases automatically
|
||||
// Using src instead of lib, so we always get fresh source
|
||||
'@docusaurus/(browserContext|BrowserOnly|ComponentCreator|constants|docusaurusContext|ExecutionEnvironment|Head|Interpolate|isInternalUrl|Link|Noop|renderRoutes|router|Translate|use.*)':
|
||||
'@docusaurus/core/lib/client/exports/$1',
|
||||
'@docusaurus/core/src/client/exports/$1',
|
||||
// Maybe point to a fixture?
|
||||
'@generated/.*': '<rootDir>/jest/emptyModule.js',
|
||||
// TODO use "projects" + multiple configs if we work on another theme?
|
||||
'@theme/(.*)': '@docusaurus/theme-classic/src/theme/$1',
|
||||
'@site/(.*)': 'website/$1',
|
||||
|
||||
// TODO why Jest can't figure node package entry points?
|
||||
// Using src instead of lib, so we always get fresh source
|
||||
'@docusaurus/plugin-content-docs/client':
|
||||
'@docusaurus/plugin-content-docs/lib/client/index.js',
|
||||
'@docusaurus/plugin-content-docs/src/client/index.ts',
|
||||
},
|
||||
globals: {
|
||||
window: {
|
||||
|
|
|
@ -13,3 +13,5 @@ Lorem ipsum
|
|||
Some content here
|
||||
|
||||
## I ♥ unicode.
|
||||
|
||||
export const c = 1;
|
||||
|
|
|
@ -114,6 +114,8 @@ Lorem ipsum
|
|||
Some content here
|
||||
|
||||
## I ♥ unicode.
|
||||
|
||||
export const c = 1;
|
||||
"
|
||||
`;
|
||||
|
||||
|
@ -241,5 +243,7 @@ Lorem ipsum
|
|||
Some content here
|
||||
|
||||
## I ♥ unicode.
|
||||
|
||||
export const c = 1;
|
||||
"
|
||||
`;
|
||||
|
|
|
@ -31,7 +31,6 @@ export function toValue(node: PhrasingContent | Heading): string {
|
|||
case 'link':
|
||||
return stringifyContent(node);
|
||||
default:
|
||||
return toString(node);
|
||||
}
|
||||
|
||||
return toString(node);
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ const RedirectPluginOptionValidation = Joi.object<RedirectOption>({
|
|||
const isString = Joi.string().required().not(null);
|
||||
|
||||
const UserOptionsSchema = Joi.object<UserPluginOptions>({
|
||||
id: Joi.string().optional(), // TODO remove once validation migrated to new system
|
||||
id: Joi.string().optional(), // TODO remove once validation migrated to new system
|
||||
fromExtensions: Joi.array().items(isString),
|
||||
toExtensions: Joi.array().items(isString),
|
||||
redirects: Joi.array().items(RedirectPluginOptionValidation),
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`toGlobalDataVersion generates the right docs, sidebars, and metadata 1`] = `
|
||||
Object {
|
||||
"docs": Array [
|
||||
Object {
|
||||
"id": "main",
|
||||
"path": "/current/main",
|
||||
"sidebar": "tutorial",
|
||||
},
|
||||
Object {
|
||||
"id": "doc",
|
||||
"path": "/current/doc",
|
||||
"sidebar": "tutorial",
|
||||
},
|
||||
Object {
|
||||
"id": "/current/generated",
|
||||
"path": "/current/generated",
|
||||
"sidebar": "tutorial",
|
||||
},
|
||||
],
|
||||
"isLast": true,
|
||||
"label": "Label",
|
||||
"mainDocId": "main",
|
||||
"name": "current",
|
||||
"path": "/current",
|
||||
"sidebars": Object {
|
||||
"another": Object {
|
||||
"link": Object {
|
||||
"label": "Generated",
|
||||
"path": "/current/generated",
|
||||
},
|
||||
},
|
||||
"links": Object {},
|
||||
"tutorial": Object {
|
||||
"link": Object {
|
||||
"label": "main",
|
||||
"path": "/current/main",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
`;
|
|
@ -0,0 +1,100 @@
|
|||
/**
|
||||
* 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 {toGlobalDataVersion} from '../globalData';
|
||||
|
||||
describe('toGlobalDataVersion', () => {
|
||||
it('generates the right docs, sidebars, and metadata', () => {
|
||||
expect(
|
||||
toGlobalDataVersion({
|
||||
versionName: 'current',
|
||||
versionLabel: 'Label',
|
||||
isLast: true,
|
||||
versionPath: '/current',
|
||||
mainDocId: 'main',
|
||||
docs: [
|
||||
{
|
||||
unversionedId: 'main',
|
||||
permalink: '/current/main',
|
||||
sidebar: 'tutorial',
|
||||
},
|
||||
{
|
||||
unversionedId: 'doc',
|
||||
permalink: '/current/doc',
|
||||
sidebar: 'tutorial',
|
||||
},
|
||||
],
|
||||
sidebars: {
|
||||
another: [
|
||||
{
|
||||
type: 'category',
|
||||
label: 'Generated',
|
||||
link: {
|
||||
type: 'generated-index',
|
||||
permalink: '/current/generated',
|
||||
},
|
||||
items: [
|
||||
{
|
||||
type: 'doc',
|
||||
id: 'doc',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
tutorial: [
|
||||
{
|
||||
type: 'doc',
|
||||
id: 'main',
|
||||
},
|
||||
{
|
||||
type: 'category',
|
||||
label: 'Generated',
|
||||
link: {
|
||||
type: 'generated-index',
|
||||
permalink: '/current/generated',
|
||||
},
|
||||
items: [
|
||||
{
|
||||
type: 'doc',
|
||||
id: 'doc',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
links: [
|
||||
{
|
||||
type: 'link',
|
||||
href: 'foo',
|
||||
label: 'Foo',
|
||||
},
|
||||
{
|
||||
type: 'link',
|
||||
href: 'bar',
|
||||
label: 'Bar',
|
||||
},
|
||||
],
|
||||
},
|
||||
categoryGeneratedIndices: [
|
||||
{
|
||||
title: 'Generated',
|
||||
slug: '/current/generated',
|
||||
permalink: '/current/generated',
|
||||
sidebar: 'tutorial',
|
||||
},
|
||||
],
|
||||
versionBanner: 'unreleased',
|
||||
versionBadge: true,
|
||||
versionClassName: 'current-cls',
|
||||
tagsPath: '/current/tags',
|
||||
contentPath: '',
|
||||
contentPathLocalized: '',
|
||||
sidebarFilePath: '',
|
||||
routePriority: 0.5,
|
||||
}),
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
});
|
|
@ -111,6 +111,27 @@ describe('getSlug', () => {
|
|||
).toBe('/dir with spâce/hey $hello/my dôc');
|
||||
});
|
||||
|
||||
it('throws for invalid routes', () => {
|
||||
expect(() =>
|
||||
getSlug({
|
||||
baseID: 'my dôc',
|
||||
source: '@site/docs/dir with spâce/hey $hello/doc.md',
|
||||
sourceDirName: '/dir with spâce/hey $hello',
|
||||
frontMatterSlug: '//',
|
||||
}),
|
||||
).toThrowErrorMatchingInlineSnapshot(`
|
||||
"We couldn't compute a valid slug for document with ID \\"my dôc\\" in \\"/dir with spâce/hey $hello\\" directory.
|
||||
The slug we computed looks invalid: //.
|
||||
Maybe your slug front matter is incorrect or there are special characters in the file path?
|
||||
By using front matter to set a custom slug, you should be able to fix this error:
|
||||
|
||||
---
|
||||
slug: /my/customDocPath
|
||||
---
|
||||
"
|
||||
`);
|
||||
});
|
||||
|
||||
it('handles current dir', () => {
|
||||
expect(
|
||||
getSlug({baseID: 'doc', source: '@site/docs/doc.md', sourceDirName: '.'}),
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -25,7 +25,7 @@ const DocFrontMatterSchema = Joi.object<DocFrontMatter>({
|
|||
hide_table_of_contents: Joi.boolean(),
|
||||
keywords: Joi.array().items(Joi.string().required()),
|
||||
image: URISchema,
|
||||
description: Joi.string().allow(''), // see https://github.com/facebook/docusaurus/issues/4591#issuecomment-822372398
|
||||
description: Joi.string().allow(''), // see https://github.com/facebook/docusaurus/issues/4591#issuecomment-822372398
|
||||
slug: Joi.string(),
|
||||
sidebar_label: Joi.string(),
|
||||
sidebar_position: Joi.number(),
|
||||
|
|
|
@ -391,6 +391,10 @@ export const isCategoryIndex: CategoryIndexMatcher = ({
|
|||
return eligibleDocIndexNames.includes(fileName.toLowerCase());
|
||||
};
|
||||
|
||||
/**
|
||||
* `guides/sidebar/autogenerated.md` ->
|
||||
* `'autogenerated', '.md', ['sidebar', 'guides']`
|
||||
*/
|
||||
export function toCategoryIndexMatcherParam({
|
||||
source,
|
||||
sourceDirName,
|
||||
|
@ -406,28 +410,6 @@ export function toCategoryIndexMatcherParam({
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* `guides/sidebar/autogenerated.md` ->
|
||||
* `'autogenerated', '.md', ['sidebar', 'guides']`
|
||||
*/
|
||||
export function splitPath(str: string): {
|
||||
/**
|
||||
* The list of directories, from lowest level to highest.
|
||||
* If there's no dir name, directories is ['.']
|
||||
*/
|
||||
directories: string[];
|
||||
/** The file name, without extension */
|
||||
fileName: string;
|
||||
/** The extension, with a leading dot */
|
||||
extension: string;
|
||||
} {
|
||||
return {
|
||||
fileName: path.parse(str).name,
|
||||
extension: path.parse(str).ext,
|
||||
directories: path.dirname(str).split(path.sep).reverse(),
|
||||
};
|
||||
}
|
||||
|
||||
// Return both doc ids
|
||||
// TODO legacy retro-compatibility due to old versioned sidebars using
|
||||
// versioned doc ids ("id" should be removed & "versionedId" should be renamed
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
*/
|
||||
|
||||
import _ from 'lodash';
|
||||
import {normalizeUrl} from '@docusaurus/utils';
|
||||
import type {Sidebars} from './sidebars/types';
|
||||
import {createSidebarsUtils} from './sidebars/utils';
|
||||
import type {
|
||||
|
@ -20,7 +19,7 @@ import type {
|
|||
GlobalDoc,
|
||||
} from '@docusaurus/plugin-content-docs/client';
|
||||
|
||||
export function toGlobalDataDoc(doc: DocMetadata): GlobalDoc {
|
||||
function toGlobalDataDoc(doc: DocMetadata): GlobalDoc {
|
||||
return {
|
||||
id: doc.unversionedId,
|
||||
path: doc.permalink,
|
||||
|
@ -28,7 +27,7 @@ export function toGlobalDataDoc(doc: DocMetadata): GlobalDoc {
|
|||
};
|
||||
}
|
||||
|
||||
export function toGlobalDataGeneratedIndex(
|
||||
function toGlobalDataGeneratedIndex(
|
||||
doc: CategoryGeneratedIndexMetadata,
|
||||
): GlobalDoc {
|
||||
return {
|
||||
|
@ -38,7 +37,7 @@ export function toGlobalDataGeneratedIndex(
|
|||
};
|
||||
}
|
||||
|
||||
export function toGlobalSidebars(
|
||||
function toGlobalSidebars(
|
||||
sidebars: Sidebars,
|
||||
version: LoadedVersion,
|
||||
): Record<string, GlobalSidebar> {
|
||||
|
@ -52,7 +51,7 @@ export function toGlobalSidebars(
|
|||
link: {
|
||||
path:
|
||||
firstLink.type === 'generated-index'
|
||||
? normalizeUrl([version.versionPath, firstLink.slug])
|
||||
? firstLink.permalink
|
||||
: version.docs.find(
|
||||
(doc) =>
|
||||
doc.id === firstLink.id || doc.unversionedId === firstLink.id,
|
||||
|
|
|
@ -18,8 +18,14 @@ declare module '@docusaurus/plugin-content-docs' {
|
|||
};
|
||||
|
||||
export type CategoryIndexMatcherParam = {
|
||||
/** The file name, without extension */
|
||||
fileName: string;
|
||||
/**
|
||||
* The list of directories, from lowest level to highest.
|
||||
* If there's no dir name, directories is ['.']
|
||||
*/
|
||||
directories: string[];
|
||||
/** The extension, with a leading dot */
|
||||
extension: string;
|
||||
};
|
||||
export type CategoryIndexMatcher = (
|
||||
|
|
|
@ -111,7 +111,7 @@ describe('createSidebarsUtils', () => {
|
|||
link: {
|
||||
type: 'generated-index',
|
||||
slug: '/s4-category-slug',
|
||||
permalink: '/s4-category-permalink',
|
||||
permalink: '/s4-category-slug',
|
||||
},
|
||||
items: [
|
||||
{type: 'doc', id: 'doc8'},
|
||||
|
@ -291,7 +291,7 @@ describe('createSidebarsUtils', () => {
|
|||
});
|
||||
expect(getFirstLink('sidebar4')).toEqual({
|
||||
type: 'generated-index',
|
||||
slug: '/s4-category-slug',
|
||||
permalink: '/s4-category-slug',
|
||||
label: 'S4 Category',
|
||||
});
|
||||
});
|
||||
|
|
|
@ -139,6 +139,11 @@ export type SidebarsUtils = {
|
|||
getCategoryGeneratedIndexNavigation: (
|
||||
categoryGeneratedIndexPermalink: string,
|
||||
) => SidebarNavigation;
|
||||
/**
|
||||
* This function may return undefined. This is usually a user mistake, because
|
||||
* it means this sidebar will never be displayed; however, we can still use
|
||||
* `displayed_sidebar` to make it displayed. Pretty weird but valid use-case
|
||||
*/
|
||||
getFirstLink: (sidebarId: string) =>
|
||||
| {
|
||||
type: 'doc';
|
||||
|
@ -147,7 +152,7 @@ export type SidebarsUtils = {
|
|||
}
|
||||
| {
|
||||
type: 'generated-index';
|
||||
slug: string;
|
||||
permalink: string;
|
||||
label: string;
|
||||
}
|
||||
| undefined;
|
||||
|
@ -295,7 +300,7 @@ Available document ids are:
|
|||
}
|
||||
| {
|
||||
type: 'generated-index';
|
||||
slug: string;
|
||||
permalink: string;
|
||||
label: string;
|
||||
}
|
||||
| undefined {
|
||||
|
@ -316,7 +321,7 @@ Available document ids are:
|
|||
} else if (item.link?.type === 'generated-index') {
|
||||
return {
|
||||
type: 'generated-index',
|
||||
slug: item.link.slug,
|
||||
permalink: item.link.permalink,
|
||||
label: item.label,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -63,12 +63,11 @@ export default function getSlug({
|
|||
function ensureValidSlug(slug: string): string {
|
||||
if (!isValidPathname(slug)) {
|
||||
throw new Error(
|
||||
`We couldn't compute a valid slug for document with id "${baseID}" in "${sourceDirName}" directory.
|
||||
`We couldn't compute a valid slug for document with ID "${baseID}" in "${sourceDirName}" directory.
|
||||
The slug we computed looks invalid: ${slug}.
|
||||
Maybe your slug front matter is incorrect or you use weird chars in the file path?
|
||||
By using the slug front matter, you should be able to fix this error, by using the slug of your choice:
|
||||
Maybe your slug front matter is incorrect or there are special characters in the file path?
|
||||
By using front matter to set a custom slug, you should be able to fix this error:
|
||||
|
||||
Example =>
|
||||
---
|
||||
slug: /my/customDocPath
|
||||
---
|
||||
|
|
|
@ -274,10 +274,7 @@ function translateVersion(
|
|||
translationFiles: Record<string, TranslationFile>,
|
||||
): LoadedVersion {
|
||||
const versionTranslations =
|
||||
translationFiles[getVersionFileName(version.versionName)]?.content;
|
||||
if (!versionTranslations) {
|
||||
return version;
|
||||
}
|
||||
translationFiles[getVersionFileName(version.versionName)]!.content;
|
||||
return {
|
||||
...version,
|
||||
versionLabel:
|
||||
|
|
|
@ -74,9 +74,9 @@ function ensureValidVersionString(version: unknown): asserts version is string {
|
|||
function ensureValidVersionArray(
|
||||
versionArray: unknown,
|
||||
): asserts versionArray is string[] {
|
||||
if (!(versionArray instanceof Array)) {
|
||||
if (!Array.isArray(versionArray)) {
|
||||
throw new Error(
|
||||
`The versions file should contain an array of versions! Found content: ${JSON.stringify(
|
||||
`The versions file should contain an array of version names! Found content: ${JSON.stringify(
|
||||
versionArray,
|
||||
)}`,
|
||||
);
|
||||
|
|
|
@ -92,13 +92,13 @@ function useVersionPersistence(): DocsVersionPersistence {
|
|||
return useThemeConfig().docs.versionPersistence;
|
||||
}
|
||||
|
||||
// Value that will be accessible through context: [state,api]
|
||||
// Value that will be accessible through context: [state,api]
|
||||
function useContextValue() {
|
||||
const allDocsData = useAllDocsData();
|
||||
const versionPersistence = useVersionPersistence();
|
||||
const pluginIds = useMemo(() => Object.keys(allDocsData), [allDocsData]);
|
||||
|
||||
// Initial state is empty, as we can't read browser storage in node/SSR
|
||||
// Initial state is empty, as we can't read browser storage in node/SSR
|
||||
const [state, setState] = useState(() => getInitialState(pluginIds));
|
||||
|
||||
// On mount, we set the state read from browser storage
|
||||
|
|
|
@ -218,7 +218,7 @@ export default function SearchPage(): JSX.Element {
|
|||
algoliaHelper.on(
|
||||
'result',
|
||||
({results: {query, hits, page, nbHits, nbPages}}) => {
|
||||
if (query === '' || !(hits instanceof Array)) {
|
||||
if (query === '' || !Array.isArray(hits)) {
|
||||
searchResultStateDispatcher({type: 'reset'});
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -250,7 +250,7 @@ Lorem Ipsum
|
|||
});
|
||||
});
|
||||
|
||||
it('parses markdown h1 title at the top followed by h2 title', () => {
|
||||
it('parses markdown h1 title at the top followed by h2 title', () => {
|
||||
const markdown = dedent`
|
||||
|
||||
# Markdown Title
|
||||
|
|
|
@ -9,7 +9,15 @@ import {jest} from '@jest/globals';
|
|||
import normalizeLocation from '../normalizeLocation';
|
||||
|
||||
describe('normalizeLocation', () => {
|
||||
it('rewrite locations with index.html', () => {
|
||||
it('rewrites locations with index.html', () => {
|
||||
expect(
|
||||
normalizeLocation({
|
||||
pathname: '/index.html',
|
||||
}),
|
||||
).toEqual({
|
||||
pathname: '/',
|
||||
});
|
||||
|
||||
expect(
|
||||
normalizeLocation({
|
||||
pathname: '/docs/introduction/index.html',
|
||||
|
@ -35,7 +43,7 @@ describe('normalizeLocation', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('untouched pathnames', () => {
|
||||
it('leaves pathnames untouched', () => {
|
||||
const replaceMock = jest.spyOn(String.prototype, 'replace');
|
||||
|
||||
expect(
|
||||
|
|
|
@ -89,12 +89,12 @@ export function interpolate<Str extends string, Value extends ReactNode>(
|
|||
export default function Interpolate<Str extends string>({
|
||||
children,
|
||||
values,
|
||||
}: InterpolateProps<Str>): ReactNode {
|
||||
}: InterpolateProps<Str>): JSX.Element {
|
||||
if (typeof children !== 'string') {
|
||||
console.warn('Illegal <Interpolate> children', children);
|
||||
throw new Error(
|
||||
'The Docusaurus <Interpolate> component only accept simple string values',
|
||||
);
|
||||
}
|
||||
return interpolate(children, values);
|
||||
return <>{interpolate(children, values)}</>;
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import type {ReactNode} from 'react';
|
||||
import React from 'react';
|
||||
import {interpolate, type InterpolateValues} from '@docusaurus/Interpolate';
|
||||
import type {TranslateParam, TranslateProps} from '@docusaurus/Translate';
|
||||
|
||||
|
@ -46,7 +46,7 @@ export default function Translate<Str extends string>({
|
|||
children,
|
||||
id,
|
||||
values,
|
||||
}: TranslateProps<Str>): ReactNode {
|
||||
}: TranslateProps<Str>): JSX.Element {
|
||||
if (children && typeof children !== 'string') {
|
||||
console.warn('Illegal <Translate> children', children);
|
||||
throw new Error(
|
||||
|
@ -55,5 +55,5 @@ export default function Translate<Str extends string>({
|
|||
}
|
||||
|
||||
const localizedMessage: string = getLocalizedMessage({message: children, id});
|
||||
return interpolate(localizedMessage, values);
|
||||
return <>{interpolate(localizedMessage, values)}</>;
|
||||
}
|
||||
|
|
|
@ -5,46 +5,55 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import {jest} from '@jest/globals';
|
||||
import React from 'react';
|
||||
import renderer from 'react-test-renderer';
|
||||
import BrowserOnly from '../BrowserOnly';
|
||||
import {Context} from '../browserContext';
|
||||
|
||||
jest.mock('@docusaurus/useIsBrowser', () => () => true);
|
||||
|
||||
describe('BrowserOnly', () => {
|
||||
describe('<BrowserOnly>', () => {
|
||||
it('rejects react element children', () => {
|
||||
process.env.NODE_ENV = 'development';
|
||||
expect(() => {
|
||||
renderer.create(
|
||||
<BrowserOnly>
|
||||
{/* @ts-expect-error test */}
|
||||
<span>{window.location.href}</span>
|
||||
</BrowserOnly>,
|
||||
);
|
||||
}).toThrowErrorMatchingInlineSnapshot(`
|
||||
expect(() =>
|
||||
renderer
|
||||
.create(
|
||||
<Context.Provider value>
|
||||
<BrowserOnly>
|
||||
{/* @ts-expect-error test */}
|
||||
<span>{window.location.href}</span>
|
||||
</BrowserOnly>
|
||||
</Context.Provider>,
|
||||
)
|
||||
.toJSON(),
|
||||
).toThrowErrorMatchingInlineSnapshot(`
|
||||
"Docusaurus error: The children of <BrowserOnly> must be a \\"render function\\", e.g. <BrowserOnly>{() => <span>{window.location.href}</span>}</BrowserOnly>.
|
||||
Current type: React element"
|
||||
`);
|
||||
});
|
||||
|
||||
it('rejects string children', () => {
|
||||
process.env.NODE_ENV = 'development';
|
||||
expect(() => {
|
||||
renderer.create(
|
||||
// @ts-expect-error test
|
||||
<BrowserOnly> </BrowserOnly>,
|
||||
<Context.Provider value>
|
||||
{/* @ts-expect-error test */}
|
||||
<BrowserOnly> </BrowserOnly>
|
||||
</Context.Provider>,
|
||||
);
|
||||
}).toThrowErrorMatchingInlineSnapshot(`
|
||||
"Docusaurus error: The children of <BrowserOnly> must be a \\"render function\\", e.g. <BrowserOnly>{() => <span>{window.location.href}</span>}</BrowserOnly>.
|
||||
Current type: string"
|
||||
`);
|
||||
});
|
||||
|
||||
it('accepts valid children', () => {
|
||||
expect(
|
||||
renderer
|
||||
.create(
|
||||
<BrowserOnly fallback={<span>Loading</span>}>
|
||||
{() => <span>{window.location.href}</span>}
|
||||
</BrowserOnly>,
|
||||
<Context.Provider value>
|
||||
<BrowserOnly fallback={<span>Loading</span>}>
|
||||
{() => <span>{window.location.href}</span>}
|
||||
</BrowserOnly>
|
||||
</Context.Provider>,
|
||||
)
|
||||
.toJSON(),
|
||||
).toMatchInlineSnapshot(`
|
||||
|
@ -53,4 +62,36 @@ describe('BrowserOnly', () => {
|
|||
</span>
|
||||
`);
|
||||
});
|
||||
|
||||
it('returns fallback when not in browser', () => {
|
||||
expect(
|
||||
renderer
|
||||
.create(
|
||||
<Context.Provider value={false}>
|
||||
<BrowserOnly fallback={<span>Loading</span>}>
|
||||
{() => <span>{window.location.href}</span>}
|
||||
</BrowserOnly>
|
||||
</Context.Provider>,
|
||||
)
|
||||
.toJSON(),
|
||||
).toMatchInlineSnapshot(`
|
||||
<span>
|
||||
Loading
|
||||
</span>
|
||||
`);
|
||||
});
|
||||
|
||||
it('gracefully falls back', () => {
|
||||
expect(
|
||||
renderer
|
||||
.create(
|
||||
<Context.Provider value={false}>
|
||||
<BrowserOnly>
|
||||
{() => <span>{window.location.href}</span>}
|
||||
</BrowserOnly>
|
||||
</Context.Provider>,
|
||||
)
|
||||
.toJSON(),
|
||||
).toMatchInlineSnapshot(`null`);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -6,9 +6,10 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import {interpolate} from '../Interpolate';
|
||||
import renderer from 'react-test-renderer';
|
||||
import Interpolate, {interpolate} from '../Interpolate';
|
||||
|
||||
describe('Interpolate', () => {
|
||||
describe('interpolate', () => {
|
||||
it('without placeholders', () => {
|
||||
const text = 'Hello how are you?';
|
||||
expect(interpolate(text)).toEqual(text);
|
||||
|
@ -86,3 +87,50 @@ describe('Interpolate', () => {
|
|||
expect(interpolate(text, values)).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('<Interpolate>', () => {
|
||||
it('without placeholders', () => {
|
||||
const text = 'Hello how are you?';
|
||||
expect(renderer.create(<Interpolate>{text}</Interpolate>).toJSON()).toEqual(
|
||||
text,
|
||||
);
|
||||
});
|
||||
|
||||
it('placeholders with string values', () => {
|
||||
const text = 'Hello {name} how are you {day}?';
|
||||
const values = {name: 'Sébastien', day: 'today'};
|
||||
expect(
|
||||
renderer
|
||||
.create(<Interpolate values={values}>{text}</Interpolate>)
|
||||
.toJSON(),
|
||||
).toMatchInlineSnapshot(`"Hello Sébastien how are you today?"`);
|
||||
});
|
||||
|
||||
it('acceptance test', () => {
|
||||
const text = 'Hello {name} how are you {day}? Another {unprovidedValue}!';
|
||||
const values = {
|
||||
name: 'Sébastien',
|
||||
day: <span>today</span>,
|
||||
extraUselessValue1: <div>test</div>,
|
||||
extraUselessValue2: 'hi',
|
||||
};
|
||||
expect(
|
||||
renderer
|
||||
.create(<Interpolate values={values}>{text}</Interpolate>)
|
||||
.toJSON(),
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('rejects when children is not string', () => {
|
||||
expect(() =>
|
||||
renderer.create(
|
||||
<Interpolate>
|
||||
{/* @ts-expect-error: for test */}
|
||||
<span>aaa</span>
|
||||
</Interpolate>,
|
||||
),
|
||||
).toThrowErrorMatchingInlineSnapshot(
|
||||
`"The Docusaurus <Interpolate> component only accept simple string values"`,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -5,28 +5,75 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import {translate} from '../Translate';
|
||||
import React from 'react';
|
||||
import renderer from 'react-test-renderer';
|
||||
import Translate, {translate} from '../Translate';
|
||||
|
||||
describe('translate', () => {
|
||||
it('accept id and use it as fallback', () => {
|
||||
it('accepts id and uses it as fallback', () => {
|
||||
expect(translate({id: 'some-id'})).toBe('some-id');
|
||||
});
|
||||
|
||||
it('accept message and use it as fallback', () => {
|
||||
it('accepts message and uses it as fallback', () => {
|
||||
expect(translate({message: 'some-message'})).toBe('some-message');
|
||||
});
|
||||
|
||||
it('accept id+message and use message as fallback', () => {
|
||||
it('accepts id+message and uses message as fallback', () => {
|
||||
expect(translate({id: 'some-id', message: 'some-message'})).toBe(
|
||||
'some-message',
|
||||
);
|
||||
});
|
||||
|
||||
it('reject when no id or message', () => {
|
||||
// TODO tests are not resolving type defs correctly
|
||||
it('rejects when no id or message', () => {
|
||||
// TODO tests are not resolving type defs correctly. We need to include test
|
||||
// files in a tsconfig file
|
||||
// @ts-expect-error: TS should protect when both id/message are missing
|
||||
expect(() => translate({})).toThrowErrorMatchingInlineSnapshot(
|
||||
`"Docusaurus translation declarations must have at least a translation id or a default translation message"`,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('<Translate>', () => {
|
||||
it('accepts id and uses it as fallback', () => {
|
||||
expect(renderer.create(<Translate id="some-id" />).toJSON()).toBe(
|
||||
'some-id',
|
||||
);
|
||||
});
|
||||
|
||||
it('accepts message and uses it as fallback', () => {
|
||||
expect(renderer.create(<Translate>some-message</Translate>).toJSON()).toBe(
|
||||
'some-message',
|
||||
);
|
||||
});
|
||||
|
||||
it('accepts id+message and uses message as fallback', () => {
|
||||
expect(
|
||||
renderer
|
||||
.create(<Translate id="some-id">some-message</Translate>)
|
||||
.toJSON(),
|
||||
).toBe('some-message');
|
||||
});
|
||||
|
||||
it('rejects when no id or message', () => {
|
||||
expect(() =>
|
||||
// @ts-expect-error: TS should protect when both id/message are missing
|
||||
renderer.create(<Translate />),
|
||||
).toThrowErrorMatchingInlineSnapshot(
|
||||
`"Docusaurus translation declarations must have at least a translation id or a default translation message"`,
|
||||
);
|
||||
});
|
||||
|
||||
it('rejects when children is not a string', () => {
|
||||
expect(() =>
|
||||
renderer.create(
|
||||
<Translate id="foo">
|
||||
{/* @ts-expect-error: for test */}
|
||||
<span>aaa</span>
|
||||
</Translate>,
|
||||
),
|
||||
).toThrowErrorMatchingInlineSnapshot(
|
||||
`"The Docusaurus <Translate> component only accept simple string values"`,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,6 +1,18 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Interpolate acceptance test 1`] = `
|
||||
exports[`<Interpolate> acceptance test 1`] = `
|
||||
Array [
|
||||
"Hello ",
|
||||
"Sébastien",
|
||||
" how are you ",
|
||||
<span>
|
||||
today
|
||||
</span>,
|
||||
"? Another {unprovidedValue}!",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`interpolate acceptance test 1`] = `
|
||||
Array [
|
||||
<React.Fragment>
|
||||
Hello
|
||||
|
@ -18,7 +30,7 @@ Array [
|
|||
]
|
||||
`;
|
||||
|
||||
exports[`Interpolate placeholders with JSX values 1`] = `
|
||||
exports[`interpolate placeholders with JSX values 1`] = `
|
||||
Array [
|
||||
<React.Fragment>
|
||||
Hello
|
||||
|
@ -38,7 +50,7 @@ Array [
|
|||
]
|
||||
`;
|
||||
|
||||
exports[`Interpolate placeholders with mixed vales 1`] = `
|
||||
exports[`interpolate placeholders with mixed vales 1`] = `
|
||||
Array [
|
||||
<React.Fragment>
|
||||
Hello
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
/**
|
||||
* 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.
|
||||
*
|
||||
* @jest-environment jsdom
|
||||
*/
|
||||
|
||||
// Jest doesn't allow pragma below other comments. https://github.com/facebook/jest/issues/12573
|
||||
// eslint-disable-next-line header/header
|
||||
import React from 'react';
|
||||
import {renderHook} from '@testing-library/react-hooks/server';
|
||||
import {BrowserContextProvider} from '../browserContext';
|
||||
import useIsBrowser from '../useIsBrowser';
|
||||
|
||||
describe('BrowserContextProvider', () => {
|
||||
const {result, hydrate} = renderHook(() => useIsBrowser(), {
|
||||
wrapper: ({children}) => (
|
||||
<BrowserContextProvider>{children}</BrowserContextProvider>
|
||||
),
|
||||
});
|
||||
it('has value false on first render', () => {
|
||||
expect(result.current).toBe(false);
|
||||
});
|
||||
it('has value true on hydration', () => {
|
||||
hydrate();
|
||||
expect(result.current).toBe(true);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,41 @@
|
|||
/**
|
||||
* 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.
|
||||
*
|
||||
* @jest-environment jsdom
|
||||
*/
|
||||
|
||||
// Jest doesn't allow pragma below other comments. https://github.com/facebook/jest/issues/12573
|
||||
// eslint-disable-next-line header/header
|
||||
import React from 'react';
|
||||
import {renderHook} from '@testing-library/react-hooks/server';
|
||||
import {DocusaurusContextProvider} from '../docusaurusContext';
|
||||
import useDocusaurusContext from '../useDocusaurusContext';
|
||||
|
||||
// This test currently isn't quite useful because the @generated aliases point
|
||||
// to the empty modules. Maybe we can point that to fixtures in the future.
|
||||
describe('DocusaurusContextProvider', () => {
|
||||
const {result, hydrate} = renderHook(() => useDocusaurusContext(), {
|
||||
wrapper: ({children}) => (
|
||||
<DocusaurusContextProvider>{children}</DocusaurusContextProvider>
|
||||
),
|
||||
});
|
||||
const value = result.current;
|
||||
it('returns right value', () => {
|
||||
expect(value).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"codeTranslations": Object {},
|
||||
"globalData": Object {},
|
||||
"i18n": Object {},
|
||||
"siteConfig": Object {},
|
||||
"siteMetadata": Object {},
|
||||
}
|
||||
`);
|
||||
});
|
||||
it('has reference-equal value on hydration', () => {
|
||||
hydrate();
|
||||
expect(result.current).toBe(value);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,122 @@
|
|||
/**
|
||||
* 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 React from 'react';
|
||||
import {renderHook} from '@testing-library/react-hooks';
|
||||
import useGlobalData, {
|
||||
useAllPluginInstancesData,
|
||||
usePluginData,
|
||||
} from '../useGlobalData';
|
||||
import {Context} from '../docusaurusContext';
|
||||
|
||||
describe('useGlobalData', () => {
|
||||
it('returns global data from context', () => {
|
||||
expect(
|
||||
renderHook(() => useGlobalData(), {
|
||||
wrapper: ({children}) => (
|
||||
// eslint-disable-next-line react/jsx-no-constructed-context-values
|
||||
<Context.Provider value={{globalData: {foo: 'bar'}}}>
|
||||
{children}
|
||||
</Context.Provider>
|
||||
),
|
||||
}).result.current,
|
||||
).toEqual({foo: 'bar'});
|
||||
});
|
||||
|
||||
it('throws when global data not found', () => {
|
||||
// Can it actually happen?
|
||||
expect(
|
||||
() =>
|
||||
renderHook(() => useGlobalData(), {
|
||||
wrapper: ({children}) => (
|
||||
// eslint-disable-next-line react/jsx-no-constructed-context-values
|
||||
<Context.Provider value={{}}>{children}</Context.Provider>
|
||||
),
|
||||
}).result.current,
|
||||
).toThrowErrorMatchingInlineSnapshot(`"Docusaurus global data not found."`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('useAllPluginInstancesData', () => {
|
||||
it('returns plugin data namespace', () => {
|
||||
expect(
|
||||
renderHook(() => useAllPluginInstancesData('foo'), {
|
||||
wrapper: ({children}) => (
|
||||
<Context.Provider
|
||||
// eslint-disable-next-line react/jsx-no-constructed-context-values
|
||||
value={{globalData: {foo: {default: 'default', bar: 'bar'}}}}>
|
||||
{children}
|
||||
</Context.Provider>
|
||||
),
|
||||
}).result.current,
|
||||
).toEqual({default: 'default', bar: 'bar'});
|
||||
});
|
||||
|
||||
it('throws when plugin data not found', () => {
|
||||
expect(
|
||||
() =>
|
||||
renderHook(() => useAllPluginInstancesData('bar'), {
|
||||
wrapper: ({children}) => (
|
||||
<Context.Provider
|
||||
// eslint-disable-next-line react/jsx-no-constructed-context-values
|
||||
value={{globalData: {foo: {default: 'default', bar: 'bar'}}}}>
|
||||
{children}
|
||||
</Context.Provider>
|
||||
),
|
||||
}).result.current,
|
||||
).toThrowErrorMatchingInlineSnapshot(
|
||||
`"Docusaurus plugin global data not found for \\"bar\\" plugin."`,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('usePluginData', () => {
|
||||
it('returns plugin instance data', () => {
|
||||
expect(
|
||||
renderHook(() => usePluginData('foo', 'bar'), {
|
||||
wrapper: ({children}) => (
|
||||
<Context.Provider
|
||||
// eslint-disable-next-line react/jsx-no-constructed-context-values
|
||||
value={{globalData: {foo: {default: 'default', bar: 'bar'}}}}>
|
||||
{children}
|
||||
</Context.Provider>
|
||||
),
|
||||
}).result.current,
|
||||
).toBe('bar');
|
||||
});
|
||||
|
||||
it('defaults to default ID', () => {
|
||||
expect(
|
||||
renderHook(() => usePluginData('foo'), {
|
||||
wrapper: ({children}) => (
|
||||
<Context.Provider
|
||||
// eslint-disable-next-line react/jsx-no-constructed-context-values
|
||||
value={{globalData: {foo: {default: 'default', bar: 'bar'}}}}>
|
||||
{children}
|
||||
</Context.Provider>
|
||||
),
|
||||
}).result.current,
|
||||
).toBe('default');
|
||||
});
|
||||
|
||||
it('throws when plugin instance data not found', () => {
|
||||
expect(
|
||||
() =>
|
||||
renderHook(() => usePluginData('foo', 'baz'), {
|
||||
wrapper: ({children}) => (
|
||||
<Context.Provider
|
||||
// eslint-disable-next-line react/jsx-no-constructed-context-values
|
||||
value={{globalData: {foo: {default: 'default', bar: 'bar'}}}}>
|
||||
{children}
|
||||
</Context.Provider>
|
||||
),
|
||||
}).result.current,
|
||||
).toThrowErrorMatchingInlineSnapshot(
|
||||
`"Docusaurus plugin global data not found for \\"foo\\" plugin with id \\"baz\\"."`,
|
||||
);
|
||||
});
|
||||
});
|
|
@ -18,12 +18,8 @@ export default function normalizeLocation<T extends Location>(location: T): T {
|
|||
};
|
||||
}
|
||||
|
||||
let pathname = location.pathname || '/';
|
||||
pathname = pathname.trim().replace(/\/index\.html$/, '');
|
||||
|
||||
if (pathname === '') {
|
||||
pathname = '/';
|
||||
}
|
||||
const pathname =
|
||||
location.pathname.trim().replace(/\/index\.html$/, '') || '/';
|
||||
|
||||
pathnames[location.pathname] = pathname;
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ export default async function loadConfig(
|
|||
| (() => Promise<Partial<DocusaurusConfig>>);
|
||||
|
||||
const loadedConfig =
|
||||
importedConfig instanceof Function
|
||||
typeof importedConfig === 'function'
|
||||
? await importedConfig()
|
||||
: await importedConfig;
|
||||
|
||||
|
|
|
@ -464,7 +464,7 @@ ${Object.entries(registry)
|
|||
return props;
|
||||
}
|
||||
|
||||
// We want all @docusaurus/* packages to have the exact same version!
|
||||
// We want all @docusaurus/* packages to have the exact same version!
|
||||
// See https://github.com/facebook/docusaurus/issues/3371
|
||||
// See https://github.com/facebook/docusaurus/pull/3386
|
||||
function checkDocusaurusPackagesVersion(siteMetadata: DocusaurusSiteMetadata) {
|
||||
|
|
24
packages/docusaurus/src/webpack/__tests__/__fixtures__/host.crt
generated
Normal file
24
packages/docusaurus/src/webpack/__tests__/__fixtures__/host.crt
generated
Normal file
|
@ -0,0 +1,24 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIID8zCCAtugAwIBAgIUK1U7Oje+GjLlzxNryMDUT72qJZ0wDQYJKoZIhvcNAQEL
|
||||
BQAwgYgxCzAJBgNVBAYTAkNOMREwDwYDVQQIDAhTaGFuZ2hhaTERMA8GA1UEBwwI
|
||||
U2hhbmdoYWkxGDAWBgNVBAoMD0NvbXB1dGVyaXphdGlvbjESMBAGA1UEAwwJSm9z
|
||||
aC1DZW5hMSUwIwYJKoZIhvcNAQkBFhZzaWRhY2hlbjIwMDNAZ21haWwuY29tMB4X
|
||||
DTIyMDMxMjE0MzI0N1oXDTIzMDMxMjE0MzI0N1owgYgxCzAJBgNVBAYTAkNOMREw
|
||||
DwYDVQQIDAhTaGFuZ2hhaTERMA8GA1UEBwwIU2hhbmdoYWkxGDAWBgNVBAoMD0Nv
|
||||
bXB1dGVyaXphdGlvbjESMBAGA1UEAwwJSm9zaC1DZW5hMSUwIwYJKoZIhvcNAQkB
|
||||
FhZzaWRhY2hlbjIwMDNAZ21haWwuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
|
||||
MIIBCgKCAQEA7Cq2QW6rcZAm6MMo97aqkFi9dkXx97fW6vPEt2bgS9O6E+M/wXBI
|
||||
q1Dh3ud8sGP+CiEWa+7uIEwX9pRGyQo0Lkr7qZWSbsDh+RmdkiKUCiIUUTBopBjM
|
||||
jo7XF9KBM609GYoGlKYxv4adPbOMJcK/9VdJPz3NprIA1PHEqInJNnuKMMjBMhNu
|
||||
1MZ7JwING/LYBOJ/Mve08XKAcyDdWBVPe2TOfcKhEmtBTKhnOuUicuAdVtDkN34Z
|
||||
e4ZlifLo7wlQU7NNh7YDOYZz3JXB5QotuqtWkUgfpMSCWG90p4P4LExLzS+2sb7O
|
||||
C/jO0qYcKjaKAKjrA9IIyClF6VP1yFRZywIDAQABo1MwUTAdBgNVHQ4EFgQUNy2X
|
||||
+cLPh17QdR6raPKeoKLIm2QwHwYDVR0jBBgwFoAUNy2X+cLPh17QdR6raPKeoKLI
|
||||
m2QwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAzvyP1QxKR8Ik
|
||||
k7v3dzRl1gKdu6BtRL1zoFXeOjFOCVX9ORxcpCJItuTM4kEbJLhC0uFxn+zQ/Urs
|
||||
JAc56gic4fCIcxlTNPr4VtAEUQKhfGG7XTRs8Cl2Rm7E7FwNiGjdLuiPI+G+ZZbl
|
||||
TYmB5ILGzvI8LAOii17s5wFX84PehZ9gYgcgEvVBaU7lWF3WakR53Msf2bHkjk/r
|
||||
NfaINeBltOwijhzb8pWf0XG2z4olJjg1qTOgr1gNseyTwMAFwFmeXQAoidoZfKya
|
||||
DD+hY1/IgiUXi2pdmO+sMHtRBG5JdOi2cjSOcTx1xkWyb60PpW4uxKhduQPAiZRO
|
||||
266P7J962Q==
|
||||
-----END CERTIFICATE-----
|
27
packages/docusaurus/src/webpack/__tests__/__fixtures__/host.key
generated
Normal file
27
packages/docusaurus/src/webpack/__tests__/__fixtures__/host.key
generated
Normal file
|
@ -0,0 +1,27 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpAIBAAKCAQEA7Cq2QW6rcZAm6MMo97aqkFi9dkXx97fW6vPEt2bgS9O6E+M/
|
||||
wXBIq1Dh3ud8sGP+CiEWa+7uIEwX9pRGyQo0Lkr7qZWSbsDh+RmdkiKUCiIUUTBo
|
||||
pBjMjo7XF9KBM609GYoGlKYxv4adPbOMJcK/9VdJPz3NprIA1PHEqInJNnuKMMjB
|
||||
MhNu1MZ7JwING/LYBOJ/Mve08XKAcyDdWBVPe2TOfcKhEmtBTKhnOuUicuAdVtDk
|
||||
N34Ze4ZlifLo7wlQU7NNh7YDOYZz3JXB5QotuqtWkUgfpMSCWG90p4P4LExLzS+2
|
||||
sb7OC/jO0qYcKjaKAKjrA9IIyClF6VP1yFRZywIDAQABAoIBAHiHR+LW/2qC3ki2
|
||||
qWba8+udTnxZMCdzzJy2cjQDrf8k/Hd/6B7qFjxQmCXx0GIZdiJnRpEpLKCRFT3D
|
||||
6Ohba8wgepXO/x/FEs7VsuRM/264e9P/t7ff7C3pWn8O8N+Vz3QETF17ADK2GfPO
|
||||
eX0gCmXE+V3sRdOITwJerTYys904bo5CQsDQQENpcuYbZU2IYt9dw9XrTexaFwP1
|
||||
3ssOXCwpaW4kS95a6WQlwCqNTq49zqf3VGA3QG3JEdPPWhG+jEG2L4RxSosvo4wt
|
||||
MYFqeXcS5sz7WOH1gtleGL2i6WKYuLl7Bo/CLokn1tgrXjGvNpeBFvZucC+L246f
|
||||
e7iG+gkCgYEA+CcISFav/uwKNv3Sdp87kVpBAno8cZTiYvB15zAGaXuLyI/OuJNh
|
||||
lcJBhtZSN94T/mgj+gXDafjmRr4i7Q4Pu+KG95JTk1FfWv/974NxbRNrrp+4PFKb
|
||||
wxcM1cHuqq88mUPUX+k0eKPqDcuY6vHBPAV4ji1Wl+VXpREDvhKgAEUCgYEA86Kl
|
||||
xnOf3TWbEaQRJx2mMnRYLyrEEPqEMgHWlzXdWl2E9LJDGGmOEbZLv6uNcx1uWJVP
|
||||
AaoitmQNTl+rSsJY0TwqooX5zvT8po9MXUt8FvButJyYUOJZFTuLtLxFJqAzFipz
|
||||
SaiYTrEBC76uqe/87AVm0wCdJN4ajcptyibaus8CgYEAnXSm3L+kjKxZDuufT4VZ
|
||||
1rDd7ySAldFSlFTfewIOD4BFAc297YAWu1+3FEeJg8l2BkcuDMb7Z5J3Cww6PRBf
|
||||
C2iBGzXNsfw/9Q3ZotBUeFGKUhMmY6BHFVLa4gdb2RG38cgISZM/qAzZxkcZkHo1
|
||||
klAmXpCGEXuEUUiqh0BqJcECgYEAv42Gt0QbUeoetL0BO3blP9AXsWX3Z73/h+3I
|
||||
EXUpRy42JcmuVRhQuf5RCi7QdMyUAJPL3WwuBKcfixpO6+VnvYKHpuadZSlbJ32N
|
||||
NeDufH6nG9vvKdD852O80OohmF/mKqxPnn8u2Nf0EY7ndvcYLV2F3aoi42S5Dfg1
|
||||
X/YyjSMCgYAg2fEisapxje98KZ4TPvOffJRF5PRG4H6UBQvxaWw9oUjVkGM6t10U
|
||||
D6uOCYPkb+l3wBFTNAfScr22EnpW33Q5JOAfHBeE1oEoWGdMgp1C1V9ZQTIkjXyj
|
||||
YE+lrsTFVoyY+dnLcZ4U7syVkzINk10GaAKjGXD0gtrqC+cQy8z1XQ==
|
||||
-----END RSA PRIVATE KEY-----
|
1
packages/docusaurus/src/webpack/__tests__/__fixtures__/invalid.crt
generated
Normal file
1
packages/docusaurus/src/webpack/__tests__/__fixtures__/invalid.crt
generated
Normal file
|
@ -0,0 +1 @@
|
|||
Foo
|
1
packages/docusaurus/src/webpack/__tests__/__fixtures__/invalid.key
generated
Normal file
1
packages/docusaurus/src/webpack/__tests__/__fixtures__/invalid.key
generated
Normal file
|
@ -0,0 +1 @@
|
|||
Foo
|
|
@ -13,7 +13,7 @@ import loadSetup from '../../server/__tests__/testUtils';
|
|||
|
||||
describe('webpack production config', () => {
|
||||
it('simple', async () => {
|
||||
jest.spyOn(console, 'log').mockImplementation();
|
||||
jest.spyOn(console, 'log').mockImplementation(() => {});
|
||||
const props = await loadSetup('simple');
|
||||
const config = await createServerConfig({props});
|
||||
const errors = webpack.validate(config);
|
||||
|
@ -21,7 +21,7 @@ describe('webpack production config', () => {
|
|||
});
|
||||
|
||||
it('custom', async () => {
|
||||
jest.spyOn(console, 'log').mockImplementation();
|
||||
jest.spyOn(console, 'log').mockImplementation(() => {});
|
||||
const props = await loadSetup('custom');
|
||||
const config = await createServerConfig({props});
|
||||
const errors = webpack.validate(config);
|
||||
|
|
|
@ -12,6 +12,7 @@ import {
|
|||
getCustomizableJSLoader,
|
||||
applyConfigureWebpack,
|
||||
applyConfigurePostCss,
|
||||
getHttpsConfig,
|
||||
} from '../utils';
|
||||
import type {
|
||||
ConfigureWebpackFn,
|
||||
|
@ -297,3 +298,65 @@ describe('extending PostCSS', () => {
|
|||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getHttpsConfig', () => {
|
||||
const originalEnv = process.env;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
process.env = {...originalEnv};
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
process.env = originalEnv;
|
||||
});
|
||||
|
||||
it('returns true for HTTPS not env', async () => {
|
||||
await expect(getHttpsConfig()).resolves.toBe(false);
|
||||
});
|
||||
|
||||
it('returns true for HTTPS in env', async () => {
|
||||
process.env.HTTPS = 'true';
|
||||
await expect(getHttpsConfig()).resolves.toBe(true);
|
||||
});
|
||||
|
||||
it('returns custom certs if they are in env', async () => {
|
||||
process.env.HTTPS = 'true';
|
||||
process.env.SSL_CRT_FILE = path.join(__dirname, '__fixtures__/host.crt');
|
||||
process.env.SSL_KEY_FILE = path.join(__dirname, '__fixtures__/host.key');
|
||||
await expect(getHttpsConfig()).resolves.toEqual({
|
||||
key: expect.any(Buffer),
|
||||
cert: expect.any(Buffer),
|
||||
});
|
||||
});
|
||||
|
||||
it("throws if file doesn't exist", async () => {
|
||||
process.env.HTTPS = 'true';
|
||||
process.env.SSL_CRT_FILE = path.join(
|
||||
__dirname,
|
||||
'__fixtures__/nonexistent.crt',
|
||||
);
|
||||
process.env.SSL_KEY_FILE = path.join(__dirname, '__fixtures__/host.key');
|
||||
await expect(getHttpsConfig()).rejects.toThrowErrorMatchingInlineSnapshot(
|
||||
`"You specified SSL_CRT_FILE in your env, but the file \\"<PROJECT_ROOT>/packages/docusaurus/src/webpack/__tests__/__fixtures__/nonexistent.crt\\" can't be found."`,
|
||||
);
|
||||
});
|
||||
|
||||
it('throws for invalid key', async () => {
|
||||
process.env.HTTPS = 'true';
|
||||
process.env.SSL_CRT_FILE = path.join(__dirname, '__fixtures__/host.crt');
|
||||
process.env.SSL_KEY_FILE = path.join(__dirname, '__fixtures__/invalid.key');
|
||||
await expect(getHttpsConfig()).rejects.toThrowError(
|
||||
/The certificate key .*[/\\]__fixtures__[/\\]invalid\.key is invalid/,
|
||||
);
|
||||
});
|
||||
|
||||
it('throws for invalid cert', async () => {
|
||||
process.env.HTTPS = 'true';
|
||||
process.env.SSL_CRT_FILE = path.join(__dirname, '__fixtures__/invalid.crt');
|
||||
process.env.SSL_KEY_FILE = path.join(__dirname, '__fixtures__/host.key');
|
||||
await expect(getHttpsConfig()).rejects.toThrowError(
|
||||
/The certificate .*[/\\]__fixtures__[/\\]invalid\.crt is invalid/,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -5,11 +5,6 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
// Inspired by https://github.com/you-dont-need/You-Dont-Need-Lodash-Underscore#_difference
|
||||
export function difference<T>(...arrays: T[][]): T[] {
|
||||
return arrays.reduce((a, b) => a.filter((c) => !b.includes(c)));
|
||||
}
|
||||
|
||||
// Inspired by https://github.com/you-dont-need/You-Dont-Need-Lodash-Underscore#_sortby-and-_orderby
|
||||
export function sortBy<T>(
|
||||
array: T[],
|
||||
|
|
Loading…
Add table
Reference in a new issue