",
'name': "Odoo Studio: %s customization" % (self.testView.name)
})
self.start_tour("/odoo?debug=tests", 'web_studio_test_edit_with_xml_editor', login="admin", timeout=200)
self.assertEqual(studioView.arch, "
")
def test_enter_x2many_edition_and_add_field(self):
normalize_origin = self.env.registry.get("ir.ui.view").normalize
def mock_normalize(*args, **kwargs):
random.seed('https://youtu.be/tFjNH9l6-sQ')
return normalize_origin(*args, **kwargs)
self.addCleanup(random.seed)
self.patch(self.env.registry.get("ir.ui.view"), "normalize", mock_normalize)
doesNotHaveGroup = self.env["res.groups"].create({
"name": "studio does not have"
})
doesNotHaveGroupXmlId = self.env["ir.model.data"].create({
"name": "studio_test_doesnothavegroup",
"model": "res.groups",
"module": "web_studio",
"res_id": doesNotHaveGroup.id,
})
userView = self.env["ir.ui.view"].create({
"name": "simple user",
"model": "res.users",
"type": "form",
"arch": '''
'''.format(doesnothavegroup=doesNotHaveGroupXmlId.complete_name)
})
userViewXmlId = self.env["ir.model.data"].create({
"name": "studio_test_user_view",
"model": "ir.ui.view",
"module": "web_studio",
"res_id": userView.id,
})
self.testView.arch = '''
''' % userViewXmlId.complete_name
studioView = _get_studio_view(self.testView)
self.assertFalse(studioView.exists())
self.start_tour("/odoo?debug=tests", 'web_studio_enter_x2many_edition_and_add_field', login="admin", timeout=200)
studioView = _get_studio_view(self.testView)
assertViewArchEqual(self, studioView.arch, """
""".format(doesnothavegroup=doesNotHaveGroupXmlId.complete_name))
def test_enter_x2many_auto_inlined_subview(self):
userView = self.env["ir.ui.view"].create({
"name": "simple user",
"model": "res.users",
"type": "list",
"arch": '''
'''
})
userViewXmlId = self.env["ir.model.data"].create({
"name": "studio_test_user_view",
"model": "ir.ui.view",
"module": "web_studio",
"res_id": userView.id,
})
self.testView.arch = '''
''' % userViewXmlId.complete_name
studioView = _get_studio_view(self.testView)
self.assertFalse(studioView.exists())
self.start_tour("/odoo?debug=tests", 'web_studio_enter_x2many_auto_inlined_subview', login="admin", timeout=200)
studioView = _get_studio_view(self.testView)
assertViewArchEqual(self, studioView.arch, """
""")
def test_enter_x2many_auto_inlined_subview_with_multiple_field_matching(self):
user_view = self.env["ir.ui.view"].create({
"name": "simple user",
"model": "res.users",
"type": "list",
"arch": '''
'''
})
user_view_xml_id = self.env["ir.model.data"].create({
"name": "studio_test_user_view",
"model": "ir.ui.view",
"module": "web_studio",
"res_id": user_view.id,
})
self.testView.arch = '''
''' % (user_view_xml_id.complete_name, user_view_xml_id.complete_name)
studio_view = _get_studio_view(self.testView)
self.assertFalse(studio_view.exists())
self.start_tour("/odoo?debug=tests", 'web_studio_enter_x2many_auto_inlined_subview_with_multiple_field_matching',
login="admin", timeout=200)
studio_view = _get_studio_view(self.testView)
assertViewArchEqual(self, studio_view.arch, """
""")
def test_field_with_group(self):
operations = []
def edit_view_mocked(*args, **kwargs):
operations.extend(kwargs["operations"] if "operations" in kwargs else args[3])
watch_edit_view(self, edit_view_mocked)
doesNotHaveGroup = self.env["res.groups"].create({
"name": "studio does not have"
})
doesNotHaveGroupXmlId = self.env["ir.model.data"].create({
"name": "studio_test_doesnothavegroup",
"model": "res.groups",
"module": "web_studio",
"res_id": doesNotHaveGroup.id,
})
self.testView.write({
"type": "list",
"arch": '''
'''.format(doesnothavegroup=doesNotHaveGroupXmlId.complete_name)
})
self.testAction.write({
"view_ids": [Command.clear(), Command.create({"view_id": self.testView.id, "view_mode": "list"})]
})
self.start_tour("/odoo?debug=tests", 'web_studio_field_with_group', login="admin", timeout=200)
self.assertEqual(len(operations), 1)
self.assertEqual(operations[0]["target"]["xpath_info"], [{'tag': 'list', 'indice': 1}, {'tag': 'field', 'indice': 3}])
studioView = _get_studio_view(self.testView)
assertViewArchEqual(self, studioView.arch, """
""")
def test_studio_no_fetch_subview(self):
doesNotHaveGroup = self.env["res.groups"].create({
"name": "studio does not have"
})
doesNotHaveGroupXmlId = self.env["ir.model.data"].create({
"name": "studio_test_doesnothavegroup",
"model": "res.groups",
"module": "web_studio",
"res_id": doesNotHaveGroup.id,
})
def create_new_field_mocked(*args, **kwargs):
# For this test we need to patch groups from the phone field. Unfortunately it seems
# that the patch is erased each time we create a new field so we have to patch after.
self.patch(type(self.env["res.partner"]).phone, "groups", doesNotHaveGroupXmlId.complete_name)
watch_create_new_field(self, create_new_field_mocked)
self.patch(type(self.env["res.partner"]).phone, "groups", doesNotHaveGroupXmlId.complete_name)
self.testView.write({
"arch": '''
'''
})
self.start_tour("/odoo", 'web_studio_no_fetch_subview', login="admin")
def test_elements_with_groups_form(self):
operations = []
def edit_view_mocked(*args, **kwargs):
operations.extend(kwargs["operations"] if "operations" in kwargs else args[3])
watch_edit_view(self, edit_view_mocked)
doesNotHaveGroup = self.env["res.groups"].create({
"name": "studio does not have"
})
doesNotHaveGroupXmlId = self.env["ir.model.data"].create({
"name": "studio_test_doesnothavegroup",
"model": "res.groups",
"module": "web_studio",
"res_id": doesNotHaveGroup.id,
})
hasGroup = self.env["res.groups"].create({
"name": "studio has group",
"user_ids": [Command.link(2)]
})
hasGroupXmlId = self.env["ir.model.data"].create({
"name": "studio_test_hasgroup",
"model": "res.groups",
"module": "web_studio",
"res_id": hasGroup.id,
})
self.patch(type(self.env["res.partner"]).function, "groups", doesNotHaveGroupXmlId.complete_name)
self.testView.write({
"arch": '''
'''.format(doesnothavegroup=doesNotHaveGroupXmlId.complete_name, hasgroup=hasGroupXmlId.complete_name)
})
self.start_tour("/odoo", 'web_studio_elements_with_groups_form', login="admin", timeout=600000)
self.assertEqual(len(operations), 1)
self.assertEqual(operations[0]["target"]["xpath_info"], [{'indice': 1, 'tag': 'form'}, {'indice': 1, 'tag': 'group'}, {'indice': 3, 'tag': 'field'}])
studioView = _get_studio_view(self.testView)
assertViewArchEqual(self, studioView.arch, """
""")
def test_element_group_in_sidebar(self):
group = self.env["res.groups"].create({
"name": "Test Group",
"user_ids": [Command.link(2)]
})
groupXmlId = self.env["ir.model.data"].create({
"name": "test_group",
"model": "res.groups",
"module": "web_studio",
"res_id": group.id,
})
self.testView.write({
"arch": '''
'''.format(group=groupXmlId.complete_name)
})
self.start_tour("/odoo?debug=tests", 'test_element_group_in_sidebar', login="admin", timeout=600000)
def test_create_one2many_lines_then_edit_name(self):
self.testView.arch = '''
'''
custom_fields_before_studio = self.env["ir.model.fields"].search([
("state", "=", "manual"),
])
self.start_tour("/odoo?debug=tests", 'web_studio_test_create_one2many_lines_then_edit_name', login="admin", timeout=30000)
custom_fields = self.env["ir.model.fields"].search_read([
("state", "=", "manual"),
("id", "not in", custom_fields_before_studio.ids),
], fields=["name", "ttype", "field_description"])
self.maxDiff = None
self.assertCountEqual(
[{key: val for key, val in field.items() if key != 'id'} for field in custom_fields],
[
{"name": "x_studio_new_name", 'ttype': 'one2many', 'field_description': 'new name'},
{"name": "x_name", 'ttype': 'char', 'field_description': 'Description'},
{"name": "x_res_partner_id", 'ttype': 'many2one', 'field_description': 'X Res Partner'},
{"name": "x_studio_sequence", 'ttype': 'integer', 'field_description': 'Sequence'},
]
)
def test_address_view_id_no_edit(self):
self.testView.write({
"arch": '''
'''
})
self.env.company.country_id.address_view_id = self.env.ref('base.view_partner_address_form')
self.start_tour("/odoo?debug=tests", 'web_studio_test_address_view_id_no_edit', login="admin", timeout=200)
studio_view = _get_studio_view(self.testView)
assertViewArchEqual(self, studio_view.arch, """
True
""")
def test_custom_selection_field_edit_values(self):
self.testView.arch = '''
'''
self.start_tour("/odoo?debug=tests", 'web_studio_custom_selection_field_edit_values', login="admin", timeout=200)
selection_field = self.env["ir.model.fields"].search(
[
("state", "=", "manual"),
("model", "=", "res.partner"),
("ttype", "=", "selection")
],
limit=1
)
self.assertEqual(selection_field.selection_ids.mapped("name"), ["some value", "another value"])
def test_create_new_model_from_existing_view(self):
self.testView.write({
"model": "res.users",
"type": "kanban",
"arch": '''
'''
})
self.testAction.view_ids.view_mode = "kanban"
self.start_tour("/odoo?debug=tests", 'web_studio_test_create_new_model_from_existing_view', login="admin",
timeout=200)
def test_create_model_with_clickable_stages(self):
self.testView.arch = '''
'''
self.start_tour("/odoo?debug=tests", 'web_studio_test_create_model_with_clickable_stages', login="admin", timeout=200)
def test_enter_x2many_edition_with_multiple_subviews(self):
doesNotHaveGroup = self.env["res.groups"].create({
"name": "studio does not have"
})
doesNotHaveGroupXmlId = self.env["ir.model.data"].create({
"name": "studio_test_doesnothavegroup",
"model": "res.groups",
"module": "web_studio",
"res_id": doesNotHaveGroup.id,
})
hasGroup = self.env["res.groups"].create({
"name": "studio has group",
"user_ids": [Command.link(2)]
})
hasGroupXmlId = self.env["ir.model.data"].create({
"name": "studio_test_hasgroup",
"model": "res.groups",
"module": "web_studio",
"res_id": hasGroup.id,
})
self.testView.arch = '''
'''.format(doesnothavegroup=doesNotHaveGroupXmlId.complete_name, hasgroup=hasGroupXmlId.complete_name)
self.start_tour("/odoo?debug=tests", 'web_studio_test_enter_x2many_edition_with_multiple_subviews',
login="admin", timeout=200)
def test_enter_x2many_edition_with_multiple_subviews_correct_xpath(self):
operations = []
def edit_view_mocked(*args, **kwargs):
operations.extend(kwargs["operations"] if "operations" in kwargs else args[3])
watch_edit_view(self, edit_view_mocked)
doesNotHaveGroup = self.env["res.groups"].create({
"name": "studio does not have"
})
doesNotHaveGroupXmlId = self.env["ir.model.data"].create({
"name": "studio_test_doesnothavegroup",
"model": "res.groups",
"module": "web_studio",
"res_id": doesNotHaveGroup.id,
})
hasGroup = self.env["res.groups"].create({
"name": "studio has group",
"user_ids": [Command.link(2)]
})
hasGroupXmlId = self.env["ir.model.data"].create({
"name": "studio_test_hasgroup",
"model": "res.groups",
"module": "web_studio",
"res_id": hasGroup.id,
})
self.testView.arch = '''
'''.format(doesnothavegroup=doesNotHaveGroupXmlId.complete_name, hasgroup=hasGroupXmlId.complete_name)
self.start_tour("/odoo?debug=tests", 'web_studio_test_enter_x2many_edition_with_multiple_subviews_correct_xpath',
login="admin", timeout=200)
studioView = _get_studio_view(self.testView)
assertViewArchEqual(self, studioView.arch, """
""")
self.assertEqual(len(operations), 1)
self.assertDictEqual(operations[0], {
'type': 'add',
'target': {
'tag': 'field',
'attrs': {
'name': 'name'
},
'xpath_info': [
{
'tag': 'list',
'indice': 2
},
{
'tag': 'field',
'indice': 1
},
],
'subview_xpath': "/form[1]/field[2]/list[2]",
},
'position': 'before',
'node': {
'tag': 'field',
'attrs': {
'name': 'active',
'optional': 'show'
}
}
})
def test_studio_arch_has_measure_field_ids(self):
view = self.env["ir.ui.view"].create({
"name": "simple view",
"model": "res.partner",
"type": "pivot",
"arch": '''
'''
})
studio_view = self.env[view.model].with_context(studio=True).get_view(view.id, view.type)
field_id = self.env['ir.model.fields'].search([('model', '=', view.model), ('name', 'in', ['display_name'])]).ids[0]
assertViewArchEqual(self, studio_view["arch"], '''
'''.format(field_id=field_id))
def test_field_with_groups_in_tree_node_has_groups_too(self):
# The field has a group in python in which the user is
# The node has also a group in which the user is not
hasGroup = self.env["res.groups"].create({
"name": "studio has group",
"user_ids": [Command.link(self.env.user.id)]
})
hasGroupXmlId = self.env["ir.model.data"].create({
"name": "studio_test_hasgroup",
"model": "res.groups",
"module": "web_studio",
"res_id": hasGroup.id,
})
doesNotHaveGroup = self.env["res.groups"].create({
"name": "studio does not have"
})
doesNotHaveGroupXmlId = self.env["ir.model.data"].create({
"name": "studio_test_doesnothavegroup",
"model": "res.groups",
"module": "web_studio",
"res_id": doesNotHaveGroup.id,
})
self.patch(self.env.registry['res.partner'].color, "groups", hasGroupXmlId.complete_name)
view = self.env["ir.ui.view"].create({
"name": "simple view",
"model": "res.partner",
"type": "list",
"arch": '''
'''.format(doesnothavegroup=doesNotHaveGroupXmlId.complete_name)
})
arch = self.env[view.model].with_context(studio=True).get_view(view.id, view.type)["arch"]
studio_groups = json.dumps([{
"id": doesNotHaveGroup.id,
"name": doesNotHaveGroup.name,
"display_name": doesNotHaveGroup.display_name,
"forbid": False,
}])
xml_temp = E.field(name="color", groups=doesNotHaveGroupXmlId.complete_name, column_invisible="True", actual_invisible="False", studio_groups=studio_groups)
expected = '''
{xml_stringified}
'''.format(xml_stringified=etree.tostring(xml_temp).decode("utf-8"))
assertViewArchEqual(self, arch, expected)
def test_set_tree_column_conditional_invisibility(self):
self.testViewList = self.env["ir.ui.view"].create({
"name": "simple partner",
"model": "res.partner",
"type": "list",
"arch": '''
'''
})
self.testAction.write({
"view_ids": [
Command.clear(),
Command.create({"view_id": self.testViewList.id, "view_mode": "list"}),
]
})
self.start_tour("/odoo?debug=tests", 'web_studio_set_tree_node_conditional_invisibility', login="admin", timeout=200)
arch = self.env[self.testViewList.model].with_context(studio=True).get_view(self.testViewList.id, self.testViewList.type)["arch"]
expected = '''
'''.format(color_modifiers="display_name == "Robert"")
assertViewArchEqual(self, arch, expected)
def test_set_view_default_group_by(self):
self.testViewList = self.env["ir.ui.view"].create({
"name": "simple partner",
"model": "res.partner",
"type": "list",
"arch": '''
'''
})
self.testAction.write({
"view_ids": [
Command.clear(),
Command.create({"view_id": self.testViewList.id, "view_mode": "list"}),
]
})
self.start_tour("/odoo?debug=tests", 'web_studio_set_view_default_group_by', login="admin", timeout=200)
arch = self.env[self.testViewList.model].with_context(studio=True).get_view(self.testViewList.id, self.testViewList.type)["arch"]
expected = '''
'''
assertViewArchEqual(self, arch, expected)
def test_studio_view_is_last(self):
# The studio view created should have, in all cases, a priority greater than all views
# that are part of the inheritance
self.testView.arch = '''
'''
self.env["ir.ui.view"].create({
"name": "simple view inherit",
"inherit_id": self.testView.id,
"mode": "extension",
"priority": 123,
"model": "res.partner",
"type": "form",
"arch": '''
'''
})
self.start_tour("/odoo?debug=tests", 'web_studio_test_studio_view_is_last', login="admin", timeout=200)
studioView = _get_studio_view(self.testView)
self.assertEqual(studioView.priority, 1230)
self.maxDiff = None
assertViewArchEqual(self, studioView["arch"], '''
''')
def test_edit_form_subview_attributes(self):
self.testView.arch = '''
'''
self.start_tour("/odoo?debug=tests", 'web_studio_test_edit_form_subview_attributes', login="admin", timeout=200)
studioView = _get_studio_view(self.testView)
assertViewArchEqual(self, studioView.arch, """
false
""")
def test_x2many_two_levels_edition(self):
self.testView.arch = '''
'''
self.start_tour("/odoo?debug=tests", 'web_studio_x2many_two_levels_edition', login="admin", timeout=200)
studioView = _get_studio_view(self.testView)
assertViewArchEqual(self, studioView.arch, '''
''')
def test_field_group_studio_no_fetch(self):
doesNotHaveGroup = self.env["res.groups"].create({
"name": "studio does not have"
})
doesNotHaveGroupXmlId = self.env["ir.model.data"].create({
"name": "studio_test_doesnothavegroup",
"model": "res.groups",
"module": "web_studio",
"res_id": doesNotHaveGroup.id,
})
self.patch(type(self.env["res.partner"]).function, "groups", doesNotHaveGroupXmlId.complete_name)
self.testViewForm = self.env["ir.ui.view"].create({
"name": "simple partner",
"model": "res.partner",
"type": "form",
"arch": '''
'''
})
self.testViewList = self.env["ir.ui.view"].create({
"name": "simple partner",
"model": "res.partner",
"type": "list",
"arch": '''
'''
})
self.testViewKanban = self.env["ir.ui.view"].create({
"name": "simple partner",
"model": "res.partner",
"type": "kanban",
"arch": '''
'''
})
self.testViewCalendar = self.env["ir.ui.view"].create({
"name": "simple partner",
"model": "res.partner",
"type": "calendar",
"arch": '''
'''
})
self.testAction.write({
"view_ids": [
Command.clear(),
Command.create({"view_id": self.testViewForm.id, "view_mode": "form"}),
Command.create({"view_id": self.testViewList.id, "view_mode": "list"}),
Command.create({"view_id": self.testViewKanban.id, "view_mode": "kanban"}),
Command.create({"view_id": self.testViewCalendar.id, "view_mode": "calendar"}),
]
})
self.start_tour("/odoo?debug=tests", 'web_studio_field_group_studio_no_fetch', login="admin", timeout=200)
def test_monetary_create(self):
self.create_empty_app()
self.newView.arch = '''
'''
self.start_tour("/odoo?debug=tests", 'web_studio_monetary_create', login="admin")
# There is a new currency and there is a new monetary
fields = self.env[self.newModel.model]._fields
currency_name_list = list(filter(lambda key: fields[key]._description_type == 'many2one' and fields[key]._description_relation == 'res.currency', fields.keys()))
monetary_name_list = list(filter(lambda key: fields[key].type == 'monetary', fields.keys()))
self.assertEqual(len(currency_name_list), 1)
self.assertEqual(len(monetary_name_list), 1)
# The studio arch contains the new monetary and the new currency
studioView = _get_studio_view(self.newView)
assertViewArchEqual(self, studioView.arch, f"""
""")
def test_monetary_change_currency_name(self):
self.create_empty_app()
currency = self.env["ir.model.fields"].create({
"name": "x_studio_currency_test",
"model": "x_test_model",
"model_id": self.newModel.id,
"ttype": "many2one",
"relation": "res.currency",
})
self.env["ir.model.fields"].create({
"name": "x_studio_monetary_test",
"model": "x_test_model",
"model_id": self.newModel.id,
"ttype": "monetary",
"currency_field": "x_studio_currency_test",
})
self.newView.arch = '''
'''
self.start_tour("/odoo?debug=tests", 'web_studio_monetary_change_currency_name', login="admin")
self.assertEqual(currency.field_description, "NewCurrency")
def test_related_monetary_creation(self):
res_partner_model_id = self.env["ir.model"].search([("model", "=", "res.partner")]).id
self.create_empty_app()
self.env["ir.model.fields"].create({
"name": "x_studio_currency_test",
"model": "res.partner",
"model_id": res_partner_model_id,
"ttype": "many2one",
"relation": "res.currency",
})
self.env["ir.model.fields"].create({
"name": "x_studio_monetary_test",
"model": "res.partner",
"model_id": res_partner_model_id,
"ttype": "monetary",
"currency_field": "x_studio_currency_test",
})
self.testView.arch = '''
'''
self.env["ir.model.fields"].create({
"name": "x_test",
"model": "x_test_model",
"model_id": self.newModel.id,
"ttype": "many2one",
"relation": "res.partner",
})
self.newView.arch = '''
'''
self.start_tour("/odoo?debug=tests", 'web_studio_related_monetary_creation', login="admin")
# There is only one currency and there is a new monetary
fields = self.env["x_test_model"]._fields
currency_name_list = list(filter(lambda key: fields[key]._description_type == 'many2one' and fields[key]._description_relation == 'res.currency', fields.keys()))
monetary_name_list = list(filter(lambda key: fields[key].type == 'monetary', fields.keys()))
self.assertEqual(len(currency_name_list), 1)
self.assertEqual(len(monetary_name_list), 1)
# The monetary has been created and is a related field
monetary_field = self.env['x_test_model']._fields[monetary_name_list[0]]
self.assertEqual(monetary_field.related, "x_test.x_studio_monetary_test")
self.assertEqual(monetary_field.get_currency_field(self.env["x_test_model"]), "x_studio_x_test_x_studio_currency_test")
self.assertEqual(monetary_field.get_description(self.env)["aggregator"], "sum")
# A currency has been created because there was none in the model/view
currency_field = self.env['x_test_model']._fields['x_studio_x_test_x_studio_currency_test']
self.assertEqual(currency_field.get_description(self.env, "string")["string"], "Contact X Studio Currency Test")
self.assertEqual(currency_name_list[0], 'x_studio_x_test_x_studio_currency_test')
self.assertEqual(currency_field.related_field, self.env['res.partner']._fields['x_studio_currency_test'])
def test_monetary_change_currency_field(self):
self.create_empty_app()
self.env["ir.model.fields"].create({
"name": "x_studio_currency_test",
"model": "x_test_model",
"model_id": self.newModel.id,
"ttype": "many2one",
"relation": "res.currency",
})
self.env["ir.model.fields"].create({
"name": "x_studio_currency_test2",
"model": "x_test_model",
"model_id": self.newModel.id,
"ttype": "many2one",
"relation": "res.currency",
})
monetary = self.env["ir.model.fields"].create({
"name": "x_studio_monetary_test",
"model": "x_test_model",
"model_id": self.newModel.id,
"ttype": "monetary",
"currency_field": "x_studio_currency_test",
})
self.newView.arch = '''
'''
self.start_tour("/odoo?debug=tests", 'web_studio_monetary_change_currency_field', login="admin")
# The currency_field in the monetary field changed
self.assertEqual(monetary.currency_field, 'x_studio_currency_test2')
def test_monetary_change_currency_not_in_view(self):
self.create_empty_app()
self.env["ir.model.fields"].create({
"name": "x_studio_currency_test",
"model": "x_test_model",
"model_id": self.newModel.id,
"ttype": "many2one",
"relation": "res.currency",
})
self.env["ir.model.fields"].create({
"name": "x_studio_currency_test2",
"model": "x_test_model",
"model_id": self.newModel.id,
"ttype": "many2one",
"relation": "res.currency",
})
monetary = self.env["ir.model.fields"].create({
"name": "x_studio_monetary_test",
"model": "x_test_model",
"model_id": self.newModel.id,
"ttype": "monetary",
"currency_field": "x_studio_currency_test",
})
self.newView.arch = '''
'''
self.start_tour("/odoo?debug=tests", 'web_studio_monetary_change_currency_not_in_view', login="admin")
# The currency_field in the monetary field changed
self.assertEqual(monetary.currency_field, 'x_studio_currency_test2')
studioView = _get_studio_view(self.newView)
assertViewArchEqual(self, studioView.arch, """
""")
def test_monetary_add_existing_monetary(self):
self.create_empty_app()
self.env["ir.model.fields"].create({
"name": "x_studio_currency_test",
"model": "x_test_model",
"model_id": self.newModel.id,
"ttype": "many2one",
"relation": "res.currency",
})
self.env["ir.model.fields"].create({
"name": "x_studio_monetary_test",
"model": "x_test_model",
"model_id": self.newModel.id,
"ttype": "monetary",
"currency_field": "x_studio_currency_test",
})
self.newView.arch = '''
'''
self.start_tour("/odoo?debug=tests", 'web_studio_monetary_add_existing_monetary', login="admin")
# The studio arch contains the monetary and the associated currency
studioView = _get_studio_view(self.newView)
assertViewArchEqual(self, studioView.arch, """
""")
def test_monetary_create_monetary_with_existing_currency(self):
self.create_empty_app()
self.env["ir.model.fields"].create({
"name": "x_studio_currency_test",
"model": "x_test_model",
"model_id": self.newModel.id,
"ttype": "many2one",
"relation": "res.currency",
})
self.newView.arch = '''
'''
self.start_tour("/odoo?debug=tests", 'web_studio_monetary_create_monetary_with_existing_currency', login="admin")
# There is only one currency and there is a new monetary
fields = self.env[self.newModel.model]._fields
currency_name_list = list(filter(lambda key: fields[key]._description_type == 'many2one' and fields[key]._description_relation == 'res.currency', fields.keys()))
monetary_name_list = list(filter(lambda key: fields[key].type == 'monetary', fields.keys()))
self.assertEqual(len(currency_name_list), 1)
self.assertEqual(len(monetary_name_list), 1)
# The studio arch contains the new monetary but no currency as it exist in the original arch
studioView = _get_studio_view(self.newView)
assertViewArchEqual(self, studioView.arch, f"""
""")
def test_move_similar_field(self):
self.testView.arch = '''
'''
self.start_tour("/odoo?debug=tests", 'web_studio_test_move_similar_field', login="admin", timeout=400)
studioView = _get_studio_view(self.testView)
assertViewArchEqual(self, studioView.arch, '''
''')
def create_user_view(self):
self.testView.write({
"name": "simple user",
"model": "res.users",
"arch": '''
'''
})
self.testAction.write({
"name": "simple user",
"res_model": "res.users",
})
self.testActionXmlId.name = "studio_test_user_action"
self.testMenu.name = "Studio Test User"
self.testMenuXmlId.name = "studio_test_user_menu"
def test_related_binary_field_with_filename(self):
self.create_user_view()
partner_field = self.env["ir.model.fields"].create({
"field_description": "New File",
"name": "x_new_file",
"ttype": "binary",
"model": "res.partner",
"model_id": self.env["ir.model"]._get('res.partner').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.partner').id,
"state": "manual",
})
self.start_tour("/odoo?debug=tests", 'web_studio_test_related_file', login="admin", timeout=400)
studioView = _get_studio_view(self.testView)
binary_field = self.env["ir.model.fields"].search([('model', '=', 'res.users'), ('ttype', '=', 'binary'), ('name', 'like', 'x_studio_related')])
self.assertEqual(len(binary_field), 1)
self.assertEqual(binary_field.related_field_id, partner_field)
assertViewArchEqual(self, studioView.arch,
'''
'''.format(binary_field=binary_field))
def test_nested_related_binary_field_with_filename(self):
self.create_user_view()
self.env["ir.model.fields"].create({
"field_description": "New File",
"name": "x_new_file",
"ttype": "binary",
"model": "res.partner.category",
"model_id": self.env["ir.model"]._get('res.partner.category').id,
"state": "manual",
})
self.env["ir.model.fields"].create({
"field_description": "New File filename",
"name": "x_new_file_filename",
"ttype": "char",
"model": "res.partner.category",
"model_id": self.env["ir.model"]._get('res.partner.category').id,
"state": "manual",
})
partner_field = self.env["ir.model.fields"].create({
"field_description": "New File",
"name": "x_new_related_file",
"ttype": "binary",
"model": "res.partner",
"model_id": self.env["ir.model"]._get('res.partner').id,
"state": "manual",
"related": "category_id.x_new_file"
})
self.env["ir.model.fields"].create({
"field_description": "New File filename",
"name": "x_new_related_file_filename",
"ttype": "char",
"model": "res.partner",
"model_id": self.env["ir.model"]._get('res.partner').id,
"state": "manual",
"related": "category_id.x_new_file_filename"
})
self.start_tour("/odoo?debug=tests", 'web_studio_test_related_file', login="admin", timeout=400)
studioView = _get_studio_view(self.testView)
binary_field = self.env["ir.model.fields"].search([('model', '=', 'res.users'), ('ttype', '=', 'binary'), ('name', 'like', 'x_studio_related')])
self.assertEqual(len(binary_field), 1)
self.assertEqual(binary_field.related_field_id, partner_field)
assertViewArchEqual(self, studioView.arch,
'''
'''.format(binary_field=binary_field))
def test_related_binary_field_without_filename(self):
self.create_user_view()
partner_field = self.env["ir.model.fields"].create({
"field_description": "New File",
"name": "x_new_file",
"ttype": "binary",
"model": "res.partner",
"model_id": self.env["ir.model"]._get('res.partner').id,
"state": "manual",
})
self.start_tour("/odoo?debug=tests", 'web_studio_test_related_file', login="admin", timeout=400)
studioView = _get_studio_view(self.testView)
binary_field = self.env["ir.model.fields"].search([('model', '=', 'res.users'), ('ttype', '=', 'binary'), ('name', 'like', 'x_studio_related')])
self.assertEqual(len(binary_field), 1)
self.assertEqual(binary_field.related_field_id, partner_field)
assertViewArchEqual(self, studioView.arch,
'''
'''.format(binary_field=binary_field))
def test_undo_new_field(self):
self.testView.arch = '''
'''
self.start_tour("/odoo?debug=tests", "web_studio_test_undo_new_field", login="admin", timeout=200)
def test_change_lone_attr_modifier_form(self):
self.testView.arch = """
"""
self.start_tour("/odoo?debug=tests", "web_studio_test_change_lone_attr_modifier_form", login="admin")
studioView = _get_studio_view(self.testView)
assertViewArchEqual(self, studioView.arch, '''
False
''')
def test_drag_and_drop_boolean(self):
self.testView.arch = '''
'''
self.start_tour("/odoo?debug=tests", 'web_studio_boolean_field_drag_and_drop', login="admin", timeout=200)
studioView = _get_studio_view(self.testView)
boolean_field = self.env['ir.model.fields'].search([('name', 'like', 'x_studio_boolean')])[0]
assertViewArchEqual(self, studioView.arch, '''
'''.format(boolean_field=boolean_field))
def test_new_field_rename_description(self):
self.testView.arch = '''
'''
self.start_tour("/odoo", "web_studio_test_new_field_rename_description", login="admin")
new_field = self.env["ir.model.fields"]._get("res.partner", "x_studio_my_new_field")
self.assertEqual(new_field.field_description, "my new field")
studioView = _get_studio_view(self.testView)
self.assertXMLEqual(studioView.arch, """
""")
def test_edit_digits_option(self):
self.testView.arch = '''
'''
self.start_tour("/odoo?debug=tests", 'web_studio_test_edit_digits_option', login="admin", timeout=200)
studioView = _get_studio_view(self.testView)
assertViewArchEqual(self, studioView.arch, """
{"digits":[4,2]}
""")
def test_button_rainbow_effect(self):
self.testView.arch = """
Button """
self.start_tour("/odoo", "web_studio.test_button_rainbow_effect", login="admin")
studioView = _get_studio_view(self.testView)
attachment = self.env["ir.attachment"].search([("name", "=", "my_studio_image.png")])
self.assertXMLEqual(studioView.arch, """
{'img_url': '/web/content/%s'}
""" % attachment.id)
def test_context_write_cleaned(self):
view = self.env["ir.ui.view"].create({
"model": "res.users",
"type": "form",
"arch": """
"""
})
operations = [
{
"type": "add",
"target": {
"tag": "div",
"attrs": {
"class": "oe_button_box"
}
},
"position": "inside",
"node": {
"tag": "button",
"field": self.env["ir.model.fields"]._get("res.partner", "user_id").id,
"string": "Test studio new button",
"attrs": {
"class": "oe_stat_button",
"icon": "fa-diamond"
}
}
}
]
create_action = self.env.registry["ir.actions.actions"]._create
action_created = False
def mock_act_create(rec_set, *args, **kwargs):
nonlocal action_created
action_created = True
context = dict(rec_set.env.context)
del context["tz"]
self.assertEqual(context, {'lang': 'en_US', 'uid': 2, 'arbitrary_key': 'arbitrary', "studio": 1})
return create_action(rec_set, *args, **kwargs)
self.patch(self.env.registry["ir.actions.actions"], "_create", mock_act_create)
self.authenticate("admin", "admin")
with mute_logger("odoo.addons.base.models.ir_ui_view"):
response = self.url_open("/web_studio/edit_view",
headers={"Content-Type": "application/json"},
data=json.dumps({
"params": {
"view_id": view.id,
"studio_view_arch": "",
"model": "res.users",
"operations": operations,
"context": {
"default_type": "some_type",
"arbitrary_key": "arbitrary",
"studio": 1,
}
},
}))
action = self.env["ir.actions.act_window"].search([("res_model", "=", "res.partner")], order="create_date DESC", limit=1)
self.assertTrue(action_created, True)
self.assertEqual(action.type, "ir.actions.act_window")
self.assertEqual(action.name, "Test studio new button")
self.assertXMLEqual(response.json()["result"]["views"]["form"]["arch"], f"""
""")
def test_add_button_xml_id(self):
base_view = self.env["ir.ui.view"].create({
"name": "test_partner_simple",
"model": "res.partner",
"mode": "primary",
"type": "form",
"arch": """
"""
})
operations = [
{
"type": "add",
"target": {
"tag": "div",
"attrs": {
"class": "oe_button_box"
}
},
"view_id": base_view.id,
"position": "inside",
"node": {
"tag": "button",
"field": self.env["ir.model.fields"]._get("res.partner", "parent_id").id,
"string": "aa",
"attrs": {
"class": "oe_stat_button",
"icon": "fa-diamond"
}
}
}
]
with mute_logger("odoo.addons.base.models.ir_ui_view"):
self.authenticate("admin", "admin")
self.url_open("/web_studio/edit_view", data=json.dumps({
"params": {
"view_id": base_view.id,
"model": "res.partner",
"studio_view_arch": "
",
"operations": operations,
"context": {"studio": 1},
}
}), headers={"Content-Type": "application/json"})
action = self.env["ir.actions.act_window"].search([], limit=1, order="create_date DESC")
self.assertTrue(action.xml_id.startswith("studio_customization."))
form = base_view.get_combined_arch()
self.assertXMLEqual(form, f"""
""")
def test_reload_after_restoring_default_view(self):
self.start_tour("/odoo?debug=tests", 'web_studio_test_reload_after_restoring_default_view', login="admin")
def test_add_all_types_fields_related(self):
self.create_user_view()
self.start_tour("/odoo?debug=tests", 'web_studio_test_add_all_types_fields_related', login="admin")
field = self.env["ir.model.fields"].search([('model', '=', 'res.users'), ('name', 'like', 'x_studio_related')])
target_field = self.env["ir.model.fields"].search([('model', '=', 'res.users'), ('name', '=', 'display_name')])
self.assertEqual(len(field), 1)
self.assertEqual(field.related_field_id, target_field)
def test_add_many2one_without_related(self):
""" Test ensure raise a warning and not the current view does not contain studio arch when the user
tries to add one2many field but the model does not contain any related many2one field.
"""
self.create_empty_app()
self.newView.arch = '''
'''
self.start_tour("/odoo?debug=tests", 'web_studio_add_one2many_no_related_many2one', login="admin")
studioView = _get_studio_view(self.newView)
# Does not contain studio arch
assertViewArchEqual(self, studioView.arch, False)
def test_approval_button_xml_id(self):
self.testView.arch = """
"""
self.start_tour("/odoo?debug=tests", "web_studio_test_approval_button_xml_id", login="admin")
approvals = self.env["studio.approval.rule"].get_approval_spec([{"action_id": "base.action_model_data", "model": "res.partner", "res_id": False}])
partner_approvals = dict(approvals["res.partner"])
self.assertTrue(partner_approvals[(False, False, "base.action_model_data")]["rules"])
def test_kanban_field_bold(self):
self.testView.write({
"model": "res.partner",
"type": "kanban",
"arch": '''
'''
})
self.testAction.view_ids.view_mode = "kanban"
self.start_tour("/odoo?debug=tests", 'web_studio_test_kanban_field_bold', login="admin",
timeout=200)
studioView = _get_studio_view(self.testView)
self.assertXMLEqual(studioView.arch, """
fs-6 whatever
fw-bold text-muted
""")
def test_kanban_menu_ribbon(self):
self.testViewKanban = self.env["ir.ui.view"].create({
"name": "simple partner",
"model": "res.partner",
"type": "kanban",
"arch": '''
'''
})
self.testAction.write({
"view_ids": [
Command.clear(),
Command.create({"view_id": self.testViewKanban.id, "view_mode": "kanban"}),
]
})
self.start_tour("/odoo?debug=tests", 'web_studio_test_kanban_menu_ribbon', login="admin")
studio_view = _get_studio_view(self.testViewKanban)
self.assertXMLEqual(studio_view.arch, """
Edit
Delete
""")
def test_create_related(self):
self.testViewForm = self.env["ir.ui.view"].create({
"name": "simple partner",
"model": "res.partner",
"type": "form",
"arch": '''
'''
})
self.testAction.write({
"view_ids": [
Command.clear(),
Command.create({"view_id": self.testViewForm.id, "view_mode": "form"}),
]
})
self.assertEqual(self.env["res.partner"]._fields["child_ids"].type, "one2many")
self.start_tour("/odoo?debug=tests", 'web_studio_test_create_related', login="admin")
partnerModel = self.env["ir.model"]._get("res.partner")
new_field = self.env["ir.model.fields"].search([("model_id", "=", partnerModel.id)], order="id DESC", limit=1)
self.assertEqual(new_field.related, "parent_id.child_ids")
studio_view = _get_studio_view(self.testViewForm)
self.assertXMLEqual(studio_view.arch, f"""
""")
def test_negated_groups(self):
doesNotHaveGroup = self.env["res.groups"].create({
"name": "studio does not have"
})
doesNotHaveGroupXmlId = self.env["ir.model.data"].create({
"name": "studio_test_doesnothavegroup",
"model": "res.groups",
"module": "web_studio",
"res_id": doesNotHaveGroup.id,
})
hasGroup = self.env["res.groups"].create({
"name": "studio has group",
})
hasGroupXmlId = self.env["ir.model.data"].create({
"name": "studio_test_hasgroup",
"model": "res.groups",
"module": "web_studio",
"res_id": hasGroup.id,
})
self.testView.arch = f"""
"""
self.start_tour("/web?debug=tests", "web_studio_test_negated_groups", login="admin")
studio_view = _get_studio_view(self.testView)
assertViewArchEqual(self, studio_view.arch, """
web_studio.studio_test_doesnothavegroup,!base.group_system
""")
def test_edit_duplicate_attribute_form(self):
self.testView.arch = '''
'''
self.start_tour("/odoo?debug=tests", 'web_studio_test_edit_duplicate_attribute_form', login="admin")
studio_view = _get_studio_view(self.testView)
self.assertXMLEqual(studio_view.arch, """
true
false
""")
def test_edit_duplicate_attribute_list(self):
self.testViewList = self.env["ir.ui.view"].create({
"name": "simple partner",
"model": "res.partner",
"type": "list",
"arch": '''
'''
})
self.testAction.write({
"view_ids": [
Command.clear(),
Command.create({"view_id": self.testViewList.id, "view_mode": "list"}),
]
})
self.start_tour("/odoo?debug=tests", 'web_studio_test_edit_duplicate_attribute_list', login="admin")
studio_view = _get_studio_view(self.testViewList)
self.assertXMLEqual(studio_view.arch, """
true
false
""")
def test_use_action_domain(self):
self.env["res.partner"].search([]).write({'employee': False})
self.env["res.partner"].create({
"name": "Michel",
"employee": True,
})
self.env["res.partner"].create({
"name": "Paul",
"employee": False,
})
self.testAction.domain = "[['employee', '=', True]]"
self.testView.write({
"type": "list",
"arch": '''
'''
})
self.testAction.write({
"view_ids": [Command.clear(), Command.create({"view_id": self.testView.id, "view_mode": "list"})]
})
self.start_tour("/web?debug=tests", "web_studio_test_use_action_domain", login="admin")
def test_drag_before_sheet(self):
self.testView.arch = """
"""
self.start_tour("/web?debug=tests", "web_studio_test_drag_before_sheet", login="admin")
studio_view = _get_studio_view(self.testView)
[html_field] = self.env['ir.model.fields'].search([('name', '=like', 'x_studio_html_field_%')], limit=1)
assertViewArchEqual(self, studio_view.arch, '''
'''.format(html_field=html_field))
def test_default_value_company(self):
with self.with_user("admin"):
company2 = self.env["res.company"].create({"name": "company2"})
self.testView.arch = "
"
cookies = {
"cids": str(company2.id)
}
self.start_tour("/web?debug=tests", "web_studio_test_default_value_company", login="admin", cookies=cookies)
field_name = self.env["ir.model.fields"]._get("res.partner", "name")
ir_default = self.env["ir.default"].search(["&", ("field_id", "=", field_name.id), ("company_id", "=", company2.id)])
self.assertEqual(ir_default.json_value, '"from studio"')
def test_empty_default_group_by(self):
self.testView.write({
"type": "kanban",
"arch": '''
'''
})
self.testAction.write({
"view_ids": [Command.clear(), Command.create({"view_id": self.testView.id, "view_mode": "kanban"})]
})
self.start_tour("/odoo?debug=tests", 'web_studio_empty_default_group_by', login="admin")
studio_view = _get_studio_view(self.testView)
assertViewArchEqual(self, studio_view.arch, '''
''')
def test_subview_multiple_occurences(self):
self.testView.arch = """
"""
self.start_tour("/web?debug=tests", "web_studio_test_subview_multiple_occurences", login="admin")
studio_view = _get_studio_view(self.testView)
assertViewArchEqual(self, studio_view.arch, """
new label from tour
""")
def test_deleted_action_in_form_view(self):
model = self.env["ir.model"]._get("res.partner")
action = self.env['ir.actions.server'].sudo().create({
'name': 'Test Action',
'model_id': model.id,
'binding_model_id': model.id,
'state': 'code',
'code': 'pass',
})
self.env["ir.model.data"].create({
"name": "Test_Action",
"model": "ir.actions.server",
"res_id": action.id,
"noupdate": True,
})
self.start_tour("/odoo?debug=tests", 'web_studio_test_create_action_in_form_view', login="admin")
action.unlink()
self.start_tour("/odoo?debug=tests", 'web_studio_test_remove_action_in_form_view', login="admin")
def test_automagically_added_fields(self):
form = self.env["ir.ui.view"].create({
"name": "pForm",
"model": "res.partner",
"type": "form",
"arch": """
""",
})
list_view = self.env["ir.ui.view"].create({
"name": "pList",
"model": "res.partner",
"type": "list",
"arch": """
""",
})
kanban = self.env["ir.ui.view"].create({
"name": "pList",
"model": "res.partner",
"type": "kanban",
"arch": """
""",
})
all_views = [list_view, form, kanban]
res = self.env["res.partner"].get_views([[v.id, v.type] for v in all_views])
self.assertEqual(len(res["views"]), 3)
for v in res["views"].values():
tree = etree.fromstring(v["arch"])
added = tree.xpath("//field[@name='function']")[0]
self.assertTrue(added.get("data-used-by"))
self.assertEqual(added.getparent(), tree)
self.assertEqual(added.getnext(), None)
self.testAction.view_ids = [Command.clear()] + [Command.create({"view_mode": v.type, "view_id": v.id}) for v in all_views]
self.start_tour(f"/odoo/action-{self.testAction.id}?debug=tests", "web_studio_test_automagically_added_fields", login="admin")
def test_apply_group_no_one(self):
self.testView.arch = """
"""
self.start_tour("/web?debug=0", "web_studio_test_apply_group_no_one", login="admin")
studio_view = _get_studio_view(self.testView)
assertViewArchEqual(self, studio_view.arch, """
base.group_no_one
""")
def test_get_view_with_group_no_one(self):
view = self.env["ir.ui.view"].create({
"name": "simple view",
"model": "res.partner",
"type": "form",
"arch": """
"""
})
view_arch_from_studio = self.env[view.model].with_context(studio=True).get_view(view.id, view.type)["arch"]
tree = etree.fromstring(view_arch_from_studio)
field_node = tree.find("field")
self.assertDictEqual({**field_node.attrib}, {
"name": "function",
"groups": "base.group_no_one",
"studio_groups": '[{"id": 7, "name": "Technical Features", "display_name": "Technical Features", "forbid": false}]'
})
def test_add_existing_binary_with_filename(self):
self.env["ir.model.fields"].create({
"ttype": "binary",
"model": "res.partner",
"model_id": self.env["ir.model"]._get("res.partner").id,
"name": "x_test_binary",
"field_description": "Binary with filename",
})
self.env["ir.model.fields"].create({
"ttype": "char",
"model": "res.partner",
"model_id": self.env["ir.model"]._get("res.partner").id,
"name": "x_test_binary_filename",
})
self.testView.arch = '''
'''
self.start_tour("/odoo?debug=tests", 'web_studio_test_add_existing_binary_with_filename', login="admin")
studio_view = _get_studio_view(self.testView)
assertViewArchEqual(self, studio_view.arch, '''
''')
def test_negated_groups_do_not_interfere_with_invisible(self):
doesNotHaveGroup = self.env["res.groups"].create({
"name": "studio does not have"
})
doesNotHaveGroupXmlId = self.env["ir.model.data"].create({
"name": "studio_test_doesnothavegroup",
"model": "res.groups",
"module": "web_studio",
"res_id": doesNotHaveGroup.id,
})
self.testView.arch = f'''
'''
self.start_tour("/odoo?debug=tests", 'web_studio_test_negated_groups_do_not_interfere_with_invisible', login="admin")
studioView = _get_studio_view(self.testView)
assertViewArchEqual(self, studioView.arch, """
display_name == "Robert"
""")
def test_cohort_measure_values(self):
testCohortView = self.env["ir.ui.view"].create({
"name": "simple partner",
"model": "res.partner",
"type": "cohort",
"arch": '''
'''
})
self.start_tour("/web?debug=tests", "web_studio_test_cohort_measure_values", login="admin")
studio_view = _get_studio_view(testCohortView)
assertViewArchEqual(self, studio_view.arch, """
color
""")