@theia/core
Version:
Theia is a cloud & desktop IDE framework implemented in TypeScript.
331 lines • 18.8 kB
JavaScript
"use strict";
// *****************************************************************************
// Copyright (C) 2022 Ericsson and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
// *****************************************************************************
Object.defineProperty(exports, "__esModule", { value: true });
const inversify_1 = require("inversify");
const preference_validation_service_1 = require("./preference-validation-service");
const preference_contribution_1 = require("./preference-contribution");
const preference_language_override_service_1 = require("./preference-language-override-service");
const assert = require("assert");
/* eslint-disable no-null/no-null */
describe('Preference Validation Service', () => {
const container = new inversify_1.Container();
container.bind(preference_contribution_1.PreferenceSchemaProvider).toConstantValue({ getDefaultValue: preference_contribution_1.PreferenceSchemaProvider.prototype.getDefaultValue });
container.bind(preference_language_override_service_1.PreferenceLanguageOverrideService).toSelf().inSingletonScope();
const validator = container.resolve(preference_validation_service_1.PreferenceValidationService);
const validateBySchema = validator.validateBySchema.bind(validator, 'dummy');
describe('should validate strings', () => {
const expected = 'expected';
it('good input -> should return the same string', () => {
const actual = validateBySchema(expected, { type: 'string' });
assert.strictEqual(actual, expected);
});
it('bad input -> should return the default', () => {
const actual = validateBySchema(3, { type: 'string', default: expected });
assert.strictEqual(actual, expected);
});
it('bad input -> should return string even if default is not a string', () => {
const actual = validateBySchema(3, { type: 'string', default: 3 });
assert.strictEqual(typeof actual, 'string');
assert.strictEqual(actual, '3');
});
it('bad input -> should return an empty string if no default', () => {
const actual = validateBySchema(3, { type: 'string' });
assert.strictEqual(actual, '');
});
});
describe('should validate numbers', () => {
const expected = 1.23;
it('good input -> should return the same number', () => {
const actual = validateBySchema(expected, { type: 'number' });
assert.strictEqual(actual, expected);
});
it('bad input -> should return the default', () => {
const actual = validateBySchema('zxy', { type: 'number', default: expected });
assert.strictEqual(actual, expected);
});
it('bad input -> should return a number even if the default is not a number', () => {
const actual = validateBySchema('zxy', { type: 'number', default: ['fun array'] });
assert.strictEqual(actual, 0);
});
it('bad input -> should return 0 if no default', () => {
const actual = validateBySchema('zxy', { type: 'number' });
assert.strictEqual(actual, 0);
});
it('should do its best to make a number of a string', () => {
const actual = validateBySchema(expected.toString(), { type: 'number' });
assert.strictEqual(actual, expected);
});
it('should return the max if input is greater than max', () => {
const maximum = 50;
const actual = validateBySchema(100, { type: 'number', maximum });
assert.strictEqual(actual, maximum);
});
it('should return the minimum if input is less than minimum', () => {
const minimum = 30;
const actual = validateBySchema(15, { type: 'number', minimum });
assert.strictEqual(actual, minimum);
});
});
describe('should validate integers', () => {
const expected = 2;
it('good input -> should return the same number', () => {
const actual = validateBySchema(expected, { type: 'integer' });
assert.strictEqual(actual, expected);
});
it('bad input -> should return the default', () => {
const actual = validateBySchema('zxy', { type: 'integer', default: expected });
assert.strictEqual(actual, expected);
});
it('bad input -> should return 0 if no default', () => {
const actual = validateBySchema('zxy', { type: 'integer' });
assert.strictEqual(actual, 0);
});
it('should round a non-integer', () => {
const actual = validateBySchema(1.75, { type: 'integer' });
assert.strictEqual(actual, expected);
});
});
describe('should validate booleans', () => {
it('good input -> should return the same value', () => {
assert.strictEqual(validateBySchema(true, { type: 'boolean' }), true);
assert.strictEqual(validateBySchema(false, { type: 'boolean' }), false);
});
it('bad input -> should return the default', () => {
const actual = validateBySchema(['not a boolean!'], { type: 'boolean', default: true });
assert.strictEqual(actual, true);
});
it('bad input -> should return false if no default', () => {
const actual = validateBySchema({ isBoolean: 'no' }, { type: 'boolean' });
assert.strictEqual(actual, false);
});
it('should treat string "true" and "false" as equivalent to booleans', () => {
assert.strictEqual(validateBySchema('true', { type: 'boolean' }), true);
assert.strictEqual(validateBySchema('false', { type: 'boolean' }), false);
});
});
describe('should validate null', () => {
it('should always just return null', () => {
assert.strictEqual(validateBySchema({ whatever: ['anything'] }, { type: 'null' }), null);
assert.strictEqual(validateBySchema('not null', { type: 'null' }), null);
assert.strictEqual(validateBySchema(123, { type: 'null', default: 123 }), null);
});
});
describe('should validate enums', () => {
const expected = 'expected';
const defaultValue = 'default';
const options = [expected, defaultValue, 'other-value'];
it('good value -> should return same value', () => {
const actual = validateBySchema(expected, { enum: options });
assert.strictEqual(actual, expected);
});
it('bad value -> should return default value', () => {
const actual = validateBySchema('not-in-enum', { enum: options, defaultValue });
assert.strictEqual(actual, defaultValue);
});
it('bad value -> should return first value if no default or bad default', () => {
const noDefault = validateBySchema(['not-in-enum'], { enum: options });
assert.strictEqual(noDefault, expected);
const badDefault = validateBySchema({ inEnum: false }, { enum: options, default: 'not-in-enum' });
assert.strictEqual(badDefault, expected);
});
});
describe('should validate objects', () => {
it('should reject non object types', () => {
const schema = { type: 'object' };
assert.deepStrictEqual(validateBySchema(null, schema), {});
assert.deepStrictEqual(validateBySchema('null', schema), {});
assert.deepStrictEqual(validateBySchema(3, schema), {});
});
it('should reject objects that are missing required fields', () => {
const schema = { type: 'object', properties: { 'required': { type: 'string' }, 'not-required': { type: 'number' } }, required: ['required'] };
assert.deepStrictEqual(validateBySchema({ 'not-required': 3 }, schema), {});
const defaultValue = { required: 'present' };
assert.deepStrictEqual(validateBySchema({ 'not-required': 3 }, Object.assign(Object.assign({}, schema), { defaultValue })), defaultValue);
});
it('should reject objects that have impermissible extra properties', () => {
const schema = { type: 'object', properties: { 'required': { type: 'string' } }, additionalProperties: false };
assert.deepStrictEqual(validateBySchema({ 'required': 'hello', 'not-required': 3 }, schema), {});
});
it('should accept objects with extra properties if extra properties are not forbidden', () => {
const input = { 'required': 'hello', 'not-forbidden': 3 };
const schema = { type: 'object', properties: { 'required': { type: 'string' } }, additionalProperties: true };
assert.deepStrictEqual(validateBySchema(input, schema), input);
assert.deepStrictEqual(validateBySchema(input, Object.assign(Object.assign({}, schema), { additionalProperties: undefined })), input);
});
it("should reject objects with properties that violate the property's rules", () => {
const input = { required: 'not-a-number!' };
const schema = { type: 'object', properties: { required: { type: 'number' } } };
assert.deepStrictEqual(validateBySchema(input, schema), {});
});
it('should reject objects with extra properties that violate the extra property rules', () => {
const input = { required: 3, 'not-required': 'not-a-number!' };
const schema = { type: 'object', properties: { required: { type: 'number' } }, additionalProperties: { type: 'number' } };
assert.deepStrictEqual(validateBySchema(input, schema), {});
});
});
describe('should validate arrays', () => {
const expected = ['one-string', 'two-string'];
it('good input -> should return same value', () => {
const actual = validateBySchema(expected, { type: 'array', items: { type: 'string' } });
assert.deepStrictEqual(actual, expected);
const augmentedExpected = [3, ...expected, 4];
const augmentedActual = validateBySchema(augmentedExpected, { type: 'array', items: { type: ['number', 'string'] } });
assert.deepStrictEqual(augmentedActual, augmentedExpected);
});
it('bad input -> should filter out impermissible items', () => {
const actual = validateBySchema([3, ...expected, []], { type: 'array', items: { type: 'string' } });
assert.deepStrictEqual(actual, expected);
});
});
describe('should validate tuples', () => {
const schema = {
'type': 'array',
'items': [{
'type': 'number',
},
{
'type': 'string',
}],
};
it('good input -> returns same object', () => {
const expected = [1, 'two'];
assert.strictEqual(validateBySchema(expected, schema), expected);
});
it('bad input -> should use the default if supplied present and valid', () => {
const defaultValue = [8, 'three'];
const withDefault = Object.assign(Object.assign({}, schema), { default: defaultValue });
assert.strictEqual(validateBySchema('not even an array!', withDefault), defaultValue);
assert.strictEqual(validateBySchema(['first fails', 'second ok'], withDefault), defaultValue);
assert.strictEqual(validateBySchema([], withDefault), defaultValue);
assert.strictEqual(validateBySchema([2, ['second fails']], withDefault), defaultValue);
});
it('bad input -> in the absence of a default, it should return any good values or the default for each subschema', () => {
const withSubDefault = Object.assign(Object.assign({}, schema), { items: [{ type: 'string', default: 'cool' }, ...schema.items] });
assert.deepStrictEqual(validateBySchema('not an array', withSubDefault), ['cool', 0, '']);
assert.deepStrictEqual(validateBySchema([2, 8, null], withSubDefault), ['cool', 8, '']);
});
it("bad input -> uses the default, but fixes fields that don't match schema", () => {
const defaultValue = [8, 8];
const withDefault = Object.assign(Object.assign({}, schema), { default: defaultValue });
assert.deepStrictEqual(validateBySchema('something invalid', withDefault), [8, '']);
});
});
describe('should validate type arrays', () => {
const type = ['boolean', 'string', 'number'];
it('good input -> returns same value', () => {
const goodBoolean = validateBySchema(true, { type });
assert.strictEqual(goodBoolean, true);
const goodString = validateBySchema('string', { type });
assert.strictEqual(goodString, 'string');
const goodNumber = validateBySchema(1.23, { type });
assert.strictEqual(goodNumber, 1.23);
});
it('bad input -> returns default if default valid', () => {
const stringDefault = 'default';
const booleanDefault = true;
const numberDefault = 100;
assert.strictEqual(validateBySchema([], { type, default: stringDefault }), stringDefault);
assert.strictEqual(validateBySchema([], { type, default: booleanDefault }), booleanDefault);
assert.strictEqual(validateBySchema([], { type, default: numberDefault }), numberDefault);
});
it("bad input -> returns first validator's result if no default or bad default", () => {
assert.strictEqual(validateBySchema([], { type }), false);
assert.strictEqual(validateBySchema([], { type, default: {} }), false);
});
});
describe('should validate anyOfs', () => {
const schema = { anyOf: [{ type: 'number', minimum: 1 }, { type: 'array', items: { type: 'string' } }], default: 5 };
it('good input -> returns same value', () => {
assert.strictEqual(validateBySchema(3, schema), 3);
const goodArray = ['a string', 'here too'];
assert.strictEqual(validateBySchema(goodArray, schema), goodArray);
});
it('bad input -> returns default if present and valid', () => {
assert.strictEqual(validateBySchema({}, schema), 5);
});
it('bad input -> first validator, if default absent or default ill-formed', () => {
assert.strictEqual(validateBySchema({}, Object.assign(Object.assign({}, schema), { default: 0 })), 1);
assert.strictEqual(validateBySchema({}, Object.assign(Object.assign({}, schema), { default: undefined })), 1);
});
});
describe('should validate oneOfs', () => {
// Between 4 and 6 should be rejected
const schema = { oneOf: [{ type: 'number', minimum: 1, maximum: 6 }, { type: 'number', minimum: 4, maximum: 10 }], default: 8 };
it('good input -> returns same value', () => {
assert.strictEqual(validateBySchema(2, schema), 2);
assert.strictEqual(validateBySchema(7, schema), 7);
});
it('bad input -> returns default if present and valid', () => {
assert.strictEqual(validateBySchema(5, schema), 8);
});
it('bad input -> returns value if default absent or invalid.', () => {
assert.strictEqual(validateBySchema(5, Object.assign(Object.assign({}, schema), { default: undefined })), 5);
});
});
describe('should validate consts', () => {
const schema = { const: { 'the only': 'possible value' }, default: 'ignore-the-default' };
const goodValue = { 'the only': 'possible value' };
it('good input -> returns same value', () => {
assert.strictEqual(validateBySchema(goodValue, schema), goodValue);
});
it('bad input -> returns the const value for any other value', () => {
assert.deepStrictEqual(validateBySchema('literally anything else', schema), goodValue);
assert.deepStrictEqual(validateBySchema('ignore-the-default', schema), goodValue);
});
});
describe('should maintain triple equality for valid object types', () => {
const arraySchema = { type: 'array', items: { type: 'string' } };
it('maintains triple equality for arrays', () => {
const input = ['one-string', 'two-string'];
assert(validateBySchema(input, arraySchema) === input);
});
it('does not maintain triple equality if the array is only partially correct', () => {
const input = ['one-string', 'two-string', 3];
assert.notStrictEqual(validateBySchema(input, arraySchema), input);
});
it('maintains triple equality for objects', () => {
const schema = {
'type': 'object',
properties: {
primitive: { type: 'string' },
complex: { type: 'object', properties: { nested: { type: 'number' } } }
}
};
const input = { primitive: 'is a string', complex: { nested: 3 } };
assert(validateBySchema(input, schema) === input);
});
});
it('should return the value if any error occurs', () => {
let wasCalled = false;
const originalValidator = validator['validateString'];
validator['validateString'] = () => {
wasCalled = true;
throw new Error('Only a test!');
};
const input = { shouldBeValid: false };
const output = validateBySchema(input, { type: 'string' });
assert(wasCalled);
assert(input === output);
validator['validateString'] = originalValidator;
});
it('should return the same object if no validation possible', () => {
for (const input of ['whatever', { valid: 'hard to say' }, 234, ["no one knows if I'm not", 'so I am']]) {
assert(validateBySchema(input, {}) === input);
}
});
});
//# sourceMappingURL=preference-validation-service.spec.js.map