Commit 66de99c1 authored by Romain Courteaud's avatar Romain Courteaud

[erp5_core] Make live test gadget reusable

Remove all globals to allow multiple live tests running on the same page.
Stop reading the test output if the browser tab doesn't have the focus.
Stop hardcoding the URL in the javascript.
Stop hardcoding other fields path in the javascript (+ embed the textarea in the gadget).

Propagate all parameters via formulator.
It will allow to use this gadget from another context document.

Run live test from the RJS UI
parent 30b4f89e
...@@ -16,13 +16,13 @@ ...@@ -16,13 +16,13 @@
<key> <string>categories</string> </key> <key> <string>categories</string> </key>
<value> <value>
<tuple> <tuple>
<string>action_type/object_action</string> <string>action_type/object_jio_action</string>
</tuple> </tuple>
</value> </value>
</item> </item>
<item> <item>
<key> <string>category</string> </key> <key> <string>category</string> </key>
<value> <string>object_action</string> </value> <value> <string>object_jio_action</string> </value>
</item> </item>
<item> <item>
<key> <string>condition</string> </key> <key> <string>condition</string> </key>
......
request = context.REQUEST return context.Base_renderForm('ComponentTool_viewLiveTestDialog')
if request.get('form', None) is not None:
context.REQUEST['form']['field_your_text_output']=""
context.REQUEST['form']['text_output']=""
return context.ComponentTool_viewLiveTestDialog(
text_output="")
...@@ -50,7 +50,7 @@ ...@@ -50,7 +50,7 @@
</item> </item>
<item> <item>
<key> <string>_params</string> </key> <key> <string>_params</string> </key>
<value> <string>debug=None,verbose=None,test=None,run_only=None,**kw</string> </value> <value> <string>**kw</string> </value>
</item> </item>
<item> <item>
<key> <string>id</string> </key> <key> <string>id</string> </key>
......
...@@ -75,7 +75,6 @@ ...@@ -75,7 +75,6 @@
<key> <string>bottom</string> </key> <key> <string>bottom</string> </key>
<value> <value>
<list> <list>
<string>your_text_output</string>
<string>live_test_gadget</string> <string>live_test_gadget</string>
</list> </list>
</value> </value>
......
...@@ -11,7 +11,9 @@ ...@@ -11,7 +11,9 @@
<value> <value>
<list> <list>
<string>editable</string> <string>editable</string>
<string>enabled</string>
<string>gadget_url</string> <string>gadget_url</string>
<string>renderjs_extra</string>
<string>title</string> <string>title</string>
</list> </list>
</value> </value>
...@@ -50,6 +52,16 @@ ...@@ -50,6 +52,16 @@
<key> <string>tales</string> </key> <key> <string>tales</string> </key>
<value> <value>
<dictionary> <dictionary>
<item>
<key> <string>editable</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item> <item>
<key> <string>field_id</string> </key> <key> <string>field_id</string> </key>
<value> <string></string> </value> <value> <string></string> </value>
...@@ -61,9 +73,19 @@ ...@@ -61,9 +73,19 @@
<item> <item>
<key> <string>gadget_url</string> </key> <key> <string>gadget_url</string> </key>
<value> <value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent> <persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value> </value>
</item> </item>
<item>
<key> <string>renderjs_extra</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary> </dictionary>
</value> </value>
</item> </item>
...@@ -75,6 +97,10 @@ ...@@ -75,6 +97,10 @@
<key> <string>editable</string> </key> <key> <string>editable</string> </key>
<value> <int>0</int> </value> <value> <int>0</int> </value>
</item> </item>
<item>
<key> <string>enabled</string> </key>
<value> <int>1</int> </value>
</item>
<item> <item>
<key> <string>field_id</string> </key> <key> <string>field_id</string> </key>
<value> <string>my_gadget_field</string> </value> <value> <string>my_gadget_field</string> </value>
...@@ -87,6 +113,12 @@ ...@@ -87,6 +113,12 @@
<key> <string>gadget_url</string> </key> <key> <string>gadget_url</string> </key>
<value> <string></string> </value> <value> <string></string> </value>
</item> </item>
<item>
<key> <string>renderjs_extra</string> </key>
<value>
<list/>
</value>
</item>
<item> <item>
<key> <string>title</string> </key> <key> <string>title</string> </key>
<value> <string>Test Watcher</string> </value> <value> <string>Test Watcher</string> </value>
...@@ -98,6 +130,19 @@ ...@@ -98,6 +130,19 @@
</pickle> </pickle>
</record> </record>
<record id="2" aka="AAAAAAAAAAI="> <record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="TALESMethod" module="Products.Formulator.TALESField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_text</string> </key>
<value> <string>python: here.REQUEST.get(\'field_your_test\', \'\')</string> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle> <pickle>
<global name="TALESMethod" module="Products.Formulator.TALESField"/> <global name="TALESMethod" module="Products.Formulator.TALESField"/>
</pickle> </pickle>
...@@ -110,4 +155,17 @@ ...@@ -110,4 +155,17 @@
</dictionary> </dictionary>
</pickle> </pickle>
</record> </record>
<record id="4" aka="AAAAAAAAAAQ=">
<pickle>
<global name="TALESMethod" module="Products.Formulator.TALESField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_text</string> </key>
<value> <string>python: {\'run_test_url\': here.absolute_url() + \'/runLiveTest\', \'read_test_url\': here.absolute_url() + \'/readTestOutput\', \'test_list\': here.REQUEST.get(\'field_your_test\', \'\'), \'run_only\': here.REQUEST.get(\'field_your_run_only\', \'\'), \'debug\': int(here.REQUEST.get(\'field_your_debug\', \'\') == \'on\'), \'verbose\': int(here.REQUEST.get(\'field_your_verbose\', \'\') == \'on\')}</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData> </ZopeData>
...@@ -227,13 +227,17 @@ ...@@ -227,13 +227,17 @@
<key> <string>hidden</string> </key> <key> <string>hidden</string> </key>
<value> <int>0</int> </value> <value> <int>0</int> </value>
</item> </item>
<item>
<key> <string>input_type</string> </key>
<value> <string>text</string> </value>
</item>
<item> <item>
<key> <string>max_length</string> </key> <key> <string>max_length</string> </key>
<value> <string></string> </value> <value> <string></string> </value>
</item> </item>
<item> <item>
<key> <string>required</string> </key> <key> <string>required</string> </key>
<value> <int>0</int> </value> <value> <int>1</int> </value>
</item> </item>
<item> <item>
<key> <string>title</string> </key> <key> <string>title</string> </key>
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ProxyField" module="Products.ERP5Form.ProxyField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>delegated_list</string> </key>
<value>
<list>
<string>css_class</string>
<string>height</string>
<string>title</string>
<string>width</string>
</list>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>your_text_output</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>css_class</string> </key>
<value> <string>live_test_output</string> </value>
</item>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_view_mode_raw_text_content</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string>Base_viewFieldLibrary</string> </value>
</item>
<item>
<key> <string>height</string> </key>
<value> <int>30</int> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Test Output</string> </value>
</item>
<item>
<key> <string>width</string> </key>
<value> <int>120</int> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<!DOCTYPE html>
<html> <html>
<head> <head>
<title>Live test gadget</title>
<script src="rsvp.js" type="text/javascript"></script>
<script src="renderjs.js" type="text/javascript"></script>
<script src="jio.js" type="text/javascript"></script> <script src="jio.js" type="text/javascript"></script>
<script src="gadget_live_tests.js" type="text/javascript"></script> <script src="gadget_live_tests.js" type="text/javascript"></script>
</head> </head>
<body> <body>
<textarea class="live_test_output" rows="30" cols="120" spellcheck="false" style="font-family: monospace"></textarea>
</body> </body>
</html> </html>
\ No newline at end of file
/*global window, rJS, jIO, RSVP, location, document, FormData, console */ /*global window, rJS, jIO, RSVP, location, FormData, console */
/*jslint indent: 2, maxlen: 80, nomen: true */ /*jslint indent: 2, maxlen: 80, nomen: true */
(function (rJS, jIO, RSVP, window, document, FormData) { (function (rJS, jIO, RSVP, window, FormData) {
"use strict"; "use strict";
var my_url_run_test = document.baseURI + 'runLiveTest',
my_url_read_test = document.baseURI + 'readTestOutput',
paused = false,
data_textarea =
document.querySelector("[name='field_your_text_output']"),
continue_loop = true,
tests_still_running = true,
last_call = false,
data_size = 0,
form_data = new FormData();
data_textarea.value = ""; rJS(window)
.declareMethod('render', function (options) {
return this.changeState({
test_list: options.test_list,
run_only: options.run_only,
debug: options.debug,
verbose: options.verbose,
run_test_url: options.run_test_url,
read_test_url: options.read_test_url,
// Reset everything on render
render_timestamp: new Date().getTime(),
last_call: false,
data_size: 0,
test_running: false,
continue_loop: true,
paused: false
});
})
form_data.append("test_list", .onStateChange(function (modification_dict) {
document.querySelector("[name='field_your_test']").value); if (modification_dict.hasOwnProperty('render_timestamp')) {
form_data.append("run_only", return this.triggerLiveTest();
document.querySelector("[name='field_your_run_only']").value); }
form_data.append("debug", })
document.querySelector("[name='field_your_debug']").checked ===
true ? 1 : 0);
form_data.append("verbose",
document.querySelector("[name='field_your_verbose']").checked ===
true ? 1 : 0);
// if the user scrolls in the window we do not want it to be updated. .declareJob('triggerLiveTest', function () {
// so set paused flag to false var form_data = new FormData(),
function scrollFunction() { gadget = this;
paused = (data_textarea.scrollHeight - data_textarea.scrollTop) >
(data_textarea.clientHeight + 1);
// if the service was paused when the tests are finished,
// set continue_loop to false
if (!paused && !tests_still_running) {
continue_loop = false;
}
}
data_textarea.onscroll = scrollFunction; form_data.append("test_list", gadget.state.test_list);
form_data.append("run_only", gadget.state.run_only);
form_data.append("debug", gadget.state.debug);
form_data.append("verbose", gadget.state.verbose);
rJS(window).declareService(function () { // Reset textarea content
var queue = new RSVP.Queue(); gadget.element.querySelector('textarea').value = '';
function launchLiveTest() { return new RSVP.Queue()
queue.push(function () { .push(function () {
return jIO.util.ajax({ return RSVP.all([
type: "POST", // Trigger the test.
url: my_url_run_test, // It is considered as running while the ajax query is not over
data: form_data jIO.util.ajax({
type: "POST",
url: gadget.state.run_test_url,
data: form_data
}),
// a delay of 2 seconds so the test can be launched
// before results are read
RSVP.Queue()
.push(function () {
return RSVP.delay(2000);
})
.push(function () {
return gadget.changeState({test_running: true});
})
]);
}).push(function () {
var state_dict = {test_running: false};
// set continue_loop to false ONLY IF the test is not paused.
// Otherwise it will be set when user scrolls to the end
if (!gadget.state.paused) {
state_dict.continue_loop = false;
}
return gadget.changeState(state_dict);
})
.push(undefined, function (error) {
console.warn("Error launching live tests", error);
throw error;
}); });
}).push(function () { })
tests_still_running = false;
// set continue_loop to false ONLY IF the test is not paused.
// Otherwise it will be set when user scrolls to the end
if (!paused) {
continue_loop = false;
}
}, function (error) {
console.error("Error launching live tests", error);
});
}
return queue.push(function () {
return launchLiveTest();
});
}).declareService(function () {
var queue = new RSVP.Queue();
function getLiveTestOutput() { .onLoop(function () {
queue.push(function () { var data_textarea = this.element.querySelector('textarea'),
return jIO.util.ajax({ gadget = this,
type: "GET", state_dict = {};
url: my_url_read_test
}); if (gadget.state.paused) {
}).push(function (evt) { return;
var data = evt.target.response; }
// cut the characters that are already presented if (!gadget.state.continue_loop) {
data = data.substring(data_size); if (gadget.state.last_call) {
if ((!paused || last_call) && data.length !== undefined) { // Stop reading test output
// to put the data in the correct place return;
data_size = data_size + data.length;
// add the new data
data_textarea.value = data_textarea.value + data;
data_textarea.scrollTop = data_textarea.scrollHeight;
}
return RSVP.delay(1000);
}, function (error) {
console.error("Error refreshing live test output", error);
}).push(function () {
if (continue_loop) {
return getLiveTestOutput();
} }
if (!continue_loop) { state_dict.last_call = true;
if (!last_call) { }
last_call = true;
return getLiveTestOutput(); return new RSVP.Queue()
.push(function () {
return jIO.util.ajax({
type: "GET",
url: gadget.state.read_test_url
});
}).push(function (evt) {
var data = evt.target.response;
// cut the characters that are already presented
data = data.substring(gadget.state.data_size);
if ((!gadget.state.paused) && data.length !== undefined) {
// to put the data in the correct place
state_dict.data_size = gadget.state.data_size + data.length;
// add the new data
data_textarea.value = data_textarea.value + data;
data_textarea.scrollTop = data_textarea.scrollHeight;
} }
} }, function (error) {
}); // If last call failed, retry
} state_dict.last_call = false;
return queue.push(function () { console.warn("Error refreshing live test output", error);
// a delay of 2 seconds so the test can be launched })
// before results are read .push(function () {
return RSVP.delay(2000); return gadget.changeState(state_dict);
}).push(getLiveTestOutput()); });
});
}(rJS, jIO, RSVP, window, document, FormData)); }, 1000)
\ No newline at end of file
.onEvent('scroll', function scrollFunction() {
var data_textarea = this.element.querySelector('textarea'),
state_dict = {},
gadget = this;
// if the user scrolls in the window we do not want it to be updated.
// so set paused flag to false
state_dict.paused =
(data_textarea.scrollHeight - data_textarea.scrollTop) >
(data_textarea.clientHeight + 1);
// if the service was paused when the tests are finished,
// set continue_loop to false
if (!gadget.state.paused && !gadget.state.test_running) {
state_dict.continue_loop = false;
}
return gadget.changeState(state_dict);
});
}(rJS, jIO, RSVP, window, FormData));
\ 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