import json
from psycopg2.extras import Json
from lxml import etree
from psycopg2 import DataError
from odoo import Command
from odoo.addons.base.models.ir_actions_report import IrActionsReport
from odoo.addons.web_studio.controllers.main import WebStudioController
from odoo.addons.web_studio.controllers.report import WebStudioReportController, get_report_view_copy, _get_and_write_studio_view
from odoo.addons.web.controllers.report import ReportController
from odoo.http import _request_stack, route
from odoo.tests.common import HttpCase, TransactionCase
from odoo.tests import tagged
from odoo.tools import DotDict, mute_logger
parserXML = etree.XMLParser(remove_blank_text=False)
parserHTML = etree.HTMLParser(remove_blank_text=False)
def html_to_xml_tree(stringHTML, body=True):
temp = etree.fromstring(stringHTML, parser=parserHTML)
if body:
temp = temp.getroottree().find("body")[0]
temp = etree.tostring(temp)
return etree.fromstring(temp, parser=parserXML)
def _remove_oe_context(tree):
for node in tree.iter(etree.Element):
node.attrib.pop("oe-context", None)
def get_combined_and_studio_arch(view):
studio_view = _get_and_write_studio_view(view)
return view.get_combined_arch(), studio_view.arch
class TestReportEditor(TransactionCase):
def setUp(self):
super(TestReportEditor, self).setUp()
self.session = DotDict({'debug': ''})
self.is_frontend = False
_request_stack.push(self) # crappy hack to use a fake Request
self.WebStudioController = WebStudioController()
def test_copy_inherit_report(self):
report = self.env['ir.actions.report'].create({
'name': 'test inherit report user',
'report_name': 'web_studio.test_inherit_report_user',
'model': 'res.users',
})
self.env['ir.ui.view'].create({
'type': 'qweb',
'name': 'web_studio.test_inherit_report_hi',
'key': 'web_studio.test_inherit_report_hi',
'arch': '''
hi
''',
})
parent_view = self.env['ir.ui.view'].create({
'type': 'qweb',
'name': 'web_studio.test_inherit_report_user_parent',
'key': 'web_studio.test_inherit_report_user_parent',
'arch': '''
!
''',
})
self.env['ir.ui.view'].create({
'type': 'qweb',
'name': 'web_studio.test_inherit_report_user',
'key': 'web_studio.test_inherit_report_user',
'arch': '''
!!
''',
'inherit_id': parent_view.id,
})
# check original report render to expected output
report_html = report._render_template(report.report_name).decode()
self.assertEqual(''.join(report_html.split()), 'hi!hi!!')
# duplicate original report
report.copy_report_and_template()
copy_report = self.env['ir.actions.report'].search([
('report_name', '=', 'web_studio.test_inherit_report_user_copy_1'),
])
# check duplicated report render to expected output
copy_report_html = copy_report._render_template(copy_report.report_name).decode()
self.assertEqual(''.join(copy_report_html.split()), 'hi!hi!!')
# check that duplicated view is inheritance combination of original view
copy_view = self.env['ir.ui.view'].search([
('key', '=', copy_report.report_name),
])
self.assertFalse(copy_view.inherit_id, 'copied view does not inherit another one')
found = len(copy_view.arch_db.split('test_inherit_report_hi_copy_1')) - 1
self.assertEqual(found, 2, 't-call is duplicated one time and used 2 times')
def test_duplicate(self):
# Inheritance during an upgrade work only with loaded views
# The following force the inheritance to work for all views
# so the created view is correctly inherited
self.env = self.env(context={'load_all_views': True})
# Create a report/view containing "foo"
report = self.env['ir.actions.report'].create({
'name': 'test duplicate',
'report_name': 'web_studio.test_duplicate_foo',
'model': 'res.users',})
self.env['ir.ui.view'].create({
'type': 'qweb',
'name': 'test_duplicate_foo',
'key': 'web_studio.test_duplicate_foo',
'arch': "foo",})
duplicate_domain = [('report_name', '=like', 'web_studio.test_duplicate_foo_copy_%')]
# Duplicate the report and retrieve the duplicated view
report.copy_report_and_template()
copy1 = self.env['ir.actions.report'].search(duplicate_domain)
copy1.ensure_one() # watchdog
copy1_view = self.env['ir.ui.view'].search([
('key', '=', copy1.report_name)])
copy1_view.ensure_one() # watchdog
# Inherit the view to replace "foo" by "bar"
self.env['ir.ui.view'].create({
'inherit_id': copy1_view.id,
'key': copy1.report_name,
'arch': '''
bar
''' % copy1.report_name,})
# Assert the duplicated view renders "bar" then unlink the report
copy1_html = copy1._render_template(copy1.report_name).decode()
self.assertEqual(''.join(copy1_html.split()), 'bar')
copy1.unlink()
# Re-duplicate the original report, it must renders "foo"
report.copy_report_and_template()
copy2 = self.env['ir.actions.report'].search(duplicate_domain)
copy2.ensure_one()
copy2_html = copy2._render_template(copy2.report_name).decode()
self.assertEqual(''.join(copy2_html.split()), 'foo')
def test_copy_custom_model_rendering(self):
report = self.env['ir.actions.report'].search([('report_name', '=', 'base.report_irmodulereference')])
report.copy_report_and_template()
copy = self.env['ir.actions.report'].search([('report_name', '=', 'base.report_irmodulereference_copy_1')])
report_model = self.env['ir.actions.report']._get_rendering_context_model(copy)
self.assertIsNotNone(report_model)
def test_duplicate_keep_translations(self):
def create_view(name, **kwargs):
arch = '
ab')
views += create_view("abb", inherit_id=target.id, mode="primary")
for view in views.with_context(lang='fr_FR'):
terms = view._fields['arch_db'].get_trans_terms(view.arch_db)
view.update_field_translations('arch_db', {'fr_FR': {term: '%s in fr' % term for term in terms}})
combined_arch = '
'
self.assertEqual(target._read_template(target.id), combined_arch)
# duplicate original report, views will be combined into one
report.copy_report_and_template()
copy_view = self.env['ir.ui.view'].search([
('key', '=', 'web_studio.test_keep_translations_ab_copy_1'),
])
self.assertEqual(copy_view.arch, combined_arch)
# translations of combined views have been copied to the new view
new_arch = '
a_ in fr
ab in fr
a_ in fr
aba in fr
ab in fr
'
self.assertEqual(copy_view.with_context(lang='fr_FR').arch, new_arch)
def tearDown(self):
super(TestReportEditor, self).tearDown()
_request_stack.pop()
@tagged('post_install', '-at_install')
class TestReportEditorUIUnit(HttpCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.testAction = cls.env["ir.actions.act_window"].create({
"name": "simple partner",
"res_model": "res.partner",
})
cls.testActionXmlId = cls.env["ir.model.data"].create({
"name": "studio_test_partner_action",
"model": "ir.actions.act_window",
"module": "web_studio",
"res_id": cls.testAction.id,
})
cls.testMenu = cls.env["ir.ui.menu"].create({
"name": "Studio Test Partner",
"action": "ir.actions.act_window,%s" % cls.testAction.id
})
cls.testMenuXmlId = cls.env["ir.model.data"].create({
"name": "studio_test_partner_menu",
"model": "ir.ui.menu",
"module": "web_studio",
"res_id": cls.testMenu.id,
})
cls.report = cls.env['ir.actions.report'].create({
'name': 'test report',
'report_name': 'web_studio.test_report',
'model': 'res.partner',
})
cls.report_xml_id = cls.env["ir.model.data"].create({
"name": "studio_test_report",
"model": "ir.actions.report",
"module": "web_studio",
"res_id": cls.report.id,
})
cls.main_view = cls.env['ir.ui.view'].create({
'type': 'qweb',
'name': 'web_studio.test_report',
'key': 'web_studio.test_report',
'arch': '''
''',
})
cls.main_view_xml_id = cls.env["ir.model.data"].create({
"name": "studio_test_report_view",
"model": "ir.ui.view",
"module": "web_studio",
"res_id": cls.main_view.id,
})
cls.main_view_document = cls.env['ir.ui.view'].create({
'type': 'qweb',
'name': 'web_studio.test_report_document',
'key': 'web_studio.test_report_document',
'arch': '''
''',
})
cls.main_view_document_xml_id = cls.env["ir.model.data"].create({
"name": "test_report_document",
"model": "ir.ui.view",
"module": "web_studio",
"res_id": cls.main_view_document.id,
})
@property
def tour_url(self):
return f"/web#action=studio&mode=editor&_action={self.testAction.id}&_tab=reports&_report_id={self.report.id}&menu_id={self.testMenu.id}"
def _clear_routing(self):
self.env.registry.clear_cache('routing')
def test_basic_report_edition(self):
original_main_view_doc_arch = self.main_view_document.arch
self.start_tour(self.tour_url, "web_studio.test_basic_report_edition", login="admin")
self.assertEqual(self.report.name, "modified in test")
self.assertTrue(self.main_view_xml_id.noupdate)
self.assertTrue(self.main_view_document_xml_id.noupdate)
self.assertTrue(self.report_xml_id.noupdate)
main_arch, studio_arch = get_combined_and_studio_arch(self.main_view)
self.assertXMLEqual(main_arch, """
""")
self.assertXMLEqual(studio_arch, """
""")
doc_arch, studio_arch = get_combined_and_studio_arch(self.main_view_document)
# Not sure about this one. Due to the absence of relevant branding
# The entire view is replaced (case "entire_view" in report.py)
self.assertXMLEqual(doc_arch, """
edited with odoo editor 2
""")
self.assertXMLEqual(studio_arch, """
edited with odoo editor 2
""")
copied_view = get_report_view_copy(self.main_view_document)
self.assertXMLEqual(original_main_view_doc_arch, copied_view.arch)
self.assertEqual(copied_view.name, "web_studio_backup__web_studio.test_report_document")
self.assertEqual(copied_view.key, f"web_studio.__backup__._{self.main_view_document.id}_._{self.main_view_document.key}_")
self.assertFalse(copied_view.active)
self.assertFalse(copied_view.inherit_id)
def test_basic_report_edition_without_datas(self):
passed_in_rendering_context = False
def _get_rendering_context_mock(self_model, report, docids, data):
nonlocal passed_in_rendering_context
passed_in_rendering_context = True
self.assertTrue(self_model.env.context.get("studio"))
self.assertTrue(data.get("studio"))
return {'reason': 'Something went wrong.'}
self.patch(IrActionsReport, '_get_rendering_context', _get_rendering_context_mock)
self.start_tour(self.tour_url, "web_studio.test_basic_report_edition", login="admin")
self.assertTrue(passed_in_rendering_context)
def test_basic_report_edition_xml(self):
self.start_tour(self.tour_url, "web_studio.test_basic_report_edition_xml", login="admin")
self.assertTrue(self.main_view_xml_id.noupdate)
self.assertTrue(self.main_view_document_xml_id.noupdate)
self.assertTrue(self.report_xml_id.noupdate)
self.assertXMLEqual(self.main_view.arch, """
in main view
""")
self.assertXMLEqual(self.main_view_document.arch, """
in document view
""")
def test_basic_report_edition_discard(self):
self.start_tour(self.tour_url, "web_studio.test_basic_report_edition_discard", login="admin")
self.assertXMLEqual(self.main_view.arch, """
""")
self.assertXMLEqual(self.main_view_document.arch, """
""")
def test_basic_report_edition_cancel_discard(self):
self.start_tour(self.tour_url, "web_studio.test_basic_report_edition_cancel_discard", login="admin")
main_arch, _ = get_combined_and_studio_arch(self.main_view)
self.assertXMLEqual(main_arch, """
""")
def test_basic_report_edition_xml_discard(self):
self.start_tour(self.tour_url, "web_studio.test_basic_report_edition_xml_discard", login="admin")
self.assertXMLEqual(self.main_view.arch, """
""")
self.assertXMLEqual(self.main_view_document.arch, """
""")
def test_basic_report_edition_error(self):
save_report = WebStudioReportController.save_report
self._clear_routing()
self.addCleanup(self._clear_routing)
error = None
@route('/web_studio/save_report', type='json', auth='user')
def save_report_mocked(*args, **kwargs):
try:
return save_report(*args, **kwargs)
except Exception as e:
nonlocal error
error = e
raise e
self.patch(WebStudioReportController, "save_report", save_report_mocked)
main_view_arch = self.main_view.arch
document_view_arch = self.main_view_document.arch
with mute_logger("odoo.http"):
self.start_tour(self.tour_url, "web_studio.test_basic_report_edition_error", login="admin")
self.assertTrue(error)
self.assertFalse(self.main_view_xml_id.noupdate)
self.assertFalse(self.main_view_document_xml_id.noupdate)
self.assertFalse(self.report_xml_id.noupdate)
self.assertXMLEqual(self.main_view.arch, main_view_arch)
self.assertXMLEqual(self.main_view_document.arch, document_view_arch)
def test_basic_report_edition_xml_error(self):
save_report = WebStudioReportController.save_report
self._clear_routing()
self.addCleanup(self._clear_routing)
error = None
@route('/web_studio/save_report', type='json', auth='user')
def save_report_mocked(*args, **kwargs):
try:
return save_report(*args, **kwargs)
except Exception as e:
nonlocal error
error = e
raise e
self.patch(WebStudioReportController, "save_report", save_report_mocked)
main_view_arch = self.main_view.arch
document_view_arch = self.main_view_document.arch
with mute_logger("odoo.http"):
self.start_tour(self.tour_url, "web_studio.test_basic_report_edition_xml_error", login="admin")
self.assertTrue(error)
self.assertFalse(self.main_view_xml_id.noupdate)
self.assertFalse(self.main_view_document_xml_id.noupdate)
self.assertFalse(self.report_xml_id.noupdate)
self.assertXMLEqual(self.main_view.arch, main_view_arch)
self.assertXMLEqual(self.main_view_document.arch, document_view_arch)
def test_report_reset_archs(self):
self.main_view_document.arch_fs = "web_studio/tests/test_report_editor.xml"
self.start_tour(self.tour_url, "web_studio.test_report_reset_archs", login="admin")
self.assertXMLEqual(self.main_view_document.arch, """
from file
""")
def test_report_reset_archs_studio_view(self):
self.authenticate("admin", "admin")
studio_view = _get_and_write_studio_view(self.main_view_document, {"arch": "
"})
self.assertTrue(studio_view.active)
self.url_open("/web_studio/reset_report_archs",
data=json.dumps({"params": {"report_id": self.report.id}}),
headers={"Content-Type": "application/json"}
)
self.assertFalse(studio_view.active)
def test_print_preview(self):
self.start_tour(self.tour_url, "web_studio.test_print_preview", login="admin")
def test_table_rendering(self):
self.main_view_document.arch = """
"""
self.start_tour(self.tour_url, "web_studio.test_table_rendering", login="admin")
arch, _ = get_combined_and_studio_arch(self.main_view_document)
self.assertXMLEqual(arch, """
p edited with odooEditor
""")
def test_nested_table_rendering(self):
self.main_view_document.arch = """
"""
self.authenticate("admin", "admin")
response = self.url_open(
"/web_studio/get_report_qweb",
data=json.dumps({"params": {"report_id": self.report.id}}),
headers={"Content-Type": "application/json"}
)
qweb_html = response.json()["result"]
tree = html_to_xml_tree(qweb_html)
_remove_oe_context(tree)
div_node = tree.xpath("//t[@t-name='web_studio.test_report_document']")[0][0]
self.assertXMLEqual(etree.tostring(div_node), """
""")
def test_field_placeholder(self):
self.main_view_document.arch = """
"""
url = self.tour_url.replace("/web", "/web?debug=assets")
self.start_tour(url, "web_studio.test_field_placeholder", login="admin")
arch, _ = get_combined_and_studio_arch(self.main_view)
self.assertXMLEqual(arch, """
""")
arch, _ = get_combined_and_studio_arch(self.main_view_document)
self.assertXMLEqual(arch, """
some default value
""")
def test_toolbar_appearance(self):
self.main_view_document.arch = """
"""
self.start_tour(self.tour_url, "web_studio.test_toolbar_appearance", login="admin")
def test_add_field_blank_report(self):
studio_views = self.env["ir.ui.view"].search([("key", "=ilike", "studio_customization.%")])
self.start_tour(self.tour_url, "web_studio.test_add_field_blank_report", login="admin")
new_view = self.env["ir.ui.view"].search([("key", "=ilike", "studio_customization.%"), ("id", "not in", studio_views.ids), ("name", "like", "document")])
new_view_arch, _ = get_combined_and_studio_arch(new_view)
self.assertXMLEqual(new_view_arch, """
""")
def test_edition_without_lang(self):
self.env["res.lang"]._activate_lang("fr_FR")
self.env["res.users"].browse(2).lang = "fr_FR"
self.main_view_document.arch = """
original term
"""
translations = {
"fr_FR": {
"original term": "translated term"
}
}
self.main_view_document.update_field_translations("arch_db", translations)
self.start_tour(self.tour_url, "web_studio.test_edition_without_lang", login="admin")
_, studio_arch = get_combined_and_studio_arch(self.main_view_document)
self.assertXMLEqual(studio_arch, """
original term edited
""")
new_translations = self.main_view_document.get_field_translations("arch_db")
new_translations_values = {k["lang"]: k for k in new_translations[0]}
self.assertEqual(
new_translations_values["en_US"]["source"],
"original term"
)
self.assertEqual(
new_translations_values["en_US"]["value"],
""
)
self.assertEqual(
new_translations_values["fr_FR"]["source"],
"original term"
)
self.assertEqual(
new_translations_values["fr_FR"]["value"],
"translated term"
)
studio_translation = _get_and_write_studio_view(self.main_view_document).get_field_translations("arch_db")
studio_translation_values = {k["lang"]: k for k in studio_translation[0]}
self.assertEqual(
studio_translation_values["en_US"]["source"],
"original term edited"
)
self.assertEqual(
studio_translation_values["en_US"]["value"],
""
)
self.assertEqual(
studio_translation_values["fr_FR"]["source"],
"original term edited"
)
self.assertEqual(
studio_translation_values["fr_FR"]["value"],
"translated edited term"
)
def test_report_xml_other_record(self):
ResPartner = self.env["res.partner"]
p1 = ResPartner.create({'name': "partner_1"})
p2 = ResPartner.create({'name': "partner_2"})
original_search = ResPartner.search
def mock_search(self, *args, **kwargs):
if not args and not kwargs:
return (p1 | p2).ids
return original_search(*args, **kwargs)
self.patch(type(ResPartner), "search", mock_search)
self.start_tour(self.tour_url, "web_studio.test_report_xml_other_record", login="admin")
def test_partial_eval(self):
self.main_view_document.arch = """
lol
couic
"""
self.start_tour(self.tour_url, "web_studio.test_partial_eval", login="admin")
def test_render_multicompany(self):
company1 = self.env.company
self.main_view_document.arch = """
doc
"""
external_report_layout_id = self.env['ir.ui.view'].create({
'type': 'qweb',
'name': 'web_studio.test_layout',
'key': 'web_studio.test_layout',
'arch': '''
''',
})
company2 = self.env["res.company"].create({"name": "couic", "external_report_layout_id": external_report_layout_id.id})
self.env["res.users"].browse(2).write({"company_ids": [Command.link(company2.id)]})
tour_url = self.tour_url + f"&cids={company2.id}-{company1.id}"
self.start_tour(tour_url, "web_studio.test_render_multicompany", login="admin")
def test_report_edition_binary_field(self):
self.env["ir.model.fields"].create({
"field_description": "New File",
"name": "x_new_file",
"ttype": "binary",
"model": "res.company",
"model_id": self.env["ir.model"]._get('res.company').id,
"state": "manual",
})
self.env["ir.model.fields"].create({
"field_description": "New File filename",
"name": "x_new_file_filename",
"ttype": "char",
"model": "res.partner",
"model_id": self.env["ir.model"]._get('res.company').id,
"state": "manual",
})
self.env["ir.model.fields"].create({
"field_description": "New Image",
"name": "x_new_image",
"ttype": "binary",
"model": "res.company",
"model_id": self.env["ir.model"]._get('res.company').id,
"state": "manual",
})
self.start_tour(self.tour_url, "web_studio.test_report_edition_binary_field", login="admin")
arch, _ = get_combined_and_studio_arch(self.main_view_document)
self.assertXMLEqual(arch, """
file default valueimage default value
""")
def test_report_edition_dynamic_table(self):
url = self.tour_url.replace("/web", "/web?debug=assets")
self.start_tour(url, "web_studio.test_report_edition_dynamic_table", login="admin")
arch, _ = get_combined_and_studio_arch(self.main_view_document)
self.assertXMLEqual(arch, """
| First Column |
Some Summary
|
""")
def test_saving_xml_editor_reload(self):
self.start_tour(self.tour_url, "web_studio.test_saving_xml_editor_reload", login="admin")
def test_error_at_loading(self):
bad_view = self.env["ir.ui.view"].create({
"name": "bad view",
"inherit_id": self.main_view_document.id,
"arch": """
"""
})
arch = """
will crash
"""
self.env.cr.execute(
"""UPDATE ir_ui_view SET arch_db = %s WHERE id = %s""",
(Json({"en_US": arch}), bad_view.id)
)
with mute_logger("odoo.http"):
self.start_tour(self.tour_url, "web_studio.test_error_at_loading", login="admin")
with mute_logger("odoo.http"):
url = self.tour_url.replace("/web", "/web?debug=1")
self.start_tour(url, "web_studio.test_error_at_loading_debug", login="admin")
def test_xml_and_form_diff(self):
url = self.tour_url.replace("/web", "/web?debug=1")
self.start_tour(url, "web_studio.test_xml_and_form_diff", login="admin")
def test_record_model_differs_from_action(self):
dummy = self.env["ir.model"].create({
"name": "dummy.test",
"model": "x_dummy.test"
})
self.env['ir.model.access'].create({
"name": "dummy",
"perm_read": True,
"model_id": dummy.id,
})
self.report.model = dummy.model
self.report.name = "dummy test"
self.start_tour(f"/web#action=studio&mode=editor&_action={self.testAction.id}&_tab=reports&menu_id={self.testMenu.id}", "web_studio.test_record_model_differs_from_action", login="admin")
def test_recursive_t_calls(self):
self.authenticate("admin", "admin")
self.main_view_document.arch = """
"""
response = self.url_open(
"/web_studio/get_report_qweb",
data=json.dumps({"params": {"report_id": self.report.id}}),
headers={"Content-Type": "application/json"}
)
qweb_html = response.json()["result"]
tree = html_to_xml_tree(qweb_html)
_remove_oe_context(tree)
tcall = tree.xpath("//t[@t-call='web_studio.test_report_document']")[0]
self.assertXMLEqual(etree.tostring(tcall), f"""
""")
def test_can_have_multiple_times_same_t_call(self):
self.authenticate("admin", "admin")
self.main_view.arch = """
"""
self.main_view_document.arch = """
"""
response = self.url_open(
"/web_studio/get_report_qweb",
data=json.dumps({"params": {"report_id": self.report.id}}),
headers={"Content-Type": "application/json"}
)
qweb_html = response.json()["result"]
tree = html_to_xml_tree(qweb_html)
_remove_oe_context(tree)
tcall = tree.xpath("//t[@t-call='web_studio.test_report_document']")
self.assertEqual(len(tcall), 2)
self.assertXMLEqual(etree.tostring(tcall[0]), f"""
""")
self.assertXMLEqual(etree.tostring(tcall[1]), f"""
""")
def test_remove_branding_on_copy(self):
self.start_tour(self.tour_url, "web_studio.test_remove_branding_on_copy", login="admin")
def test_test_different_view_document_name(self):
self.main_view_document_xml_id.name = "test_report_document_1"
self.start_tour(self.tour_url, "web_studio.test_different_view_document_name", login="admin")
def test_formalize_qweb_client_t_call_structure(self):
self.authenticate("admin", "admin")
html_container = self.env["ir.ui.view"]._get("web.html_container")
tcalled_0 = self.env["ir.ui.view"].create({
'type': 'qweb',
'name': 'web_studio.t_call_0',
'key': 'web_studio.t_call_0',
'arch': """
"""
})
tcalled_1 = self.env["ir.ui.view"].create({
'type': 'qweb',
'name': 'web_studio.t_call_1',
'key': 'web_studio.t_call_1',
'arch': """
"""
})
self.main_view_document.arch = """
call zero one
call zero two
"""
response = self.url_open(
"/web_studio/get_report_qweb",
data=json.dumps({"params": {"report_id": self.report.id}}),
headers={"Content-Type": "application/json"}
)
qweb_html = response.json()["result"]
tree = html_to_xml_tree(qweb_html)
_remove_oe_context(tree)
verification_tree = etree.fromstring(
"""
""")
_remove_oe_context(verification_tree)
self.assertXMLEqual(
etree.tostring(tree),
etree.tounicode(tree).format(
main_view=self.main_view, main_view_document=self.main_view_document, tcalled_0=tcalled_0, tcalled_1=tcalled_1, html_container=html_container
)
)
def test_edit_main_arch(self):
self.env["ir.ui.view"].create({
'type': 'qweb',
'name': 'web_studio.t_call_0',
'key': 'web_studio.t_call_0',
'arch': """
"""
})
self.main_view_document.arch = """
outside
inside
"""
self.start_tour(self.tour_url, "web_studio.test_edit_main_arch", login="admin")
arch, studio_arch = get_combined_and_studio_arch(self.main_view_document)
self.assertXMLEqual(arch, """
outside
inside
""")
self.assertXMLEqual(studio_arch, """
outside
inside
""")
def test_edit_in_t_call(self):
self.env["ir.ui.view"].create({
'type': 'qweb',
'name': 'web_studio.t_call_0',
'key': 'web_studio.t_call_0',
'arch': """
"""
})
self.main_view_document.arch = """
outside
inside
"""
self.start_tour(self.tour_url, "web_studio.test_edit_in_t_call", login="admin")
arch, studio_arch = get_combined_and_studio_arch(self.main_view_document)
self.assertXMLEqual(arch, """
outside
inside
""")
self.assertXMLEqual(studio_arch, """
outside
inside
""")
def test_edit_main_and_in_t_call(self):
self.env["ir.ui.view"].create({
'type': 'qweb',
'name': 'web_studio.t_call_0',
'key': 'web_studio.t_call_0',
'arch': """
"""
})
self.main_view_document.arch = """
outside
inside
"""
self.start_tour(self.tour_url, "web_studio.test_edit_main_and_in_t_call", login="admin")
arch, studio_arch = get_combined_and_studio_arch(self.main_view_document)
self.assertXMLEqual(arch, """
outside
inside
""")
self.assertXMLEqual(studio_arch, """
outside
inside
""")
def test_image_crop(self):
attach = self.env["ir.attachment"].create({
"name": "test image crop",
"datas": "iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91JpzAAAAF0lEQVR4nGJxKFrEwMDAxAAGgAAAAP//D+IBWx9K7TUAAAAASUVORK5CYII="
})
self.main_view_document.arch = f"""
"""
self.start_tour(self.tour_url, "web_studio.test_image_crop", login="admin")
def test_add_non_searchable_field(self):
with self.with_user("admin"):
partner = self.env["res.partner"].search([], limit=1)
partner.image_1024 = "iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91JpzAAAAF0lEQVR4nGJxKFrEwMDAxAAGgAAAAP//D+IBWx9K7TUAAAAASUVORK5CYII="
self.start_tour(self.tour_url, "web_studio.test_add_non_searchable_field", login="admin")
arch, _ = get_combined_and_studio_arch(self.main_view_document)
self.assertXMLEqual(arch, """
file default value
""")
def test_translations_are_copied(self):
self.env["res.lang"]._activate_lang("fr_FR")
self.main_view_document.arch = """
term1
"""
self.main_view_document.update_field_translations("arch_db", {"fr_FR": {"term1": "baguette"}})
inheriting = self.env["ir.ui.view"].create({
"type": "qweb",
"inherit_id": self.main_view_document.id,
"arch": """
term2
"""
})
inheriting.update_field_translations("arch_db", {"fr_FR": {"term2": "croissant"}})
self.start_tour(self.tour_url, "web_studio.test_translations_are_copied", login="admin")
html, _ = self.report.with_context(lang="fr_FR")._render_qweb_html(self.report.id, [1])
main = etree.fromstring(html).find(".//main")
self.assertXMLEqual(etree.tostring(main), """
baguette
term3 from edition
croissant
""")
def test_translations_with_br_nodes_are_still_matched(self):
self.env["res.lang"]._activate_lang("fr_FR")
self.main_view_document.arch = """
Hi
"""
self.main_view_document.update_field_translations("arch_db", {
"fr_FR": {"
Hi": "
Salut"}})
self.start_tour(self.tour_url, "web_studio.test_translations_with_br_nodes_are_still_matched", login="admin")
html, _ = self.report.with_context(lang="fr_FR")._render_qweb_html(self.report.id, [1])
main = etree.fromstring(html).find(".//main")
self.assertXMLEqual(etree.tostring(main), """
added text
Salut
""")
def test_evaluate_bad_queries(self):
self.main_view_document.arch = """
"""
ResPartner = self.env.registry.get("res.partner")
search = ResPartner.search
errors = []
def mock_search(record_set, *args, **kwargs):
try:
return search(record_set, *args, **kwargs)
except Exception as e:
errors.append(e)
raise
self.patch(ResPartner, "search", mock_search)
self.authenticate("admin", "admin")
with mute_logger("odoo.sql_db"):
response = self.url_open(
"/web_studio/get_report_qweb",
data=json.dumps({"params": {"report_id": self.report.id}}),
headers={"Content-Type": "application/json"}
)
self.assertEqual(len(errors), 2)
for e in errors:
self.assertTrue(isinstance(e, DataError))
qweb_html = response.json()["result"]
tree = html_to_xml_tree(qweb_html)
_remove_oe_context(tree)
div_node = tree.xpath("//t[@t-name='web_studio.test_report_document']")[0][0]
self.assertXMLEqual(etree.tostring(div_node), """
""")