feat(docs, blog): add support for tags.yml, predefined list of tags (#10137)

Co-authored-by: Sébastien Lorber <slorber@users.noreply.github.com>
Co-authored-by: OzakIOne <OzakIOne@users.noreply.github.com>
Co-authored-by: sebastien <lorber.sebastien@gmail.com>
Co-authored-by: slorber <slorber@users.noreply.github.com>
This commit is contained in:
ozaki 2024-05-31 17:32:08 +02:00 committed by GitHub
parent 1049294ba6
commit 0eb7b64aac
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
63 changed files with 2597 additions and 722 deletions

View file

@ -0,0 +1,538 @@
/**
* 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 * as path from 'path';
import * as fs from 'fs-extra';
import * as tmp from 'tmp-promise';
import * as YAML from 'js-yaml';
import {
ensureUniquePermalinks,
getTagsFile,
getTagsFilePathsToWatch,
normalizeTagsFile,
} from '../tagsFile';
import type {TagsFile, TagsFileInput} from '@docusaurus/utils';
describe('ensureUniquePermalinks', () => {
it('throw when one duplicate permalink found', () => {
const definedTags: TagsFile = {
open: {
label: 'Open Source',
permalink: '/custom-open-source',
description: 'Learn about the open source',
},
closed: {
label: 'Closed Source',
permalink: '/custom-open-source',
description: 'Learn about the closed source',
},
};
expect(() => ensureUniquePermalinks(definedTags))
.toThrowErrorMatchingInlineSnapshot(`
"Duplicate permalinks found in tags file:
- /custom-open-source"
`);
});
it('throw when multiple duplicate permalink found', () => {
const definedTags: TagsFile = {
open: {
label: 'Open Source',
permalink: '/custom-open-source',
description: 'Learn about the open source',
},
closed: {
label: 'Closed Source',
permalink: '/custom-open-source',
description: 'Learn about the closed source',
},
hello: {
label: 'Hello',
permalink: '/hello',
description: 'Learn about the hello',
},
world: {
label: 'Hello',
permalink: '/hello',
description: 'Learn about the world',
},
};
expect(() => ensureUniquePermalinks(definedTags))
.toThrowErrorMatchingInlineSnapshot(`
"Duplicate permalinks found in tags file:
- /custom-open-source
- /hello"
`);
});
it('do not throw when no duplicate permalink found', () => {
const definedTags: TagsFile = {
open: {
label: 'Open Source',
permalink: '/open-source',
description: 'Learn about the open source',
},
closed: {
label: 'Closed Source',
permalink: '/closed-source',
description: 'Learn about the closed source',
},
};
expect(() => ensureUniquePermalinks(definedTags)).not.toThrow();
});
});
describe('normalizeTagsFile', () => {
it('normalize null tag', () => {
const input: TagsFileInput = {
'kebab case test': null,
};
const expectedOutput: TagsFile = {
'kebab case test': {
description: undefined,
label: 'Kebab case test',
permalink: '/kebab-case-test',
},
};
expect(normalizeTagsFile(input)).toEqual(expectedOutput);
});
it('normalize partial tag with label', () => {
const input: TagsFileInput = {
world: {label: 'WORLD'},
};
const expectedOutput: TagsFile = {
world: {
description: undefined,
label: 'WORLD',
permalink: '/world',
},
};
expect(normalizeTagsFile(input)).toEqual(expectedOutput);
});
it('normalize partial tag with description', () => {
const input: TagsFileInput = {
world: {description: 'World description test'},
};
const expectedOutput: TagsFile = {
world: {
description: 'World description test',
label: 'World',
permalink: '/world',
},
};
expect(normalizeTagsFile(input)).toEqual(expectedOutput);
});
it('normalize partial tag with permalink', () => {
const input: TagsFileInput = {
world: {permalink: 'world'},
};
const expectedOutput: TagsFile = {
world: {
description: undefined,
label: 'World',
permalink: 'world',
},
};
expect(normalizeTagsFile(input)).toEqual(expectedOutput);
});
it('does not modify fully defined tags', () => {
const input: TagsFileInput = {
tag1: {
label: 'Custom Label',
description: 'Custom Description',
permalink: 'custom-permalink',
},
};
expect(normalizeTagsFile(input)).toEqual(input);
});
it('handle special characters in keys', () => {
const input: TagsFileInput = {
'special@char$!key': null,
};
const expectedOutput: TagsFile = {
'special@char$!key': {
description: undefined,
label: 'Special@char$!key',
permalink: '/special-char-key',
},
};
expect(normalizeTagsFile(input)).toEqual(expectedOutput);
});
it('handle special characters in keys with chinese characters', () => {
const input: TagsFileInput = {
特殊字符测试: null,
};
const expectedOutput: TagsFile = {
: {
description: undefined,
label: '特殊字符测试',
permalink: '/特殊字符测试',
},
};
expect(normalizeTagsFile(input)).toEqual(expectedOutput);
});
it('normalize test', () => {
const input: TagsFileInput = {
world: {permalink: 'aze'},
hello: {permalink: 'h e l l o'},
};
const expectedOutput = {
world: {
description: undefined,
label: 'World',
permalink: 'aze',
},
hello: {
description: undefined,
label: 'Hello',
permalink: 'h e l l o',
},
};
expect(normalizeTagsFile(input)).toEqual(expectedOutput);
});
});
describe('getTagsFilePathsToWatch', () => {
it('returns tags file paths - tags undefined', () => {
expect(
getTagsFilePathsToWatch({
tags: undefined,
contentPaths: {
contentPath: '/user/blog',
contentPathLocalized: '/i18n/blog',
},
}),
).toEqual(['/i18n/blog/tags.yml', '/user/blog/tags.yml']);
});
it('returns tags file paths - tags.yml', () => {
expect(
getTagsFilePathsToWatch({
tags: 'tags.yml',
contentPaths: {
contentPath: '/user/blog',
contentPathLocalized: '/i18n/blog',
},
}),
).toEqual(['/i18n/blog/tags.yml', '/user/blog/tags.yml']);
});
it('returns tags file paths - customTags.yml', () => {
expect(
getTagsFilePathsToWatch({
tags: 'customTags.yml',
contentPaths: {
contentPath: '/user/blog',
contentPathLocalized: '/i18n/blog',
},
}),
).toEqual(['/i18n/blog/customTags.yml', '/user/blog/customTags.yml']);
});
it('returns [] - tags: null', () => {
expect(
getTagsFilePathsToWatch({
tags: null,
contentPaths: {
contentPath: '/user/blog',
contentPathLocalized: '/i18n/blog',
},
}),
).toEqual([]);
});
it('returns [] - tags: false', () => {
expect(
getTagsFilePathsToWatch({
tags: false,
contentPaths: {
contentPath: '/user/blog',
contentPathLocalized: '/i18n/blog',
},
}),
).toEqual([]);
});
});
describe('getTagsFile', () => {
async function createTestTagsFile({
filePath,
tagsFileInput,
}: {
filePath: string;
tagsFileInput: TagsFileInput;
}): Promise<{dir: string}> {
async function createTmpDir() {
return (
await tmp.dir({
prefix: 'jest-createTmpSiteDir',
})
).path;
}
const contentPath = await createTmpDir();
const finalFilePath = path.join(contentPath, filePath);
const fileContent = YAML.dump(tagsFileInput);
await fs.writeFile(finalFilePath, fileContent);
return {dir: contentPath};
}
type Params = Parameters<typeof getTagsFile>[0];
it('reads tags file - regular', async () => {
const {dir} = await createTestTagsFile({
filePath: 'tags.yml',
tagsFileInput: {
tag1: {label: 'Tag1 Label'},
tag2: {description: 'Tag2 Description'},
tag3: {
label: 'Tag3 Label',
permalink: '/tag-3',
description: 'Tag3 Description',
},
},
});
const params: Params = {
contentPaths: {contentPath: dir, contentPathLocalized: dir},
tags: 'tags.yml',
};
await expect(getTagsFile(params)).resolves.toMatchInlineSnapshot(`
{
"tag1": {
"description": undefined,
"label": "Tag1 Label",
"permalink": "/tag-1",
},
"tag2": {
"description": "Tag2 Description",
"label": "Tag2",
"permalink": "/tag-2",
},
"tag3": {
"description": "Tag3 Description",
"label": "Tag3 Label",
"permalink": "/tag-3",
},
}
`);
});
it('reads tags file - only keys', async () => {
const {dir} = await createTestTagsFile({
filePath: 'tags.yml',
tagsFileInput: {
tagKey: null,
},
});
const params: Params = {
contentPaths: {contentPath: dir, contentPathLocalized: dir},
tags: 'tags.yml',
};
await expect(getTagsFile(params)).resolves.toMatchInlineSnapshot(`
{
"tagKey": {
"description": undefined,
"label": "Tagkey",
"permalink": "/tag-key",
},
}
`);
});
it('reads tags file - tags option undefined', async () => {
const {dir} = await createTestTagsFile({
filePath: 'tags.yml',
tagsFileInput: {
tag: {label: 'tag label'},
},
});
const params: Params = {
contentPaths: {contentPath: dir, contentPathLocalized: dir},
tags: undefined,
};
await expect(getTagsFile(params)).resolves.toMatchInlineSnapshot(`
{
"tag": {
"description": undefined,
"label": "tag label",
"permalink": "/tag",
},
}
`);
});
it('reads tags file - empty file', async () => {
const {dir} = await createTestTagsFile({
filePath: 'tags.yml',
tagsFileInput: {},
});
const params: Params = {
contentPaths: {contentPath: dir, contentPathLocalized: dir},
tags: undefined,
};
await expect(getTagsFile(params)).resolves.toEqual({});
});
it('reads tags file - prioritizes reading from localized content path', async () => {
const {dir} = await createTestTagsFile({
filePath: 'tags.yml',
tagsFileInput: {
tag: {label: 'tag label'},
},
});
const {dir: dirLocalized} = await createTestTagsFile({
filePath: 'tags.yml',
tagsFileInput: {
tag: {label: 'tag label (localized)'},
},
});
const params: Params = {
contentPaths: {contentPath: dir, contentPathLocalized: dirLocalized},
tags: undefined,
};
await expect(getTagsFile(params)).resolves.toMatchInlineSnapshot(`
{
"tag": {
"description": undefined,
"label": "tag label (localized)",
"permalink": "/tag",
},
}
`);
});
it('reads tags file - custom tags file path', async () => {
const {dir} = await createTestTagsFile({
filePath: 'custom-tags-path.yml',
tagsFileInput: {
tag: {label: 'tag label'},
},
});
const params: Params = {
contentPaths: {contentPath: dir, contentPathLocalized: dir},
tags: 'custom-tags-path.yml',
};
await expect(getTagsFile(params)).resolves.toMatchInlineSnapshot(`
{
"tag": {
"description": undefined,
"label": "tag label",
"permalink": "/tag",
},
}
`);
});
it('throws if duplicate permalink', async () => {
const {dir} = await createTestTagsFile({
filePath: 'tags.yml',
tagsFileInput: {
tag1: {permalink: '/duplicate'},
tag2: {permalink: '/duplicate'},
},
});
const params: Params = {
contentPaths: {contentPath: dir, contentPathLocalized: dir},
tags: undefined,
};
await expect(getTagsFile(params)).rejects.toMatchInlineSnapshot(`
[Error: Duplicate permalinks found in tags file:
- /duplicate]
`);
});
it('throws if custom tags file path does not exist', async () => {
const params: Params = {
contentPaths: {contentPath: 'any', contentPathLocalized: 'localizedAny'},
tags: 'custom-tags-path.yml',
};
await expect(getTagsFile(params)).rejects
.toThrowErrorMatchingInlineSnapshot(`
"No tags file 'custom-tags-path.yml' could be found in any of those directories:
- localizedAny
- any"
`);
});
it('does not read tags file - tags option null/false', async () => {
const {dir} = await createTestTagsFile({
filePath: 'tags.yml',
tagsFileInput: {
tag: {label: 'tag label'},
},
});
await expect(
getTagsFile({
contentPaths: {contentPath: dir, contentPathLocalized: dir},
tags: null,
}),
).resolves.toBeNull();
await expect(
getTagsFile({
contentPaths: {contentPath: dir, contentPathLocalized: dir},
tags: false,
}),
).resolves.toBeNull();
});
it('does not read tags file - tags files has non-default name', async () => {
const {dir} = await createTestTagsFile({
filePath: 'bad-tags-file-name.yml',
tagsFileInput: {
tag: {label: 'tag label'},
},
});
const params: Params = {
contentPaths: {contentPath: dir, contentPathLocalized: dir},
tags: undefined,
};
await expect(getTagsFile(params)).resolves.toBeNull();
});
});