215 lines
10 KiB
Python
215 lines
10 KiB
Python
from lxml import etree
|
|
from odoo import Command
|
|
from odoo.tests import tagged
|
|
from odoo.tests.common import BaseCase, HttpCase
|
|
from odoo.addons.web_studio.controllers.export import StudioExporter
|
|
from odoo.addons.web_studio.wizard.studio_export_wizard import _find_circular_dependencies, FIELDS_TO_EXPORT
|
|
from odoo.addons.http_routing.tests.common import MockRequest
|
|
|
|
|
|
class TestExport(HttpCase):
|
|
def setUp(self):
|
|
super().setUp()
|
|
self.exporter = StudioExporter()
|
|
|
|
def test_export_currency_field(self):
|
|
base_currency_field = self.env["res.partner"]._fields.get("currency_id")
|
|
IrModelFields = self.env["ir.model.fields"].with_context(studio=True)
|
|
if not base_currency_field or not (base_currency_field.type == "many2one" and base_currency_field.comodel_name == "res.currency"):
|
|
IrModelFields.create({
|
|
"state": "base",
|
|
"name": "x_currency" if base_currency_field else "currency_id",
|
|
"model_id": self.env["ir.model"]._get("res.partner").id,
|
|
"ttype": "many2one",
|
|
"relation": "res.currency"
|
|
})
|
|
|
|
currency_field = IrModelFields.create({
|
|
"name": "x_test_currency",
|
|
"model_id": self.env["ir.model"]._get("res.partner").id,
|
|
"ttype": "many2one",
|
|
"relation": "res.currency"
|
|
})
|
|
monetary = IrModelFields.create({
|
|
"name": "x_test_monetary",
|
|
"model_id": self.env["ir.model"]._get("res.partner").id,
|
|
"ttype": "monetary",
|
|
"currency_field": currency_field.name,
|
|
})
|
|
|
|
studio_module = self.env['ir.module.module'].get_studio_module()
|
|
data = self.env['ir.model.data'].search([
|
|
('studio', '=', True), ("model", "=", "ir.model.fields"), ("res_id", "in", (currency_field | monetary).ids)
|
|
])
|
|
data = self.env["studio.export.wizard.data"].create(
|
|
[{"model": d.model, "res_id": d.res_id, "studio": d.studio} for d in data]
|
|
)
|
|
wizard = self.env['studio.export.wizard'].create({
|
|
"default_export_data": [Command.set(data.ids)],
|
|
"additional_models": [],
|
|
})
|
|
export_info = wizard.get_export_info()
|
|
content_iter = iter(self.exporter.generate_module_files(studio_module, export_info))
|
|
|
|
file_name = content = None
|
|
with MockRequest(self.env):
|
|
while file_name != "data/ir_model_fields.xml":
|
|
file_name, content = next(content_iter)
|
|
|
|
arch_fields = etree.fromstring(content)
|
|
records = arch_fields.findall("record")
|
|
currency_field = records[0]
|
|
self.assertEqual(currency_field.find("./field[@name='name']").text, "x_test_currency")
|
|
self.assertEqual(currency_field.find("./field[@name='currency_field']"), None)
|
|
|
|
monetary_field = records[1]
|
|
self.assertEqual(monetary_field.find("./field[@name='name']").text, "x_test_monetary")
|
|
self.assertEqual(monetary_field.find("./field[@name='currency_field']").text, "x_test_currency")
|
|
|
|
monetary.currency_field = False
|
|
export_info = wizard.get_export_info()
|
|
content_iter = iter(self.exporter.generate_module_files(studio_module, export_info))
|
|
|
|
file_name = content = None
|
|
with MockRequest(self.env):
|
|
while file_name != "data/ir_model_fields.xml":
|
|
file_name, content = next(content_iter)
|
|
|
|
arch_fields = etree.fromstring(content)
|
|
records = arch_fields.findall("record")
|
|
currency_field = records[0]
|
|
self.assertEqual(currency_field.find("./field[@name='name']").text, "x_test_currency")
|
|
self.assertEqual(currency_field.find("./field[@name='currency_field']"), None)
|
|
|
|
monetary_field = records[1]
|
|
self.assertEqual(monetary_field.find("./field[@name='name']").text, "x_test_monetary")
|
|
# This assertion is correct technically: the python monetary field will fallback
|
|
# on one of the hardcoded currency field names.
|
|
# For this test though, on res.partner, the actual field will crash
|
|
self.assertEqual(monetary_field.find("./field[@name='currency_field']"), None)
|
|
|
|
def test_export_automation(self):
|
|
ba = self.env["base.automation"].with_context(studio=True).create({
|
|
"name": "Cron BaseAuto",
|
|
"trigger": "on_time",
|
|
"model_id": self.env.ref("base.model_res_users").id,
|
|
})
|
|
data = self.env['ir.model.data'].search([
|
|
('studio', '=', True), ("model", "=", "base.automation"), ("res_id", "=", ba.id)
|
|
])
|
|
|
|
studio_module = self.env['ir.module.module'].get_studio_module()
|
|
data = self.env["studio.export.wizard.data"].create(
|
|
[{"model": d.model, "res_id": d.res_id, "studio": d.studio} for d in data]
|
|
)
|
|
wizard = self.env['studio.export.wizard'].create({
|
|
"default_export_data": [Command.set(data.ids)],
|
|
})
|
|
export_info = wizard.get_export_info()
|
|
content_iter = iter(self.exporter.generate_module_files(studio_module, export_info))
|
|
file_name = content = None
|
|
with MockRequest(self.env):
|
|
while file_name != "data/base_automation.xml":
|
|
file_name, content = next(content_iter)
|
|
|
|
arch = etree.fromstring(content)
|
|
records = arch.findall("record")
|
|
self.assertEqual(len(records), 1)
|
|
record = records[0]
|
|
field_names = {field.get("name") for field in record.findall("./field")}
|
|
self.assertEqual(field_names, {
|
|
"model_id",
|
|
"name",
|
|
"trg_date_range_type",
|
|
"trigger"
|
|
})
|
|
|
|
def test_export_unarchived_records(self):
|
|
IrFilter = self.env['ir.filters'].with_context(studio=True)
|
|
filter_1 = IrFilter.create({
|
|
'name': 'Filter 1',
|
|
'model_id': self.env['ir.model']._get('res.partner').id,
|
|
'domain': '[]',
|
|
})
|
|
|
|
filter_2 = IrFilter.create({
|
|
'name': 'Filter 2',
|
|
'model_id': self.env['ir.model']._get('res.partner').id,
|
|
'domain': '[]',
|
|
})
|
|
|
|
self.assertTrue(filter_1.active, "Filter 1 should be active initially")
|
|
self.assertTrue(filter_2.active, "Filter 2 should be active initially")
|
|
|
|
filter_1.active = False
|
|
self.assertFalse(filter_1.active, "Filter should be archived")
|
|
|
|
filter_data = self.env['ir.model.data'].search([
|
|
('studio', '=', True),
|
|
('model', '=', 'ir.filters'),
|
|
])
|
|
self.assertEqual(len(filter_data), 2, "Should have two ir.model.data records: one archived and one active filter")
|
|
|
|
wizard = self.env['studio.export.wizard'].create({})
|
|
default_data = wizard._default_studio_export_data()
|
|
wizard.default_export_data = [Command.set(default_data.ids)]
|
|
export_info = wizard.get_export_info()
|
|
|
|
studio_module = self.env['ir.module.module'].get_studio_module()
|
|
content_iter = iter(self.exporter.generate_module_files(studio_module, export_info))
|
|
|
|
file_name = content = None
|
|
with MockRequest(self.env):
|
|
while file_name != "data/ir_filters.xml":
|
|
file_name, content = next(content_iter)
|
|
|
|
arch_filters = etree.fromstring(content)
|
|
records = arch_filters.findall('record')
|
|
self.assertEqual(len(records), 1, "Only the active filter should be exported.")
|
|
|
|
def test_fields_to_export_are_not_excluded(self):
|
|
for model, fields_to_export in FIELDS_TO_EXPORT.items():
|
|
export_model = self.env["studio.export.model"].create({
|
|
"model_id": self.env["ir.model"]._get(model).id,
|
|
})
|
|
excluded_fields = export_model.excluded_fields.mapped("name")
|
|
self.assertEqual(set(fields_to_export), set(fields_to_export) - set(excluded_fields))
|
|
|
|
|
|
@tagged("post_install", "-at_install")
|
|
class TestExportTours(HttpCase):
|
|
def test_can_export_new_module(self):
|
|
self.start_tour("/odoo?debug=tests", 'can_export_new_module', login="admin")
|
|
# check the export result made by the tour
|
|
wizard = self.env['studio.export.wizard'].search([], limit=1, order='id desc')
|
|
data, files, circular_dependencies = wizard.get_export_info()
|
|
self.assertEqual(circular_dependencies, [])
|
|
self.assertEqual(data, wizard.default_export_data)
|
|
self.assertEqual(len(wizard.additional_export_data), 0)
|
|
exported_models = {f[0] for f in files}
|
|
self.assertEqual(exported_models, {
|
|
'ir.actions.act_window', 'ir.default',
|
|
'ir.model', 'ir.model.fields', 'ir.model.access',
|
|
'ir.ui.view', 'ir.ui.menu',
|
|
})
|
|
self.assertEqual(len(data.filtered(lambda r: r.model == 'ir.model')), 1)
|
|
self.assertEqual(self.env['ir.model'].browse(data.filtered(lambda r: r.model == 'ir.model').res_id).name, 'My App Records')
|
|
created_menus = self.env['ir.ui.menu'].browse(data.filtered(lambda r: r.model == 'ir.ui.menu').mapped("res_id")).mapped("name")
|
|
self.assertEqual(len(created_menus), 2)
|
|
self.assertTrue('My New App' in created_menus)
|
|
self.assertTrue('My App Records' in created_menus)
|
|
|
|
|
|
class TestCircularDependencies(BaseCase):
|
|
def test_circular_dependencies(self):
|
|
self.assertEqual(_find_circular_dependencies({}), [])
|
|
self.assertEqual(_find_circular_dependencies({1: []}), [])
|
|
self.assertEqual(_find_circular_dependencies({1: [1]}), [])
|
|
self.assertEqual(_find_circular_dependencies({1: [2]}), [])
|
|
self.assertEqual(_find_circular_dependencies({1: [2], 2: [3]}), [])
|
|
self.assertEqual(_find_circular_dependencies({1: [2, 3], 2: [3], 3: [4]}), [])
|
|
self.assertEqual(_find_circular_dependencies({1: [2], 2: [3], 3: [1]}), [[1, 2, 3, 1]])
|
|
self.assertEqual(_find_circular_dependencies({1: [2, 3], 2: [3], 3: [1]}), [[1, 2, 3, 1]])
|
|
self.assertEqual(_find_circular_dependencies({1: [2], 2: [3], 3: [4], 4: [1]}), [[1, 2, 3, 4, 1]])
|
|
self.assertEqual(_find_circular_dependencies({1: [2], 2: [1], 3: [4], 4: [5], 5: [3]}), [[1, 2, 1], [3, 4, 5, 3]])
|