ask-cli
Version:
Alexa Skills Kit (ASK) Command Line Interfaces
287 lines (227 loc) • 12.6 kB
JavaScript
const { expect } = require('chai');
const sinon = require('sinon');
const { camelCase, standardize } = require('@src/utils/string-utils');
const CliError = require('@src/exceptions/cli-error');
const { customizationMap } = require('@src/commands/smapi/customizations/parameters-map');
const { CliCustomizationProcessor, BODY_PATH_DELIMITER, MAX_NESTED_PROPERTIES } = require('@src/commands/smapi/cli-customization-processor');
const makeParameter = (options = {}) => {
const defaultValues = {
name: 'someId',
in: 'path',
description: 'some description',
required: true,
type: 'string'
};
return { ...defaultValues, ...options };
};
function mapExpectedParams(parentName, bodyPropertyName) {
const name = `${parentName} ${bodyPropertyName}`;
const bodyPath = `${parentName}${BODY_PATH_DELIMITER}${bodyPropertyName}`;
const key = camelCase(name);
return { name, bodyPath, key };
}
describe('Smapi test - CliCustomizationProcessor class', () => {
const enumValues = ['a', 'b'];
const refObjectDescription = 'description for type';
const enumDescription = 'some enum description';
const parentDescription = 'some parent description';
const bodyPropertyOneName = 'propertyOne';
const bodyPropertyTwoName = 'propertyTwo';
const nestedPropertyName = 'nestedProperty';
const nestedEnumPropertyName = 'nestedEnumProperty';
const bodyProperty = { type: 'number' };
const bodyPropertyWithSimpleArray = { type: 'array', items: { $ref: '#/definitions/SomeEnumType' } };
const bodyPropertyWithComplexArray = { type: 'array', items: { $ref: '#/definitions/SomeSimpleObjectType' } };
const bodyPropertyWithDescription = { description: 'property description', type: 'boolean' };
const nestedProperty = { $ref: 'SomeObjectType' };
const nestedEnumProperty = { $ref: 'SomeEnumTypeWithDescription' };
let processor;
let parentOperation;
let definitions;
const objectWithTooManyProperties = {};
for (let i = 0; i < MAX_NESTED_PROPERTIES; i++) {
objectWithTooManyProperties[i] = 'test';
}
beforeEach(() => {
processor = new CliCustomizationProcessor();
parentOperation = { customizationMetadata: {
flatParamsMap: new Map()
} };
definitions = new Map([
['SomeEnumType', { enum: enumValues }],
['SomeEnumTypeWithDescription', { enum: enumValues, description: enumDescription }],
['SomeTypeWithDescription', { enum: enumValues, description: refObjectDescription }],
['SomeSimpleObjectType', { type: 'object', properties: {} }],
['SomeObjectType', { description: parentDescription,
required: ['propertyTwo'],
properties: { propertyOne: bodyProperty,
propertyTwo: bodyPropertyWithDescription,
propertyThree: { $ref: 'SomeEnumType' },
propertyFour: { $ref: 'SomeEnumTypeWithDescription' },
propertyFive: bodyPropertyWithSimpleArray,
propertySix: bodyPropertyWithComplexArray,
propertySeven: { type: 'object', properties: { } } } }],
['SomeNestedType', { required: [nestedPropertyName],
properties: { nestedProperty } }],
['SomeTooManyPropertiesType', { properties: { ...objectWithTooManyProperties } }],
['SomeTooNestedType', { properties: { test: { $ref: 'SomeNestedType' } } }],
['SomeNestedEnumType', { required: [nestedPropertyName],
description: refObjectDescription,
properties: { nestedEnumProperty } }]
]);
});
describe('# processOperationName', () => {
it('| should convert to kebab case and strip last 2 characters for version', () => {
const result = processor.processOperationName('getContentUploadByIdV1');
expect(result).eql('get-content-upload-by-id');
});
});
describe('# processOperation', () => {
it('| should create empty flatParamsMap on operator object', () => {
const operation = { customizationMetadata: {} };
processor.processOperation('test', operation);
expect(operation.customizationMetadata.flatParamsMap).eql(new Map());
});
});
describe('# processParameter', () => {
it('| should add simple non body paramter', () => {
const parameter = makeParameter();
processor.processParameter(parameter, parentOperation, definitions);
const key = camelCase(parameter.name);
const { flatParamsMap } = parentOperation.customizationMetadata;
const expected = { ...parameter, enum: undefined };
expect(flatParamsMap.get(standardize(key))).eql(expected);
});
it('| should skip processing of parameter', () => {
const parameter = makeParameter();
const skip = true;
sinon.stub(customizationMap, 'get').returns({ skip });
processor.processParameter(parameter, parentOperation, definitions);
expect(parentOperation.customizationMetadata.flatParamsMap.size).eql(0);
});
it('| should add simple array of non body paramter', () => {
const parameter = makeParameter({ type: 'array' });
processor.processParameter(parameter, parentOperation, definitions);
const key = camelCase(parameter.name);
const { flatParamsMap } = parentOperation.customizationMetadata;
const expected = { ...parameter, enum: undefined, isArray: true };
expect(flatParamsMap.get(standardize(key))).eql(expected);
});
it('| should add reference enum of non body paramter', () => {
const parameter = makeParameter({ type: 'array', items: { $ref: '#/definitions/SomeEnumType' } });
processor.processParameter(parameter, parentOperation, definitions);
const key = camelCase(parameter.name);
const { flatParamsMap } = parentOperation.customizationMetadata;
const expected = { ...parameter, enum: enumValues, isArray: true };
expect(flatParamsMap.get(standardize(key))).eql(expected);
});
it('| should add reference items of non body paramter and use reference item description if not defined', () => {
const parameter = makeParameter({ type: 'array', items: { $ref: '#/definitions/SomeTypeWithDescription' } });
processor.processParameter(parameter, parentOperation, definitions);
const key = camelCase(parameter.name);
const { flatParamsMap } = parentOperation.customizationMetadata;
const expected = { ...parameter, enum: enumValues, description: refObjectDescription, isArray: true };
expect(flatParamsMap.get(standardize(key))).eql(expected);
});
it('| should add body parameter', () => {
const parameter = makeParameter({ in: 'body', schema: { $ref: '#/definitions/SomeObjectType' } });
processor.processParameter(parameter, parentOperation, definitions);
const rootName = camelCase(parameter.name);
const { flatParamsMap } = parentOperation.customizationMetadata;
const expectedOne = { ...bodyProperty,
description: undefined,
required: false,
rootName,
bodyPath: bodyPropertyOneName,
name: bodyPropertyOneName,
json: false,
isNumber: true };
expect(flatParamsMap.get(standardize(bodyPropertyOneName))).eql(expectedOne);
const expectedTwo = { ...bodyPropertyWithDescription,
required: true,
rootName,
bodyPath: bodyPropertyTwoName,
name: bodyPropertyTwoName,
json: false,
isBoolean: true };
expect(flatParamsMap.get(standardize(bodyPropertyTwoName))).eql(expectedTwo);
});
it('| should add customized body parameter', () => {
sinon.stub(customizationMap, 'get').returns({
skipUnwrap: true
});
const parameter = makeParameter({ in: 'body', schema: { $ref: '#/definitions/SomeObjectType' } });
processor.processParameter(parameter, parentOperation, definitions);
const { flatParamsMap } = parentOperation.customizationMetadata;
const { name, required, description } = parameter;
const expected = { name, required, description, json: true };
expect(flatParamsMap.get(standardize(parameter.name))).eql(expected);
});
it('| should add customized body parameter with custom properties', () => {
const name = 'customName';
const required = true;
const description = 'custom description';
sinon.stub(customizationMap, 'get').returns({
customParameters: [{ name, description, required }]
});
const parameter = makeParameter({ in: 'body', schema: { $ref: '#/definitions/SomeObjectType' } });
processor.processParameter(parameter, parentOperation, definitions);
const { flatParamsMap } = parentOperation.customizationMetadata;
const expected = { name, required, description };
expect(flatParamsMap.get(standardize(name))).eql(expected);
});
it('| should add one level deep body parameter', () => {
const parameter = makeParameter({ in: 'body', schema: { $ref: '#/definitions/SomeNestedType' } });
processor.processParameter(parameter, parentOperation, definitions);
const rootName = camelCase(parameter.name);
const { flatParamsMap } = parentOperation.customizationMetadata;
let { name, bodyPath, key } = mapExpectedParams(nestedPropertyName, bodyPropertyOneName);
let expected = { description: parentDescription,
name,
rootName,
bodyPath,
required: false,
enum: null,
json: false,
isNumber: true,
type: 'number' };
expect(flatParamsMap.get(standardize(key))).eql(expected);
const expectedParams = mapExpectedParams(nestedPropertyName, bodyPropertyTwoName);
name = expectedParams.name;
bodyPath = expectedParams.bodyPath;
key = expectedParams.key;
expected = { ...bodyPropertyWithDescription, name, rootName, bodyPath, required: true, enum: null, json: false, isBoolean: true };
expect(flatParamsMap.get(standardize(key))).eql(expected);
});
it('| should fail since the body parameter is nested too deep', () => {
const parameter = makeParameter({ in: 'body', schema: { $ref: '#/definitions/SomeTooNestedType' } });
const badFn = () => processor.processParameter(parameter, parentOperation, definitions);
expect(badFn).to.throw(CliError, 'Cannot unwrap more then');
});
it('| should fail since the body parameter has too many properties', () => {
const parameter = makeParameter({ in: 'body', schema: { $ref: '#/definitions/SomeTooManyPropertiesType' } });
const badFn = () => processor.processParameter(parameter, parentOperation, definitions);
expect(badFn).to.throw(CliError, 'number of body properties exceeds');
});
it('| should add one level deep body enum parameter', () => {
const parameter = makeParameter({ in: 'body', schema: { $ref: '#/definitions/SomeNestedEnumType' } });
processor.processParameter(parameter, parentOperation, definitions);
const rootName = camelCase(parameter.name);
const { flatParamsMap } = parentOperation.customizationMetadata;
const name = nestedEnumPropertyName;
const bodyPath = nestedEnumPropertyName;
const expected = { ...bodyProperty,
name,
rootName,
bodyPath,
required: false,
enum: enumValues,
description: enumDescription,
type: undefined };
expect(flatParamsMap.get(standardize(nestedEnumPropertyName))).eql(expected);
});
});
afterEach(() => {
sinon.restore();
});
});