mirror of
https://github.com/facebook/docusaurus.git
synced 2025-05-10 15:47:23 +02:00
test: improve test coverage; multiple internal refactors (#6912)
This commit is contained in:
parent
12a7305238
commit
ad88f5cc87
78 changed files with 1613 additions and 1149 deletions
|
@ -10,7 +10,8 @@ import {
|
|||
parseMarkdownContentTitle,
|
||||
parseMarkdownString,
|
||||
parseMarkdownHeadingId,
|
||||
} from '../markdownParser';
|
||||
writeMarkdownHeadingId,
|
||||
} from '../markdownUtils';
|
||||
import dedent from 'dedent';
|
||||
|
||||
describe('createExcerpt', () => {
|
||||
|
@ -801,3 +802,108 @@ describe('parseMarkdownHeadingId', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('writeMarkdownHeadingId', () => {
|
||||
it('works for simple level-2 heading', () => {
|
||||
expect(writeMarkdownHeadingId('## ABC')).toBe('## ABC {#abc}');
|
||||
});
|
||||
|
||||
it('works for simple level-3 heading', () => {
|
||||
expect(writeMarkdownHeadingId('### ABC')).toBe('### ABC {#abc}');
|
||||
});
|
||||
|
||||
it('works for simple level-4 heading', () => {
|
||||
expect(writeMarkdownHeadingId('#### ABC')).toBe('#### ABC {#abc}');
|
||||
});
|
||||
|
||||
it('unwraps markdown links', () => {
|
||||
const input = `## hello [facebook](https://facebook.com) [crowdin](https://crowdin.com/translate/docusaurus-v2/126/en-fr?filter=basic&value=0)`;
|
||||
expect(writeMarkdownHeadingId(input)).toBe(
|
||||
`${input} {#hello-facebook-crowdin}`,
|
||||
);
|
||||
});
|
||||
|
||||
it('can slugify complex headings', () => {
|
||||
const input = '## abc [Hello] How are you %Sébastien_-_$)( ## -56756';
|
||||
expect(writeMarkdownHeadingId(input)).toBe(
|
||||
// cSpell:ignore ébastien
|
||||
`${input} {#abc-hello-how-are-you-sébastien_-_---56756}`,
|
||||
);
|
||||
});
|
||||
|
||||
it('does not duplicate duplicate id', () => {
|
||||
expect(writeMarkdownHeadingId('## hello world {#hello-world}')).toBe(
|
||||
'## hello world {#hello-world}',
|
||||
);
|
||||
});
|
||||
|
||||
it('respects existing heading', () => {
|
||||
expect(writeMarkdownHeadingId('## New heading {#old-heading}')).toBe(
|
||||
'## New heading {#old-heading}',
|
||||
);
|
||||
});
|
||||
|
||||
it('overwrites heading ID when asked to', () => {
|
||||
expect(
|
||||
writeMarkdownHeadingId('## New heading {#old-heading}', {
|
||||
overwrite: true,
|
||||
}),
|
||||
).toBe('## New heading {#new-heading}');
|
||||
});
|
||||
|
||||
it('maintains casing when asked to', () => {
|
||||
expect(
|
||||
writeMarkdownHeadingId('## getDataFromAPI()', {
|
||||
maintainCase: true,
|
||||
}),
|
||||
).toBe('## getDataFromAPI() {#getDataFromAPI}');
|
||||
});
|
||||
|
||||
it('transform the headings', () => {
|
||||
const input = `
|
||||
|
||||
# Ignored title
|
||||
|
||||
## abc
|
||||
|
||||
### Hello world
|
||||
|
||||
\`\`\`
|
||||
# Heading in code block
|
||||
\`\`\`
|
||||
|
||||
## Hello world
|
||||
|
||||
\`\`\`
|
||||
# Heading in escaped code block
|
||||
\`\`\`
|
||||
|
||||
### abc {#abc}
|
||||
|
||||
`;
|
||||
|
||||
const expected = `
|
||||
|
||||
# Ignored title
|
||||
|
||||
## abc {#abc-1}
|
||||
|
||||
### Hello world {#hello-world}
|
||||
|
||||
\`\`\`
|
||||
# Heading in code block
|
||||
\`\`\`
|
||||
|
||||
## Hello world {#hello-world-1}
|
||||
|
||||
\`\`\`
|
||||
# Heading in escaped code block
|
||||
\`\`\`
|
||||
|
||||
### abc {#abc}
|
||||
|
||||
`;
|
||||
|
||||
expect(writeMarkdownHeadingId(input)).toEqual(expected);
|
||||
});
|
||||
});
|
|
@ -15,11 +15,18 @@ import {
|
|||
removeTrailingSlash,
|
||||
resolvePathname,
|
||||
encodePath,
|
||||
buildSshUrl,
|
||||
buildHttpsUrl,
|
||||
hasSSHProtocol,
|
||||
} from '../urlUtils';
|
||||
|
||||
describe('normalizeUrl', () => {
|
||||
it('normalizes urls correctly', () => {
|
||||
const asserts = [
|
||||
{
|
||||
input: [],
|
||||
output: '',
|
||||
},
|
||||
{
|
||||
input: ['/', ''],
|
||||
output: '/',
|
||||
|
@ -248,3 +255,60 @@ describe('encodePath', () => {
|
|||
expect(encodePath('a/你好/')).toBe('a/%E4%BD%A0%E5%A5%BD/');
|
||||
});
|
||||
});
|
||||
|
||||
describe('buildSshUrl', () => {
|
||||
it('builds a normal ssh url', () => {
|
||||
const url = buildSshUrl('github.com', 'facebook', 'docusaurus');
|
||||
expect(url).toBe('git@github.com:facebook/docusaurus.git');
|
||||
});
|
||||
it('builds a ssh url with port', () => {
|
||||
const url = buildSshUrl('github.com', 'facebook', 'docusaurus', '422');
|
||||
expect(url).toBe('ssh://git@github.com:422/facebook/docusaurus.git');
|
||||
});
|
||||
});
|
||||
|
||||
describe('buildHttpsUrl', () => {
|
||||
it('builds a normal http url', () => {
|
||||
const url = buildHttpsUrl(
|
||||
'user:pass',
|
||||
'github.com',
|
||||
'facebook',
|
||||
'docusaurus',
|
||||
);
|
||||
expect(url).toBe('https://user:pass@github.com/facebook/docusaurus.git');
|
||||
});
|
||||
it('builds a normal http url with port', () => {
|
||||
const url = buildHttpsUrl(
|
||||
'user:pass',
|
||||
'github.com',
|
||||
'facebook',
|
||||
'docusaurus',
|
||||
'5433',
|
||||
);
|
||||
expect(url).toBe(
|
||||
'https://user:pass@github.com:5433/facebook/docusaurus.git',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('hasSSHProtocol', () => {
|
||||
it('recognizes explicit SSH protocol', () => {
|
||||
const url = 'ssh://git@github.com:422/facebook/docusaurus.git';
|
||||
expect(hasSSHProtocol(url)).toBe(true);
|
||||
});
|
||||
|
||||
it('recognizes implied SSH protocol', () => {
|
||||
const url = 'git@github.com:facebook/docusaurus.git';
|
||||
expect(hasSSHProtocol(url)).toBe(true);
|
||||
});
|
||||
|
||||
it('does not recognize HTTPS with credentials', () => {
|
||||
const url = 'https://user:pass@github.com/facebook/docusaurus.git';
|
||||
expect(hasSSHProtocol(url)).toBe(false);
|
||||
});
|
||||
|
||||
it('does not recognize plain HTTPS URL', () => {
|
||||
const url = 'https://github.com:5433/facebook/docusaurus.git';
|
||||
expect(hasSSHProtocol(url)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -45,6 +45,9 @@ export {
|
|||
addLeadingSlash,
|
||||
addTrailingSlash,
|
||||
removeTrailingSlash,
|
||||
hasSSHProtocol,
|
||||
buildHttpsUrl,
|
||||
buildSshUrl,
|
||||
} from './urlUtils';
|
||||
export {
|
||||
type Tag,
|
||||
|
@ -60,7 +63,9 @@ export {
|
|||
parseFrontMatter,
|
||||
parseMarkdownContentTitle,
|
||||
parseMarkdownString,
|
||||
} from './markdownParser';
|
||||
writeMarkdownHeadingId,
|
||||
type WriteHeadingIDOptions,
|
||||
} from './markdownUtils';
|
||||
export {
|
||||
type ContentPaths,
|
||||
type BrokenMarkdownLink,
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
import logger from '@docusaurus/logger';
|
||||
import matter from 'gray-matter';
|
||||
import {createSlugger, type Slugger} from './slugger';
|
||||
|
||||
// Input: ## Some heading {#some-heading}
|
||||
// Output: {text: "## Some heading", id: "some-heading"}
|
||||
|
@ -205,3 +206,72 @@ This can happen if you use special characters in front matter values (try using
|
|||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
function unwrapMarkdownLinks(line: string): string {
|
||||
return line.replace(/\[(?<alt>[^\]]+)\]\([^)]+\)/g, (match, p1) => p1);
|
||||
}
|
||||
|
||||
function addHeadingId(
|
||||
line: string,
|
||||
slugger: Slugger,
|
||||
maintainCase: boolean,
|
||||
): string {
|
||||
let headingLevel = 0;
|
||||
while (line.charAt(headingLevel) === '#') {
|
||||
headingLevel += 1;
|
||||
}
|
||||
|
||||
const headingText = line.slice(headingLevel).trimEnd();
|
||||
const headingHashes = line.slice(0, headingLevel);
|
||||
const slug = slugger.slug(unwrapMarkdownLinks(headingText).trim(), {
|
||||
maintainCase,
|
||||
});
|
||||
|
||||
return `${headingHashes}${headingText} {#${slug}}`;
|
||||
}
|
||||
|
||||
export type WriteHeadingIDOptions = {
|
||||
maintainCase?: boolean;
|
||||
overwrite?: boolean;
|
||||
};
|
||||
|
||||
export function writeMarkdownHeadingId(
|
||||
content: string,
|
||||
options: WriteHeadingIDOptions = {maintainCase: false, overwrite: false},
|
||||
): string {
|
||||
const {maintainCase = false, overwrite = false} = options;
|
||||
const lines = content.split('\n');
|
||||
const slugger = createSlugger();
|
||||
|
||||
// If we can't overwrite existing slugs, make sure other headings don't
|
||||
// generate colliding slugs by first marking these slugs as occupied
|
||||
if (!overwrite) {
|
||||
lines.forEach((line) => {
|
||||
const parsedHeading = parseMarkdownHeadingId(line);
|
||||
if (parsedHeading.id) {
|
||||
slugger.slug(parsedHeading.id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let inCode = false;
|
||||
return lines
|
||||
.map((line) => {
|
||||
if (line.startsWith('```')) {
|
||||
inCode = !inCode;
|
||||
return line;
|
||||
}
|
||||
// Ignore h1 headings, as we don't create anchor links for those
|
||||
if (inCode || !line.startsWith('##')) {
|
||||
return line;
|
||||
}
|
||||
const parsedHeading = parseMarkdownHeadingId(line);
|
||||
|
||||
// Do not process if id is already there
|
||||
if (parsedHeading.id && !overwrite) {
|
||||
return line;
|
||||
}
|
||||
return addHeadingId(parsedHeading.text, slugger, maintainCase);
|
||||
})
|
||||
.join('\n');
|
||||
}
|
|
@ -154,3 +154,40 @@ export function addTrailingSlash(str: string): string {
|
|||
export function removeTrailingSlash(str: string): string {
|
||||
return removeSuffix(str, '/');
|
||||
}
|
||||
|
||||
export function buildSshUrl(
|
||||
githubHost: string,
|
||||
organizationName: string,
|
||||
projectName: string,
|
||||
githubPort?: string,
|
||||
): string {
|
||||
if (githubPort) {
|
||||
return `ssh://git@${githubHost}:${githubPort}/${organizationName}/${projectName}.git`;
|
||||
}
|
||||
return `git@${githubHost}:${organizationName}/${projectName}.git`;
|
||||
}
|
||||
|
||||
export function buildHttpsUrl(
|
||||
gitCredentials: string,
|
||||
githubHost: string,
|
||||
organizationName: string,
|
||||
projectName: string,
|
||||
githubPort?: string,
|
||||
): string {
|
||||
if (githubPort) {
|
||||
return `https://${gitCredentials}@${githubHost}:${githubPort}/${organizationName}/${projectName}.git`;
|
||||
}
|
||||
return `https://${gitCredentials}@${githubHost}/${organizationName}/${projectName}.git`;
|
||||
}
|
||||
|
||||
export function hasSSHProtocol(sourceRepoUrl: string): boolean {
|
||||
try {
|
||||
if (new URL(sourceRepoUrl).protocol === 'ssh:') {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch {
|
||||
// Fails when there isn't a protocol
|
||||
return /^(?:[\w-]+@)?[\w.-]+:[\w./-]+/.test(sourceRepoUrl); // git@github.com:facebook/docusaurus.git
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue