Skip to content

Commit 14c0d70

Browse files
committed
Fixed lint, testing and added new tests
1 parent 5bef0ca commit 14c0d70

4 files changed

Lines changed: 319 additions & 19 deletions

File tree

src/ConfigParser.js

Lines changed: 36 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,15 @@ var libxml = require('libxmljs2');
55
var request = require('sync-request');
66
var SCHEMA_URL = 'https://raw.githubusercontent.com/Wirecloud/wirecloud/master/src/wirecloud/commons/utils/template/schemas/xml_schema.xsd';
77

8+
/* jshint latedef:nofunc */
9+
10+
class TemplateParseException extends Error {
11+
constructor(message) {
12+
super(message);
13+
this.name = "TemplateParseException";
14+
}
15+
}
16+
817
function getContent(path) {
918
try {
1019
return fs.readFileSync(path).toString();
@@ -26,7 +35,7 @@ ConfigParser.prototype.parseContent = function (content) {
2635
throw new Error('Invalid config file format');
2736
}
2837
}
29-
}
38+
};
3039

3140
function ConfigParser(options) {
3241
if (typeof options === 'string') {
@@ -35,7 +44,7 @@ function ConfigParser(options) {
3544

3645
var content = options.path ? getContent(options.path) : options.content;
3746
this.data = this.parseContent(content);
38-
if (!this.validate()) {
47+
if (options.validate && !this.validate()) {
3948
throw new Error('Validation Error: Invalid config.xml file');
4049
}
4150
}
@@ -55,11 +64,11 @@ ConfigParser.prototype.validate = function () {
5564
}
5665
else if (this.type === "json") {
5766
try {
58-
this.validateJson(body);
67+
this.validateJson();
5968
return true;
6069
}
6170
catch (e) {
62-
throw e;
71+
return false;
6372
}
6473
}
6574

@@ -165,7 +174,7 @@ function checkContactsFields(fields, place, required = false) {
165174
}
166175

167176
place[field] = [];
168-
} else if (typeof place[field] === 'string' || Array.isArray(place[field]) || place[field] instanceof Tuple) {
177+
} else if (typeof place[field] === 'string' || Array.isArray(place[field])) {
169178

170179
} else {
171180
throw new TemplateParseException(`${field} field must be a list or string`);
@@ -211,13 +220,21 @@ function checkComponentInfo(data, componentType) {
211220
ConfigParser.prototype.validateJson = function () {
212221
checkStringFields(['title', 'description', 'longdescription', 'email', 'homepage', 'doc', 'changelog', 'image', 'smartphoneimage', 'license', 'licenseurl', 'issuetracker'], this.data);
213222
checkContactsFields(['authors', 'contributors'], this.data);
223+
checkIntegerFields(['macversion'], this.data, false);
224+
if (!('macversion' in this.data)) {
225+
this.data['macversion'] = 1;
226+
}
227+
// Extra check for the macversion field, as it currently only supports 1 and 2
228+
if (this.data['macversion'] !== 1 && this.data['macversion'] !== 2) {
229+
throw new TemplateParseException('Invalid value for the macversion field (currently only 1 or 2 are supported)');
230+
}
214231
// TODO ???checkStringFields(['type'], this.data, true)
215232
// Normalize/check preferences and properties (only for widgets and operators)
216233
if (this.data['type'] !== 'mashup') {
217234
checkArrayFields(['preferences', 'properties'], this.data);
218235
for (let preference of this.data['preferences']) {
219236
checkStringFields(['name', 'type'], preference, true);
220-
checkStringFields(['label', 'description', 'default'], reference);
237+
checkStringFields(['label', 'description', 'default'], preference);
221238
checkBooleanFields(['readonly', 'secure'], preference);
222239
checkStringFields(['value'], preference, undefined, true);
223240
checkBooleanFields('required', preference);
@@ -232,6 +249,10 @@ ConfigParser.prototype.validateJson = function () {
232249
}
233250

234251
if (this.data['type'] === 'widget') {
252+
if (this.data['macversion'] > 1) {
253+
checkStringFields(['entrypoint'], this.data, true);
254+
}
255+
235256
checkArrayFields(['altcontents'], this.data);
236257
if (this.data['contents'] === null || typeof this.data['contents'] !== 'object') {
237258
throw new TemplateParseException('Missing widget content info');
@@ -243,6 +264,10 @@ ConfigParser.prototype.validateJson = function () {
243264
checkContentsField(altcontent);
244265
}
245266
}
267+
} else if (this.data['type'] === 'operator') {
268+
if (this.data['macversion'] > 1) {
269+
checkStringFields(['entrypoint'], this.data, true);
270+
}
246271
} else if (this.data['type'] === 'mashup') {
247272
checkArrayFields(['params', 'tabs', 'embedded'], this.data);
248273

@@ -317,20 +342,14 @@ ConfigParser.prototype.validateJson = function () {
317342
// Requirements
318343
checkArrayFields(['requirements'], this.data);
319344
}
320-
}
345+
};
321346

322-
class TemplateParseException extends Error {
323-
constructor(message) {
324-
super(message);
325-
this.name = "TemplateParseException";
326-
}
327-
}
328347
ConfigParser.prototype.getData = function (configFile) {
329348
return {
330-
name: this.data.root()._attr('name').value(),
331-
vendor: this.data.root()._attr('vendor').value(),
332-
version: this.data.root()._attr('version').value(),
333-
type: this.data.root().name()
349+
name: (this.type === 'xml') ? this.data.root()._attr('name').value() : this.data['name'],
350+
vendor: (this.type === 'xml') ? this.data.root()._attr('vendor').value() : this.data['vendor'],
351+
version: (this.type === 'xml') ? this.data.root()._attr('version').value() : this.data['version'],
352+
type: (this.type === 'xml') ? this.data.root().name() : this.data['type']
334353
};
335354
};
336355

test/configParser.spec.js

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ describe('Config Parser', function () {
2828
var failContructor = function () {
2929
return new ConfigParser('test/fixtures/syntacticallyIncorrect.xml');
3030
};
31-
expect(failContructor).to.throw(Error, 'Start tag expected, \'<\' not found');
31+
expect(failContructor).to.throw(Error, 'Invalid config file format');
3232
});
3333

3434
it('should read the contents of a file if they are passed in the options object', function () {
@@ -37,6 +37,12 @@ describe('Config Parser', function () {
3737
expect(parser.data).not.to.be.undefined;
3838
});
3939

40+
it('should read the contents of a JSON file if they are passed in the options object', function () {
41+
var content = fs.readFileSync('test/fixtures/validConfig.json').toString();
42+
var parser = new ConfigParser({content: content});
43+
expect(parser.data).not.to.be.undefined;
44+
});
45+
4046
// The file passed in content is syntactically incorrect so if there is no exception thrown
4147
// the parser read the file in the path
4248
it('should use the path instead of the content within the options object', function () {
@@ -60,12 +66,23 @@ describe('Config Parser', function () {
6066
expect(parser.validate()).to.equal(false);
6167
});
6268

69+
it('should not validate an invalid JSON config file', function () {
70+
var parser = new ConfigParser('test/fixtures/invalidConfig.json');
71+
expect(parser.validate()).to.equal(false);
72+
});
73+
74+
var spy = sinon.spy(ConfigParser.prototype, 'validate');
75+
6376
it('should validate the config file when building the object', function () {
64-
var spy = sinon.spy(ConfigParser.prototype, 'validate');
6577
var parser = new ConfigParser({path: 'test/fixtures/validConfig.xml', validate: true});
6678
expect(spy.called).to.equal(true);
6779
});
6880

81+
it('should validate the JSON config file when building the object', function () {
82+
var parser = new ConfigParser({path: 'test/fixtures/validConfig.json', validate: true});
83+
expect(spy.called).to.equal(true);
84+
});
85+
6986
it('should fail to parse the schema if it is syntactically incorrect', function () {
7087
sinon.stub(libxml, 'parseXml').onCall(1).throws(new Error());
7188
var parser = new ConfigParser('test/fixtures/validConfig.xml');
@@ -96,6 +113,21 @@ describe('Config Parser', function () {
96113
expect(actualData.version).to.equal(expectedData.version);
97114
expect(actualData.type).to.equal(expectedData.type);
98115
});
116+
117+
it('should get all required data from a JSON config file', function () {
118+
var expectedData = {
119+
name: 'panel',
120+
vendor: 'CoNWeT',
121+
version: '2.0.2',
122+
type: 'widget'
123+
};
124+
var parser = new ConfigParser('test/fixtures/validConfig.json');
125+
var actualData = parser.getData();
126+
expect(actualData.name).to.equal(expectedData.name);
127+
expect(actualData.vendor).to.equal(expectedData.vendor);
128+
expect(actualData.version).to.equal(expectedData.version);
129+
expect(actualData.type).to.equal(expectedData.type);
130+
});
99131
});
100132

101133
});

test/fixtures/invalidConfig.json

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
{
2+
"type": "widget",
3+
"vendor": "CoNWeT",
4+
"name": "panel",
5+
"version": "2.0.2",
6+
"macversion": 3,
7+
"title": "Panel",
8+
"description": "Displaying simple texts messages, like measures, in an easy way",
9+
"longdescription": "DESCRIPTION.md",
10+
"authors": [
11+
{
12+
"name": "Álvaro Arranz García",
13+
"email": "aarranz@conwet.com"
14+
}
15+
],
16+
"contributors": [],
17+
"email": "aarranz@conwet.com",
18+
"image": "images/catalogue.png",
19+
"smartphoneimage": "",
20+
"homepage": "https://github.com/Wirecloud/panel-widget",
21+
"doc": "doc/userguide.md",
22+
"license": "Apache License 2.0",
23+
"licenseurl": "http://www.apache.org/licenses/LICENSE-2.0.html",
24+
"issuetracker": "https://github.com/Wirecloud/panel-widget/issues",
25+
"changelog": "doc/changelog.md",
26+
"requirements": [
27+
{
28+
"type": "feature",
29+
"name": "StyledElements"
30+
}
31+
],
32+
"preferences": [
33+
{
34+
"name": "default-value",
35+
"type": "text",
36+
"label": "Default Value",
37+
"description": "",
38+
"readonly": false,
39+
"default": "--",
40+
"value": null,
41+
"secure": false,
42+
"multiuser": false,
43+
"required": false
44+
},
45+
{
46+
"name": "default-unit",
47+
"type": "text",
48+
"label": "Default Unit",
49+
"description": "",
50+
"readonly": false,
51+
"default": "",
52+
"value": null,
53+
"secure": false,
54+
"multiuser": false,
55+
"required": false
56+
},
57+
{
58+
"name": "max-height",
59+
"type": "text",
60+
"label": "Max height (Percentage)",
61+
"description": "",
62+
"readonly": false,
63+
"default": "60",
64+
"value": null,
65+
"secure": false,
66+
"multiuser": false,
67+
"required": false
68+
},
69+
{
70+
"name": "min-height",
71+
"type": "text",
72+
"label": "Min height (Percentage)",
73+
"description": "",
74+
"readonly": false,
75+
"default": "10",
76+
"value": null,
77+
"secure": false,
78+
"multiuser": false,
79+
"required": false
80+
},
81+
{
82+
"name": "decimals",
83+
"type": "text",
84+
"label": "Decimals",
85+
"description": "Number of decimals to use for number values. Empty for using all the available decimals",
86+
"readonly": false,
87+
"default": "1",
88+
"value": null,
89+
"secure": false,
90+
"multiuser": false,
91+
"required": false
92+
}
93+
],
94+
"properties": [],
95+
"wiring": {
96+
"inputs": [
97+
{
98+
"name": "textinput",
99+
"type": "text",
100+
"label": "Contents",
101+
"description": "Contents",
102+
"actionlabel": "",
103+
"friendcode": "panel-content"
104+
}
105+
],
106+
"outputs": []
107+
},
108+
"contents": {
109+
"src": "index.html",
110+
"contenttype": "text/html",
111+
"charset": "utf-8",
112+
"useplatformstyle": true,
113+
"cacheable": true
114+
},
115+
"altcontents": [],
116+
"widget_width": "400px",
117+
"widget_height": "200px",
118+
"js_files": [
119+
"js/main.js"
120+
],
121+
"translations": {},
122+
"default_lang": "en",
123+
"translation_index_usage": {}
124+
}

0 commit comments

Comments
 (0)