techdocs-node: refactor generate helpers test to avoid mock-fs

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2023-10-04 17:10:06 +02:00
parent 689a1aaf8f
commit eace331db0
3 changed files with 90 additions and 87 deletions
@@ -17,9 +17,8 @@
import { getVoidLogger } from '@backstage/backend-common';
import { ConfigReader } from '@backstage/config';
import { ScmIntegrations } from '@backstage/integration';
import { createMockDirectory } from '@backstage/backend-test-utils';
import fs from 'fs-extra';
import mockFs from 'mock-fs';
import os from 'os';
import path, { resolve as resolvePath } from 'path';
import { ParsedLocationAnnotation } from '../../helpers';
import {
@@ -88,14 +87,12 @@ const mkdocsYmlWithEnvTag = fs.readFileSync(
const mockLogger = getVoidLogger();
const warn = jest.spyOn(mockLogger, 'warn');
const rootDir = os.platform() === 'win32' ? 'C:\\rootDir' : '/rootDir';
const scmIntegrations = ScmIntegrations.fromConfig(new ConfigReader({}));
describe('helpers', () => {
afterEach(() => {
mockFs.restore();
});
const mockDir = createMockDirectory();
afterEach(mockDir.clear);
describe('getGeneratorKey', () => {
it('should return techdocs as the only generator key', () => {
@@ -188,13 +185,13 @@ describe('helpers', () => {
describe('patchMkdocsYmlPreBuild', () => {
beforeEach(() => {
mockFs({
'/mkdocs.yml': mkdocsYml,
'/mkdocs_default.yml': mkdocsDefaultYml,
'/mkdocs_with_repo_url.yml': mkdocsYmlWithRepoUrl,
'/mkdocs_with_edit_uri.yml': mkdocsYmlWithEditUri,
'/mkdocs_with_extensions.yml': mkdocsYmlWithExtensions,
'/mkdocs_with_comments.yml': mkdocsYmlWithComments,
mockDir.setContent({
'mkdocs.yml': mkdocsYml,
'mkdocs_default.yml': mkdocsDefaultYml,
'mkdocs_with_repo_url.yml': mkdocsYmlWithRepoUrl,
'mkdocs_with_edit_uri.yml': mkdocsYmlWithEditUri,
'mkdocs_with_extensions.yml': mkdocsYmlWithExtensions,
'mkdocs_with_comments.yml': mkdocsYmlWithComments,
});
});
@@ -205,13 +202,13 @@ describe('helpers', () => {
};
await patchMkdocsYmlPreBuild(
'/mkdocs.yml',
mockDir.resolve('mkdocs.yml'),
mockLogger,
parsedLocationAnnotation,
scmIntegrations,
);
const updatedMkdocsYml = await fs.readFile('/mkdocs.yml');
const updatedMkdocsYml = await fs.readFile(mockDir.resolve('mkdocs.yml'));
expect(updatedMkdocsYml.toString()).toContain(
'repo_url: https://github.com/backstage/backstage',
@@ -225,13 +222,15 @@ describe('helpers', () => {
};
await patchMkdocsYmlPreBuild(
'/mkdocs_with_extensions.yml',
mockDir.resolve('mkdocs_with_extensions.yml'),
mockLogger,
parsedLocationAnnotation,
scmIntegrations,
);
const updatedMkdocsYml = await fs.readFile('/mkdocs_with_extensions.yml');
const updatedMkdocsYml = await fs.readFile(
mockDir.resolve('mkdocs_with_extensions.yml'),
);
expect(updatedMkdocsYml.toString()).toContain(
'repo_url: https://github.com/backstage/backstage',
@@ -248,13 +247,15 @@ describe('helpers', () => {
};
await patchMkdocsYmlPreBuild(
'/mkdocs_with_repo_url.yml',
mockDir.resolve('mkdocs_with_repo_url.yml'),
mockLogger,
parsedLocationAnnotation,
scmIntegrations,
);
const updatedMkdocsYml = await fs.readFile('/mkdocs_with_repo_url.yml');
const updatedMkdocsYml = await fs.readFile(
mockDir.resolve('mkdocs_with_repo_url.yml'),
);
expect(updatedMkdocsYml.toString()).toContain(
'repo_url: https://github.com/backstage/backstage',
@@ -271,13 +272,15 @@ describe('helpers', () => {
};
await patchMkdocsYmlPreBuild(
'/mkdocs_with_edit_uri.yml',
mockDir.resolve('mkdocs_with_edit_uri.yml'),
mockLogger,
parsedLocationAnnotation,
scmIntegrations,
);
const updatedMkdocsYml = await fs.readFile('/mkdocs_with_edit_uri.yml');
const updatedMkdocsYml = await fs.readFile(
mockDir.resolve('mkdocs_with_edit_uri.yml'),
);
expect(updatedMkdocsYml.toString()).toContain(
'edit_uri: https://github.com/backstage/backstage/edit/main/docs',
@@ -294,13 +297,15 @@ describe('helpers', () => {
};
await patchMkdocsYmlPreBuild(
'/mkdocs_with_comments.yml',
mockDir.resolve('mkdocs_with_comments.yml'),
mockLogger,
parsedLocationAnnotation,
scmIntegrations,
);
const updatedMkdocsYml = await fs.readFile('/mkdocs_with_comments.yml');
const updatedMkdocsYml = await fs.readFile(
mockDir.resolve('mkdocs_with_comments.yml'),
);
expect(updatedMkdocsYml.toString()).toContain(
'# This is a comment that is removed after editing',
@@ -312,20 +317,20 @@ describe('helpers', () => {
describe('patchMkdocsYmlWithPlugins', () => {
beforeEach(() => {
mockFs({
'/mkdocs_with_techdocs_plugin.yml': mkdocsYmlWithTechdocsPlugins,
'/mkdocs_without_plugins.yml': mkdocsYmlWithoutPlugins,
'/mkdocs_with_additional_plugins.yml': mkdocsYmlWithAdditionalPlugins,
mockDir.setContent({
'mkdocs_with_techdocs_plugin.yml': mkdocsYmlWithTechdocsPlugins,
'mkdocs_without_plugins.yml': mkdocsYmlWithoutPlugins,
'mkdocs_with_additional_plugins.yml': mkdocsYmlWithAdditionalPlugins,
});
});
it('should not add additional plugins if techdocs exists already in mkdocs file', async () => {
await patchMkdocsYmlWithPlugins(
'/mkdocs_with_techdocs_plugin.yml',
mockDir.resolve('mkdocs_with_techdocs_plugin.yml'),
mockLogger,
);
const updatedMkdocsYml = await fs.readFile(
'/mkdocs_with_techdocs_plugin.yml',
mockDir.resolve('mkdocs_with_techdocs_plugin.yml'),
);
const parsedYml = yaml.load(updatedMkdocsYml.toString()) as {
plugins: string[];
@@ -335,11 +340,13 @@ describe('helpers', () => {
});
it("should add the needed plugin if it doesn't exist in mkdocs file", async () => {
await patchMkdocsYmlWithPlugins(
'/mkdocs_without_plugins.yml',
mockDir.resolve('mkdocs_without_plugins.yml'),
mockLogger,
);
const updatedMkdocsYml = await fs.readFile('/mkdocs_without_plugins.yml');
const updatedMkdocsYml = await fs.readFile(
mockDir.resolve('mkdocs_without_plugins.yml'),
);
const parsedYml = yaml.load(updatedMkdocsYml.toString()) as {
plugins: string[];
};
@@ -348,11 +355,11 @@ describe('helpers', () => {
});
it('should not override existing plugins', async () => {
await patchMkdocsYmlWithPlugins(
'/mkdocs_with_additional_plugins.yml',
mockDir.resolve('mkdocs_with_additional_plugins.yml'),
mockLogger,
);
const updatedMkdocsYml = await fs.readFile(
'/mkdocs_with_additional_plugins.yml',
mockDir.resolve('mkdocs_with_additional_plugins.yml'),
);
const parsedYml = yaml.load(updatedMkdocsYml.toString()) as {
plugins: string[];
@@ -364,13 +371,13 @@ describe('helpers', () => {
});
it('should add all provided default plugins', async () => {
await patchMkdocsYmlWithPlugins(
'/mkdocs_with_additional_plugins.yml',
mockDir.resolve('mkdocs_with_additional_plugins.yml'),
mockLogger,
['techdocs-core', 'custom-plugin'],
);
const updatedMkdocsYml = await fs.readFile(
'/mkdocs_with_additional_plugins.yml',
mockDir.resolve('mkdocs_with_additional_plugins.yml'),
);
const parsedYml = yaml.load(updatedMkdocsYml.toString()) as {
plugins: string[];
@@ -386,45 +393,45 @@ describe('helpers', () => {
warn.mockClear();
});
it('should have no effect if docs/index.md exists', async () => {
mockFs({
'/docs/index.md': 'index.md content',
'/docs/README.md': 'docs/README.md content',
mockDir.setContent({
'docs/index.md': 'index.md content',
'docs/README.md': 'docs/README.md content',
});
await patchIndexPreBuild({ inputDir: '/', logger: mockLogger });
await patchIndexPreBuild({ inputDir: mockDir.path, logger: mockLogger });
await expect(fs.readFile('/docs/index.md', 'utf-8')).resolves.toEqual(
'index.md content',
);
await expect(
fs.readFile(mockDir.resolve('docs/index.md'), 'utf-8'),
).resolves.toEqual('index.md content');
expect(warn).not.toHaveBeenCalledWith();
});
it("should use docs/README.md if docs/index.md doesn't exists", async () => {
mockFs({
'/docs/README.md': 'docs/README.md content',
'/README.md': 'main README.md content',
mockDir.setContent({
'docs/README.md': 'docs/README.md content',
'README.md': 'main README.md content',
});
await patchIndexPreBuild({ inputDir: '/', logger: mockLogger });
await patchIndexPreBuild({ inputDir: mockDir.path, logger: mockLogger });
await expect(fs.readFile('/docs/index.md', 'utf-8')).resolves.toEqual(
'docs/README.md content',
);
await expect(
fs.readFile(mockDir.resolve('docs/index.md'), 'utf-8'),
).resolves.toEqual('docs/README.md content');
expect(warn.mock.calls).toEqual([
[`${path.normalize('docs/index.md')} not found.`],
]);
});
it('should use README.md if neither docs/index.md or docs/README.md exist', async () => {
mockFs({
'/README.md': 'main README.md content',
mockDir.setContent({
'README.md': 'main README.md content',
});
await patchIndexPreBuild({ inputDir: '/', logger: mockLogger });
await patchIndexPreBuild({ inputDir: mockDir.path, logger: mockLogger });
await expect(fs.readFile('/docs/index.md', 'utf-8')).resolves.toEqual(
'main README.md content',
);
await expect(
fs.readFile(mockDir.resolve('docs/index.md'), 'utf-8'),
).resolves.toEqual('main README.md content');
expect(warn.mock.calls).toEqual([
[`${path.normalize('docs/index.md')} not found.`],
[`${path.normalize('docs/README.md')} not found.`],
@@ -433,11 +440,13 @@ describe('helpers', () => {
});
it('should not use any file as index.md if no one matches the requirements', async () => {
mockFs({});
mockDir.setContent({});
await patchIndexPreBuild({ inputDir: '/', logger: mockLogger });
await patchIndexPreBuild({ inputDir: mockDir.path, logger: mockLogger });
await expect(fs.readFile('/docs/index.md', 'utf-8')).rejects.toThrow();
await expect(
fs.readFile(mockDir.resolve('docs/index.md'), 'utf-8'),
).rejects.toThrow();
const paths = [
path.normalize('docs/index.md'),
path.normalize('docs/README.md'),
@@ -449,7 +458,7 @@ describe('helpers', () => {
...paths.map(p => [`${p} not found.`]),
[
`Could not find any techdocs' index file. Please make sure at least one of ${paths
.map(p => path.sep + p)
.map(p => mockDir.resolve(p))
.join(' ')} exists.`,
],
]);
@@ -463,13 +472,11 @@ describe('helpers', () => {
};
beforeEach(() => {
mockFs({
[rootDir]: mockFiles,
});
mockDir.setContent(mockFiles);
});
it('should create the file if it does not exist', async () => {
const filePath = path.join(rootDir, 'wrong_techdocs_metadata.json');
const filePath = mockDir.resolve('wrong_techdocs_metadata.json');
await createOrUpdateMetadata(filePath, mockLogger);
// Check if the file exists
@@ -479,7 +486,7 @@ describe('helpers', () => {
});
it('should throw error when the JSON is invalid', async () => {
const filePath = path.join(rootDir, 'invalid_techdocs_metadata.json');
const filePath = mockDir.resolve('invalid_techdocs_metadata.json');
await expect(
createOrUpdateMetadata(filePath, mockLogger),
@@ -487,7 +494,7 @@ describe('helpers', () => {
});
it('should add build timestamp to the metadata json', async () => {
const filePath = path.join(rootDir, 'techdocs_metadata.json');
const filePath = mockDir.resolve('techdocs_metadata.json');
await createOrUpdateMetadata(filePath, mockLogger);
@@ -496,7 +503,7 @@ describe('helpers', () => {
});
it('should add list of files to the metadata json', async () => {
const filePath = path.join(rootDir, 'techdocs_metadata.json');
const filePath = mockDir.resolve('techdocs_metadata.json');
await createOrUpdateMetadata(filePath, mockLogger);
@@ -508,16 +515,14 @@ describe('helpers', () => {
describe('storeEtagMetadata', () => {
beforeEach(() => {
mockFs({
[rootDir]: {
'invalid_techdocs_metadata.json': 'dsds',
'techdocs_metadata.json': '{"site_name": "Tech Docs"}',
},
mockDir.setContent({
'invalid_techdocs_metadata.json': 'dsds',
'techdocs_metadata.json': '{"site_name": "Tech Docs"}',
});
});
it('should throw error when the JSON is invalid', async () => {
const filePath = path.join(rootDir, 'invalid_techdocs_metadata.json');
const filePath = mockDir.resolve('invalid_techdocs_metadata.json');
await expect(storeEtagMetadata(filePath, 'etag123abc')).rejects.toThrow(
'Unexpected token',
@@ -525,7 +530,7 @@ describe('helpers', () => {
});
it('should add etag to the metadata json', async () => {
const filePath = path.join(rootDir, 'techdocs_metadata.json');
const filePath = mockDir.resolve('techdocs_metadata.json');
await storeEtagMetadata(filePath, 'etag123abc');
@@ -535,34 +540,31 @@ describe('helpers', () => {
});
describe('getMkdocsYml', () => {
const inputDir = resolvePath(__filename, '../__fixtures__/');
const siteOptions = {
name: mockEntity.metadata.title,
};
it('returns expected contents when .yml file is present', async () => {
const key = path.join(inputDir, 'mkdocs.yml');
mockFs({ [key]: mkdocsYml });
mockDir.setContent({ 'mkdocs.yml': mkdocsYml });
const {
path: mkdocsPath,
content,
configIsTemporary,
} = await getMkdocsYml(inputDir, siteOptions);
} = await getMkdocsYml(mockDir.path, siteOptions);
expect(mkdocsPath).toBe(key);
expect(mkdocsPath).toBe(mockDir.resolve('mkdocs.yml'));
expect(content).toBe(mkdocsYml.toString());
expect(configIsTemporary).toBe(false);
});
it('returns expected contents when .yaml file is present', async () => {
const key = path.join(inputDir, 'mkdocs.yaml');
mockFs({ [key]: mkdocsYml });
mockDir.setContent({ 'mkdocs.yaml': mkdocsYml });
const {
path: mkdocsPath,
content,
configIsTemporary,
} = await getMkdocsYml(inputDir, siteOptions);
expect(mkdocsPath).toBe(key);
} = await getMkdocsYml(mockDir.path, siteOptions);
expect(mkdocsPath).toBe(mockDir.resolve('mkdocs.yaml'));
expect(content).toBe(mkdocsYml.toString());
expect(configIsTemporary).toBe(false);
});
@@ -571,17 +573,16 @@ describe('helpers', () => {
const defaultSiteOptions = {
name: 'Default Test site name',
};
const key = path.join(inputDir, 'mkdocs.yml');
const mockPathExists = jest.spyOn(fs, 'pathExists');
mockPathExists.mockImplementation(() => Promise.resolve(false));
mockFs({ [key]: mkdocsDefaultYml });
mockDir.setContent({ 'mkdocs.yml': mkdocsDefaultYml });
const {
path: mkdocsPath,
content,
configIsTemporary,
} = await getMkdocsYml(inputDir, defaultSiteOptions);
} = await getMkdocsYml(mockDir.path, defaultSiteOptions);
expect(mkdocsPath).toBe(key);
expect(mkdocsPath).toBe(mockDir.resolve('mkdocs.yml'));
expect(content.split(/[\r\n]+/g)).toEqual(
mkdocsDefaultYml.toString().split(/[\r\n]+/g),
);