@redocly/cli 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/lib/commands/build-docs/index.js +2 -4
- package/lib/commands/build-docs/utils.d.ts +1 -1
- package/lib/commands/build-docs/utils.js +3 -3
- package/package.json +2 -2
- package/src/__mocks__/@redocly/openapi-core.ts +80 -0
- package/src/__mocks__/documents.ts +63 -0
- package/src/__mocks__/fs.ts +6 -0
- package/src/__mocks__/perf_hooks.ts +3 -0
- package/src/__mocks__/redoc.ts +2 -0
- package/src/__mocks__/utils.ts +19 -0
- package/src/__tests__/commands/build-docs.test.ts +62 -0
- package/src/__tests__/commands/bundle.test.ts +150 -0
- package/src/__tests__/commands/join.test.ts +122 -0
- package/src/__tests__/commands/lint.test.ts +190 -0
- package/src/__tests__/commands/push-region.test.ts +58 -0
- package/src/__tests__/commands/push.test.ts +492 -0
- package/src/__tests__/fetch-with-timeout.test.ts +35 -0
- package/src/__tests__/fixtures/config.ts +21 -0
- package/src/__tests__/fixtures/openapi.json +0 -0
- package/src/__tests__/fixtures/openapi.yaml +0 -0
- package/src/__tests__/fixtures/redocly.yaml +0 -0
- package/src/__tests__/utils.test.ts +564 -0
- package/src/__tests__/wrapper.test.ts +57 -0
- package/src/assert-node-version.ts +8 -0
- package/src/commands/build-docs/index.ts +50 -0
- package/src/commands/build-docs/template.hbs +23 -0
- package/src/commands/build-docs/types.ts +24 -0
- package/src/commands/build-docs/utils.ts +110 -0
- package/src/commands/bundle.ts +177 -0
- package/src/commands/join.ts +811 -0
- package/src/commands/lint.ts +151 -0
- package/src/commands/login.ts +27 -0
- package/src/commands/preview-docs/index.ts +190 -0
- package/src/commands/preview-docs/preview-server/default.hbs +24 -0
- package/src/commands/preview-docs/preview-server/hot.js +42 -0
- package/src/commands/preview-docs/preview-server/oauth2-redirect.html +21 -0
- package/src/commands/preview-docs/preview-server/preview-server.ts +156 -0
- package/src/commands/preview-docs/preview-server/server.ts +91 -0
- package/src/commands/push.ts +441 -0
- package/src/commands/split/__tests__/fixtures/samples.json +61 -0
- package/src/commands/split/__tests__/fixtures/spec.json +70 -0
- package/src/commands/split/__tests__/fixtures/webhooks.json +85 -0
- package/src/commands/split/__tests__/index.test.ts +137 -0
- package/src/commands/split/index.ts +385 -0
- package/src/commands/split/types.ts +85 -0
- package/src/commands/stats.ts +119 -0
- package/src/custom.d.ts +1 -0
- package/src/fetch-with-timeout.ts +21 -0
- package/src/index.ts +484 -0
- package/src/js-utils.ts +17 -0
- package/src/types.ts +40 -0
- package/src/update-version-notifier.ts +106 -0
- package/src/utils.ts +590 -0
- package/src/wrapper.ts +42 -0
- package/tsconfig.json +9 -0
- package/tsconfig.tsbuildinfo +1 -0
package/CHANGELOG.md
ADDED
|
@@ -34,10 +34,8 @@ const handlerBuildCommand = (argv, configFromFile) => __awaiter(void 0, void 0,
|
|
|
34
34
|
const redocCurrentVersion = require('../../../package.json').dependencies.redoc.substring(1); // remove ~
|
|
35
35
|
try {
|
|
36
36
|
const elapsed = utils_2.getExecutionTime(startedAt);
|
|
37
|
-
const api = yield redoc_1.loadAndBundleSpec(openapi_core_1.isAbsoluteUrl(pathToApi)
|
|
38
|
-
|
|
39
|
-
: path_1.resolve(argv.config ? path_1.dirname(argv.config) : '', pathToApi));
|
|
40
|
-
const pageHTML = yield utils_1.getPageHTML(api, pathToApi, Object.assign(Object.assign({}, options), { redocCurrentVersion }));
|
|
37
|
+
const api = yield redoc_1.loadAndBundleSpec(openapi_core_1.isAbsoluteUrl(pathToApi) ? pathToApi : path_1.resolve(pathToApi));
|
|
38
|
+
const pageHTML = yield utils_1.getPageHTML(api, pathToApi, Object.assign(Object.assign({}, options), { redocCurrentVersion }), argv.config);
|
|
41
39
|
fs_1.mkdirSync(path_1.dirname(options.output), { recursive: true });
|
|
42
40
|
fs_1.writeFileSync(options.output, pageHTML);
|
|
43
41
|
const sizeInKiB = Math.ceil(Buffer.byteLength(pageHTML) / 1024);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Config } from '@redocly/openapi-core';
|
|
2
2
|
import type { BuildDocsOptions } from './types';
|
|
3
3
|
export declare function getObjectOrJSON(openapiOptions: string | Record<string, unknown>, config: Config): JSON | Record<string, unknown> | Config;
|
|
4
|
-
export declare function getPageHTML(api: any, pathToApi: string, { title, disableGoogleFont, templateFileName, templateOptions, redocOptions, redocCurrentVersion, }: BuildDocsOptions): Promise<string>;
|
|
4
|
+
export declare function getPageHTML(api: any, pathToApi: string, { title, disableGoogleFont, templateFileName, templateOptions, redocOptions, redocCurrentVersion, }: BuildDocsOptions, configPath?: string): Promise<string>;
|
|
5
5
|
export declare function sanitizeJSONString(str: string): string;
|
|
6
6
|
export declare function escapeClosingScriptTag(str: string): string;
|
|
7
7
|
export declare function escapeUnicode(str: string): string;
|
|
@@ -49,9 +49,9 @@ function getObjectOrJSON(openapiOptions, config) {
|
|
|
49
49
|
return {};
|
|
50
50
|
}
|
|
51
51
|
exports.getObjectOrJSON = getObjectOrJSON;
|
|
52
|
-
function getPageHTML(api, pathToApi, { title, disableGoogleFont, templateFileName, templateOptions, redocOptions = {}, redocCurrentVersion, }) {
|
|
52
|
+
function getPageHTML(api, pathToApi, { title, disableGoogleFont, templateFileName, templateOptions, redocOptions = {}, redocCurrentVersion, }, configPath) {
|
|
53
53
|
return __awaiter(this, void 0, void 0, function* () {
|
|
54
|
-
process.stderr.write('Prerendering docs');
|
|
54
|
+
process.stderr.write('Prerendering docs\n');
|
|
55
55
|
const apiUrl = redocOptions.specUrl || (openapi_core_1.isAbsoluteUrl(pathToApi) ? pathToApi : undefined);
|
|
56
56
|
const store = yield redoc_1.createStore(api, apiUrl, redocOptions);
|
|
57
57
|
const sheet = new styled_components_1.ServerStyleSheet();
|
|
@@ -61,7 +61,7 @@ function getPageHTML(api, pathToApi, { title, disableGoogleFont, templateFileNam
|
|
|
61
61
|
templateFileName = templateFileName
|
|
62
62
|
? templateFileName
|
|
63
63
|
: (redocOptions === null || redocOptions === void 0 ? void 0 : redocOptions.htmlTemplate)
|
|
64
|
-
? redocOptions.htmlTemplate
|
|
64
|
+
? path_1.resolve(configPath ? path_1.dirname(configPath) : '', redocOptions.htmlTemplate)
|
|
65
65
|
: path_1.join(__dirname, './template.hbs');
|
|
66
66
|
const template = handlebars_1.compile(fs_1.readFileSync(templateFileName).toString());
|
|
67
67
|
return template({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@redocly/cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"bin": {
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"Roman Hotsiy <roman@redoc.ly> (https://redoc.ly/)"
|
|
35
35
|
],
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@redocly/openapi-core": "1.0.
|
|
37
|
+
"@redocly/openapi-core": "1.0.1",
|
|
38
38
|
"assert-node-version": "^1.0.3",
|
|
39
39
|
"chokidar": "^3.5.1",
|
|
40
40
|
"colorette": "^1.2.0",
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { ConfigFixture } from './../../__tests__/fixtures/config';
|
|
2
|
+
import { Document } from '@redocly/openapi-core';
|
|
3
|
+
import { firstDocument, secondDocument } from '../documents';
|
|
4
|
+
|
|
5
|
+
export const __redoclyClient = {
|
|
6
|
+
isAuthorizedWithRedocly: jest.fn().mockResolvedValue(true),
|
|
7
|
+
isAuthorizedWithRedoclyByRegion: jest.fn().mockResolvedValue(true),
|
|
8
|
+
login: jest.fn(),
|
|
9
|
+
registryApi: {
|
|
10
|
+
setAccessTokens: jest.fn(),
|
|
11
|
+
authStatus: jest.fn(),
|
|
12
|
+
prepareFileUpload: jest.fn().mockResolvedValue({
|
|
13
|
+
signedUploadUrl: 'signedUploadUrl',
|
|
14
|
+
filePath: 'filePath',
|
|
15
|
+
}),
|
|
16
|
+
pushApi: jest.fn(),
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const RedoclyClient = jest.fn(() => __redoclyClient);
|
|
21
|
+
export const loadConfig = jest.fn(() => ConfigFixture);
|
|
22
|
+
export const getMergedConfig = jest.fn();
|
|
23
|
+
export const lint = jest.fn();
|
|
24
|
+
export const bundle = jest.fn(() => ({ bundle: { parsed: null }, problems: null }));
|
|
25
|
+
export const getTotals = jest.fn(() => ({ errors: 0 }));
|
|
26
|
+
export const formatProblems = jest.fn();
|
|
27
|
+
export const slash = jest.fn();
|
|
28
|
+
export const findConfig = jest.fn();
|
|
29
|
+
export const doesYamlFileExist = jest.fn();
|
|
30
|
+
export const bundleDocument = jest.fn(() => Promise.resolve({ problems: {} }));
|
|
31
|
+
export const detectOpenAPI = jest.fn();
|
|
32
|
+
export const isAbsoluteUrl = jest.fn();
|
|
33
|
+
|
|
34
|
+
export class BaseResolver {
|
|
35
|
+
cache = new Map<string, Promise<Document | ResolveError>>();
|
|
36
|
+
|
|
37
|
+
getFiles = jest.fn();
|
|
38
|
+
resolveExternalRef = jest.fn();
|
|
39
|
+
loadExternalRef = jest.fn;
|
|
40
|
+
parseDocument = jest.fn();
|
|
41
|
+
resolveDocument = jest
|
|
42
|
+
.fn()
|
|
43
|
+
.mockImplementationOnce(() =>
|
|
44
|
+
Promise.resolve({ source: { absoluteRef: 'ref' }, parsed: firstDocument })
|
|
45
|
+
)
|
|
46
|
+
.mockImplementationOnce(() =>
|
|
47
|
+
Promise.resolve({ source: { absoluteRef: 'ref' }, parsed: secondDocument })
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export class ResolveError extends Error {
|
|
52
|
+
constructor(public originalError: Error) {
|
|
53
|
+
super(originalError.message);
|
|
54
|
+
Object.setPrototypeOf(this, ResolveError.prototype);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export class YamlParseError extends Error {
|
|
59
|
+
constructor(public originalError: Error) {
|
|
60
|
+
super(originalError.message);
|
|
61
|
+
Object.setPrototypeOf(this, YamlParseError.prototype);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export enum OasVersion {
|
|
66
|
+
Version2 = 'oas2',
|
|
67
|
+
Version3_0 = 'oas3_0',
|
|
68
|
+
Version3_1 = 'oas3_1',
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export enum Oas3Operations {
|
|
72
|
+
get = 'get',
|
|
73
|
+
put = 'put',
|
|
74
|
+
post = 'post',
|
|
75
|
+
delete = 'delete',
|
|
76
|
+
options = 'options',
|
|
77
|
+
head = 'head',
|
|
78
|
+
patch = 'patch',
|
|
79
|
+
trace = 'trace',
|
|
80
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
export const firstDocument = {
|
|
2
|
+
openapi: '3.0.0',
|
|
3
|
+
servers: [{ url: 'http://localhost:8080' }],
|
|
4
|
+
info: {
|
|
5
|
+
description: 'example test',
|
|
6
|
+
version: '1.0.0',
|
|
7
|
+
title: 'Swagger Petstore',
|
|
8
|
+
termsOfService: 'http://swagger.io/terms/',
|
|
9
|
+
license: {
|
|
10
|
+
name: 'Apache 2.0',
|
|
11
|
+
url: 'http://www.apache.org/licenses/LICENSE-2.0.html',
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
paths: {
|
|
15
|
+
'/GETUser/{userId}': {
|
|
16
|
+
summary: 'get user by id',
|
|
17
|
+
description: 'user info',
|
|
18
|
+
servers: [{ url: '/user' }, { url: '/pet', description: 'pet server' }],
|
|
19
|
+
|
|
20
|
+
get: {
|
|
21
|
+
tags: ['pet'],
|
|
22
|
+
summary: 'Find pet by ID',
|
|
23
|
+
description: 'Returns a single pet',
|
|
24
|
+
operationId: 'getPetById',
|
|
25
|
+
servers: [{ url: '/pet' }],
|
|
26
|
+
},
|
|
27
|
+
parameters: [{ name: 'param1', in: 'header', schema: { description: 'string' } }],
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
components: {},
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export const secondDocument = {
|
|
34
|
+
openapi: '3.0.0',
|
|
35
|
+
servers: [{ url: 'http://localhost:8080' }],
|
|
36
|
+
info: {
|
|
37
|
+
description: 'example test',
|
|
38
|
+
version: '1.0.0',
|
|
39
|
+
title: 'Swagger Petstore',
|
|
40
|
+
termsOfService: 'http://swagger.io/terms/',
|
|
41
|
+
license: {
|
|
42
|
+
name: 'Apache 2.0',
|
|
43
|
+
url: 'http://www.apache.org/licenses/LICENSE-2.0.html',
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
post: {
|
|
47
|
+
'/GETUser/{userId}': {
|
|
48
|
+
summary: 'get user',
|
|
49
|
+
description: 'user information',
|
|
50
|
+
servers: [{ url: '/user' }, { url: '/pet', description: '' }],
|
|
51
|
+
|
|
52
|
+
get: {
|
|
53
|
+
tags: ['pet'],
|
|
54
|
+
summary: 'Find pet by ID',
|
|
55
|
+
description: 'Returns a single pet',
|
|
56
|
+
operationId: 'getPetById',
|
|
57
|
+
servers: [{ url: '/pet' }],
|
|
58
|
+
},
|
|
59
|
+
parameters: [{ name: 'param1', in: 'header', schema: { description: 'string' } }],
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
components: {},
|
|
63
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { ConfigFixture } from '../__tests__/fixtures/config';
|
|
2
|
+
|
|
3
|
+
export const getFallbackApisOrExit = jest.fn((entrypoints) =>
|
|
4
|
+
entrypoints.map((path: string) => ({ path }))
|
|
5
|
+
);
|
|
6
|
+
export const dumpBundle = jest.fn(() => '');
|
|
7
|
+
export const slash = jest.fn();
|
|
8
|
+
export const pluralize = jest.fn();
|
|
9
|
+
export const getExecutionTime = jest.fn();
|
|
10
|
+
export const printExecutionTime = jest.fn();
|
|
11
|
+
export const printUnusedWarnings = jest.fn();
|
|
12
|
+
export const printLintTotals = jest.fn();
|
|
13
|
+
export const getOutputFileName = jest.fn(() => ({ outputFile: 'test.yaml', ext: 'yaml' }));
|
|
14
|
+
export const handleError = jest.fn();
|
|
15
|
+
export const exitWithError = jest.fn();
|
|
16
|
+
export const writeYaml = jest.fn();
|
|
17
|
+
export const loadConfigAndHandleErrors = jest.fn(() => ConfigFixture);
|
|
18
|
+
export const checkIfRulesetExist = jest.fn();
|
|
19
|
+
export const sortTopLevelKeysForOas = jest.fn((document) => document);
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { createStore, loadAndBundleSpec } from 'redoc';
|
|
2
|
+
import { renderToString } from 'react-dom/server';
|
|
3
|
+
import { handlerBuildCommand } from '../../commands/build-docs';
|
|
4
|
+
import { BuildDocsArgv } from '../../commands/build-docs/types';
|
|
5
|
+
import { getPageHTML } from '../../commands/build-docs/utils';
|
|
6
|
+
import { getFallbackApisOrExit } from '../../utils';
|
|
7
|
+
|
|
8
|
+
jest.mock('redoc');
|
|
9
|
+
jest.mock('fs');
|
|
10
|
+
jest.mock('../../utils');
|
|
11
|
+
|
|
12
|
+
const config = {
|
|
13
|
+
output: '',
|
|
14
|
+
title: 'Test',
|
|
15
|
+
disableGoogleFont: false,
|
|
16
|
+
templateFileName: '',
|
|
17
|
+
templateOptions: {},
|
|
18
|
+
redocOptions: {},
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
jest.mock('react-dom/server', () => ({
|
|
22
|
+
renderToString: jest.fn(),
|
|
23
|
+
}));
|
|
24
|
+
|
|
25
|
+
jest.mock('handlebars', () => ({
|
|
26
|
+
compile: jest.fn(() => jest.fn(() => '<html></html>')),
|
|
27
|
+
}));
|
|
28
|
+
|
|
29
|
+
jest.mock('mkdirp', () => ({
|
|
30
|
+
sync: jest.fn(),
|
|
31
|
+
}));
|
|
32
|
+
|
|
33
|
+
describe('build-docs', () => {
|
|
34
|
+
it('should return correct html and call function for ssr', async () => {
|
|
35
|
+
const result = await getPageHTML({}, '../some-path/openapi.yaml', {
|
|
36
|
+
...config,
|
|
37
|
+
redocCurrentVersion: '2.0.0',
|
|
38
|
+
});
|
|
39
|
+
expect(renderToString).toBeCalledTimes(1);
|
|
40
|
+
expect(createStore).toBeCalledTimes(1);
|
|
41
|
+
expect(result).toBe('<html></html>');
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('should work correctly when calling handlerBuildCommand', async () => {
|
|
45
|
+
const processExitMock = jest.spyOn(process, 'exit').mockImplementation();
|
|
46
|
+
await handlerBuildCommand(
|
|
47
|
+
{
|
|
48
|
+
o: '',
|
|
49
|
+
title: 'test',
|
|
50
|
+
disableGoogleFont: false,
|
|
51
|
+
template: '',
|
|
52
|
+
templateOptions: {},
|
|
53
|
+
theme: { openapi: {} },
|
|
54
|
+
api: '../some-path/openapi.yaml',
|
|
55
|
+
} as BuildDocsArgv,
|
|
56
|
+
{} as any
|
|
57
|
+
);
|
|
58
|
+
expect(loadAndBundleSpec).toBeCalledTimes(1);
|
|
59
|
+
expect(getFallbackApisOrExit).toBeCalledTimes(1);
|
|
60
|
+
expect(processExitMock).toBeCalledTimes(0);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { lint, bundle, getTotals, getMergedConfig } from '@redocly/openapi-core';
|
|
2
|
+
|
|
3
|
+
import { BundleOptions, handleBundle } from '../../commands/bundle';
|
|
4
|
+
import { handleError } from '../../utils';
|
|
5
|
+
import { commandWrapper } from '../../wrapper';
|
|
6
|
+
import SpyInstance = jest.SpyInstance;
|
|
7
|
+
import { Arguments } from 'yargs';
|
|
8
|
+
|
|
9
|
+
jest.mock('@redocly/openapi-core');
|
|
10
|
+
jest.mock('../../utils');
|
|
11
|
+
|
|
12
|
+
(getMergedConfig as jest.Mock).mockImplementation((config) => config);
|
|
13
|
+
|
|
14
|
+
describe('bundle', () => {
|
|
15
|
+
let processExitMock: SpyInstance;
|
|
16
|
+
let exitCb: any;
|
|
17
|
+
|
|
18
|
+
beforeEach(() => {
|
|
19
|
+
processExitMock = jest.spyOn(process, 'exit').mockImplementation();
|
|
20
|
+
jest.spyOn(process, 'once').mockImplementation((_e, cb) => {
|
|
21
|
+
exitCb = cb;
|
|
22
|
+
return process.on(_e, cb);
|
|
23
|
+
});
|
|
24
|
+
jest.spyOn(process.stderr, 'write').mockImplementation(() => true);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
afterEach(() => {
|
|
28
|
+
(lint as jest.Mock).mockClear();
|
|
29
|
+
(bundle as jest.Mock).mockClear();
|
|
30
|
+
(getTotals as jest.Mock).mockReset();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('bundles definitions w/o linting', async () => {
|
|
34
|
+
const apis = ['foo.yaml', 'bar.yaml'];
|
|
35
|
+
|
|
36
|
+
await commandWrapper(handleBundle)({
|
|
37
|
+
apis,
|
|
38
|
+
ext: 'yaml',
|
|
39
|
+
format: 'codeframe',
|
|
40
|
+
} as Arguments<BundleOptions>);
|
|
41
|
+
|
|
42
|
+
expect(lint).toBeCalledTimes(0);
|
|
43
|
+
expect(bundle).toBeCalledTimes(apis.length);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('exits with code 0 when bundles definitions', async () => {
|
|
47
|
+
const apis = ['foo.yaml', 'bar.yaml', 'foobar.yaml'];
|
|
48
|
+
|
|
49
|
+
await commandWrapper(handleBundle)({
|
|
50
|
+
apis,
|
|
51
|
+
ext: 'yaml',
|
|
52
|
+
format: 'codeframe',
|
|
53
|
+
} as Arguments<BundleOptions>);
|
|
54
|
+
|
|
55
|
+
await exitCb?.();
|
|
56
|
+
expect(processExitMock).toHaveBeenCalledWith(0);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('bundles definitions w/ linting', async () => {
|
|
60
|
+
const apis = ['foo.yaml', 'bar.yaml', 'foobar.yaml'];
|
|
61
|
+
|
|
62
|
+
(getTotals as jest.Mock).mockReturnValue({
|
|
63
|
+
errors: 0,
|
|
64
|
+
warnings: 0,
|
|
65
|
+
ignored: 0,
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
await commandWrapper(handleBundle)({
|
|
69
|
+
apis,
|
|
70
|
+
ext: 'yaml',
|
|
71
|
+
format: 'codeframe',
|
|
72
|
+
lint: true,
|
|
73
|
+
} as Arguments<BundleOptions>);
|
|
74
|
+
|
|
75
|
+
expect(lint).toBeCalledTimes(apis.length);
|
|
76
|
+
expect(bundle).toBeCalledTimes(apis.length);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('exits with code 0 when bundles definitions w/linting w/o errors', async () => {
|
|
80
|
+
const apis = ['foo.yaml', 'bar.yaml', 'foobar.yaml'];
|
|
81
|
+
|
|
82
|
+
await commandWrapper(handleBundle)({
|
|
83
|
+
apis,
|
|
84
|
+
ext: 'yaml',
|
|
85
|
+
format: 'codeframe',
|
|
86
|
+
lint: true,
|
|
87
|
+
} as Arguments<BundleOptions>);
|
|
88
|
+
|
|
89
|
+
await exitCb?.();
|
|
90
|
+
expect(processExitMock).toHaveBeenCalledWith(0);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('exits with code 1 when bundles definitions w/linting w/errors', async () => {
|
|
94
|
+
const apis = ['foo.yaml'];
|
|
95
|
+
|
|
96
|
+
(getTotals as jest.Mock).mockReturnValue({
|
|
97
|
+
errors: 1,
|
|
98
|
+
warnings: 0,
|
|
99
|
+
ignored: 0,
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
await commandWrapper(handleBundle)({
|
|
103
|
+
apis,
|
|
104
|
+
ext: 'yaml',
|
|
105
|
+
format: 'codeframe',
|
|
106
|
+
lint: true,
|
|
107
|
+
} as Arguments<BundleOptions>);
|
|
108
|
+
|
|
109
|
+
expect(lint).toBeCalledTimes(apis.length);
|
|
110
|
+
await exitCb?.();
|
|
111
|
+
expect(processExitMock).toHaveBeenCalledWith(1);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('handleError is called when bundles an invalid definition', async () => {
|
|
115
|
+
const apis = ['invalid.json'];
|
|
116
|
+
|
|
117
|
+
(bundle as jest.Mock).mockImplementationOnce(() => {
|
|
118
|
+
throw new Error('Invalid definition');
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
await commandWrapper(handleBundle)({
|
|
122
|
+
apis,
|
|
123
|
+
ext: 'json',
|
|
124
|
+
format: 'codeframe',
|
|
125
|
+
lint: false,
|
|
126
|
+
} as Arguments<BundleOptions>);
|
|
127
|
+
|
|
128
|
+
expect(handleError).toHaveBeenCalledTimes(1);
|
|
129
|
+
expect(handleError).toHaveBeenCalledWith(new Error('Invalid definition'), 'invalid.json');
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it("handleError isn't called when bundles a valid definition", async () => {
|
|
133
|
+
const apis = ['foo.yaml'];
|
|
134
|
+
|
|
135
|
+
(getTotals as jest.Mock).mockReturnValue({
|
|
136
|
+
errors: 0,
|
|
137
|
+
warnings: 0,
|
|
138
|
+
ignored: 0,
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
await commandWrapper(handleBundle)({
|
|
142
|
+
apis,
|
|
143
|
+
ext: 'yaml',
|
|
144
|
+
format: 'codeframe',
|
|
145
|
+
lint: false,
|
|
146
|
+
} as Arguments<BundleOptions>);
|
|
147
|
+
|
|
148
|
+
expect(handleError).toHaveBeenCalledTimes(0);
|
|
149
|
+
});
|
|
150
|
+
});
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { handleJoin } from '../../commands/join';
|
|
2
|
+
import { exitWithError, writeYaml } from '../../utils';
|
|
3
|
+
import { yellow } from 'colorette';
|
|
4
|
+
import { detectOpenAPI } from '@redocly/openapi-core';
|
|
5
|
+
import { loadConfig } from '../../__mocks__/@redocly/openapi-core';
|
|
6
|
+
import { ConfigFixture } from '../fixtures/config';
|
|
7
|
+
|
|
8
|
+
jest.mock('../../utils');
|
|
9
|
+
jest.mock('colorette');
|
|
10
|
+
|
|
11
|
+
describe('handleJoin fails', () => {
|
|
12
|
+
const colloreteYellowMock = yellow as jest.Mock<any, any>;
|
|
13
|
+
colloreteYellowMock.mockImplementation((string: string) => string);
|
|
14
|
+
|
|
15
|
+
it('should call exitWithError because only one entrypoint', async () => {
|
|
16
|
+
await handleJoin({ apis: ['first.yaml'] }, {} as any, 'cli-version');
|
|
17
|
+
expect(exitWithError).toHaveBeenCalledWith(`At least 2 apis should be provided. \n\n`);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('should call exitWithError because passed all 3 options for tags', async () => {
|
|
21
|
+
await handleJoin(
|
|
22
|
+
{
|
|
23
|
+
apis: ['first.yaml', 'second.yaml'],
|
|
24
|
+
'prefix-tags-with-info-prop': 'something',
|
|
25
|
+
'without-x-tag-groups': true,
|
|
26
|
+
'prefix-tags-with-filename': true,
|
|
27
|
+
},
|
|
28
|
+
{} as any,
|
|
29
|
+
'cli-version'
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
expect(exitWithError).toHaveBeenCalledWith(
|
|
33
|
+
`You use prefix-tags-with-filename, prefix-tags-with-info-prop, without-x-tag-groups together.\nPlease choose only one! \n\n`
|
|
34
|
+
);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('should call exitWithError because passed all 2 options for tags', async () => {
|
|
38
|
+
await handleJoin(
|
|
39
|
+
{
|
|
40
|
+
apis: ['first.yaml', 'second.yaml'],
|
|
41
|
+
'without-x-tag-groups': true,
|
|
42
|
+
'prefix-tags-with-filename': true,
|
|
43
|
+
},
|
|
44
|
+
{} as any,
|
|
45
|
+
'cli-version'
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
expect(exitWithError).toHaveBeenCalledWith(
|
|
49
|
+
`You use prefix-tags-with-filename, without-x-tag-groups together.\nPlease choose only one! \n\n`
|
|
50
|
+
);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('should call exitWithError because Only OpenAPI 3 is supported', async () => {
|
|
54
|
+
await handleJoin(
|
|
55
|
+
{
|
|
56
|
+
apis: ['first.yaml', 'second.yaml'],
|
|
57
|
+
},
|
|
58
|
+
ConfigFixture as any,
|
|
59
|
+
'cli-version'
|
|
60
|
+
);
|
|
61
|
+
expect(exitWithError).toHaveBeenCalledWith('Only OpenAPI 3 is supported: undefined \n\n');
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('should call writeYaml function', async () => {
|
|
65
|
+
(detectOpenAPI as jest.Mock).mockReturnValue('oas3_0');
|
|
66
|
+
await handleJoin(
|
|
67
|
+
{
|
|
68
|
+
apis: ['first.yaml', 'second.yaml'],
|
|
69
|
+
},
|
|
70
|
+
ConfigFixture as any,
|
|
71
|
+
'cli-version'
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
expect(writeYaml).toHaveBeenCalledWith(expect.any(Object), 'openapi.yaml', expect.any(Boolean));
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('should call writeYaml function with custom output file', async () => {
|
|
78
|
+
(detectOpenAPI as jest.Mock).mockReturnValue('oas3_0');
|
|
79
|
+
await handleJoin(
|
|
80
|
+
{
|
|
81
|
+
apis: ['first.yaml', 'second.yaml'],
|
|
82
|
+
output: 'output.yml',
|
|
83
|
+
},
|
|
84
|
+
ConfigFixture as any,
|
|
85
|
+
'cli-version'
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
expect(writeYaml).toHaveBeenCalledWith(expect.any(Object), 'output.yml', expect.any(Boolean));
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('should call skipDecorators and skipPreprocessors', async () => {
|
|
92
|
+
(detectOpenAPI as jest.Mock).mockReturnValue('oas3_0');
|
|
93
|
+
await handleJoin(
|
|
94
|
+
{
|
|
95
|
+
apis: ['first.yaml', 'second.yaml'],
|
|
96
|
+
},
|
|
97
|
+
ConfigFixture as any,
|
|
98
|
+
'cli-version'
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
const config = loadConfig();
|
|
102
|
+
expect(config.styleguide.skipDecorators).toHaveBeenCalled();
|
|
103
|
+
expect(config.styleguide.skipPreprocessors).toHaveBeenCalled();
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('should not call skipDecorators and skipPreprocessors', async () => {
|
|
107
|
+
(detectOpenAPI as jest.Mock).mockReturnValue('oas3_0');
|
|
108
|
+
await handleJoin(
|
|
109
|
+
{
|
|
110
|
+
apis: ['first.yaml', 'second.yaml'],
|
|
111
|
+
decorate: true,
|
|
112
|
+
preprocess: true,
|
|
113
|
+
},
|
|
114
|
+
ConfigFixture as any,
|
|
115
|
+
'cli-version'
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
const config = loadConfig();
|
|
119
|
+
expect(config.styleguide.skipDecorators).not.toHaveBeenCalled();
|
|
120
|
+
expect(config.styleguide.skipPreprocessors).not.toHaveBeenCalled();
|
|
121
|
+
});
|
|
122
|
+
});
|