Commit b22e7b69 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 34e4ada5
......@@ -16,13 +16,13 @@
<key> <string>categories</string> </key>
<value>
<tuple>
<string>action_type/object_action</string>
<string>action_type/object_jio_action</string>
</tuple>
</value>
</item>
<item>
<key> <string>category</string> </key>
<value> <string>object_action</string> </value>
<value> <string>object_jio_action</string> </value>
</item>
<item>
<key> <string>condition</string> </key>
......
request = context.REQUEST
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="")
return context.Base_renderForm('ComponentTool_viewLiveTestDialog')
......@@ -50,7 +50,7 @@
</item>
<item>
<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>
<key> <string>id</string> </key>
......
......@@ -75,7 +75,6 @@
<key> <string>bottom</string> </key>
<value>
<list>
<string>your_text_output</string>
<string>live_test_gadget</string>
</list>
</value>
......
......@@ -11,7 +11,9 @@
<value>
<list>
<string>editable</string>
<string>enabled</string>
<string>gadget_url</string>
<string>renderjs_extra</string>
<string>title</string>
</list>
</value>
......@@ -50,6 +52,16 @@
<key> <string>tales</string> </key>
<value>
<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>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
......@@ -61,9 +73,19 @@
<item>
<key> <string>gadget_url</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</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>
</value>
</item>
......@@ -75,6 +97,10 @@
<key> <string>editable</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_gadget_field</string> </value>
......@@ -87,6 +113,12 @@
<key> <string>gadget_url</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>renderjs_extra</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Test Watcher</string> </value>
......@@ -98,6 +130,19 @@
</pickle>
</record>
<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>
<global name="TALESMethod" module="Products.Formulator.TALESField"/>
</pickle>
......@@ -110,4 +155,17 @@
</dictionary>
</pickle>
</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>
......@@ -227,13 +227,17 @@
<key> <string>hidden</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>input_type</string> </key>
<value> <string>text</string> </value>
</item>
<item>
<key> <string>max_length</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>required</string> </key>
<value> <int>0</int> </value>
<value> <int>1</int> </value>
</item>
<item>
<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>
<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="gadget_live_tests.js" type="text/javascript"></script>
</head>
<body>
<textarea class="live_test_output" rows="30" cols="120" spellcheck="false" style="font-family: monospace"></textarea>
</body>
</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 */
(function (rJS, jIO, RSVP, window, document, FormData) {
(function (rJS, jIO, RSVP, window, FormData) {
"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 = "";
form_data.append("test_list",
document.querySelector("[name='field_your_test']").value);
form_data.append("run_only",
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);
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
});
})
// if the user scrolls in the window we do not want it to be updated.
// so set paused flag to false
function scrollFunction() {
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;
}
.onStateChange(function (modification_dict) {
if (modification_dict.hasOwnProperty('render_timestamp')) {
return this.triggerLiveTest();
}
})
data_textarea.onscroll = scrollFunction;
.declareJob('triggerLiveTest', function () {
var form_data = new FormData(),
gadget = this;
rJS(window).declareService(function () {
var queue = new RSVP.Queue();
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);
function launchLiveTest() {
queue.push(function () {
return jIO.util.ajax({
// Reset textarea content
gadget.element.querySelector('textarea').value = '';
return new RSVP.Queue()
.push(function () {
return RSVP.all([
// Trigger the test.
// It is considered as running while the ajax query is not over
jIO.util.ajax({
type: "POST",
url: my_url_run_test,
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 () {
tests_still_running = false;
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 (!paused) {
continue_loop = false;
if (!gadget.state.paused) {
state_dict.continue_loop = false;
}
}, function (error) {
console.error("Error launching live tests", error);
return gadget.changeState(state_dict);
})
.push(undefined, function (error) {
console.warn("Error launching live tests", error);
throw error;
});
})
.onLoop(function () {
var data_textarea = this.element.querySelector('textarea'),
gadget = this,
state_dict = {};
if (gadget.state.paused) {
return;
}
if (!gadget.state.continue_loop) {
if (gadget.state.last_call) {
// Stop reading test output
return;
}
state_dict.last_call = true;
}
return queue.push(function () {
return launchLiveTest();
});
}).declareService(function () {
var queue = new RSVP.Queue();
function getLiveTestOutput() {
queue.push(function () {
return new RSVP.Queue()
.push(function () {
return jIO.util.ajax({
type: "GET",
url: my_url_read_test
url: gadget.state.read_test_url
});
}).push(function (evt) {
var data = evt.target.response;
// cut the characters that are already presented
data = data.substring(data_size);
if ((!paused || last_call) && data.length !== undefined) {
data = data.substring(gadget.state.data_size);
if ((!gadget.state.paused) && data.length !== undefined) {
// to put the data in the correct place
data_size = data_size + data.length;
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;
}
return RSVP.delay(1000);
}, function (error) {
console.error("Error refreshing live test output", error);
}).push(function () {
if (continue_loop) {
return getLiveTestOutput();
}
if (!continue_loop) {
if (!last_call) {
last_call = true;
return getLiveTestOutput();
}
}
// If last call failed, retry
state_dict.last_call = false;
console.warn("Error refreshing live test output", error);
})
.push(function () {
return gadget.changeState(state_dict);
});
}, 1000)
.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 queue.push(function () {
// a delay of 2 seconds so the test can be launched
// before results are read
return RSVP.delay(2000);
}).push(getLiveTestOutput());
return gadget.changeState(state_dict);
});
}(rJS, jIO, RSVP, window, document, FormData));
\ No newline at end of file
}(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