[MERGE] Sync with website-al
bzr revid: tde@openerp.com-20131219102240-43nvqj9hkzn5ymt2
This commit is contained in:
@@ -204,12 +204,29 @@ class ManyToOne(orm.AbstractModel):
|
||||
_inherit = ['website.qweb.field', 'ir.qweb.field.many2one']
|
||||
|
||||
def from_html(self, cr, uid, model, column, element, context=None):
|
||||
# FIXME: this behavior is really weird, what if the user wanted to edit the name of the related thingy? Should m2os really be editable without a widget?
|
||||
matches = self.pool[column._obj].name_search(
|
||||
cr, uid, name=element.text_content().strip(), context=context)
|
||||
# FIXME: no match? More than 1 match?
|
||||
assert len(matches) == 1
|
||||
return matches[0][0]
|
||||
# FIXME: layering violations all the things
|
||||
Model = self.pool[element.get('data-oe-model')]
|
||||
M2O = self.pool[column._obj]
|
||||
field = element.get('data-oe-field')
|
||||
id = int(element.get('data-oe-id'))
|
||||
value = element.text_content().strip()
|
||||
|
||||
# if anything blows up, just ignore it and bail
|
||||
try:
|
||||
# get parent record
|
||||
[obj] = Model.read(cr, uid, [id], [field])
|
||||
# get m2o record id
|
||||
(m2o_id, _) = obj[field]
|
||||
# assume _rec_name and write directly to it
|
||||
M2O.write(cr, uid, [m2o_id], {
|
||||
M2O._rec_name: value
|
||||
}, context=context)
|
||||
except:
|
||||
logger.exception("Could not save %r to m2o field %s of model %s",
|
||||
value, field, Model._name)
|
||||
|
||||
# not necessary, but might as well be explicit about it
|
||||
return None
|
||||
|
||||
class HTML(orm.AbstractModel):
|
||||
_name = 'website.qweb.field.html'
|
||||
@@ -281,7 +298,7 @@ class Image(orm.AbstractModel):
|
||||
match.group('module'), 'static', *(rest.split('/')))
|
||||
|
||||
if not path:
|
||||
return False
|
||||
return None
|
||||
|
||||
try:
|
||||
with open(path, 'rb') as f:
|
||||
@@ -292,7 +309,7 @@ class Image(orm.AbstractModel):
|
||||
return f.read().encode('base64')
|
||||
except Exception:
|
||||
logger.exception("Failed to load local image %r", url)
|
||||
return False
|
||||
return None
|
||||
|
||||
def load_remote_url(self, url):
|
||||
try:
|
||||
@@ -310,7 +327,7 @@ class Image(orm.AbstractModel):
|
||||
image.load()
|
||||
except Exception:
|
||||
logger.exception("Failed to load remote image %r", url)
|
||||
return False
|
||||
return None
|
||||
|
||||
# don't use original data in case weird stuff was smuggled in, with
|
||||
# luck PIL will remove some of it?
|
||||
|
||||
@@ -70,9 +70,11 @@ class view(osv.osv):
|
||||
el.get('data-oe-type'))
|
||||
value = converter.from_html(cr, uid, Model, column, el)
|
||||
|
||||
Model.write(cr, uid, [int(el.get('data-oe-id'))], {
|
||||
field: value
|
||||
}, context=context)
|
||||
if value is not None:
|
||||
# TODO: batch writes?
|
||||
Model.write(cr, uid, [int(el.get('data-oe-id'))], {
|
||||
field: value
|
||||
}, context=context)
|
||||
|
||||
def to_field_ref(self, cr, uid, el, context=None):
|
||||
# filter out meta-information inserted in the document
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* ---- CKEditor Minimal Reset ---- */
|
||||
/* ---- CKEditor Minimal Reset ---- {{{ */
|
||||
.navbar.navbar-inverse .cke_chrome {
|
||||
border: none;
|
||||
}
|
||||
@@ -28,7 +28,7 @@
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
/* ---- OpenERP Style ---- */
|
||||
/* ---- OpenERP Style ---- {{{ */
|
||||
.oe_website_editorbar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
@@ -103,7 +103,7 @@
|
||||
top: 2px;
|
||||
}
|
||||
|
||||
/* ---- EDITOR BAR ---- */
|
||||
/* ---- EDITOR BAR ---- {{{ */
|
||||
table.editorbar-panel {
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
@@ -115,6 +115,7 @@ table.editorbar-panel td.selected {
|
||||
background-color: #b1c9d9;
|
||||
}
|
||||
|
||||
/* ---- TRANSLATIONS ---- {{{ */
|
||||
.oe_translate_or {
|
||||
color: white;
|
||||
padding: 0 0 0 1em;
|
||||
@@ -145,6 +146,7 @@ table.editorbar-panel td.selected {
|
||||
background: #ffffb6;
|
||||
}
|
||||
|
||||
/* ---- MENU ---- {{{ */
|
||||
div.oe_menu_buttons {
|
||||
top: -8px;
|
||||
right: -8px;
|
||||
@@ -171,11 +173,12 @@ ul.oe_menu_editor .disclose {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* ---- RTE ---- */
|
||||
/* ---- RTE ---- {{{ */
|
||||
.oe_editable .btn, .btn.oe_editable {
|
||||
-webkit-user-select: auto;
|
||||
-moz-user-select: auto;
|
||||
user-select: auto;
|
||||
cursor: text !important;
|
||||
}
|
||||
|
||||
.modal-dialog.select-image {
|
||||
@@ -233,12 +236,11 @@ ul.oe_menu_editor .disclose {
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
/* ---- SNIPPET EDITOR ---- */
|
||||
/* ---- SNIPPET EDITOR ---- {{{ */
|
||||
#oe_snippets {
|
||||
position: fixed;
|
||||
left: 0px;
|
||||
right: 0px;
|
||||
top: 51px;
|
||||
background: #282828;
|
||||
-webkit-box-shadow: 0px 10px 10px -10px black inset;
|
||||
-moz-box-shadow: 0px 10px 10px -10px black inset;
|
||||
@@ -368,7 +370,7 @@ ul.oe_menu_editor .disclose {
|
||||
background: #747474, radial-gradient(rgba(0, 0, 0, 0.25), rgba(0, 0, 0, 0.4));
|
||||
}
|
||||
|
||||
/* ---- SNIPPETS DROP ZONES ---- */
|
||||
/* ---- SNIPPETS DROP ZONES ---- {{{ */
|
||||
.oe_drop_zone.oe_insert {
|
||||
display: block;
|
||||
height: 48px;
|
||||
@@ -429,7 +431,7 @@ ul.oe_menu_editor .disclose {
|
||||
padding-top: 12px;
|
||||
}
|
||||
|
||||
/* ---- SNIPPET MANIPULATOR ---- */
|
||||
/* ---- SNIPPET MANIPULATOR ---- {{{ */
|
||||
.oe_overlay {
|
||||
display: none;
|
||||
position: absolute;
|
||||
@@ -599,7 +601,7 @@ ul.oe_menu_editor .disclose {
|
||||
cursor: move !important;
|
||||
}
|
||||
|
||||
/* ---- MOBILE PREVIEW ---- */
|
||||
/* ---- MOBILE PREVIEW ---- {{{ */
|
||||
.oe_mobile_preview.modal .modal-content {
|
||||
height: 660px;
|
||||
background-color: black;
|
||||
@@ -655,7 +657,7 @@ ul.oe_menu_editor .disclose {
|
||||
background-color: black;
|
||||
}
|
||||
|
||||
/* ---- SEO TOOLS ---- */
|
||||
/* ---- SEO TOOLS ---- {{{ */
|
||||
.oe_seo_configuration .modal-dialog {
|
||||
width: 80%;
|
||||
}
|
||||
@@ -674,10 +676,9 @@ ul.oe_menu_editor .disclose {
|
||||
border-radius: 0.4em;
|
||||
}
|
||||
|
||||
/* ---- ACE EDITOR ---- */
|
||||
/* ---- ACE EDITOR ---- {{{ */
|
||||
.oe_ace_view_editor {
|
||||
position: fixed;
|
||||
top: 51px;
|
||||
right: 0;
|
||||
z-index: 1000;
|
||||
height: 100%;
|
||||
@@ -707,7 +708,6 @@ ul.oe_menu_editor .disclose {
|
||||
position: absolute;
|
||||
top: 30px;
|
||||
right: 0;
|
||||
bottom: 51px;
|
||||
left: 0;
|
||||
}
|
||||
.oe_ace_view_editor .ace_editor .ace_gutter {
|
||||
@@ -723,7 +723,7 @@ ul.oe_menu_editor .disclose {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
/* ---- EDITOR TOUR ---- */
|
||||
/* ---- EDITOR TOUR ---- {{{ */
|
||||
div.tour-backdrop {
|
||||
z-index: 2009;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
@charset "utf-8"
|
||||
|
||||
@import "common"
|
||||
|
||||
/* ---- CKEditor Minimal Reset ---- */
|
||||
/* ---- CKEditor Minimal Reset ---- {{{ */
|
||||
|
||||
.navbar.navbar-inverse .cke_chrome
|
||||
border: none
|
||||
@@ -26,7 +24,9 @@
|
||||
border: none
|
||||
+box-shadow(none)
|
||||
|
||||
/* ---- OpenERP Style ---- */
|
||||
// }}}
|
||||
|
||||
/* ---- OpenERP Style ---- {{{ */
|
||||
|
||||
.oe_website_editorbar
|
||||
position: fixed
|
||||
@@ -85,7 +85,9 @@
|
||||
position: relative !important
|
||||
top: +2px
|
||||
|
||||
/* ---- EDITOR BAR ---- */
|
||||
// }}}
|
||||
|
||||
/* ---- EDITOR BAR ---- {{{ */
|
||||
|
||||
table.editorbar-panel
|
||||
cursor: pointer
|
||||
@@ -95,7 +97,10 @@ table.editorbar-panel
|
||||
td.selected
|
||||
background-color: #b1c9d9
|
||||
|
||||
// ---- TRANSLATIONS ---- {{{
|
||||
// }}}
|
||||
|
||||
/* ---- TRANSLATIONS ---- {{{ */
|
||||
|
||||
.oe_translate_or
|
||||
color: white
|
||||
padding: 0 0 0 1em
|
||||
@@ -112,9 +117,11 @@ table.editorbar-panel
|
||||
padding: 0 10px
|
||||
.oe_translatable_todo
|
||||
background: rgb(255, 255, 182)
|
||||
|
||||
// }}}
|
||||
|
||||
// -------- MENU -------- {{{
|
||||
/* ---- MENU ---- {{{ */
|
||||
|
||||
div.oe_menu_buttons
|
||||
top: -8px
|
||||
right: -8px
|
||||
@@ -137,12 +144,13 @@ ul.oe_menu_editor
|
||||
|
||||
// }}}
|
||||
|
||||
/* ---- RTE ---- */
|
||||
/* ---- RTE ---- {{{ */
|
||||
|
||||
// bootstrap makes .btn elements unselectable -> RTE double-click can't know
|
||||
// about them either
|
||||
.oe_editable .btn, .btn.oe_editable
|
||||
+user-select(auto)
|
||||
cursor: text !important
|
||||
|
||||
.modal-dialog.select-image
|
||||
width: 80%
|
||||
@@ -197,13 +205,15 @@ ul.oe_menu_editor
|
||||
// This z-index is due to .navbar of bootstrap
|
||||
z-index: 1000
|
||||
|
||||
/* ---- SNIPPET EDITOR ---- */
|
||||
// }}}
|
||||
|
||||
/* ---- SNIPPET EDITOR ---- {{{ */
|
||||
|
||||
#oe_snippets
|
||||
position: fixed
|
||||
left: 0px
|
||||
right: 0px
|
||||
top: 51px
|
||||
// top property is set programmatically
|
||||
background: rgb(40,40,40)
|
||||
+box-shadow(0px 10px 10px -10px black inset)
|
||||
z-index: 1010
|
||||
@@ -286,7 +296,9 @@ ul.oe_menu_editor
|
||||
#oe_snippets .oe_snippet_thumbnail
|
||||
@include background(#747474, radial-gradient(rgba(0,0,0,0.25),rgba(0,0,0,0.4)))
|
||||
|
||||
/* ---- SNIPPETS DROP ZONES ---- */
|
||||
// }}}}
|
||||
|
||||
/* ---- SNIPPETS DROP ZONES ---- {{{ */
|
||||
|
||||
.oe_drop_zone.oe_insert
|
||||
display: block
|
||||
@@ -333,7 +345,9 @@ ul.oe_menu_editor
|
||||
margin-bottom: 32px
|
||||
padding-top: 12px
|
||||
|
||||
/* ---- SNIPPET MANIPULATOR ---- */
|
||||
// }}}
|
||||
|
||||
/* ---- SNIPPET MANIPULATOR ---- {{{ */
|
||||
|
||||
.oe_overlay
|
||||
display: none
|
||||
@@ -454,8 +468,9 @@ ul.oe_menu_editor
|
||||
.move-important, .move-important *
|
||||
cursor: move !important
|
||||
|
||||
// }}}
|
||||
|
||||
/* ---- MOBILE PREVIEW ---- */
|
||||
/* ---- MOBILE PREVIEW ---- {{{ */
|
||||
|
||||
$mobile_preview_background: #000000
|
||||
$mobile_preview_border: #1C1F1F
|
||||
@@ -498,7 +513,9 @@ $icon_close: #E00101
|
||||
.modal-footer
|
||||
background-color: $mobile_preview_background
|
||||
|
||||
/* ---- SEO TOOLS ---- */
|
||||
// }}}
|
||||
|
||||
/* ---- SEO TOOLS ---- {{{ */
|
||||
|
||||
$remove_color: $icon_close
|
||||
$in_title_color: #5cb85c
|
||||
@@ -516,15 +533,16 @@ $highlighted_text_color: #ffffff
|
||||
padding: .2em .4em .2em .5em
|
||||
+border-radius(.4em)
|
||||
|
||||
/* ---- ACE EDITOR ---- */
|
||||
// }}}
|
||||
|
||||
/* ---- ACE EDITOR ---- {{{ */
|
||||
|
||||
$editorbar_height: 30px
|
||||
// TODO Fix => might break with themes
|
||||
$navbar_height: 51px
|
||||
|
||||
.oe_ace_view_editor
|
||||
position: fixed
|
||||
top: $navbar_height
|
||||
// top property is set programmatically
|
||||
right: 0
|
||||
z-index: 1000
|
||||
height: 100%
|
||||
@@ -546,7 +564,7 @@ $navbar_height: 51px
|
||||
position: absolute
|
||||
top: $editorbar_height
|
||||
right: 0
|
||||
bottom: $navbar_height
|
||||
// bottom property is set programmatically
|
||||
left: 0
|
||||
.ace_gutter
|
||||
cursor: ew-resize
|
||||
@@ -556,9 +574,15 @@ $navbar_height: 51px
|
||||
z-index: -1000
|
||||
+opacity(0)
|
||||
|
||||
/* ---- EDITOR TOUR ---- */
|
||||
// }}}
|
||||
|
||||
/* ---- EDITOR TOUR ---- {{{ */
|
||||
|
||||
div.tour-backdrop
|
||||
z-index: 2009
|
||||
.popover.tour
|
||||
z-index: 2010
|
||||
|
||||
// }}}
|
||||
|
||||
// vim:tabstop=4:shiftwidth=4:softtabstop=4:fdm=marker:
|
||||
|
||||
@@ -1,35 +1,34 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
var globalEditor;
|
||||
|
||||
var hash = "#advanced-view-editor";
|
||||
|
||||
var website = openerp.website;
|
||||
website.add_template_file('/website/static/src/xml/website.ace.xml');
|
||||
|
||||
website.ready().then(function () {
|
||||
if (window.location.hash.indexOf(hash) >= 0) {
|
||||
launch();
|
||||
}
|
||||
});
|
||||
|
||||
function launch () {
|
||||
if (globalEditor) {
|
||||
globalEditor.open();
|
||||
} else {
|
||||
globalEditor = new website.ace.ViewEditor();
|
||||
globalEditor.appendTo($(document.body));
|
||||
}
|
||||
}
|
||||
|
||||
website.EditorBar.include({
|
||||
events: _.extend({}, website.EditorBar.prototype.events, {
|
||||
'click a[data-action=ace]': 'launchAce',
|
||||
}),
|
||||
start: function () {
|
||||
var self = this;
|
||||
this.globalEditor = null;
|
||||
return this._super.apply(this, arguments).then(function () {
|
||||
if (window.location.hash.indexOf(hash) >= 0) {
|
||||
self.launchAce();
|
||||
}
|
||||
});
|
||||
},
|
||||
launchAce: function (e) {
|
||||
e.preventDefault();
|
||||
launch();
|
||||
if (e) {
|
||||
e.preventDefault();
|
||||
}
|
||||
if (this.globalEditor) {
|
||||
this.globalEditor.open();
|
||||
} else {
|
||||
this.globalEditor = new website.ace.ViewEditor(this);
|
||||
this.globalEditor.appendTo($(document.body));
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -104,6 +103,10 @@
|
||||
self.aceEditor.resize();
|
||||
self.$el.width(width);
|
||||
}
|
||||
function resizeEditorHeight(height) {
|
||||
self.$el.css('top', height);
|
||||
self.$('.ace_editor').css('bottom', height);
|
||||
}
|
||||
function storeEditorWidth() {
|
||||
window.localStorage.setItem('ace_editor_width', self.$el.width());
|
||||
}
|
||||
@@ -133,7 +136,11 @@
|
||||
$('button[data-action=edit]').click(function () {
|
||||
self.close();
|
||||
});
|
||||
this.getParent().on('change:height', this, function (editor) {
|
||||
resizeEditorHeight(editor.get('height'));
|
||||
});
|
||||
resizeEditor(readEditorWidth());
|
||||
resizeEditorHeight(this.getParent().get('height'));
|
||||
},
|
||||
loadViews: function (views) {
|
||||
var self = this;
|
||||
|
||||
@@ -374,7 +374,6 @@
|
||||
editor.edit();
|
||||
}
|
||||
});
|
||||
$body.css('padding-top', '50px'); // Not working properly: editor.$el.outerHeight());
|
||||
};
|
||||
|
||||
/* ----- TOP EDITOR BAR FOR ADMIN ---- */
|
||||
@@ -423,7 +422,6 @@
|
||||
},
|
||||
start: function() {
|
||||
var self = this;
|
||||
|
||||
this.saving_mutex = new openerp.Mutex();
|
||||
|
||||
this.$('#website-top-edit').hide();
|
||||
@@ -443,12 +441,25 @@
|
||||
this.rte.on('rte:ready', this, function () {
|
||||
self.setup_hover_buttons();
|
||||
self.trigger('rte:ready');
|
||||
self.check_height();
|
||||
});
|
||||
|
||||
$(window).on('resize', _.debounce(this.check_height.bind(this), 50));
|
||||
this.check_height();
|
||||
|
||||
return $.when(
|
||||
this._super.apply(this, arguments),
|
||||
this.rte.appendTo(this.$('#website-top-edit .nav.pull-right'))
|
||||
);
|
||||
).then(function () {
|
||||
self.check_height();
|
||||
});
|
||||
},
|
||||
check_height: function () {
|
||||
var editor_height = this.$el.outerHeight();
|
||||
if (this.get('height') != editor_height) {
|
||||
$(document.body).css('padding-top', editor_height);
|
||||
this.set('height', editor_height);
|
||||
}
|
||||
},
|
||||
edit: function () {
|
||||
this.$buttons.edit.prop('disabled', true);
|
||||
@@ -456,7 +467,7 @@
|
||||
this.$('#website-top-edit').show();
|
||||
$('.css_non_editable_mode_hidden').removeClass("css_non_editable_mode_hidden");
|
||||
|
||||
this.rte.start_edition();
|
||||
this.rte.start_edition().then(this.check_height.bind(this));
|
||||
},
|
||||
rte_changed: function () {
|
||||
this.$buttons.save.prop('disabled', false);
|
||||
|
||||
@@ -139,6 +139,10 @@
|
||||
$(document).on('click', '.dropdown-submenu a[tabindex]', function (e) {
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
this.getParent().on('change:height', this, function (editor) {
|
||||
self.$el.css('top', editor.get('height'));
|
||||
});
|
||||
},
|
||||
fetch_snippet_templates: function () {
|
||||
var self = this;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import test_views, test_converter, test_requests
|
||||
import test_views, test_converter, test_requests, test_ui
|
||||
|
||||
checks = [ test_views, test_converter, ]
|
||||
|
||||
@@ -63,20 +63,6 @@ class TestConvertBack(common.TransactionCase):
|
||||
|
||||
self.field_roundtrip('char', "ⒸⓄⓇⒼⒺ")
|
||||
|
||||
def test_m2o(self):
|
||||
Sub = self.registry('website.converter.test.sub')
|
||||
sub = partial(Sub.create, self.cr, self.uid)
|
||||
ids = [
|
||||
sub({'name': "Foo"}),
|
||||
sub({'name': "Bar"}),
|
||||
sub({'name': "Baz"}),
|
||||
]
|
||||
|
||||
self.field_rountrip_result(
|
||||
'many2one',
|
||||
ids[2],
|
||||
ids[2])
|
||||
|
||||
def test_selection(self):
|
||||
self.field_roundtrip('selection', 3)
|
||||
|
||||
@@ -104,3 +90,47 @@ class TestConvertBack(common.TransactionCase):
|
||||
You never know
|
||||
You never know until you go
|
||||
""")
|
||||
|
||||
def test_m2o(self):
|
||||
""" the M2O field conversion (from html) is markedly different from
|
||||
others as it directly writes into the m2o and returns nothing at all.
|
||||
"""
|
||||
model = 'website.converter.test'
|
||||
field = 'many2one'
|
||||
|
||||
Sub = self.registry('website.converter.test.sub')
|
||||
sub_id = Sub.create(self.cr, self.uid, {'name': "Foo"})
|
||||
|
||||
Model = self.registry(model)
|
||||
id = Model.create(self.cr, self.uid, {field: sub_id})
|
||||
[record] = Model.browse(self.cr, self.uid, [id])
|
||||
|
||||
e = document.createElement('span')
|
||||
field_value = 'record.%s' % field
|
||||
e.setAttribute('t-field', field_value)
|
||||
|
||||
rendered = self.registry('website.qweb').render_tag_field(
|
||||
e, {'field': field_value}, '', ir_qweb.QWebContext(self.cr, self.uid, {
|
||||
'record': record,
|
||||
}))
|
||||
|
||||
element = html.fromstring(rendered, parser=html.HTMLParser(encoding='utf-8'))
|
||||
# emulate edition
|
||||
element.text = "New content"
|
||||
|
||||
column = Model._all_columns[field].column
|
||||
converter = self.registry('website.qweb').get_converter_for(
|
||||
element.get('data-oe-type'))
|
||||
|
||||
value_back = converter.from_html(
|
||||
self.cr, self.uid, model, column, element)
|
||||
|
||||
self.assertIsNone(
|
||||
value_back, "the m2o converter should return None to avoid spurious"
|
||||
" or useless writes on the parent record")
|
||||
|
||||
self.assertEqual(
|
||||
Sub.browse(self.cr, self.uid, sub_id).name,
|
||||
"New content",
|
||||
"element edition should have been written directly to the m2o record"
|
||||
)
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
import json
|
||||
import subprocess
|
||||
import unittest
|
||||
import os
|
||||
import time
|
||||
|
||||
ROOT = os.path.join(os.path.dirname(__file__), 'ui_suite')
|
||||
|
||||
__all__ = ['load_tests', 'WebsiteUiSuite']
|
||||
|
||||
def _exc_info_to_string(err, test):
|
||||
return err
|
||||
|
||||
class WebsiteUiTest(unittest.TestCase):
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
def shortDescription(self):
|
||||
return None
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
class WebsiteUiSuite(unittest.TestSuite):
|
||||
def __init__(self, testfile, timeout=5000):
|
||||
self.testfile = testfile
|
||||
self.timeout = timeout
|
||||
self._test = None
|
||||
|
||||
def __iter__(self):
|
||||
return iter([self])
|
||||
|
||||
def run(self, result):
|
||||
# Test if phantom is correctly installed
|
||||
try:
|
||||
subprocess.call(['phantomjs', '-v'],
|
||||
stdout=open(os.devnull, 'w'),
|
||||
stderr=subprocess.STDOUT)
|
||||
except OSError:
|
||||
test = WebsiteUiTest('UI Tests')
|
||||
result.startTest(test)
|
||||
result.addSkip(test, "phantomjs command not found")
|
||||
result.stopTest(test)
|
||||
return
|
||||
|
||||
result._exc_info_to_string = _exc_info_to_string
|
||||
try:
|
||||
self._run(result)
|
||||
finally:
|
||||
del result._exc_info_to_string
|
||||
|
||||
def _run(self, result):
|
||||
phantom = subprocess.Popen([
|
||||
'phantomjs',
|
||||
os.path.join(ROOT, self.testfile),
|
||||
json.dumps({ 'timeout': self.timeout })
|
||||
], stdout=subprocess.PIPE)
|
||||
|
||||
self._test = WebsiteUiTest(self.testfile)
|
||||
|
||||
try:
|
||||
while True:
|
||||
line = phantom.stdout.readline()
|
||||
if line:
|
||||
# the runner expects only one result per test
|
||||
self.process(line, result)
|
||||
result.stopTest(self._test)
|
||||
break
|
||||
else:
|
||||
time.sleep(0.1)
|
||||
finally:
|
||||
# kill phantomjs if phantom.exit() wasn't called in the test
|
||||
if phantom.poll() is None:
|
||||
phantom.terminate()
|
||||
|
||||
def process(self, line, result):
|
||||
# Test protocol
|
||||
# -------------
|
||||
# use console.log in phantomjs to output test results using the following format:
|
||||
# - for a success: { "event": "success" }
|
||||
# - for a failure: { "event": "failure", "message": "Failure description" }
|
||||
# any other message is treated as an error
|
||||
result.startTest(self._test)
|
||||
try:
|
||||
args = json.loads(line)
|
||||
event = args.get('event')
|
||||
message = args.get('message', "")
|
||||
|
||||
if event == 'success':
|
||||
result.addSuccess(self._test)
|
||||
elif event == 'failure':
|
||||
result.addFailure(self._test, message)
|
||||
else:
|
||||
result.addError(self._test, "Unexpected message: %s" % line)
|
||||
except ValueError:
|
||||
result.addError(self._test, "Unexpected message: %s" % line)
|
||||
|
||||
def load_tests(loader, base, _):
|
||||
base.addTest(WebsiteUiSuite('sample_test.js'))
|
||||
return base
|
||||
@@ -0,0 +1,4 @@
|
||||
console.log('{ "event": "success" }');
|
||||
// For a failure:
|
||||
// console.log('{ "event": "failure", "message": "The test failed" }');
|
||||
phantom.exit();
|
||||
@@ -597,7 +597,7 @@ Sitemap: <t t-esc="url_root"/>sitemap.xml
|
||||
|
||||
<template id="company_description" name="Company Description">
|
||||
<address>
|
||||
<div t-field="res_company.partner_id">Name</div>
|
||||
<div t-field="res_company.name">Name</div>
|
||||
<br />
|
||||
<div>&#x2706; <span t-field="res_company.phone"></span></div>
|
||||
<div class="fa fa-envelope" t-field="res_company.email"></div>
|
||||
|
||||
@@ -52,12 +52,5 @@
|
||||
</record>
|
||||
|
||||
|
||||
<!-- Mail group for the company's jobs -->
|
||||
<record id="blog_category_2" model="blog.category">
|
||||
<field name="name">Jobs</field>
|
||||
<field name="description">Job Announces</field>
|
||||
</record>
|
||||
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
|
||||
Reference in New Issue
Block a user