fix(tests): add test cases for custom_link and custom_action

This commit is contained in:
Rushabh Mehta 2020-10-13 13:09:55 +05:30
parent 8a198f363b
commit a0a3606a7f
7 changed files with 173 additions and 75 deletions

View file

@ -159,7 +159,10 @@ def handle_exception(e):
response = None
http_status_code = getattr(e, "http_status_code", 500)
return_as_message = False
# print(frappe.get_traceback())
if frappe.conf.get('developer_mode'):
# don't fail silently
print(frappe.get_traceback())
if frappe.get_request_header('Accept') and (frappe.local.is_ajax or 'application/json' in frappe.get_request_header('Accept')):
# handle ajax responses first

View file

@ -12,41 +12,22 @@ from frappe.core.doctype.doctype.doctype import UniqueFieldnameError, IllegalMan
class TestDocType(unittest.TestCase):
def new_doctype(self, name, unique=0, depends_on=''):
return frappe.get_doc({
"doctype": "DocType",
"module": "Core",
"custom": 1,
"fields": [{
"label": "Some Field",
"fieldname": "some_fieldname",
"fieldtype": "Data",
"unique": unique,
"depends_on": depends_on,
}],
"permissions": [{
"role": "System Manager",
"read": 1,
}],
"name": name
})
def test_validate_name(self):
self.assertRaises(frappe.NameError, self.new_doctype("_Some DocType").insert)
self.assertRaises(frappe.NameError, self.new_doctype("8Some DocType").insert)
self.assertRaises(frappe.NameError, self.new_doctype("Some (DocType)").insert)
self.assertRaises(frappe.NameError, new_doctype("_Some DocType").insert)
self.assertRaises(frappe.NameError, new_doctype("8Some DocType").insert)
self.assertRaises(frappe.NameError, new_doctype("Some (DocType)").insert)
for name in ("Some DocType", "Some_DocType"):
if frappe.db.exists("DocType", name):
frappe.delete_doc("DocType", name)
doc = self.new_doctype(name).insert()
doc = new_doctype(name).insert()
doc.delete()
def test_doctype_unique_constraint_dropped(self):
if frappe.db.exists("DocType", "With_Unique"):
frappe.delete_doc("DocType", "With_Unique")
dt = self.new_doctype("With_Unique", unique=1)
dt = new_doctype("With_Unique", unique=1)
dt.insert()
doc1 = frappe.new_doc("With_Unique")
@ -67,7 +48,7 @@ class TestDocType(unittest.TestCase):
doc2.delete()
def test_validate_search_fields(self):
doc = self.new_doctype("Test Search Fields")
doc = new_doctype("Test Search Fields")
doc.search_fields = "some_fieldname"
doc.insert()
self.assertEqual(doc.name, "Test Search Fields")
@ -85,7 +66,7 @@ class TestDocType(unittest.TestCase):
self.assertRaises(frappe.ValidationError, doc.save)
def test_depends_on_fields(self):
doc = self.new_doctype("Test Depends On", depends_on="eval:doc.__islocal == 0")
doc = new_doctype("Test Depends On", depends_on="eval:doc.__islocal == 0")
doc.insert()
# check if the assignment operation is allowed in depends_on
@ -261,7 +242,7 @@ class TestDocType(unittest.TestCase):
frappe.flags.allow_doctype_export = 0
def test_unique_field_name_for_two_fields(self):
doc = self.new_doctype('Test Unique Field')
doc = new_doctype('Test Unique Field')
field_1 = doc.append('fields', {})
field_1.fieldname = 'some_fieldname_1'
field_1.fieldtype = 'Data'
@ -273,7 +254,7 @@ class TestDocType(unittest.TestCase):
self.assertRaises(UniqueFieldnameError, doc.insert)
def test_fieldname_is_not_name(self):
doc = self.new_doctype('Test Name Field')
doc = new_doctype('Test Name Field')
field_1 = doc.append('fields', {})
field_1.label = 'Name'
field_1.fieldtype = 'Data'
@ -283,7 +264,7 @@ class TestDocType(unittest.TestCase):
self.assertRaises(InvalidFieldNameError, doc.save)
def test_illegal_mandatory_validation(self):
doc = self.new_doctype('Test Illegal mandatory')
doc = new_doctype('Test Illegal mandatory')
field_1 = doc.append('fields', {})
field_1.fieldname = 'some_fieldname_1'
field_1.fieldtype = 'Section Break'
@ -292,7 +273,7 @@ class TestDocType(unittest.TestCase):
self.assertRaises(IllegalMandatoryError, doc.insert)
def test_link_with_wrong_and_no_options(self):
doc = self.new_doctype('Test link')
doc = new_doctype('Test link')
field_1 = doc.append('fields', {})
field_1.fieldname = 'some_fieldname_1'
field_1.fieldtype = 'Link'
@ -304,7 +285,7 @@ class TestDocType(unittest.TestCase):
self.assertRaises(WrongOptionsDoctypeLinkError, doc.insert)
def test_hidden_and_mandatory_without_default(self):
doc = self.new_doctype('Test hidden and mandatory')
doc = new_doctype('Test hidden and mandatory')
field_1 = doc.append('fields', {})
field_1.fieldname = 'some_fieldname_1'
field_1.fieldtype = 'Data'
@ -314,7 +295,7 @@ class TestDocType(unittest.TestCase):
self.assertRaises(HiddenAndMandatoryWithoutDefaultError, doc.insert)
def test_field_can_not_be_indexed_validation(self):
doc = self.new_doctype('Test index')
doc = new_doctype('Test index')
field_1 = doc.append('fields', {})
field_1.fieldname = 'some_fieldname_1'
field_1.fieldtype = 'Long Text'
@ -327,14 +308,14 @@ class TestDocType(unittest.TestCase):
from frappe.desk.form.linked_with import get_submitted_linked_docs, cancel_all_linked_docs
#create doctype
link_doc = self.new_doctype('Test Linked Doctype')
link_doc = new_doctype('Test Linked Doctype')
link_doc.is_submittable = 1
for data in link_doc.get('permissions'):
data.submit = 1
data.cancel = 1
link_doc.insert()
doc = self.new_doctype('Test Doctype')
doc = new_doctype('Test Doctype')
doc.is_submittable = 1
field_2 = doc.append('fields', {})
field_2.label = 'Test Linked Doctype'
@ -377,12 +358,12 @@ class TestDocType(unittest.TestCase):
doc.delete()
frappe.db.commit()
def test_ignore_cancelation_of_linked_doctype_during_cancell(self):
def test_ignore_cancelation_of_linked_doctype_during_cancel(self):
import json
from frappe.desk.form.linked_with import get_submitted_linked_docs, cancel_all_linked_docs
#create linked doctype
link_doc = self.new_doctype('Test Linked Doctype 1')
link_doc = new_doctype('Test Linked Doctype 1')
link_doc.is_submittable = 1
for data in link_doc.get('permissions'):
data.submit = 1
@ -390,7 +371,7 @@ class TestDocType(unittest.TestCase):
link_doc.insert()
#create first parent doctype
test_doc_1 = self.new_doctype('Test Doctype 1')
test_doc_1 = new_doctype('Test Doctype 1')
test_doc_1.is_submittable = 1
field_2 = test_doc_1.append('fields', {})
@ -405,7 +386,7 @@ class TestDocType(unittest.TestCase):
test_doc_1.insert()
#crete second parent doctype
doc = self.new_doctype('Test Doctype 2')
doc = new_doctype('Test Doctype 2')
doc.is_submittable = 1
field_2 = doc.append('fields', {})
@ -469,3 +450,28 @@ class TestDocType(unittest.TestCase):
doc.delete()
test_doc_1.delete()
frappe.db.commit()
def new_doctype(name, unique=0, depends_on='', fields=None):
doc = frappe.get_doc({
"doctype": "DocType",
"module": "Core",
"custom": 1,
"fields": [{
"label": "Some Field",
"fieldname": "some_fieldname",
"fieldtype": "Data",
"unique": unique,
"depends_on": depends_on,
}],
"permissions": [{
"role": "System Manager",
"read": 1,
}],
"name": name
})
if fields:
for f in fields:
doc.append('fields', f)
return doc

View file

@ -94,19 +94,19 @@ frappe.ui.form.on("Customize Form", {
frm.add_custom_button(__('Go to {0} List', [frm.doc.doc_type]), function() {
frappe.set_route('List', frm.doc.doc_type);
});
}, __('Actions'));
frm.add_custom_button(__('Refresh Form'), function() {
frm.add_custom_button(__('Reload'), function() {
frm.script_manager.trigger("doc_type");
}, "fa fa-refresh", "btn-default");
}, __('Actions'));
frm.add_custom_button(__('Reset to defaults'), function() {
frappe.customize_form.confirm(__('Remove all customizations?'), frm);
}, "fa fa-eraser", "btn-default");
}, __('Actions'));
frm.add_custom_button(__('Set Permissions'), function() {
frappe.set_route('permission-manager', frm.doc.doc_type);
}, "fa fa-lock", "btn-default");
}, __('Actions'));
if (frappe.boot.developer_mode) {
frm.add_custom_button(__('Export Customizations'), function() {
@ -131,7 +131,7 @@ frappe.ui.form.on("Customize Form", {
});
},
__("Select Module"));
});
}, __('Actions'));
}
}

View file

@ -46,8 +46,8 @@ class CustomizeForm(Document):
'''
Check if the doctype is allowed to be customized.
'''
#if self.doc_type in core_doctypes_list:
# frappe.throw(_("Core DocTypes cannot be customized."))
if self.doc_type in core_doctypes_list:
frappe.throw(_("Core DocTypes cannot be customized."))
if meta.issingle:
frappe.throw(_("Single DocTypes cannot be customized."))
@ -71,7 +71,7 @@ class CustomizeForm(Document):
for fieldname in ('links', 'actions'):
for d in meta.get(fieldname):
self.append(fieldname, d)
d1 = self.append(fieldname, d)
def create_auto_repeat_custom_field_if_requried(self, meta):
if self.allow_auto_repeat:
@ -242,35 +242,52 @@ class CustomizeForm(Document):
('DocType Action', 'actions', doctype_action_properties)
):
has_custom = False
for d in self.get(fieldname):
if not (d.custom and frappe.db.exists(doctype, d.name)):
items = []
for i, d in enumerate(self.get(fieldname) or []):
d.idx = i
if frappe.db.exists(doctype, d.name) and not d.custom:
# check property and apply property setter
original = frappe.get_doc(doctype, d.name)
for prop, prop_type in field_map.items():
if d.get(prop) != original.get(prop):
self.make_property_setter(prop, d.get(prop), prop_type,
apply_on=doctype, row_name=d.name)
items.append(d.name)
else:
# add or update custom object
if frappe.db.exists(doctype, d.name):
doc = frappe.get_doc(doctype, d.name)
else:
doc = frappe.new_doc(doctype)
doc.parent = self.doc_type
doc.parenttype = '_Custom' # dummy parenttype since its mandatory
doc.custom = 1
for prop, prop_type in field_map.items():
doc.set(prop, d.get(prop))
doc.save(ignore_permissions=True)
# custom - just insert/update
d.parent = self.doc_type
d.custom = 1
d.save(ignore_permissions=True)
has_custom = True
items.append(d.name)
if has_custom:
# save the order of the actions and links
self.make_property_setter('{}_order'.format(fieldname),
json.dumps([d.name for d in self.get(fieldname)]), 'Small Text')
self.update_order_property_setter(has_custom, fieldname)
self.clear_removed_items(doctype, items)
def update_order_property_setter(self, has_custom, fieldname):
'''
We need to maintain the order of the link/actions if the user has shuffled them.
So we create a new property (ex `links_order`) to keep a list of items.
'''
property_name = '{}_order'.format(fieldname)
if has_custom:
# save the order of the actions and links
self.make_property_setter(property_name,
json.dumps([d.name for d in self.get(fieldname)]), 'Small Text')
else:
frappe.db.delete('Property Setter', dict(property=property_name,
doc_type=self.doc_type))
def clear_removed_items(self, doctype, items):
'''
Clear rows that do not appear in `items`. These have been removed by the user.
'''
if items:
frappe.db.delete(doctype, dict(parent=self.doc_type, custom=1,
name=('not in', items)))
else:
frappe.db.delete(doctype, dict(parent=self.doc_type, custom=1))
def update_custom_fields(self):
for i, df in enumerate(self.get("fields")):

View file

@ -5,6 +5,7 @@ from __future__ import unicode_literals
import frappe, unittest, json
from frappe.test_runner import make_test_records_for_doctype
from frappe.core.doctype.doctype.doctype import InvalidFieldNameError
from frappe.core.doctype.doctype.test_doctype import new_doctype
test_dependencies = ["Custom Field", "Property Setter"]
class TestCustomizeForm(unittest.TestCase):
@ -191,3 +192,73 @@ class TestCustomizeForm(unittest.TestCase):
# core doctype is invalid, hence no attributes are set
self.assertEquals(d.get("fields"), [])
self.assertEquals(e.get("fields"), [])
def test_custom_link(self):
try:
# create a dummy doctype linked to Event
testdt_name = 'Test Link for Event'
testdt = new_doctype(testdt_name, fields=[
dict(fieldtype='Link', fieldname='event', options='Event')
]).insert()
testdt_name1 = 'Test Link for Event 1'
testdt1 = new_doctype(testdt_name1, fields=[
dict(fieldtype='Link', fieldname='event', options='Event')
]).insert()
# add a custom link
d = self.get_customize_form("Event")
d.append('links', dict(link_doctype=testdt_name, link_fieldname='event', group='Tests'))
d.append('links', dict(link_doctype=testdt_name1, link_fieldname='event', group='Tests'))
d.run_method("save_customization")
frappe.clear_cache()
event = frappe.get_meta('Event')
# check links exist
self.assertTrue([d.name for d in event.links if d.link_doctype == testdt_name])
self.assertTrue([d.name for d in event.links if d.link_doctype == testdt_name1])
# check order
order = json.loads(event.links_order)
self.assertListEqual(order, [d.name for d in event.links])
# remove the link
d = self.get_customize_form("Event")
d.links = []
d.run_method("save_customization")
frappe.clear_cache()
event = frappe.get_meta('Event')
self.assertFalse([d.name for d in (event.links or []) if d.link_doctype == testdt_name])
finally:
testdt.delete()
testdt1.delete()
def test_custom_action(self):
test_route = '#List/DocType'
# create a dummy action (route)
d = self.get_customize_form("Event")
d.append('actions', dict(label='Test Action', action_type='Route', action=test_route))
d.run_method("save_customization")
frappe.clear_cache()
event = frappe.get_meta('Event')
# check if added to meta
action = [d for d in event.actions if d.label=='Test Action']
self.assertEqual(len(action), 1)
self.assertEqual(action[0].action, test_route)
# clear the action
d = self.get_customize_form("Event")
d.actions = []
d.run_method("save_customization")
frappe.clear_cache()
event = frappe.get_meta('Event')
action = [d for d in event.actions if d.label=='Test Action']
self.assertEqual(len(action), 0)

View file

@ -341,7 +341,7 @@ class Database(object):
value = filters.get(key)
values[key] = value
if isinstance(value, (list, tuple)):
# value is a tuble like ("!=", 0)
# value is a tuple like ("!=", 0)
_operator = value[0]
values[key] = value[1]
if isinstance(value[1], (tuple, list)):
@ -959,13 +959,13 @@ class Database(object):
query = sql_dict.get(current_dialect)
return self.sql(query, values, **kwargs)
def delete(self, doctype, conditions):
def delete(self, doctype, conditions, debug=False):
if conditions:
conditions, values = self.build_conditions(conditions)
return self.sql("DELETE FROM `tab{doctype}` where {conditions}".format(
doctype=doctype,
conditions=conditions
), values)
), values, debug=debug)
else:
frappe.throw(_('No conditions provided'))

View file

@ -343,8 +343,8 @@ class Meta(Document):
def add_custom_links_and_actions(self):
for doctype, fieldname in (('DocType Link', 'links'), ('DocType Action', 'actions')):
for d in frappe.get_all(doctype, dict(parent=self.name, custom=1)):
self.get(fieldname).append(d)
for d in frappe.get_all(doctype, fields='*', filters=dict(parent=self.name, custom=1)):
self.append(fieldname, d)
# set the fields in order if specified
# order is saved as `links_order`
@ -353,14 +353,15 @@ class Meta(Document):
name_map = {d.name:d for d in self.get(fieldname)}
new_list = []
for name in order:
new_list.append(name_map[name])
name_map[name].__added = True
if name in name_map:
new_list.append(name_map[name])
# add the missing items that have not be added
# maybe these items were added to the standard product
# after the customization was done
for d in self.get(fieldname):
if not d.__added: new_list.append(d)
if not d in new_list:
new_list.append(d)
self.set(fieldname, new_list)