1
2# Copyright 2009-2017 Jaap Karssenberg <jaap.karssenberg@gmail.com>
3
4
5
6import tests
7
8from zim.newfs import TmpFile
9from zim.notebook import Path
10
11from zim.gui.customtools import *
12
13
14def clear_customtools():
15	listfile = XDG_CONFIG_HOME.file('zim/customtools/customtools.list')
16	folder = XDG_CONFIG_HOME.subdir('zim/customtools/')
17	assert 'tests/tmp' in listfile.path.replace('\\', '/')
18	assert 'tests/tmp' in folder.path.replace('\\', '/')
19	listfile.remove()
20	folder.remove_children()
21
22
23class MockStubPageView(StubPageView):
24
25	def get_word(self, format=None):
26		if format:
27			return '**FooBar**'
28		else:
29			return 'FooBar'
30
31
32@tests.slowTest
33class TestCustomTools(tests.TestCase):
34
35	mockConfigManager = False  # breaks hack in customtools
36
37	def setUp(self):
38		clear_customtools()
39
40	def testManager(self):
41		'''Test CustomToolManager API'''
42		# initialize the list
43		manager = CustomToolManager()
44		self.assertEqual(list(manager), [])
45		self.assertEqual(list(manager._names), [])
46
47		# add a tool
48		properties = {
49			'Name': 'Foo',
50			'Comment': 'Test 1 2 3',
51			'Icon': '',
52			'X-Zim-ExecTool': 'foo %t "quoted"',
53			'X-Zim-ReadOnly': False,
54			'X-Zim-ShowInToolBar': True,
55		}
56		tool = manager.create(**properties)
57		self.assertEqual(list(manager), [tool])
58		self.assertEqual(list(manager._names), ['foo-usercreated'])
59
60		self.assertTrue(tool.isvalid)
61		self.assertEqual(tool.name, 'Foo')
62		self.assertEqual(tool.comment, 'Test 1 2 3')
63		self.assertFalse(tool.isreadonly)
64		self.assertTrue(tool.showintoolbar)
65		self.assertTrue(tool.get_pixbuf(Gtk.IconSize.MENU))
66		self.assertEqual(tool.showincontextmenu, 'Text') # Auto generated
67
68		# test file saved correctly
69		#~ from pprint import pprint
70		#~ pprint(tool)
71		lines = tool.dump()
72		self.assertTrue(len(lines) > 5)
73		lines = tool.file.readlines()
74		self.assertTrue(len(lines) > 5)
75
76		# refresh list
77		manager = CustomToolManager()
78		self.assertEqual(list(manager), [tool])
79		self.assertEqual(list(manager._names), ['foo-usercreated'])
80
81		# add a second tool
82		tool1 = tool
83		properties = {
84			'Name': 'Foo',
85			'Comment': 'Test 1 2 3',
86			'Icon': None,
87			'X-Zim-ExecTool': 'foo %f',
88			'X-Zim-ReadOnly': False,
89			'X-Zim-ShowInToolBar': True,
90		}
91		tool = manager.create(**properties)
92		self.assertEqual(list(manager), [tool1, tool])
93		self.assertEqual(list(manager._names), ['foo-usercreated', 'foo-usercreated-1'])
94
95		self.assertTrue(tool.isvalid)
96		self.assertEqual(tool.name, 'Foo')
97		self.assertEqual(tool.comment, 'Test 1 2 3')
98		self.assertFalse(tool.isreadonly)
99		self.assertTrue(tool.showintoolbar)
100		self.assertTrue(tool.get_pixbuf(Gtk.IconSize.MENU))
101		self.assertEqual(tool.showincontextmenu, 'Page') # Auto generated
102
103		# switch order
104		i = manager.index(tool)
105		self.assertTrue(i == 1)
106		manager.reorder(tool, 0)
107		i = manager.index(tool)
108		self.assertTrue(i == 0)
109		self.assertEqual(list(manager._names), ['foo-usercreated-1', 'foo-usercreated'])
110
111		# delete
112		file = tool1.file
113		self.assertTrue(file.exists())
114		manager.delete(tool1)
115		self.assertEqual(list(manager._names), ['foo-usercreated-1'])
116		self.assertFalse(file.exists())
117
118	def testParseExec(self):
119		'''Test parsing of custom tool Exec strings'''
120		# %f for source file as tmp file current page
121		# %d for attachment directory
122		# %s for real source file (if any)
123		# %p for the page name
124		# %n for notebook location (file or directory)
125		# %D for document root
126		# %t for selected text or word under cursor
127		# %T for selected text or word under cursor with wiki format
128
129		notebook = self.setUpNotebook()
130		page = notebook.get_page(Path('Test:Foo'))
131		pageview = MockStubPageView(notebook, page)
132		args = (notebook, page, pageview)
133
134		tmpfile = TmpFile('tmp-page-source.txt').path
135
136		tool = CustomToolDict()
137		tool.update({
138			'Name': 'Test',
139			'Comment': 'Test 1 2 3',
140			'X-Zim-ExecTool': 'foo',
141		})
142		for cmd, wanted in (
143			('foo %f', ('foo', tmpfile)),
144			('foo %d', ('foo', notebook.folder.folder('Test/Foo').path)),
145			('foo %s', ('foo', page.source.path)),
146			('foo %p', ('foo', 'Test:Foo')),
147			('foo %n', ('foo', notebook.folder.path)),
148			('foo %D', ('foo', '')), # no document root
149			('foo %t', ('foo', 'FooBar')),
150			('foo %T', ('foo', '**FooBar**')),
151		):
152			#~ print('>>>', cmd)
153			tool['Desktop Entry']['X-Zim-ExecTool'] = cmd
154			self.assertEqual(tool.parse_exec(args), wanted)
155
156
157class TestCustomToolsUI(tests.TestCase):
158
159	mockConfigManager = False  # breaks hack in customtools
160
161	def setUp(self):
162		clear_customtools()
163
164		notebook = self.setUpNotebook(content=('test',))
165		page = notebook.get_page(Path('test'))
166
167		self.uimanager = Gtk.UIManager()
168		group = Gtk.ActionGroup('test')
169		group.add_actions([('tools_menu', None, '_Tools')])
170		self.uimanager.insert_action_group(group)
171
172
173		self.pageview = StubPageView(notebook, page)
174		self.customtoolsui = CustomToolManagerUI(self.uimanager, self.pageview)
175
176	def get_action(self, name):
177		for group in self.uimanager.get_action_groups():
178			action = group.get_action(name)
179			if action:
180				return action
181		else:
182			raise ValueError
183
184	def testMenuUpdatedWhenToolAdded(self):
185		# Key in this test is that the changes happens via a different instance
186		# of the manager - demonstrating the singleton-like behavior
187		self.assertNotIn('edit-usercreated', self.uimanager.get_ui())
188		mymanager = CustomToolManager()
189		mytool = {
190			'Name': 'Edit',
191			'Comment': 'Test Edit',
192			'X-Zim-ExecTool': 'edit %f',
193		}
194		mymanager.create(**mytool)
195		self.assertIn('edit-usercreated', self.uimanager.get_ui())
196
197	def testMenuUpdatedWhenToolChanges(self):
198		# Key in this test is that the changes happens via a different instance
199		# of the manager - demonstrating the singleton-like behavior
200		mymanager = CustomToolManager()
201		mytool = {
202			'Name': 'Edit',
203			'Comment': 'Test Edit',
204			'X-Zim-ExecTool': 'edit %f',
205		}
206		mymanager.create(**mytool)
207		self.assertIn('edit-usercreated', self.uimanager.get_ui())
208		action = self.get_action('edit-usercreated')
209		self.assertEqual(action.get_property('label'), 'Edit')
210
211		tool = mymanager.get_tool('Edit')
212		tool.update({'Name': 'My Editting tool'})
213		tool.write()
214		self.assertIn('edit-usercreated', self.uimanager.get_ui())
215		action = self.get_action('edit-usercreated')
216		self.assertEqual(action.get_property('label'), 'My Editting tool')
217
218	def testExecuteCustomToolAction(self):
219		mymanager = CustomToolManager()
220		mytool = {
221			'Name': 'Edit',
222			'Comment': 'Test Edit',
223			'X-Zim-ExecTool': 'edit %f',
224		}
225		mymanager.create(**mytool)
226		action = self.get_action('edit-usercreated')
227
228		def tool_run(args):
229			self.assertIn(args[0], ('edit', b'edit'))
230
231		with tests.ApplicationContext(tool_run):
232			action.activate()
233
234	def testExecuteCustomToolActionModifyPage(self):
235		mymanager = CustomToolManager()
236		mytool = {
237			'Name': 'Edit',
238			'Comment': 'Test Edit',
239			'X-Zim-ExecTool': 'edit %s',
240			'X-Zim-ReadOnly': False,
241		}
242		mymanager.create(**mytool)
243		action = self.get_action('edit-usercreated')
244
245		def tool_run(args):
246			self.assertIn(args[0], ('edit', b'edit'))
247			self.pageview.page.source_file.write('test 123')
248
249		with tests.ApplicationContext(tool_run):
250			action.activate()
251
252	def testExecuteCustomToolActionReplaceSelection(self):
253		mymanager = CustomToolManager()
254		mytool = {
255			'Name': 'Edit',
256			'Comment': 'Test Edit',
257			'X-Zim-ExecTool': 'edit %f',
258			'X-Zim-ReplaceSelection': True,
259		}
260		tool = mymanager.create(**mytool)
261		self.assertTrue(tool.replaceselection)
262		action = self.get_action('edit-usercreated')
263
264		got = []
265		self.pageview.replace_selection = lambda *a, **kwa: got.append(a)
266
267		def tool_run(args):
268			self.assertIn(args[0], ('edit', b'edit'))
269			return "TEST 123"
270
271		with tests.ApplicationContext(tool_run):
272			action.activate()
273
274		self.assertEqual(got, [('TEST 123',)])
275
276class TestCustomToolDialog(tests.TestCase):
277
278	mockConfigManager = False  # breaks hack in customtools
279
280	def setUp(self):
281		clear_customtools()
282
283	def testAddCustomTool(self):
284		manager = CustomToolManager()
285		mytool = {
286			'Name': 'Add',
287			'Comment': 'Test Add',
288			'X-Zim-ExecTool': 'add %f',
289			'X-Zim-ReadOnly': False,
290			'X-Zim-ShowInToolBar': False,
291			'X-Zim-ReplaceSelection': False,
292		}
293
294		self.assertIsNone(manager.get_tool('Add'))
295
296		def add_tool(dialog):
297			dialog.set_input(**mytool)
298			dialog.assert_response_ok()
299
300		dialog = CustomToolManagerDialog(None)
301		with tests.DialogContext(add_tool):
302			dialog.on_add()
303			dialog.assert_response_ok()
304
305		# XXX listview intialization fails in test ???
306		#model = dialog.listview.get_model()
307		#self.assertIn('Add', [r[CustomToolList.NAME_COL] for r in model])
308
309		tool = manager.get_tool('Add')
310		self.assertIsNotNone(tool)
311		for key, value in list(mytool.items()):
312			self.assertEqual(tool['Desktop Entry'][key], value)
313
314	@tests.expectedFailure # Fails because of select_by_name fails - listview initialized ?
315	def testEditCustomTool(self):
316		manager = CustomToolManager()
317		mytool = {
318			'Name': 'Edit',
319			'Comment': 'Test Edit',
320			'X-Zim-ExecTool': 'edit %f',
321		}
322		manager.create(**mytool)
323		self.assertIsNotNone(manager.get_tool('Edit'))
324
325		def edit_tool(dialog):
326			dialog.set_input(Comment='Editted Comment')
327			dialog.assert_response_ok()
328
329		dialog = CustomToolManagerDialog(None)
330		#model = dialog.listview.get_model()
331		#self.assertIn('Edit', [r[CustomToolList.NAME_COL] for r in model])
332		with tests.DialogContext(edit_tool):
333			dialog.listview.select_by_name('Edit')
334			dialog.on_edit()
335			dialog.assert_response_ok()
336
337		tool = manager.get_tool('Edit')
338		self.assertEqual(tool.comment, 'Editted Comment')
339
340	@tests.expectedFailure
341	def testRemoveCustomTool(self):
342		# Need initialized listview
343		raise NotImplementedError
344
345	@tests.expectedFailure
346	def testMovePositionInList(self):
347		# Need initialized listview
348		raise NotImplementedError
349