diff --git a/frappe/tests/test_modules.py b/frappe/tests/test_modules.py new file mode 100644 index 0000000000..0ea7549bc0 --- /dev/null +++ b/frappe/tests/test_modules.py @@ -0,0 +1,162 @@ +import os +import shutil +import unittest + +import frappe +from frappe import scrub +from frappe.core.doctype.doctype.test_doctype import new_doctype +from frappe.custom.doctype.custom_field.custom_field import create_custom_field +from frappe.model.meta import trim_table +from frappe.modules import export_customizations, export_module_json, get_module_path +from frappe.modules.utils import export_doc, sync_customizations +from frappe.utils import now_datetime + + +def write_file(path, content): + with open(path, "w") as f: + f.write(content) + + +def delete_file(path): + os.remove(path) + + +def delete_path(path): + shutil.rmtree(path) + + +class TestUtils(unittest.TestCase): + def setUp(self): + if self._testMethodName == "test_export_module_json_no_export": + self._dev_mode = frappe.local.conf.developer_mode + self._in_import = frappe.local.flags.in_import + frappe.local.flags.in_import = True + frappe.local.conf.developer_mode = True + + if self._testMethodName in ("test_export_customizations", "test_sync_customizations"): + df = { + "fieldname": "test_export_customizations_field", + "label": "Custom Data Field", + "fieldtype": "Data", + } + self.custom_field = create_custom_field("Note", df=df) + + if self._testMethodName == "test_export_doc": + self.note = frappe.new_doc("Note") + self.note.title = frappe.generate_hash("Note", length=10) + self.note.save() + + if self._testMethodName == "test_make_boilerplate": + self.doctype = new_doctype("Test DocType Boilerplate") + self.doctype.insert() + + def tearDown(self): + if self._testMethodName == "test_export_module_json_no_export": + frappe.local.conf.developer_mode = self._dev_mode + frappe.local.flags.in_import = self._in_import + + if self._testMethodName in ("test_export_customizations", "test_sync_customizations"): + self.custom_field.delete() + trim_table("Note", dry_run=False) + delattr(self, "custom_field") + + if self._testMethodName == "test_export_doc": + self.note.delete() + delattr(self, "note") + + if self._testMethodName == "test_make_boilerplate": + self.doctype.delete() + frappe.db.sql_ddl("DROP TABLE `tabTest DocType Boilerplate`") + delattr(self, "doctype") + + def test_export_module_json_no_export(self): + doc = frappe.get_last_doc("DocType") + self.assertIsNone(export_module_json(doc=doc, is_standard=True, module=doc.module)) + + def test_export_module_json(self): + doc = frappe.get_last_doc("DocType") + export_doc_path = os.path.join( + get_module_path(doc.module), + scrub(doc.doctype), + scrub(doc.name), + f"{scrub(doc.name)}.json", + ) + with open(export_doc_path, "r") as f: + export_doc_before = frappe.parse_json(f.read()) + last_modified_before = os.path.getmtime(export_doc_path) + self.addCleanup(write_file, path=export_doc_path, content=frappe.as_json(export_doc_before)) + export_path = export_module_json(doc=doc, is_standard=True, module=doc.module) + last_modified_after = os.path.getmtime(export_doc_path) + with open(f"{export_path}.json", "r") as f: + export_doc_after = frappe.parse_json(f.read()) + self.assertTrue(last_modified_after > last_modified_before) + + def test_export_customizations(self): + file_path = export_customizations(module="Custom", doctype="Note") + self.addCleanup(delete_file, path=file_path) + self.assertTrue(file_path.endswith("/custom/custom/note.json")) + self.assertTrue(os.path.exists(file_path)) + + def test_sync_customizations(self): + custom_field = frappe.get_doc( + "Custom Field", {"dt": "Note", "fieldname": "test_export_customizations_field"} + ) + + file_path = export_customizations(module="Custom", doctype="Note", sync_on_migrate=True) + custom_field.db_set("modified", now_datetime()) + custom_field.reload() + + self.assertTrue(file_path.endswith("/custom/custom/note.json")) + self.assertTrue(os.path.exists(file_path)) + last_modified_before = custom_field.modified + + sync_customizations(app="frappe") + + self.assertTrue(file_path.endswith("/custom/custom/note.json")) + self.assertTrue(os.path.exists(file_path)) + custom_field.reload() + last_modified_after = custom_field.modified + + self.assertNotEqual(last_modified_after, last_modified_before) + self.addCleanup(delete_file, path=file_path) + + def test_reload_doc(self): + frappe.db.set_value("DocType", "Note", "migration_hash", "", update_modified=False) + self.assertFalse(frappe.db.get_value("DocType", "Note", "migration_hash")) + frappe.db.set_value( + "DocField", + {"parent": "Note", "fieldname": "title"}, + "fieldtype", + "Text", + update_modified=False, + ) + self.assertEqual( + frappe.db.get_value("DocField", {"parent": "Note", "fieldname": "title"}, "fieldtype"), + "Text", + ) + frappe.reload_doctype("Note") + self.assertEqual( + frappe.db.get_value("DocField", {"parent": "Note", "fieldname": "title"}, "fieldtype"), + "Data", + ) + self.assertTrue(frappe.db.get_value("DocType", "Note", "migration_hash")) + + def test_export_doc(self): + exported_doc_path = frappe.get_app_path( + "frappe", "desk", "note", self.note.name, f"{self.note.name}.json" + ) + folder_path = os.path.abspath(os.path.dirname(exported_doc_path)) + export_doc(doctype="Note", name=self.note.name) + self.addCleanup(delete_path, path=folder_path) + self.assertTrue(os.path.exists(exported_doc_path)) + + def test_make_boilerplate(self): + scrubbed = frappe.scrub(self.doctype.name) + self.assertFalse( + os.path.exists(frappe.get_app_path("frappe", "core", "doctype", scrubbed, f"{scrubbed}.json")) + ) + self.doctype.custom = False + self.doctype.save() + self.assertTrue( + os.path.exists(frappe.get_app_path("frappe", "core", "doctype", scrubbed, f"{scrubbed}.json")) + )