adxutil
Version:
Utilities tools for Askia Design eXtension
1,200 lines (1,005 loc) • 122 kB
JavaScript
describe('ADXValidator', function () {
var fs = require('fs'),
spies = {},
format = require('util').format,
common,
adxValidator,
Validator,
Configurator,
errMsg,
warnMsg,
successMsg,
msg;
beforeEach(function () {
// Clean the cache, obtain a fresh instance of the ADXValidator each time
var adxValidatorKey = require.resolve('../../app/validator/ADXValidator.js'),
commonKey = require.resolve('../../app/common/common.js');
delete require.cache[commonKey];
common = require('../../app/common/common.js');
delete require.cache[adxValidatorKey];
adxValidator = require('../../app/validator/ADXValidator.js');
Configurator = require('../../app/configurator/ADXConfigurator.js').Configurator;
Validator = adxValidator.Validator;
var oldValidate = Validator.prototype.validate;
spies.validateHook = function () {};
spies.sequence = null;
Validator.prototype.validate = function () {
this.rootdir = '/root';
if (spies.sequence) {
this.validators.sequence = spies.sequence;
}
spies.validateHook.apply(this, arguments);
oldValidate.apply(this, arguments);
};
// Messages
errMsg = common.messages.error;
warnMsg = common.messages.warning;
successMsg = common.messages.success;
msg = common.messages.message;
// Court-circuit the Configurator
spyOn(Configurator.prototype, 'load');
// Court-circuit the validation outputs
spyOn(common, 'writeError');
spyOn(common, 'writeWarning');
spyOn(common, 'writeSuccess');
spyOn(common, 'writeMessage');
// Court-circuit the access of the filesystem
spies.fs = {
stat : spyOn(fs, 'stat'),
exists : spyOn(fs, 'exists'),
readdirSync : spyOn(fs, 'readdirSync'),
readFile : spyOn(fs, 'readFile')
};
// Add matchers
this.addMatchers({
/**
* Validate that the actual array contains the expected value
* @param {*} expected
* @returns {Boolean}
*/
toContains: function(expected) {
var actual = this.actual,
notText = this.isNot ? " not" : "",
expectedValue = expected;
this.message = function () {
return "Expected " + actual + notText + " contains " + expectedValue;
};
if (!Array.isArray(actual)) {
return false;
}
return actual.some(function (value) {
if (Array.isArray(expected)) {
expectedValue = value;
return (expected.indexOf(value) !== -1);
}
return value === expected;
});
}
});
});
// Add extra hook
function extraHook(fn){
var previousHook = spies.validateHook;
spies.validateHook = function () {
previousHook.apply(this, arguments);
fn.apply(this, arguments);
};
}
describe('#validate', function () {
beforeEach(function () {
spyOn(Validator.prototype, 'writeError');
spyOn(Validator.prototype, 'writeWarning');
spyOn(Validator.prototype, 'writeSuccess');
spyOn(Validator.prototype, 'writeMessage');
});
it("should call each function in the ADXValidator.validators.sequence", function () {
var seqLen, callCount = 0;
function increment() {
callCount++;
this.resume(null);
}
spies.validateHook = function () {
var i, seq = this.validators.sequence;
for (i = 0, seqLen = seq.length; i < seqLen; i++) {
this[seq[i]] = increment;
}
};
adxValidator.validate(null, 'adx/path/dir');
expect(callCount).toBe(seqLen);
});
it("should break the validations when at least one validators call #resume with an error", function () {
var key, callCount = 0;
function increment() {
var err = null;
callCount++;
if (callCount === 3) {
err = new Error("An error occurred in the third validation");
}
this.resume(err);
}
spies.validateHook = function () {
var i, seq = this.validators.sequence;
for (i = 0, seqLen = seq.length; i < seqLen; i++) {
this[seq[i]] = increment;
}
};
adxValidator.validate(null, 'adx/path/dir');
expect(callCount).toBe(3);
});
it("should call the callback function in arg at the end of the validation", function () {
var key, wasCalled = false;
function fakeValidation() {
this.resume(null);
}
spies.validateHook = function () {
var i, seq = this.validators.sequence;
for (i = 0, seqLen = seq.length; i < seqLen; i++) {
this[seq[i]] = fakeValidation;
}
};
adxValidator.validate(null, 'adx/path/dir', function () {
wasCalled = true;
});
expect(wasCalled).toBe(true);
});
it("should output error message of the failed validator", function () {
spies.validateHook = function () {
this.validators.sequence = ['raiseError'];
this.raiseError = function () {
this.resume(new Error("An error occurred"));
};
};
adxValidator.validate(null, 'adx/path/dir');
expect(Validator.prototype.writeError).toHaveBeenCalledWith("An error occurred");
});
it("should run the unit tests when called with the `program#test=true`", function () {
var seq;
spies.validateHook = function () {
this.resume = function () {
seq = this.validators.sequence;
};
};
adxValidator.validate({
test : true
}, 'adx/path/dir');
expect(seq).toContains(['runADXUnitTests']);
});
it("should not run the unit tests when called with the `program#test=false`", function () {
var seq;
spies.validateHook = function () {
this.resume = function () {
seq = this.validators.sequence;
};
};
adxValidator.validate({
test : false
}, 'adc/path/dir');
expect(seq).not.toContains(['runADXUnitTests']);
});
it("should run the auto unit tests when called with the `program#autoTest=true`", function () {
var seq;
spies.validateHook = function () {
this.resume = function () {
seq = this.validators.sequence;
};
};
adxValidator.validate({
autoTest : true
}, 'adx/path/dir');
expect(seq).toContains(['runAutoTests']);
});
it("should not run the auto unit tests when called with the `program#autoTest=false`", function () {
var seq;
spies.validateHook = function () {
this.resume = function () {
seq = this.validators.sequence;
};
};
adxValidator.validate({
autoTest : false
}, 'adx/path/dir');
expect(seq).not.toContains(['runAutoTests']);
});
it("should run the xml validation when called with the `program#xml=true`", function () {
var seq;
spies.validateHook = function () {
this.resume = function () {
seq = this.validators.sequence;
};
};
adxValidator.validate({
xml : true
}, 'adx/path/dir');
expect(seq).toContains([
'validateXMLAgainstXSD',
'initConfigXMLDoc',
'validateADXInfo',
'validateADXInfoConstraints',
'validateADXOutputs',
'validateADXProperties'
]);
});
it("should not run the xml validation when called with the `program#xml=false`", function () {
var seq;
spies.validateHook = function () {
this.resume = function () {
seq = this.validators.sequence;
};
};
adxValidator.validate({
xml : false
}, 'adx/path/dir');
expect(seq).not.toContains([
'validateXMLAgainstXSD',
'initConfigXMLDoc',
'validateADXInfo',
'validateADXInfoConstraints',
'validateADXOutputs',
'validateADXProperties',
'validateMasterPage'
]);
});
it("should display a report with the execution time", function () {
spies.validateHook = function () {
this.validators.sequence = [];
};
adxValidator.validate(null, '/adx/path/dir');
expect(Validator.prototype.writeMessage).toHaveBeenCalledWith(msg.validationFinishedIn, 0);
});
it("should display a report using #writeError with the number of success, warnings and failures when at least one error", function () {
spies.validateHook = function () {
this.validators.sequence = [];
this.validators.sequence.length = 8;
this.report.runs = 6;
this.report.success = 1;
this.report.warnings = 2;
this.report.errors = 3;
};
adxValidator.validate(null, '/adx/path/dir');
expect(Validator.prototype.writeError).toHaveBeenCalledWith(format(msg.validationReport,6, 8, 1, 2, 3, 2));
});
it("should display a report in #writeWarning with the number of success, warnings and failures when at least one warning", function () {
spies.validateHook = function () {
this.validators.sequence = [];
this.validators.sequence.length = 6;
this.report.runs = 6;
this.report.success = 1;
this.report.warnings = 2;
this.report.errors = 0;
};
adxValidator.validate(null, '/adx/path/dir');
expect(Validator.prototype.writeWarning).toHaveBeenCalledWith(format(msg.validationReport, 6, 6, 1, 2, 0, 0));
});
it("should display a report in #writeSuccess with the number of success, warnings and failures when no warning and error", function () {
spies.validateHook = function () {
this.validators.sequence = [];
this.validators.sequence.length = 6;
this.report.runs = 6;
this.report.success = 1;
this.report.warnings = 0;
this.report.errors = 0;
};
adxValidator.validate(null, '/adx/path/dir');
expect(Validator.prototype.writeSuccess).toHaveBeenCalledWith(format(msg.validationReport,6, 6, 1, 0, 0, 0));
});
it("should set the #logger when it's defined in the options arg", function () {
var instance;
spies.validateHook = function () {
this.validators.sequence = [];
instance = this;
};
var logger = {
key : "val"
};
adxValidator.validate({
logger : logger
}, '/adx/path/dir');
expect(instance.logger).toBe(logger);
});
it("should set the #printMode when it's defined in the options arg", function () {
var instance;
spies.validateHook = function () {
this.validators.sequence = [];
instance = this;
};
adxValidator.validate({
printMode : 'html'
}, '/adx/path/dir');
expect(instance.printMode).toBe('html');
});
});
describe('#validatePathArg', function () {
beforeEach(function () {
// Modify the sequence of the validation to only call the validatePathArg method
spies.sequence = ['validatePathArg'];
spyOn(Validator.prototype, 'writeError');
spyOn(Validator.prototype, 'writeWarning');
spyOn(Validator.prototype, 'writeSuccess');
spyOn(Validator.prototype, 'writeMessage');
});
it("should output an error when the path specified doesn't exist", function () {
spies.fs.stat.andCallFake(function (path, callback) {
callback(new Error("No such file or directory"));
});
adxValidator.validate(null, '/adx/path/dir');
expect(Validator.prototype.writeError).toHaveBeenCalledWith(format(errMsg.noSuchFileOrDirectory, "\\adx\\path\\dir"));
});
it("should not output an error when the path specified exist", function () {
spies.fs.stat.andCallFake(function (path, callback) {
callback(null);
});
adxValidator.validate(null, '/adx/path/dir');
expect(Validator.prototype.writeError).not.toHaveBeenCalled();
});
it("should use the current directory when the path is not specified", function () {
spyOn(process, 'cwd').andReturn('/cwd/');
var dir;
spies.validateHook = function () {
dir = this.adxDirectoryPath;
};
adxValidator.validate(null);
expect(dir).toBe('/cwd/');
});
});
describe('#validateADXDirectoryStructure', function () {
beforeEach(function () {
// Modify the sequence of the validation to only call the validateADXDirectoryStructure method
spies.sequence = ['validateADXDirectoryStructure'];
spyOn(Validator.prototype, 'writeError');
spyOn(Validator.prototype, 'writeWarning');
spyOn(Validator.prototype, 'writeSuccess');
spyOn(Validator.prototype, 'writeMessage');
});
it("should output an error when the config.xml file doesn't exist", function () {
spies.fs.exists.andCallFake(function (path, callback) {
callback(false);
});
adxValidator.validate(null, '/adx/path/dir');
expect(Validator.prototype.writeError).toHaveBeenCalledWith(errMsg.noConfigFile);
});
it("should not output an error when the config.xml file exist", function () {
spies.fs.exists.andCallFake(function (path, callback) {
callback(true);
});
adxValidator.validate(null, '/adx/path/dir');
expect(Validator.prototype.writeError).not.toHaveBeenCalled();
});
it("should output a success message when the config.xml file exist", function () {
spies.fs.exists.andCallFake(function (path, callback) {
callback(true);
});
adxValidator.validate(null, '/adx/path/dir');
expect(Validator.prototype.writeSuccess).toHaveBeenCalled();
});
it("should search the `resources` directory", function () {
var searchResourcesDirectory = false;
spies.fs.exists.andCallFake(function (path, callback) {
callback(true);
});
spies.fs.stat.andCallFake(function (path) {
if (path === '\\adx\\path\\dir\\resources') {
searchResourcesDirectory = true;
}
});
adxValidator.validate('null', '/adx/path/dir');
expect(searchResourcesDirectory).toBe(true);
});
function loadResourcesDirectory(mode) {
it("should search the `resources/" + mode + "/` directory", function () {
var searchResources = false;
spies.fs.exists.andCallFake(function (path, callback) {
callback(true);
});
spies.fs.stat.andCallFake(function (path, callback) {
if (path === '\\adx\\path\\dir\\resources') {
callback(null, true);
} else if (path === '\\adx\\path\\dir\\resources\\' + mode) {
searchResources = true;
} else {
callback(null, false);
}
});
adxValidator.validate('null', '/adx/path/dir');
expect(searchResources).toBe(true);
});
it("should load files from the `resources/" + mode + "/` directory", function () {
var files = ['123.txt', '456.html'],
key = (mode === 'static') ? 'statics' : mode,
instance;
spies.fs.exists.andCallFake(function (path, callback) {
callback(true);
});
spies.validateHook = function () {
instance = this;
};
spies.fs.stat.andCallFake(function (path, callback) {
if (path === '\\adx\\path\\dir\\resources') {
callback(null, true);
} else if (path === '\\adx\\path\\dir\\resources\\' + mode) {
callback(null, true);
} else {
callback(null, false);
}
});
spies.fs.readdirSync.andReturn(files);
spyOn(common, 'isIgnoreFile').andReturn(false);
adxValidator.validate(null, '/adx/path/dir');
expect(instance.dirResources[key]).toEqual({
isExist : true,
'123.txt' : '123.txt',
'456.html' : '456.html'
});
});
it("should ignore certain files from the `resources/" + mode + "/` directory", function () {
var files = ['123.txt', '456.html', 'Thumbs.db'],
key = (mode === 'static') ? 'statics' : mode,
instance;
spies.fs.exists.andCallFake(function (path, callback) {
callback(true);
});
spies.validateHook = function () {
instance = this;
};
spies.fs.stat.andCallFake(function (path, callback) {
if (path === '\\adx\\path\\dir\\resources') {
callback(null, true);
} else if (path === '\\adx\\path\\dir\\resources\\' + mode) {
callback(null, true);
} else {
callback(null, false);
}
});
spies.fs.readdirSync.andReturn(files);
spyOn(common, 'isIgnoreFile').andCallFake(function (f) {
return (f === 'Thumbs.db');
});
adxValidator.validate('null', '/adx/path/dir');
expect(instance.dirResources[key]).toEqual({
isExist : true,
'123.txt' : '123.txt',
'456.html' : '456.html'
});
});
}
['dynamic', 'static', 'share'].forEach(loadResourcesDirectory);
});
describe('#validateFileExtensions', function () {
beforeEach(function () {
// Modify the sequence of the validation to only call the validateFileExtensions method
spies.sequence = ['validateFileExtensions'];
spyOn(Validator.prototype, 'writeError');
spyOn(Validator.prototype, 'writeWarning');
spyOn(Validator.prototype, 'writeSuccess');
spyOn(Validator.prototype, 'writeMessage');
});
var directories = ['dynamic', 'static', 'share'];
function testForbiddenExtensionIn(directoryName) {
it('should output an error when found in `' + directoryName + '` directory', function () {
(directoryName = directoryName === 'static' ? 'statics' : directoryName);
spies.validateHook = function () {
var dirResources = this.dirResources;
dirResources.isExist = true;
dirResources[directoryName].isExist = true;
dirResources[directoryName]['filewithforbiddenextension.exe'] = 'filewithforbiddenextension.exe';
};
adxValidator.validate(null, '/adx/path/dir');
expect(Validator.prototype.writeError).toHaveBeenCalledWith(format(errMsg.fileExtensionForbidden, ".exe"));
});
}
function testTrustExtensionIn(directoryName) {
it('should not output an error when found in `' + directoryName + '` directory', function () {
(directoryName = directoryName === 'static' ? 'statics' : directoryName);
spies.validateHook = function () {
var dirResources = this.dirResources;
dirResources.isExist = true;
dirResources[directoryName].isExist = true;
dirResources[directoryName]['trustfileextension.html'] = 'trustfileextension.html';
};
adxValidator.validate(null, '/adx/path/dir');
expect(Validator.prototype.writeError).not.toHaveBeenCalled();
});
it('should not output a warning when found in `' + directoryName + '` directory', function () {
(directoryName = directoryName === 'static' ? 'statics' : directoryName);
spies.validateHook = function () {
var dirResources = this.dirResources;
dirResources.isExist = true;
dirResources[directoryName].isExist = true;
dirResources[directoryName]['trustfileextension.html'] = 'trustfileextension.html';
};
adxValidator.validate(null, '/adx/path/dir');
expect(Validator.prototype.writeWarning).not.toHaveBeenCalled();
});
}
function testUnknownExtensionIn(directoryName) {
it('should output a warning when found in `' + directoryName + '` directory', function () {
(directoryName = directoryName === 'static' ? 'statics' : directoryName);
spies.validateHook = function () {
var dirResources = this.dirResources;
dirResources.isExist = true;
dirResources[directoryName].isExist = true;
dirResources[directoryName]['unknownextension.unknown'] = 'unknownextension.unknown';
};
adxValidator.validate(null, '/adx/path/dir');
expect(Validator.prototype.writeWarning).toHaveBeenCalledWith(warnMsg.untrustExtension, 'unknownextension.unknown');
});
}
describe('file with forbidden extension', function () {
directories.forEach(testForbiddenExtensionIn);
});
describe('file with trust extension', function () {
directories.forEach(testTrustExtensionIn);
});
describe('file with unknown extension', function () {
directories.forEach(testUnknownExtensionIn);
});
describe('all files are valid', function () {
beforeEach(function () {
spies.validateHook = function () {
var dirResources = this.dirResources;
dirResources.isExist = true;
dirResources.dynamic.isExist = true;
dirResources.statics.isExist = true;
dirResources.share.isExist = true;
dirResources.dynamic['valid.html'] = 'valid.html';
dirResources.statics['valid.js'] = 'valid.js';
dirResources.share['valid.css'] = 'valid.css';
};
})
it('should not output an error', function () {
adxValidator.validate(null, '/adx/path/dir');
expect(Validator.prototype.writeError).not.toHaveBeenCalled();
});
it('should output a success message', function () {
adxValidator.validate(null, '/adx/path/dir');
expect(Validator.prototype.writeSuccess).toHaveBeenCalledWith(successMsg.fileExtensionValidate);
});
});
describe('at least one invalid files', function () {
it('should output an error', function () {
spies.validateHook = function () {
var dirResources = this.dirResources;
dirResources.isExist = true;
dirResources.dynamic.isExist = true;
dirResources.statics.isExist = true;
dirResources.share.isExist = true;
dirResources.dynamic['valid.html'] = 'valid.html';
dirResources.statics['valid.js'] = 'valid.js';
dirResources.statics['invalid.exe'] = 'invalid.exe';
dirResources.share['valid.css'] = 'valid.css';
};
adxValidator.validate(null, '/adx/path/dir');
expect(Validator.prototype.writeError).toHaveBeenCalled();
});
});
});
describe('#initConfigXMLDoc', function () {
var validatorInstance = null;
beforeEach(function () {
validatorInstance = null;
spies.validateHook = function () {
validatorInstance = this;
};
// Modify the sequence of the validation to only call the initConfigXMLDoc method
spies.sequence = ['initConfigXMLDoc'];
spyOn(Validator.prototype, 'writeError');
spyOn(Validator.prototype, 'writeWarning');
spyOn(Validator.prototype, 'writeSuccess');
spyOn(Validator.prototype, 'writeMessage');
});
it("should init and load the ADXConfigurator", function () {
adxValidator.validate(null, '/adx/path/dir');
expect(validatorInstance.adxConfigurator instanceof Configurator).toBe(true);
expect(validatorInstance.adxConfigurator.path).toEqual('\\adx\\path\\dir');
expect(Configurator.prototype.load).toHaveBeenCalled();
});
it("should output an error when the ADXConfigurator could not load the xml", function () {
Configurator.prototype.load.andCallFake(function (cb) {
cb(new Error("Fake error"));
});
adxValidator.validate(null, '/adx/path/dir');
expect(Validator.prototype.writeError).toHaveBeenCalled();
});
it("should not output an error when the ADXConfigurator was successfully loaded", function () {
Configurator.prototype.load.andCallFake(function (cb) {
cb(null);
});
adxValidator.validate(null, '/adx/path/dir');
expect(Validator.prototype.writeError).not.toHaveBeenCalled();
});
it("should remove the validateMasterPage in the sequence while using an ADC", function () {
Configurator.prototype.load.andCallFake(function (cb) {
this.fromXml('<control></control>');
cb(null);
});
spies.sequence = ['initConfigXMLDoc', 'validateMasterPage'];
adxValidator.validate(null, '/adx/path/dir');
expect(validatorInstance.validators.sequence).toEqual(['initConfigXMLDoc']);
expect(validatorInstance.report.total).toEqual(1);
});
it("should remove the validateADXInfoConstraints in the sequence while using an ADP", function () {
Configurator.prototype.load.andCallFake(function (cb) {
this.fromXml('<page></page>');
cb(null);
});
spies.sequence = ['initConfigXMLDoc', 'validateADXInfoConstraints'];
adxValidator.validate(null, '/adx/path/dir');
expect(validatorInstance.validators.sequence).toEqual(['initConfigXMLDoc']);
expect(validatorInstance.report.total).toEqual(1);
});
});
describe('#validateXMLAgainstXSD', function () {
beforeEach(function () {
// Modify the sequence of the validation to only call the validateXMLAgainstXSD method
spies.sequence = ['validateXMLAgainstXSD'];
spyOn(Validator.prototype, 'writeError');
spyOn(Validator.prototype, 'writeWarning');
spyOn(Validator.prototype, 'writeSuccess');
spyOn(Validator.prototype, 'writeMessage');
});
it('should run the xmllint process with the 2.0.0alpha/Config.xsd and the config.xml file with old ADC 2.0 namespace', function () {
spies.validateHook = function () {
this.adxConfigurator = new Configurator('/adx/path/dir');
this.adxConfigurator.fromXml('<control xmlns="http://www.askia.com/ADCSchema"></control>');
};
var childProc = require('child_process');
spyOn(childProc, 'exec').andCallFake(function (command) {
expect(command).toBe('"\\root\\lib\\libxml\\xmllint.exe" --noout --schema "\\root\\schema\\2.0.0alpha\\Config.xsd" "\\adx\\path\\dir\\config.xml"');
});
adxValidator.validate(null, '/adx/path/dir');
expect(childProc.exec).toHaveBeenCalled();
});
it('should run the xmllint process with the 2.0.0/ADCSchema.xsd and the config.xml file with ADC 2.0', function () {
spies.validateHook = function () {
this.adxConfigurator = new Configurator('/adx/path/dir');
this.adxConfigurator.fromXml('<control></control>');
};
var childProc = require('child_process');
spyOn(childProc, 'exec').andCallFake(function (command) {
expect(command).toBe('"\\root\\lib\\libxml\\xmllint.exe" --noout --schema "\\root\\schema\\2.0.0\\ADCSchema.xsd" "\\adx\\path\\dir\\config.xml"');
});
adxValidator.validate(null, '/adx/path/dir');
expect(childProc.exec).toHaveBeenCalled();
});
it('should run the xmllint process with the 2.1.0/ADCSchema.xsd and the config.xml file with ADC 2.1', function () {
spies.validateHook = function () {
this.adxConfigurator = new Configurator('/adx/path/dir');
this.adxConfigurator.fromXml('<control version="2.1.0"></control>');
};
var childProc = require('child_process');
spyOn(childProc, 'exec').andCallFake(function (command) {
expect(command).toBe('"\\root\\lib\\libxml\\xmllint.exe" --noout --schema "\\root\\schema\\2.1.0\\ADCSchema.xsd" "\\adx\\path\\dir\\config.xml"');
});
adxValidator.validate(null, '/adx/path/dir');
expect(childProc.exec).toHaveBeenCalled();
});
it('should run the xmllint process with the 2.1.0/ADPSchema.xsd and the config.xml file with ADP 2.1', function () {
spies.validateHook = function () {
this.adxConfigurator = new Configurator('/adx/path/dir');
this.adxConfigurator.fromXml('<page version="2.1.0"></page>');
};
var childProc = require('child_process');
spyOn(childProc, 'exec').andCallFake(function (command) {
expect(command).toBe('"\\root\\lib\\libxml\\xmllint.exe" --noout --schema "\\root\\schema\\2.1.0\\ADPSchema.xsd" "\\adx\\path\\dir\\config.xml"');
});
adxValidator.validate(null, '/adx/path/dir');
expect(childProc.exec).toHaveBeenCalled();
});
it('should output an error when the xmllint process failed', function () {
spies.validateHook = function () {
this.adxConfigurator = new Configurator('/adx/path/dir');
this.adxConfigurator.fromXml('<control></control>');
};
var childProc = require('child_process');
spyOn(childProc, 'exec').andCallFake(function (command, callback) {
callback(new Error('Fake validation error'));
});
adxValidator.validate(null, '/adx/path/dir');
expect(Validator.prototype.writeError).toHaveBeenCalled();
});
it("should not output an error when the xmllint process doesn't failed", function () {
spies.validateHook = function () {
this.adxConfigurator = new Configurator('/adx/path/dir');
this.adxConfigurator.fromXml('<control></control>');
};
var childProc = require('child_process');
spyOn(childProc, 'exec').andCallFake(function (command, callback) {
callback(null);
});
adxValidator.validate(null, '/adx/path/dir');
expect(Validator.prototype.writeError).not.toHaveBeenCalled();
});
it("should output a success when the xmllint process doesn't failed", function () {
spies.validateHook = function () {
this.adxConfigurator = new Configurator('/adx/path/dir');
this.adxConfigurator.fromXml('<control></control>');
};
var childProc = require('child_process');
spyOn(childProc, 'exec').andCallFake(function (command, callback) {
callback(null);
});
adxValidator.validate(null, '/adx/path/dir');
expect(Validator.prototype.writeSuccess).toHaveBeenCalledWith(successMsg.xsdValidate);
});
});
describe("#validateADXInfo", function () {
beforeEach(function () {
// Modify the sequence of the validation to only call the validateADXInfo method
spies.sequence = ['validateADXInfo'];
spyOn(Validator.prototype, 'writeError');
spyOn(Validator.prototype, 'writeWarning');
spyOn(Validator.prototype, 'writeSuccess');
spyOn(Validator.prototype, 'writeMessage');
});
it("should output an error when the info doesn't exist", function () {
spies.validateHook = function () {
this.adxConfigurator = new Configurator('/adx/path/dir');
this.adxConfigurator.fromXml('<control></control>');
};
adxValidator.validate(null, '/adx/path/dir');
expect(Validator.prototype.writeError).toHaveBeenCalledWith(errMsg.missingInfoNode);
});
it("should output an error when the info/name doesn't exist", function () {
spies.validateHook = function () {
this.adxConfigurator = new Configurator('/adx/path/dir');
this.adxConfigurator.fromXml('<control><info></info></control>');
};
adxValidator.validate(null, '/adx/path/dir');
expect(Validator.prototype.writeError).toHaveBeenCalledWith(errMsg.missingOrEmptyNameNode);
});
it("should output an error when the info/name is empty", function () {
spies.validateHook = function () {
this.adxConfigurator = new Configurator('/adx/path/dir');
this.adxConfigurator.fromXml('<control><info><name></name></info></control>');
};
adxValidator.validate(null, '/adx/path/dir');
expect(Validator.prototype.writeError).toHaveBeenCalledWith(errMsg.missingOrEmptyNameNode);
});
it("should not output an error when the info/name is valid", function () {
spies.validateHook = function () {
this.adxConfigurator = new Configurator('/adx/path/dir');
this.adxConfigurator.fromXml('<control><info><name>Something</name></info></control>');
};
adxValidator.validate(null, '/adx/path/dir');
expect(Validator.prototype.writeError).not.toHaveBeenCalled();
});
it("should initialize the adxName property with the name of the ADX", function () {
var instance;
spies.validateHook = function () {
instance = this;
this.adxConfigurator = new Configurator('/adx/path/dir');
this.adxConfigurator.fromXml('<control><info><name>test</name></info></control>');
};
adxValidator.validate(null, '/adx/path/dir');
expect(instance.adxName).toBe('test');
});
it("should output a warning when the `style` tag is used with ADC 2.1", function () {
spies.validateHook = function () {
this.adxConfigurator = new Configurator('/adx/path/dir');
this.adxConfigurator.fromXml('<control version="2.1.0"><info><name>something</name><style width="200" height="400" /></info></control>');
};
adxValidator.validate(null, '/adx/path/dir');
expect(Validator.prototype.writeWarning).toHaveBeenCalledWith(warnMsg.deprecatedInfoStyleTag);
});
it("should not output a warning when the `style` tag is used with ADC 2.0", function () {
spies.validateHook = function () {
this.adxConfigurator = new Configurator('/adx/path/dir');
this.adxConfigurator.fromXml('<control><info><name>something</name><style width="200" height="400" /></info></control>');
};
adxValidator.validate(null, '/adx/path/dir');
expect(Validator.prototype.writeWarning).not.toHaveBeenCalledWith(warnMsg.deprecatedInfoStyleTag);
});
it("should output a warning when the `categories` tag is used with ADC 2.1", function () {
spies.validateHook = function () {
this.adxConfigurator = new Configurator('/adx/path/dir');
this.adxConfigurator.fromXml('<control version="2.1.0"><info><name>something</name><categories><category>test</category></categories></info></control>');
};
adxValidator.validate(null, '/adx/path/dir');
expect(Validator.prototype.writeWarning).toHaveBeenCalledWith(warnMsg.deprecatedInfoCategoriesTag);
});
it("should not output a warning when the `categories` tag is used with ADC 2.0", function () {
spies.validateHook = function () {
this.adxConfigurator = new Configurator('/adx/path/dir');
this.adxConfigurator.fromXml('<control><info><name>something</name><categories><category>test</category></categories></info></control>');
};
adxValidator.validate(null, '/adx/path/dir');
expect(Validator.prototype.writeWarning).not.toHaveBeenCalledWith(warnMsg.deprecatedInfoCategoriesTag);
});
});
describe('#validateADXInfoConstraints', function () {
beforeEach(function () {
// Modify the sequence of the validation to only call the validateADXInfoConstraints method
spies.sequence = ['validateADXInfoConstraints'];
spyOn(Validator.prototype, 'writeError');
spyOn(Validator.prototype, 'writeWarning');
spyOn(Validator.prototype, 'writeSuccess');
spyOn(Validator.prototype, 'writeMessage');
});
var elements = ['questions', 'responses', 'controls'],
constraintAttrs = [
{
name : 'chapter',
on : 'questions'
},
{
name : 'single',
on : 'questions'
},
{
name : 'multiple',
on : 'questions'
},
{
name : 'open',
on : 'questions'
},
{
name : 'numeric',
on : 'questions'
},
{
name : 'date',
on : 'questions'
},
{
name : 'requireParentLoop',
on : 'questions'
},
{
name : 'min',
on : 'responses'
},
{
name : 'max',
on : 'responses'
},
{
name : 'label',
on : 'controls'
},
{
name : 'checkbox',
on : 'controls'
},
{
name : 'textbox',
on : 'controls'
},
{
name : 'listbox',
on : 'controls'
},
{
name : 'radiobutton',
on : 'controls'
},
{
name : 'responseblock',
on : 'controls'
}
],
fakeAttr = {
'questions' : 'single',
'responses' : 'min',
'controls' : 'label'
};
function testDuplicateConstraints(element) {
it("should output an error when 2 constraints defined on " + element, function () {
spies.validateHook = function () {
this.adxConfigurator = new Configurator('/adx/path/dir');
this.adxConfigurator.fromXml('<control><info><constraints>' +
'<constraint on="' + element + '" ' + fakeAttr[element] + '="1"></constraint>' +
'<constraint on="' + element + '" ' + fakeAttr[element] + '="1"></constraint>' +
'</constraints></info></control>');
};
adxValidator.validate(null, '/adx/path/dir');
expect(Validator.prototype.writeError).toHaveBeenCalledWith(format(errMsg.duplicateConstraints, element));
});
}
elements.forEach(testDuplicateConstraints);
function testRequireConstraint(element) {
it("should output an error when no constraint defined on `" + element + "`", function () {
var oppositeElement = (element === 'questions') ? 'controls' : 'questions';
spies.validateHook = function () {
this.adxConfigurator = new Configurator('/adx/path/dir');
this.adxConfigurator.fromXml('<control><info><constraints>' +
'<constraint on="' + oppositeElement + '" ' + fakeAttr[oppositeElement] + '="1"></constraint>' +
'</constraints></info></control>');
};
adxValidator.validate(null, '/adx/path/dir');
expect(Validator.prototype.writeError).toHaveBeenCalledWith(format(errMsg.requireConstraintOn, element));
});
}
['questions', 'controls'].forEach(testRequireConstraint);
it("should not output an error when constraints are defined on `questions` and `controls`", function () {
spies.validateHook = function () {
this.adxConfigurator = new Configurator('/adx/path/dir');
this.adxConfigurator.fromXml('<control><info><constraints>' +
'<constraint on="questions" single="true"></constraint>' +
'<constraint on="controls" label="true"></constraint>' +
'</constraints></info></control>');
};
adxValidator.validate(null, '/adx/path/dir');
expect(Validator.prototype.writeError).not.toHaveBeenCalled();
});
function testConstraintAttribute(element) {
describe('constraint@on=' + element, function () {
constraintAttrs.forEach(function (attribute) {
var notText = (attribute.on === element) ? 'not ' : '';
it("should " + notText + "output an error when the attribute `" + attribute.name + "` is present", function () {
spies.validateHook = function () {
this.adxConfigurator = new Configurator('/adx/path/dir');
this.adxConfigurator.fromXml('<control><info><constraints>' +
'<constraint on="' + element + '" ' + attribute.name + '="true"></constraint>' +
'</constraints></info></control>');
};
adxValidator.validate(null, '/adx/path/dir');
if (attribute.on === element) {
expect(Validator.prototype.writeError).not.toHaveBeenCalledWith(format(errMsg.invalidConstraintAttribute, element, attribute.name));
} else {
expect(Validator.prototype.writeError).toHaveBeenCalledWith(format(errMsg.invalidConstraintAttribute, element, attribute.name));
}
});
});
it("should output an error when no other attribute is specified", function () {
spies.validateHook = function () {
this.adxConfigurator = new Configurator('/adx/path/dir');
this.adxConfigurator.fromXml('<control><info><constraints>' +
'<constraint on="' + element + '"></constraint>' +
'</constraints></info></control>');
};
adxValidator.validate(null, '/adx/path/dir');
expect(Validator.prototype.writeError).toHaveBeenCalledWith(format(errMsg.noRuleOnConstraint, element));
});
if (element !== 'responses') {
it("should output an error when no other attribute is specified with the truthly value", function () {
spies.validateHook = function () {
this.adxConfigurator = new Configurator('/adx/path/dir');
this.adxConfigurator.fromXml('<control><info><constraints>' +
'<constraint on="' + element + '" ' + fakeAttr[element] + '="false"></constraint>' +
'</constraints></info></control>');
};
adxValidator.validate(null, '/adx/path/dir');
expect(Validator.prototype.writeError).toHaveBeenCalledWith(format(errMsg.noRuleOnConstraint, element));
});
}
});
}
elements.forEach(testConstraintAttribute);
});
describe('#validateADXOutputs', function () {
beforeEach(function () {
// Modify the sequence of the validation to only call the validateADXOutputs method
spies.sequence = ['validateADXOutputs'];
spyOn(Validator.prototype, 'writeError');
spyOn(Validator.prototype, 'writeWarning');
spyOn(Validator.prototype, 'writeSuccess');
spyOn(Validator.prototype, 'writeMessage');
});
it('should output a warning when duplicate conditions', function () {
spies.validateHook = function () {
this.adxConfigurator = new Configurator('/adx/path/dir');
this.adxConfigurator.fromXml('<control><outputs>' +
'<output id="first" defaultGeneration="true">' +
'<condition>duplicate condition</condition>'+
'</output>' +
'<output id="second" defaultGeneration="true">' +
'<condition>duplicate condition</condition>'+
'</output>' +
'</outputs></control>');
};
adxValidator.validate(null, '/adx/path/dir');
expect(Validator.prototype.writeWarning).toHaveBeenCalledWith(warnMsg.duplicateOutputCondition, "first", "second");
});
it('should not output an error when one condition is empty', function () {
spies.validateHook = function () {
this.adxConfigurator = new Configurator('/adx/path/dir');
this.adxConfigurator.fromXml('<control><outputs>' +
'<output id="empty" defaultGeneration="true">' +
'</output>' +
'</outputs></control>');
};
adxValidator.validate(null, '/adx/path/dir');
expect(Validator.prototype.writeError).not.toHaveBeenCalled();
});
it('should output an error when at least two conditions are empty', function () {
spies.validateHook = function () {