Commit 82d84bd1 authored by Romain Courteaud's avatar Romain Courteaud :octopus:

erp5_gadget_interface_validator: stop relying on appcache

Use the precache script to get the list of gadgets.
Ensure to also check the launcher gadget.

Report subgadget service error.
parent ee887f00
No related merge requests found
......@@ -257,8 +257,7 @@
// XXX Load in an iframe
return context.declareGadget('gadget_interface_loader.html', {
scope: gadget_to_check_url,
element: element,
sandbox: 'iframe'
element: element
});
})
.push(function (result) {
......@@ -356,118 +355,139 @@
return this.changeState(options);
})
.onStateChange(function () {
.onStateChange(function (modification_dict) {
var context = this,
required_interface_list = [],
gadget_method_list = [],
error_list = [];
return getOrDeclareGadget(context, context.state.gadget_to_check_url)
.push(function (gadget_to_check) {
// Get the list of interfaces/methods
return RSVP.all([
gadget_to_check.getGadgetToCheckInterfaceList(),
gadget_to_check.getGadgetToCheckMethodList('method')
]);
})
.push(function (result_list) {
required_interface_list = result_list[0];
gadget_method_list = result_list[1];
}, function (error) {
error_list.push({
details: "Error with gadget loading\n" + (error.message || '')
if (modification_dict.hasOwnProperty('gadget_to_check_url')) {
return getOrDeclareGadget(context, context.state.gadget_to_check_url)
/*
.push(undefined, function (error) {
console.warn('oups', error);
context.element.innerHTML = '';
return;
});
})
.push(function () {
// Get all methods definition for every interface
var promise_list = [],
i;
for (i = 0; i < required_interface_list.length; i += 1) {
promise_list.push(
getDefinedInterfaceMethodList(required_interface_list[i],
error_list)
);
}
return RSVP.all(promise_list);
})
.push(function (method_table) {
var interface_method_list = [],
i,
j,
promise_list = [];
for (i = 0; i < method_table.length; i += 1) {
for (j = 0; j < method_table[i].length; j += 1) {
// Check method declared twice
if (interface_method_list.indexOf(method_table[i][j].name) >= 0) {
*/
.push(function (gadget_to_check) {
// Get the list of interfaces/methods
return RSVP.all([
gadget_to_check.getGadgetToCheckInterfaceList(),
gadget_to_check.getGadgetToCheckMethodList('method')
]);
})
.push(function (result_list) {
required_interface_list = result_list[0];
gadget_method_list = result_list[1];
}, function (error) {
error_list.push({
details: "Error with gadget loading\n" + (error.message || '')
});
})
.push(function () {
// Get all methods definition for every interface
var promise_list = [],
i;
for (i = 0; i < required_interface_list.length; i += 1) {
promise_list.push(
getDefinedInterfaceMethodList(required_interface_list[i],
error_list)
);
}
return RSVP.all(promise_list);
})
.push(function (method_table) {
var interface_method_list = [],
i,
j,
promise_list = [];
for (i = 0; i < method_table.length; i += 1) {
for (j = 0; j < method_table[i].length; j += 1) {
// Check method declared twice
if (interface_method_list.indexOf(method_table[i][j].name) >=
0) {
error_list.push({
details: "Method documented in multiple interface\n" +
method_table[i][j].name
});
} else {
interface_method_list.push(method_table[i][j].name);
}
}
}
// Check unknown method declaration
for (i = 0; i < gadget_method_list.length; i += 1) {
if (interface_method_list.indexOf(
gadget_method_list[i]
) < 0) {
error_list.push({
details: "Method documented in multiple interface\n" +
method_table[i][j].name
details: "Method not documented in the interface\n" +
gadget_method_list[i]
});
} else {
interface_method_list.push(method_table[i][j].name);
}
}
}
// Check unknown method declaration
for (i = 0; i < gadget_method_list.length; i += 1) {
if (interface_method_list.indexOf(
gadget_method_list[i]
) < 0) {
error_list.push({
details: "Method not documented in the interface\n" +
gadget_method_list[i]
});
// Check that all interfaces are implemented
for (i = 0; i < required_interface_list.length; i += 1) {
promise_list.push(
verifyAllMethodDeclared(method_table[i], gadget_method_list,
error_list)
);
}
}
return RSVP.all(promise_list);
// Check that all interfaces are implemented
for (i = 0; i < required_interface_list.length; i += 1) {
promise_list.push(
verifyAllMethodDeclared(method_table[i], gadget_method_list,
error_list)
);
}
return RSVP.all(promise_list);
})
.push(function () {
// Display result
var i,
error_message = '',
summary_message;
if (error_list.length === 0) {
summary_message = 'Success';
} else {
summary_message = 'Failure';
}
for (i = 0; i < error_list.length; i += 1) {
error_message += (error_list[i].details + '\n\n');
}
if (context.state.summary) {
if (error_message !== '') {
console.warn(error_message, error_list);
})
.push(function () {
// Display result
var i,
error_message = '',
summary_message;
if (error_list.length === 0) {
summary_message = 'Success';
} else {
summary_message = 'Failure';
}
error_message = summary_message;
} else {
error_message = summary_message + '\n\n' + error_message;
}
context.element.firstElementChild.textContent = error_message;
})
.push(undefined, function (error) {
console.warn(error);
context.element.firstElementChild.textContent =
"Unexpected error";
});
for (i = 0; i < error_list.length; i += 1) {
error_message += (error_list[i].details + '\n\n');
}
if (context.state.summary) {
if (error_message !== '') {
console.warn(error_message, error_list);
}
error_message = summary_message;
} else {
error_message = summary_message + '\n\n' + error_message;
}
context.element.firstElementChild.textContent = error_message;
})
.push(undefined, function (error) {
console.warn(error);
context.element.firstElementChild.textContent =
"Unexpected error: " + error;
});
}
if (modification_dict.hasOwnProperty('error')) {
console.warn(context.state.error);
context.element.firstElementChild.textContent =
"Unexpected error: " + context.state.error;
}
})
.allowPublicAcquisition('reportServiceError', function (param_list) {
// Subgadget services failed.
// Report it as an error instead of crashing the page
return this.changeState({error: param_list[0]});
});
}(window, rJS, RSVP, DOMParser, jIO, console, document));
\ No newline at end of file
/*jslint nomen: true, indent: 2, maxerr: 3, maxlen: 80 */
/*global rJS, window*/
(function (window, rJS) {
/*global rJS, window, document*/
(function (window, rJS, document) {
"use strict";
rJS(window)
.declareMethod("declareGadgetToCheck", function (url) {
return this.declareGadget(url, {
scope: 'gadget_to_check'
var div = document.createElement('div'),
gadget = this;
this.element.innerHTML = '';
this.element.appendChild(div);
return gadget.declareGadget(url, {
scope: 'gadget_to_check',
sandbox: 'iframe',
element: div
})
.push(function () {
// Do not return the loaded gadget.
......@@ -29,4 +35,4 @@
});
});
}(window, rJS));
\ No newline at end of file
}(window, rJS, document));
\ No newline at end of file
/*global window, rJS, RSVP, jIO, QueryFactory, SimpleQuery */
/*global window, rJS, RSVP, jIO, QueryFactory, SimpleQuery, URL */
/*jslint indent: 2, maxerr: 3, nomen: true */
(function (window, rJS, RSVP, jIO, QueryFactory, SimpleQuery) {
(function (window, rJS, RSVP, jIO, QueryFactory, SimpleQuery, URL) {
"use strict";
//////////////////////////////////////////////
......@@ -12,16 +12,25 @@
return (new RegExp(suffix + '$', 'i')).test(str);
}
function fetchAppcacheData(appcache_url) {
function fetchPrecacheData(precache_url) {
return new RSVP.Queue()
.push(function () {
return jIO.util.ajax({
url: appcache_url,
dataType: 'text'
url: precache_url,
dataType: 'json'
});
})
.push(function (evt) {
return evt.target.responseText.split('\n');
var key,
precache_dict = evt.target.response,
result_list = [],
precache_absolute_url = (new URL(precache_url, window.location)).href;
for (key in precache_dict) {
if (precache_dict.hasOwnProperty(key)) {
result_list.push((new URL(key, precache_absolute_url)).href);
}
}
return result_list;
});
}
......@@ -32,8 +41,8 @@
var gadget_list = [],
i;
for (i = 0; i < filename_list.length; i += 1) {
if (endsWith(filename_list[i], '.html') &&
(filename_list[i][0] !== '#')) {
if (endsWith(filename_list[i], '.html') ||
endsWith(filename_list[i], '/')) {
gadget_list.push(filename_list[i]);
}
}
......@@ -66,11 +75,11 @@
InterfaceValidatorStorage.prototype.buildQuery = function (options) {
// XXX HARDCODED
var query = QueryFactory.create(options.query || '');
if (!((query instanceof SimpleQuery) && (query.key === 'appcache_url'))) {
if (!((query instanceof SimpleQuery) && (query.key === 'precache_url'))) {
// Only accept simple query with an appcache_url
return [];
}
return fetchAppcacheData(query.value)
return fetchPrecacheData(query.value)
// return fetchAppcacheData('gadget_interface_validator_test.appcache')
.push(function (filename_list) {
return filterGadgetList(filename_list);
......@@ -123,4 +132,4 @@
return wrapJioCall(this, 'get', arguments);
});
}(window, rJS, RSVP, jIO, QueryFactory, SimpleQuery));
\ No newline at end of file
}(window, rJS, RSVP, jIO, QueryFactory, SimpleQuery, URL));
\ No newline at end of file
......@@ -93,7 +93,7 @@
"list_method": "portal_catalog",
"query": "urn:jio:allDocs",
"portal_type": [],
"search_column_list": [['appcache_url', 'Appcache']],
"search_column_list": [['precache_url', 'Precache']],
"sort_column_list": [],
"sort": [],
"title": "Gadgets",
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Folder" module="OFS.Folder"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_objects</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>erp5_gadget_interface_validator</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
# Add all ERP5JS gadget
url_list = [
'gadget_erp5_page_validator_report.html',
'gadget_erp5_page_validator_report.js',
'gadget_erp5_page_validator_result_list.html',
'gadget_erp5_page_validator_result_list.js',
'gadget_interface_validator_panel.html',
'gadget_interface_validator_panel.js',
'gadget_interface_validator_jio.html',
'gadget_interface_validator_jio.js',
'gadget_interface.html',
'gadget_interface.js',
'gadget_interface_loader.html',
'gadget_interface_loader.js',
]
if REQUEST is not None:
import json
REQUEST.RESPONSE.setHeader('Content-Type', 'application/json')
return json.dumps(dict.fromkeys(url_list), indent=2)
return url_list
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>Script_magic</string> </key>
<value> <int>3</int> </value>
</item>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<tuple/>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_container</string> </key>
<value> <string>container</string> </value>
</item>
<item>
<key> <string>name_context</string> </key>
<value> <string>context</string> </value>
</item>
<item>
<key> <string>name_m_self</string> </key>
<value> <string>script</string> </value>
</item>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>REQUEST=None</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>WebSection_getInterfaceValidatorPrecacheManifest</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
erp5_gadget_interface_validator
erp5_web_renderjs_ui_unsafe
\ No newline at end of file
......@@ -10,8 +10,8 @@
<tr><td rowspan="1" colspan="3">Test Gadget Interface Validation UI</td></tr>
</thead><tbody>
<tal:block metal:use-macro="here/Zuite_CommonTemplate/macros/init" />
<tal:block tal:define="check_configuration python: {'app_cache_reference': 'gadget_interface_validator_test.appcache',
'gadget_count': 8}">
<tal:block tal:define="check_configuration python: {'precache_reference': './WebSection_getInterfaceValidatorTestPrecacheManifest',
'gadget_count': 9}">
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUiInterface/macros/run_app_interface_check" />
</tal:block>
......@@ -21,7 +21,7 @@
<tr>
<td>assertText</td>
<td>//tbody/tr/td</td>
<td>gadget_interface_validator_test_correct_implemented_gadget.html</td>
<td>*/gadget_interface_validator_test_correct_implemented_gadget.html</td>
</tr>
<tr>
<td>assertText</td>
......@@ -30,26 +30,26 @@
</tr>
<tr>
<td colspan="3"><b>Check interface with invalid syntax</b></td>
<td colspan="3"><b>Check failing service</b></td>
</tr>
<tr>
<td>assertText</td>
<td>//tbody/tr[2]/td</td>
<td>gadget_interface_validator_test_invalid_interface_gadget.html</td>
<td>*/gadget_interface_validator_test_failing_service_gadget.html</td>
</tr>
<tr>
<td>assertText</td>
<td>//tbody/tr[2]/td[2]</td>
<td>Failure</td>
<td>Unexpected error*</td>
</tr>
<tr>
<td colspan="3"><b>Check missing interface</b></td>
<td colspan="3"><b>Check interface with invalid syntax</b></td>
</tr>
<tr>
<td>assertText</td>
<td>//tbody/tr[3]/td</td>
<td>gadget_interface_validator_test_missing_interface_declaration_gadget.html</td>
<td>*/gadget_interface_validator_test_invalid_interface_gadget.html</td>
</tr>
<tr>
<td>assertText</td>
......@@ -58,12 +58,12 @@
</tr>
<tr>
<td colspan="3"><b>Check missing method declaration</b></td>
<td colspan="3"><b>Check missing interface</b></td>
</tr>
<tr>
<td>assertText</td>
<td>//tbody/tr[4]/td</td>
<td>gadget_interface_validator_test_missing_method_declaration_gadget.html</td>
<td>*/gadget_interface_validator_test_missing_interface_declaration_gadget.html</td>
</tr>
<tr>
<td>assertText</td>
......@@ -72,40 +72,40 @@
</tr>
<tr>
<td colspan="3"><b>Check correct use case for multiple interface</b></td>
<td colspan="3"><b>Check missing method declaration</b></td>
</tr>
<tr>
<td>assertText</td>
<td>//tbody/tr[5]/td</td>
<td>gadget_interface_validator_test_multiple_interface_correct_implemented_gadget.html</td>
<td>*/gadget_interface_validator_test_missing_method_declaration_gadget.html</td>
</tr>
<tr>
<td>assertText</td>
<td>//tbody/tr[5]/td[2]</td>
<td>Success</td>
<td>Failure</td>
</tr>
<tr>
<td colspan="3"><b>Check duplicated method name declaration</b></td>
<td colspan="3"><b>Check correct use case for multiple interface</b></td>
</tr>
<tr>
<td>assertText</td>
<td>//tbody/tr[6]/td</td>
<td>gadget_interface_validator_test_multiple_interface_duplicated_method_name.html</td>
<td>*/gadget_interface_validator_test_multiple_interface_correct_implemented_gadget.html</td>
</tr>
<tr>
<td>assertText</td>
<td>//tbody/tr[6]/td[2]</td>
<td>Failure</td>
<td>Success</td>
</tr>
<tr>
<td colspan="3"><b>Check not existent gadget</b></td>
<td colspan="3"><b>Check duplicated method name declaration</b></td>
</tr>
<tr>
<td>assertText</td>
<td>//tbody/tr[7]/td</td>
<td>gadget_interface_validator_test_nonexistent_gadget.html</td>
<td>*/gadget_interface_validator_test_multiple_interface_duplicated_method_name.html</td>
</tr>
<tr>
<td>assertText</td>
......@@ -114,12 +114,12 @@
</tr>
<tr>
<td colspan="3"><b>Check unknown method declaration</b></td>
<td colspan="3"><b>Check not existent gadget</b></td>
</tr>
<tr>
<td>assertText</td>
<td>//tbody/tr[8]/td</td>
<td>gadget_interface_validator_test_unknown_method_declaration_gadget.html</td>
<td>*/gadget_interface_validator_test_nonexistent_gadget.html</td>
</tr>
<tr>
<td>assertText</td>
......@@ -127,6 +127,20 @@
<td>Failure</td>
</tr>
<tr>
<td colspan="3"><b>Check unknown method declaration</b></td>
</tr>
<tr>
<td>assertText</td>
<td>//tbody/tr[9]/td</td>
<td>*/gadget_interface_validator_test_unknown_method_declaration_gadget.html</td>
</tr>
<tr>
<td>assertText</td>
<td>//tbody/tr[9]/td[2]</td>
<td>Failure</td>
</tr>
</tbody></table>
</body>
</html>
\ No newline at end of file
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Unknown method declaration Test Gadget</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="http://www.renderjs.org/rel/interface" href="gadget_interface_validator_test_dummy_interface1.html">
<!-- renderjs -->
<script src="rsvp.js" type="text/javascript"></script>
<script src="renderjs.js" type="text/javascript"></script>
<!-- custom script -->
<script src="gadget_interface_validator_test_failing_service_gadget.js" type="text/javascript"></script>
</head>
<body> </body>
</html>
\ No newline at end of file
/*global window, rJS , RSVP*/
/*jslint nomen: true, indent: 2, maxerr: 3 */
(function (window, rJS) {
"use strict";
rJS(window)
.ready(function (g) {
g.props = {};
})
.declareMethod("method1", function (param1, param2) {
return;
})
.declareMethod("method2", function (param1) {
return;
})
.declareMethod("method3", function () {
return;
})
.declareMethod("method4", function () {
return;
})
.declareService(function () {
throw new Error('boom');
});
}(window, rJS));
\ No newline at end of file
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Folder" module="OFS.Folder"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_objects</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>erp5_gadget_interface_validator_ui_test</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
# Add all ERP5JS gadget
url_list = [
'gadget_interface_validator_test_correct_implemented_gadget.html',
'gadget_interface_validator_test_correct_implemented_gadget.js',
'gadget_interface_validator_test_nonexistent_gadget.html',
'gadget_interface_validator_test_nonexistent_gadget.js',
'gadget_interface_validator_test_invalid_interface_gadget.html',
'gadget_interface_validator_test_invalid_interface_gadget.js',
'gadget_interface_validator_test_missing_interface_declaration_gadget.html',
'gadget_interface_validator_test_missing_interface_declaration_gadget.js',
'gadget_interface_validator_test_missing_method_declaration_gadget.html',
'gadget_interface_validator_test_missing_method_declaration_gadget.js',
'gadget_interface_validator_test_multiple_interface_correct_implemented_gadget.html',
'gadget_interface_validator_test_multiple_interface_correct_implemented_gadget.js',
'gadget_interface_validator_test_multiple_interface_duplicated_method_name.html',
'gadget_interface_validator_test_multiple_interface_duplicated_method_name.js',
'gadget_interface_validator_test_unknown_method_declaration_gadget.html',
'gadget_interface_validator_test_unknown_method_declaration_gadget.js',
'gadget_interface_validator_test_failing_service_gadget.html',
'gadget_interface_validator_test_failing_service_gadget.js'
]
if REQUEST is not None:
import json
REQUEST.RESPONSE.setHeader('Content-Type', 'application/json')
return json.dumps(dict.fromkeys(url_list), indent=2)
return url_list
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>Script_magic</string> </key>
<value> <int>3</int> </value>
</item>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<tuple/>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_container</string> </key>
<value> <string>container</string> </value>
</item>
<item>
<key> <string>name_context</string> </key>
<value> <string>context</string> </value>
</item>
<item>
<key> <string>name_m_self</string> </key>
<value> <string>script</string> </value>
</item>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>REQUEST=None</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>WebSection_getInterfaceValidatorTestPrecacheManifest</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
erp5_gadget_interface_validator_ui_test
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment