1 /*
2 * Copyright (C) 2016 Red Hat, Inc. (www.redhat.com)
3 *
4 * This library is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation.
7 *
8 * This library is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
11 * for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this library. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17 #include "evolution-config.h"
18
19 #include <string.h>
20 #include <stdlib.h>
21
22 #include "e-util/e-util.h"
23
24 #include "test-html-editor-units-utils.h"
25
26 static guint event_processing_delay_ms = 25;
27 static gboolean in_background = FALSE;
28 static gboolean use_multiple_web_processes = FALSE;
29 static gboolean glob_keep_going = FALSE;
30 static GObject *global_web_context = NULL;
31
32 void
test_utils_set_event_processing_delay_ms(guint value)33 test_utils_set_event_processing_delay_ms (guint value)
34 {
35 event_processing_delay_ms = value;
36 }
37
38 guint
test_utils_get_event_processing_delay_ms(void)39 test_utils_get_event_processing_delay_ms (void)
40 {
41 return event_processing_delay_ms;
42 }
43
44 void
test_utils_set_background(gboolean background)45 test_utils_set_background (gboolean background)
46 {
47 in_background = background;
48 }
49
50 gboolean
test_utils_get_background(void)51 test_utils_get_background (void)
52 {
53 return in_background;
54 }
55
56 void
test_utils_set_multiple_web_processes(gboolean multiple_web_processes)57 test_utils_set_multiple_web_processes (gboolean multiple_web_processes)
58 {
59 use_multiple_web_processes = multiple_web_processes;
60 }
61
62 gboolean
test_utils_get_multiple_web_processes(void)63 test_utils_get_multiple_web_processes (void)
64 {
65 return use_multiple_web_processes;
66 }
67
68 void
test_utils_set_keep_going(gboolean keep_going)69 test_utils_set_keep_going (gboolean keep_going)
70 {
71 glob_keep_going = keep_going;
72 }
73
74 gboolean
test_utils_get_keep_going(void)75 test_utils_get_keep_going (void)
76 {
77 return glob_keep_going;
78 }
79
80 void
test_utils_free_global_memory(void)81 test_utils_free_global_memory (void)
82 {
83 g_clear_object (&global_web_context);
84 }
85
86 typedef struct _GetContentData {
87 EContentEditorContentHash *content_hash;
88 gpointer async_data;
89 } GetContentData;
90
91 static void
get_editor_content_hash_ready_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)92 get_editor_content_hash_ready_cb (GObject *source_object,
93 GAsyncResult *result,
94 gpointer user_data)
95 {
96 GetContentData *gcd = user_data;
97 GError *error = NULL;
98
99 g_assert_nonnull (gcd);
100 g_assert (E_IS_CONTENT_EDITOR (source_object));
101
102 gcd->content_hash = e_content_editor_get_content_finish (E_CONTENT_EDITOR (source_object), result, &error);
103
104 g_assert_no_error (error);
105
106 g_clear_error (&error);
107
108 test_utils_async_call_finish (gcd->async_data);
109 }
110
111 static EContentEditorContentHash *
test_utils_get_editor_content_hash_sync(EContentEditor * cnt_editor,guint32 flags)112 test_utils_get_editor_content_hash_sync (EContentEditor *cnt_editor,
113 guint32 flags)
114 {
115 GetContentData gcd;
116
117 g_assert (E_IS_CONTENT_EDITOR (cnt_editor));
118
119 gcd.content_hash = NULL;
120 gcd.async_data = test_utils_async_call_prepare ();
121
122 e_content_editor_get_content (cnt_editor, flags, "test-domain", NULL, get_editor_content_hash_ready_cb, &gcd);
123
124 g_assert (test_utils_async_call_wait (gcd.async_data, MAX (event_processing_delay_ms / 25, 1) + 1));
125 g_assert_nonnull (gcd.content_hash);
126
127 return gcd.content_hash;
128 }
129
130 typedef struct _UndoContent {
131 gchar *html;
132 gchar *plain;
133 } UndoContent;
134
135 static UndoContent *
undo_content_new(TestFixture * fixture)136 undo_content_new (TestFixture *fixture)
137 {
138 EContentEditor *cnt_editor;
139 EContentEditorContentHash *content_hash;
140 UndoContent *uc;
141
142 g_return_val_if_fail (fixture != NULL, NULL);
143 g_return_val_if_fail (E_IS_HTML_EDITOR (fixture->editor), NULL);
144
145 cnt_editor = e_html_editor_get_content_editor (fixture->editor);
146 content_hash = test_utils_get_editor_content_hash_sync (cnt_editor, E_CONTENT_EDITOR_GET_TO_SEND_HTML | E_CONTENT_EDITOR_GET_TO_SEND_PLAIN);
147
148 uc = g_new0 (UndoContent, 1);
149 uc->html = e_content_editor_util_steal_content_data (content_hash, E_CONTENT_EDITOR_GET_TO_SEND_HTML, NULL);
150 uc->plain = e_content_editor_util_steal_content_data (content_hash, E_CONTENT_EDITOR_GET_TO_SEND_PLAIN, NULL);
151
152 e_content_editor_util_free_content_hash (content_hash);
153
154 g_warn_if_fail (uc->html != NULL);
155 g_warn_if_fail (uc->plain != NULL);
156
157 return uc;
158 }
159
160 static void
undo_content_free(gpointer ptr)161 undo_content_free (gpointer ptr)
162 {
163 UndoContent *uc = ptr;
164
165 if (uc) {
166 g_free (uc->html);
167 g_free (uc->plain);
168 g_free (uc);
169 }
170 }
171
172 static gboolean
undo_content_test(TestFixture * fixture,const UndoContent * uc,gint cmd_index)173 undo_content_test (TestFixture *fixture,
174 const UndoContent *uc,
175 gint cmd_index)
176 {
177 EContentEditor *cnt_editor;
178 EContentEditorContentHash *content_hash;
179 const gchar *text;
180
181 g_return_val_if_fail (fixture != NULL, FALSE);
182 g_return_val_if_fail (E_IS_HTML_EDITOR (fixture->editor), FALSE);
183 g_return_val_if_fail (uc != NULL, FALSE);
184
185 cnt_editor = e_html_editor_get_content_editor (fixture->editor);
186 content_hash = test_utils_get_editor_content_hash_sync (cnt_editor, E_CONTENT_EDITOR_GET_TO_SEND_HTML | E_CONTENT_EDITOR_GET_TO_SEND_PLAIN);
187
188 text = e_content_editor_util_get_content_data (content_hash, E_CONTENT_EDITOR_GET_TO_SEND_HTML);
189 g_return_val_if_fail (text != NULL, FALSE);
190
191 if (!test_utils_html_equal (fixture, text, uc->html)) {
192 if (glob_keep_going)
193 g_printerr ("%s: returned HTML\n---%s---\n and expected HTML\n---%s---\n do not match at command %d\n", G_STRFUNC, text, uc->html, cmd_index);
194 else
195 g_warning ("%s: returned HTML\n---%s---\n and expected HTML\n---%s---\n do not match at command %d", G_STRFUNC, text, uc->html, cmd_index);
196
197 e_content_editor_util_free_content_hash (content_hash);
198
199 return FALSE;
200 }
201
202 text = e_content_editor_util_get_content_data (content_hash, E_CONTENT_EDITOR_GET_TO_SEND_PLAIN);
203 g_return_val_if_fail (text != NULL, FALSE);
204
205 if (!test_utils_html_equal (fixture, text, uc->plain)) {
206 if (glob_keep_going)
207 g_printerr ("%s: returned Plain\n---%s---\n and expected Plain\n---%s---\n do not match at command %d\n", G_STRFUNC, text, uc->plain, cmd_index);
208 else
209 g_warning ("%s: returned Plain\n---%s---\n and expected Plain\n---%s---\n do not match at command %d", G_STRFUNC, text, uc->plain, cmd_index);
210
211 e_content_editor_util_free_content_hash (content_hash);
212
213 return FALSE;
214 }
215
216 e_content_editor_util_free_content_hash (content_hash);
217
218 return TRUE;
219 }
220
221 static gboolean
test_utils_web_process_crashed_cb(WebKitWebView * web_view,gpointer user_data)222 test_utils_web_process_crashed_cb (WebKitWebView *web_view,
223 gpointer user_data)
224 {
225 g_warning ("%s:", G_STRFUNC);
226
227 return FALSE;
228 }
229
230 /* <Control>+<Shift>+I */
231 #define WEBKIT_INSPECTOR_MOD (GDK_CONTROL_MASK | GDK_SHIFT_MASK)
232 #define WEBKIT_INSPECTOR_KEY (GDK_KEY_I)
233
234 static gboolean
wk_editor_key_press_event_cb(WebKitWebView * web_view,GdkEventKey * event)235 wk_editor_key_press_event_cb (WebKitWebView *web_view,
236 GdkEventKey *event)
237 {
238 WebKitWebInspector *inspector;
239 gboolean handled = FALSE;
240
241 inspector = webkit_web_view_get_inspector (web_view);
242
243 if ((event->state & WEBKIT_INSPECTOR_MOD) == WEBKIT_INSPECTOR_MOD &&
244 event->keyval == WEBKIT_INSPECTOR_KEY) {
245 webkit_web_inspector_show (inspector);
246 handled = TRUE;
247 }
248
249 return handled;
250 }
251
252 typedef struct _CreateData {
253 gpointer async_data;
254 TestFixture *fixture;
255 gboolean created;
256 } CreateData;
257
258 static void
test_utils_html_editor_created_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)259 test_utils_html_editor_created_cb (GObject *source_object,
260 GAsyncResult *result,
261 gpointer user_data)
262 {
263 CreateData *create_data = user_data;
264 TestFixture *fixture;
265 EContentEditor *cnt_editor;
266 GtkWidget *html_editor;
267 GError *error = NULL;
268
269 g_return_if_fail (create_data != NULL);
270
271 create_data->created = TRUE;
272
273 fixture = create_data->fixture;
274
275 html_editor = e_html_editor_new_finish (result, &error);
276 if (error) {
277 g_warning ("%s: Failed to create editor: %s", G_STRFUNC, error->message);
278 g_clear_error (&error);
279 return;
280 }
281
282 fixture->editor = E_HTML_EDITOR (html_editor);
283
284 g_object_set (G_OBJECT (fixture->editor),
285 "halign", GTK_ALIGN_FILL,
286 "hexpand", TRUE,
287 "valign", GTK_ALIGN_FILL,
288 "vexpand", TRUE,
289 NULL);
290 gtk_widget_show (GTK_WIDGET (fixture->editor));
291 gtk_container_add (GTK_CONTAINER (fixture->window), GTK_WIDGET (fixture->editor));
292
293 fixture->focus_tracker = e_focus_tracker_new (GTK_WINDOW (fixture->window));
294
295 e_focus_tracker_set_cut_clipboard_action (fixture->focus_tracker,
296 e_html_editor_get_action (fixture->editor, "cut"));
297
298 e_focus_tracker_set_copy_clipboard_action (fixture->focus_tracker,
299 e_html_editor_get_action (fixture->editor, "copy"));
300
301 e_focus_tracker_set_paste_clipboard_action (fixture->focus_tracker,
302 e_html_editor_get_action (fixture->editor, "paste"));
303
304 e_focus_tracker_set_select_all_action (fixture->focus_tracker,
305 e_html_editor_get_action (fixture->editor, "select-all"));
306
307 e_focus_tracker_set_undo_action (fixture->focus_tracker,
308 e_html_editor_get_action (fixture->editor, "undo"));
309
310 e_focus_tracker_set_redo_action (fixture->focus_tracker,
311 e_html_editor_get_action (fixture->editor, "redo"));
312
313 /* Make sure this is off */
314 test_utils_fixture_change_setting_boolean (fixture,
315 "org.gnome.evolution.mail", "prompt-on-composer-mode-switch", FALSE);
316
317 cnt_editor = e_html_editor_get_content_editor (fixture->editor);
318 g_object_set (G_OBJECT (cnt_editor),
319 "halign", GTK_ALIGN_FILL,
320 "hexpand", TRUE,
321 "valign", GTK_ALIGN_FILL,
322 "vexpand", TRUE,
323 "height-request", 150,
324 NULL);
325
326 g_signal_connect (cnt_editor, "web-process-crashed",
327 G_CALLBACK (test_utils_web_process_crashed_cb), NULL);
328
329 if (WEBKIT_IS_WEB_VIEW (cnt_editor)) {
330 WebKitSettings *web_settings;
331
332 web_settings = webkit_web_view_get_settings (WEBKIT_WEB_VIEW (cnt_editor));
333 webkit_settings_set_enable_developer_extras (web_settings, TRUE);
334 webkit_settings_set_enable_write_console_messages_to_stdout (web_settings, TRUE);
335
336 g_signal_connect (
337 cnt_editor, "key-press-event",
338 G_CALLBACK (wk_editor_key_press_event_cb), NULL);
339
340 if (!test_utils_get_multiple_web_processes () && !global_web_context) {
341 WebKitWebContext *web_context;
342
343 web_context = webkit_web_view_get_context (WEBKIT_WEB_VIEW (cnt_editor));
344 global_web_context = G_OBJECT (g_object_ref (web_context));
345 }
346 }
347
348 gtk_window_set_focus (GTK_WINDOW (fixture->window), GTK_WIDGET (cnt_editor));
349 gtk_widget_show (fixture->window);
350
351 test_utils_async_call_finish (create_data->async_data);
352 }
353
354 /* The tests do not use the 'user_data' argument, thus the functions avoid them and the typecast is needed. */
355 typedef void (* ETestFixtureFunc) (TestFixture *fixture, gconstpointer user_data);
356
357 void
test_utils_add_test(const gchar * name,ETestFixtureSimpleFunc func)358 test_utils_add_test (const gchar *name,
359 ETestFixtureSimpleFunc func)
360 {
361 g_test_add (name, TestFixture, NULL,
362 test_utils_fixture_set_up, (ETestFixtureFunc) func, test_utils_fixture_tear_down);
363 }
364
365 static void test_utils_async_call_free (gpointer async_data);
366
367 void
test_utils_fixture_set_up(TestFixture * fixture,gconstpointer user_data)368 test_utils_fixture_set_up (TestFixture *fixture,
369 gconstpointer user_data)
370 {
371 CreateData create_data;
372
373 fixture->window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
374 fixture->undo_stack = NULL;
375 fixture->key_state = 0;
376
377 if (test_utils_get_background ()) {
378 gtk_window_set_keep_below (GTK_WINDOW (fixture->window), TRUE);
379 gtk_window_set_focus_on_map (GTK_WINDOW (fixture->window), FALSE);
380 }
381
382 create_data.async_data = test_utils_async_call_prepare ();
383 create_data.fixture = fixture;
384 create_data.created = FALSE;
385
386 e_html_editor_new (test_utils_html_editor_created_cb, &create_data);
387
388 if (create_data.created)
389 test_utils_async_call_free (create_data.async_data);
390 else
391 test_utils_async_call_wait (create_data.async_data, 60);
392
393 g_warn_if_fail (fixture->editor != NULL);
394 g_warn_if_fail (E_IS_HTML_EDITOR (fixture->editor));
395 }
396
397 static void
free_old_settings(gpointer ptr)398 free_old_settings (gpointer ptr)
399 {
400 TestSettings *data = ptr;
401
402 if (data) {
403 GSettings *settings;
404
405 settings = e_util_ref_settings (data->schema);
406 g_settings_set_value (settings, data->key, data->old_value);
407 g_clear_object (&settings);
408
409 g_variant_unref (data->old_value);
410 g_free (data->schema);
411 g_free (data->key);
412 g_free (data);
413 }
414 }
415
416 void
test_utils_fixture_tear_down(TestFixture * fixture,gconstpointer user_data)417 test_utils_fixture_tear_down (TestFixture *fixture,
418 gconstpointer user_data)
419 {
420 g_clear_object (&fixture->focus_tracker);
421
422 gtk_widget_destroy (GTK_WIDGET (fixture->window));
423 fixture->editor = NULL;
424
425 g_slist_free_full (fixture->settings, free_old_settings);
426 fixture->settings = NULL;
427
428 g_slist_free_full (fixture->undo_stack, undo_content_free);
429 fixture->undo_stack = NULL;
430 }
431
432 void
test_utils_fixture_change_setting(TestFixture * fixture,const gchar * schema,const gchar * key,GVariant * value)433 test_utils_fixture_change_setting (TestFixture *fixture,
434 const gchar *schema,
435 const gchar *key,
436 GVariant *value)
437 {
438 TestSettings *data;
439 GSettings *settings;
440
441 g_return_if_fail (fixture != NULL);
442 g_return_if_fail (schema != NULL);
443 g_return_if_fail (key != NULL);
444 g_return_if_fail (value != NULL);
445
446 g_variant_ref_sink (value);
447
448 settings = e_util_ref_settings (schema);
449
450 data = g_new0 (TestSettings, 1);
451 data->schema = g_strdup (schema);
452 data->key = g_strdup (key);
453 data->old_value = g_settings_get_value (settings, key);
454
455 /* Use prepend, thus the restore comes in the opposite order, thus a change
456 of the same key is not a problem. */
457 fixture->settings = g_slist_prepend (fixture->settings, data);
458
459 g_settings_set_value (settings, key, value);
460
461 g_clear_object (&settings);
462 g_variant_unref (value);
463 }
464
465 void
test_utils_fixture_change_setting_boolean(TestFixture * fixture,const gchar * schema,const gchar * key,gboolean value)466 test_utils_fixture_change_setting_boolean (TestFixture *fixture,
467 const gchar *schema,
468 const gchar *key,
469 gboolean value)
470 {
471 test_utils_fixture_change_setting (fixture, schema, key, g_variant_new_boolean (value));
472 }
473
474 void
test_utils_fixture_change_setting_int32(TestFixture * fixture,const gchar * schema,const gchar * key,gint value)475 test_utils_fixture_change_setting_int32 (TestFixture *fixture,
476 const gchar *schema,
477 const gchar *key,
478 gint value)
479 {
480 test_utils_fixture_change_setting (fixture, schema, key, g_variant_new_int32 (value));
481 }
482
483 void
test_utils_fixture_change_setting_string(TestFixture * fixture,const gchar * schema,const gchar * key,const gchar * value)484 test_utils_fixture_change_setting_string (TestFixture *fixture,
485 const gchar *schema,
486 const gchar *key,
487 const gchar *value)
488 {
489 test_utils_fixture_change_setting (fixture, schema, key, g_variant_new_string (value));
490 }
491
492 static void
test_utils_flush_main_context(void)493 test_utils_flush_main_context (void)
494 {
495 GMainContext *main_context;
496
497 main_context = g_main_context_default ();
498
499 while (g_main_context_pending (main_context)) {
500 g_main_context_iteration (main_context, FALSE);
501 }
502 }
503
504 static void
test_utils_async_call_free(gpointer async_data)505 test_utils_async_call_free (gpointer async_data)
506 {
507 GMainLoop *loop = async_data;
508
509 test_utils_flush_main_context ();
510
511 g_main_loop_unref (loop);
512 }
513
514 gpointer
test_utils_async_call_prepare(void)515 test_utils_async_call_prepare (void)
516 {
517 return g_main_loop_new (NULL, FALSE);
518 }
519
520 typedef struct _AsynCallData {
521 GMainLoop *loop;
522 gboolean timeout_reached;
523 } AsyncCallData;
524
525 static gboolean
test_utils_async_call_timeout_reached_cb(gpointer user_data)526 test_utils_async_call_timeout_reached_cb (gpointer user_data)
527 {
528 AsyncCallData *async_call_data = user_data;
529
530 g_return_val_if_fail (async_call_data != NULL, FALSE);
531 g_return_val_if_fail (async_call_data->loop != NULL, FALSE);
532 g_return_val_if_fail (!async_call_data->timeout_reached, FALSE);
533
534 if (!g_source_is_destroyed (g_main_current_source ())) {
535 async_call_data->timeout_reached = TRUE;
536 g_main_loop_quit (async_call_data->loop);
537 }
538
539 return FALSE;
540 }
541
542 gboolean
test_utils_async_call_wait(gpointer async_data,guint timeout_seconds)543 test_utils_async_call_wait (gpointer async_data,
544 guint timeout_seconds)
545 {
546 GMainLoop *loop = async_data;
547 AsyncCallData async_call_data;
548 GSource *source = NULL;
549
550 g_return_val_if_fail (loop != NULL, FALSE);
551
552 async_call_data.loop = loop;
553 async_call_data.timeout_reached = FALSE;
554
555 /* 0 is to wait forever */
556 if (timeout_seconds > 0) {
557 source = g_timeout_source_new_seconds (timeout_seconds);
558 g_source_set_callback (source, test_utils_async_call_timeout_reached_cb, &async_call_data, NULL);
559 g_source_attach (source, NULL);
560 }
561
562 g_main_loop_run (loop);
563
564 if (source) {
565 g_source_destroy (source);
566 g_source_unref (source);
567 }
568
569 test_utils_async_call_free (async_data);
570
571 return !async_call_data.timeout_reached;
572 }
573
574 gboolean
test_utils_async_call_finish(gpointer async_data)575 test_utils_async_call_finish (gpointer async_data)
576 {
577 GMainLoop *loop = async_data;
578
579 g_return_val_if_fail (loop != NULL, FALSE);
580
581 g_main_loop_quit (loop);
582
583 return FALSE;
584 }
585
586 gboolean
test_utils_wait_milliseconds(guint milliseconds)587 test_utils_wait_milliseconds (guint milliseconds)
588 {
589 gpointer async_data;
590
591 async_data = test_utils_async_call_prepare ();
592 g_timeout_add (milliseconds, test_utils_async_call_finish, async_data);
593
594 return test_utils_async_call_wait (async_data, milliseconds / 1000 + 1);
595 }
596
597 static void
test_utils_send_key_event(GtkWidget * widget,GdkEventType type,guint keyval,guint state)598 test_utils_send_key_event (GtkWidget *widget,
599 GdkEventType type,
600 guint keyval,
601 guint state)
602 {
603 GdkKeymap *keymap;
604 GdkKeymapKey *keys = NULL;
605 gint n_keys;
606 GdkEvent *event;
607
608 g_return_if_fail (GTK_IS_WIDGET (widget));
609
610 event = gdk_event_new (type);
611 event->key.is_modifier =
612 keyval == GDK_KEY_Shift_L ||
613 keyval == GDK_KEY_Shift_R ||
614 keyval == GDK_KEY_Control_L ||
615 keyval == GDK_KEY_Control_R ||
616 keyval == GDK_KEY_Alt_L ||
617 keyval == GDK_KEY_Alt_R;
618 event->key.keyval = keyval;
619 event->key.state = state;
620 event->key.window = g_object_ref (gtk_widget_get_window (widget));
621 event->key.send_event = TRUE;
622 event->key.length = 0;
623 event->key.string = NULL;
624 event->key.hardware_keycode = 0;
625 event->key.group = 0;
626 event->key.time = GDK_CURRENT_TIME;
627
628 gdk_event_set_device (event, gdk_seat_get_keyboard (gdk_display_get_default_seat (gtk_widget_get_display (widget))));
629
630 keymap = gdk_keymap_get_for_display (gtk_widget_get_display (widget));
631 if (gdk_keymap_get_entries_for_keyval (keymap, keyval, &keys, &n_keys)) {
632 if (n_keys > 0) {
633 event->key.hardware_keycode = keys[0].keycode;
634 event->key.group = keys[0].group;
635 }
636
637 g_free (keys);
638 }
639
640 gtk_main_do_event (event);
641
642 test_utils_wait_milliseconds (event_processing_delay_ms);
643
644 gdk_event_free (event);
645 }
646
647 gboolean
test_utils_type_text(TestFixture * fixture,const gchar * text)648 test_utils_type_text (TestFixture *fixture,
649 const gchar *text)
650 {
651 GtkWidget *widget;
652
653 g_return_val_if_fail (fixture != NULL, FALSE);
654 g_return_val_if_fail (E_IS_HTML_EDITOR (fixture->editor), FALSE);
655
656 widget = GTK_WIDGET (e_html_editor_get_content_editor (fixture->editor));
657 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
658
659 g_return_val_if_fail (text != NULL, FALSE);
660 g_return_val_if_fail (g_utf8_validate (text, -1, NULL), FALSE);
661
662 while (*text) {
663 guint keyval;
664 gunichar unichar;
665
666 unichar = g_utf8_get_char (text);
667 text = g_utf8_next_char (text);
668
669 switch (unichar) {
670 case '\n':
671 keyval = GDK_KEY_Return;
672 break;
673 case '\t':
674 keyval = GDK_KEY_Tab;
675 break;
676 case '\b':
677 keyval = GDK_KEY_BackSpace;
678 break;
679 default:
680 keyval = gdk_unicode_to_keyval (unichar);
681 break;
682 }
683
684 test_utils_send_key_event (widget, GDK_KEY_PRESS, keyval, fixture->key_state);
685 test_utils_send_key_event (widget, GDK_KEY_RELEASE, keyval, fixture->key_state);
686 }
687
688 test_utils_wait_milliseconds (event_processing_delay_ms);
689
690 return TRUE;
691 }
692
693 typedef struct _HTMLEqualData {
694 gpointer async_data;
695 gboolean equal;
696 } HTMLEqualData;
697
698 static void
test_html_equal_done_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)699 test_html_equal_done_cb (GObject *source_object,
700 GAsyncResult *result,
701 gpointer user_data)
702 {
703 HTMLEqualData *hed = user_data;
704 WebKitJavascriptResult *js_result;
705 JSCException *exception;
706 JSCValue *js_value;
707 GError *error = NULL;
708
709 g_return_if_fail (hed != NULL);
710
711 js_result = webkit_web_view_run_javascript_finish (WEBKIT_WEB_VIEW (source_object), result, &error);
712
713 g_assert_no_error (error);
714 g_clear_error (&error);
715
716 g_assert_nonnull (js_result);
717
718 js_value = webkit_javascript_result_get_js_value (js_result);
719 g_assert_nonnull (js_value);
720 g_assert (jsc_value_is_boolean (js_value));
721
722 hed->equal = jsc_value_to_boolean (js_value);
723
724 exception = jsc_context_get_exception (jsc_value_get_context (js_value));
725
726 if (exception) {
727 g_warning ("Failed to call EvoEditorTest.isHTMLEqual: %s", jsc_exception_get_message (exception));
728 jsc_context_clear_exception (jsc_value_get_context (js_value));
729 }
730
731 webkit_javascript_result_unref (js_result);
732
733 test_utils_async_call_finish (hed->async_data);
734 }
735
736 gboolean
test_utils_html_equal(TestFixture * fixture,const gchar * html1,const gchar * html2)737 test_utils_html_equal (TestFixture *fixture,
738 const gchar *html1,
739 const gchar *html2)
740 {
741 EContentEditor *cnt_editor;
742 gchar *script;
743 HTMLEqualData hed;
744
745 g_return_val_if_fail (fixture != NULL, FALSE);
746 g_return_val_if_fail (E_IS_HTML_EDITOR (fixture->editor), FALSE);
747 g_return_val_if_fail (html1 != NULL, FALSE);
748 g_return_val_if_fail (html2 != NULL, FALSE);
749
750 cnt_editor = e_html_editor_get_content_editor (fixture->editor);
751 g_return_val_if_fail (cnt_editor != NULL, FALSE);
752 g_return_val_if_fail (WEBKIT_IS_WEB_VIEW (cnt_editor), FALSE);
753
754 script = e_web_view_jsc_printf_script (
755 "var EvoEditorTest = {};\n"
756 "EvoEditorTest.fixupBlockquotes = function(parent) {\n"
757 " var ii, elems = parent.getElementsByTagName(\"BLOCKQUOTE\");\n"
758 " for (ii = 0; ii < elems.length; ii++) {\n"
759 " elems[ii].removeAttribute(\"spellcheck\");\n"
760 " }\n"
761 "}\n"
762 "EvoEditorTest.isHTMLEqual = function(html1, html2) {\n"
763 " var elem1, elem2;\n"
764 " elem1 = document.createElement(\"testHtmlEqual\");\n"
765 " elem2 = document.createElement(\"testHtmlEqual\");\n"
766 " elem1.innerHTML = html1.replace(/ /g, \" \").replace(/ /g, \" \");\n"
767 " elem2.innerHTML = html2.replace(/ /g, \" \").replace(/ /g, \" \");\n"
768 " EvoEditorTest.fixupBlockquotes(elem1);\n"
769 " EvoEditorTest.fixupBlockquotes(elem2);\n"
770 " elem1.normalize();\n"
771 " elem2.normalize();\n"
772 " return elem1.isEqualNode(elem2);\n"
773 "}\n"
774 "EvoEditorTest.isHTMLEqual(%s, %s);", html1, html2);
775
776 hed.async_data = test_utils_async_call_prepare ();
777 hed.equal = FALSE;
778
779 webkit_web_view_run_javascript (WEBKIT_WEB_VIEW (cnt_editor), script, NULL,
780 test_html_equal_done_cb, &hed);
781
782 test_utils_async_call_wait (hed.async_data, 10);
783
784 g_free (script);
785
786 return hed.equal;
787 }
788
789 static gboolean
test_utils_process_sequence(TestFixture * fixture,const gchar * sequence)790 test_utils_process_sequence (TestFixture *fixture,
791 const gchar *sequence)
792 {
793 GtkWidget *widget;
794 const gchar *seq;
795 guint keyval;
796 gboolean success = TRUE;
797
798 g_return_val_if_fail (fixture != NULL, FALSE);
799 g_return_val_if_fail (E_IS_HTML_EDITOR (fixture->editor), FALSE);
800 g_return_val_if_fail (sequence != NULL, FALSE);
801
802 widget = GTK_WIDGET (e_html_editor_get_content_editor (fixture->editor));
803 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
804
805 for (seq = sequence; *seq && success; seq++) {
806 gboolean call_press = TRUE, call_release = TRUE;
807 guint change_state = fixture->key_state;
808
809 switch (*seq) {
810 case 'S': /* Shift key press */
811 keyval = GDK_KEY_Shift_L;
812
813 if ((fixture->key_state & GDK_SHIFT_MASK) != 0) {
814 success = FALSE;
815 g_warning ("%s: Shift is already pressed", G_STRFUNC);
816 } else {
817 change_state |= GDK_SHIFT_MASK;
818 }
819 call_release = FALSE;
820 break;
821 case 's': /* Shift key release */
822 keyval = GDK_KEY_Shift_L;
823
824 if ((fixture->key_state & GDK_SHIFT_MASK) == 0) {
825 success = FALSE;
826 g_warning ("%s: Shift is already released", G_STRFUNC);
827 } else {
828 change_state &= ~GDK_SHIFT_MASK;
829 }
830 call_press = FALSE;
831 break;
832 case 'C': /* Ctrl key press */
833 keyval = GDK_KEY_Control_L;
834
835 if ((fixture->key_state & GDK_CONTROL_MASK) != 0) {
836 success = FALSE;
837 g_warning ("%s: Control is already pressed", G_STRFUNC);
838 } else {
839 change_state |= GDK_CONTROL_MASK;
840 }
841 call_release = FALSE;
842 break;
843 case 'c': /* Ctrl key release */
844 keyval = GDK_KEY_Control_L;
845
846 if ((fixture->key_state & GDK_CONTROL_MASK) == 0) {
847 success = FALSE;
848 g_warning ("%s: Control is already released", G_STRFUNC);
849 } else {
850 change_state &= ~GDK_CONTROL_MASK;
851 }
852 call_press = FALSE;
853 break;
854 case 'A': /* Alt key press */
855 keyval = GDK_KEY_Alt_L;
856
857 if ((fixture->key_state & GDK_MOD1_MASK) != 0) {
858 success = FALSE;
859 g_warning ("%s: Alt is already pressed", G_STRFUNC);
860 } else {
861 change_state |= GDK_MOD1_MASK;
862 }
863 call_release = FALSE;
864 break;
865 case 'a': /* Alt key release */
866 keyval = GDK_KEY_Alt_L;
867
868 if ((fixture->key_state & GDK_MOD1_MASK) == 0) {
869 success = FALSE;
870 g_warning ("%s: Alt is already released", G_STRFUNC);
871 } else {
872 change_state &= ~GDK_MOD1_MASK;
873 }
874 call_press = FALSE;
875 break;
876 case 'h': /* Home key press + release */
877 keyval = GDK_KEY_Home;
878 break;
879 case 'e': /* End key press + release */
880 keyval = GDK_KEY_End;
881 break;
882 case 'P': /* Page-Up key press + release */
883 keyval = GDK_KEY_Page_Up;
884 break;
885 case 'p': /* Page-Down key press + release */
886 keyval = GDK_KEY_Page_Down;
887 break;
888 case 'l': /* Arrow-Left key press + release */
889 keyval = GDK_KEY_Left;
890 break;
891 case 'r': /* Arrow-Right key press + release */
892 keyval = GDK_KEY_Right;
893 break;
894 case 'u': /* Arrow-Up key press + release */
895 keyval = GDK_KEY_Up;
896 break;
897 case 'd': /* Arrow-Down key press + release */
898 keyval = GDK_KEY_Down;
899 break;
900 case 'D': /* Delete key press + release */
901 keyval = GDK_KEY_Delete;
902 break;
903 case 'b': /* Backspace key press + release */
904 keyval = GDK_KEY_BackSpace;
905 break;
906 case 't': /* Tab key press + release */
907 keyval = GDK_KEY_Tab;
908 break;
909 case 'n': /* Return key press + release */
910 keyval = GDK_KEY_Return;
911 break;
912 case 'i': /* Insert key press + release */
913 keyval = GDK_KEY_Insert;
914 break;
915 case '^': /* Escape key press + release */
916 keyval = GDK_KEY_Escape;
917 break;
918 default:
919 success = FALSE;
920 g_warning ("%s: Unknown sequence command '%c' in sequence '%s'", G_STRFUNC, *seq, sequence);
921 break;
922 }
923
924 if (success) {
925 if (call_press)
926 test_utils_send_key_event (widget, GDK_KEY_PRESS, keyval, fixture->key_state);
927
928 if (call_release)
929 test_utils_send_key_event (widget, GDK_KEY_RELEASE, keyval, fixture->key_state);
930 }
931
932 fixture->key_state = change_state;
933 }
934
935 test_utils_wait_milliseconds (event_processing_delay_ms);
936
937 return success;
938 }
939
940 static gboolean
test_utils_execute_action(TestFixture * fixture,const gchar * action_name)941 test_utils_execute_action (TestFixture *fixture,
942 const gchar *action_name)
943 {
944 GtkAction *action;
945
946 g_return_val_if_fail (fixture != NULL, FALSE);
947 g_return_val_if_fail (E_IS_HTML_EDITOR (fixture->editor), FALSE);
948 g_return_val_if_fail (action_name != NULL, FALSE);
949
950 action = e_html_editor_get_action (fixture->editor, action_name);
951 if (action) {
952 gtk_action_activate (action);
953 } else {
954 g_warning ("%s: Failed to find action '%s'", G_STRFUNC, action_name);
955 return FALSE;
956 }
957
958 return TRUE;
959 }
960
961 static gboolean
test_utils_set_font_name(TestFixture * fixture,const gchar * font_name)962 test_utils_set_font_name (TestFixture *fixture,
963 const gchar *font_name)
964 {
965 EContentEditor *cnt_editor;
966
967 g_return_val_if_fail (fixture != NULL, FALSE);
968 g_return_val_if_fail (E_IS_HTML_EDITOR (fixture->editor), FALSE);
969
970 cnt_editor = e_html_editor_get_content_editor (fixture->editor);
971 e_content_editor_set_font_name (cnt_editor, font_name);
972
973 return TRUE;
974 }
975
976 /* Expects only the part like "undo" [ ":" number ] */
977 static gint
test_utils_maybe_extract_undo_number(const gchar * command)978 test_utils_maybe_extract_undo_number (const gchar *command)
979 {
980 const gchar *ptr;
981 gint number;
982
983 g_return_val_if_fail (command != NULL, -1);
984
985 ptr = strchr (command, ':');
986 if (!ptr)
987 return 1;
988
989 number = atoi (ptr + 1);
990 g_return_val_if_fail (number > 0, -1);
991
992 return number;
993 }
994
995 static const UndoContent *
test_utils_pick_undo_content(const GSList * undo_stack,gint number)996 test_utils_pick_undo_content (const GSList *undo_stack,
997 gint number)
998 {
999 const GSList *link;
1000
1001 g_return_val_if_fail (undo_stack != NULL, NULL);
1002
1003 number--;
1004 for (link = undo_stack; link && number > 0; link = g_slist_next (link)) {
1005 number--;
1006 }
1007
1008 g_return_val_if_fail (link != NULL, NULL);
1009 g_return_val_if_fail (link->data != NULL, NULL);
1010
1011 return link->data;
1012 }
1013
1014 /* Each line of 'commands' contains one command.
1015
1016 commands = command *("\n" command)
1017
1018 command = actioncmd ; Execute an action
1019 / modecmd ; Change editor mode to HTML or Plain Text
1020 / fnmcmd ; Set font name
1021 / seqcmd ; Sequence of special key strokes
1022 / typecmd ; Type a text
1023 / undocmd ; Undo/redo commands
1024 / waitcmd ; Wait command
1025
1026 actioncmd = "action:" name
1027
1028 modecmd = "mode:" ("html" / "plain")
1029
1030 fnmcmd = "font-name:" name
1031
1032 seqcmd = "seq:" sequence
1033
1034 sequence = "S" ; Shift key press
1035 / "s" ; Shift key release
1036 / "C" ; Ctrl key press
1037 / "c" ; Ctrl key release
1038 / "A" ; Alt key press
1039 / "a" ; Alt key release
1040 / "h" ; Home key press + release
1041 / "e" ; End key press + release
1042 / "P" ; Page-Up key press + release
1043 / "p" ; Page-Down key press + release
1044 / "l" ; Arrow-Left key press + release
1045 / "r" ; Arrow-Right key press + release
1046 / "u" ; Arrow-Up key press + release
1047 / "d" ; Arrow-Down key press + release
1048 / "D" ; Delete key press + release
1049 / "b" ; Backspace key press + release
1050 / "t" ; Tab key press + release
1051 / "n" ; Return key press + release
1052 / "i" ; Insert key press + release
1053 / "^" ; Escape key press + release
1054
1055 typecmd = "type:" text ; the 'text' can contain escaped letters with a backslash, like "\\n" transforms into "\n"
1056
1057 undocmd = "undo:" undotype
1058
1059 undotype = "undo" [ ":" number ] ; Call 'undo', number-times; if 'number' is not provided, then call it exactly once
1060 / "redo" [ ":" number ] ; Call 'redo', number-times; if 'number' is not provided, then call it exactly once
1061 / "save" ; Save current content of the editor for later tests
1062 / "drop" [ ":" number ] ; Forgets saved content, if 'number' is provided, then top number saves are forgotten
1063 / "test" [ ":" number ] ; Tests current editor content against any previously saved state; the optional
1064 ; 'number' argument can be used to specify which exact previous state to use
1065
1066 waitcmd = "wait:" milliseconds ; waits for 'milliseconds'
1067 */
1068 gboolean
test_utils_process_commands(TestFixture * fixture,const gchar * commands)1069 test_utils_process_commands (TestFixture *fixture,
1070 const gchar *commands)
1071 {
1072 gchar **cmds;
1073 gint cc;
1074 gboolean success = TRUE;
1075
1076 g_return_val_if_fail (fixture != NULL, FALSE);
1077 g_return_val_if_fail (E_IS_HTML_EDITOR (fixture->editor), FALSE);
1078 g_return_val_if_fail (commands != NULL, FALSE);
1079
1080 cmds = g_strsplit (commands, "\n", -1);
1081 for (cc = 0; cmds && cmds[cc] && success; cc++) {
1082 const gchar *command = cmds[cc];
1083
1084 if (g_str_has_prefix (command, "action:")) {
1085 test_utils_execute_action (fixture, command + 7);
1086 } else if (g_str_has_prefix (command, "mode:")) {
1087 const gchar *mode_change = command + 5;
1088
1089 if (g_str_equal (mode_change, "html")) {
1090 test_utils_execute_action (fixture, "mode-html");
1091 } else if (g_str_equal (mode_change, "plain")) {
1092 test_utils_execute_action (fixture, "mode-plain");
1093 } else {
1094 success = FALSE;
1095 g_warning ("%s: Unknown mode '%s'", G_STRFUNC, mode_change);
1096 }
1097 } else if (g_str_has_prefix (command, "font-name:")) {
1098 success = test_utils_set_font_name (fixture, command + 10);
1099 } else if (g_str_has_prefix (command, "seq:")) {
1100 success = test_utils_process_sequence (fixture, command + 4);
1101 } else if (g_str_has_prefix (command, "type:")) {
1102 gchar *text;
1103
1104 text = g_strcompress (command + 5);
1105 success = test_utils_type_text (fixture, text);
1106 if (!success)
1107 g_warning ("%s: Failed to type text '%s'", G_STRFUNC, text);
1108 g_free (text);
1109 } else if (g_str_has_prefix (command, "undo:")) {
1110 gint number;
1111
1112 command += 5;
1113
1114 if (g_str_equal (command, "undo") || g_str_has_prefix (command, "undo:")) {
1115 number = test_utils_maybe_extract_undo_number (command);
1116 while (number > 0 && success) {
1117 success = test_utils_execute_action (fixture, "undo");
1118 number--;
1119 }
1120 } else if (g_str_has_prefix (command, "redo") || g_str_has_prefix (command, "redo:")) {
1121 number = test_utils_maybe_extract_undo_number (command);
1122 while (number > 0 && success) {
1123 success = test_utils_execute_action (fixture, "redo");
1124 number--;
1125 }
1126 } else if (g_str_equal (command, "save")) {
1127 UndoContent *uc;
1128
1129 uc = undo_content_new (fixture);
1130 fixture->undo_stack = g_slist_prepend (fixture->undo_stack, uc);
1131 } else if (g_str_equal (command, "drop") || g_str_has_prefix (command, "drop:")) {
1132 number = test_utils_maybe_extract_undo_number (command);
1133 g_warn_if_fail (number <= g_slist_length (fixture->undo_stack));
1134
1135 while (number > 0 && fixture->undo_stack) {
1136 UndoContent *uc = fixture->undo_stack->data;
1137
1138 fixture->undo_stack = g_slist_remove (fixture->undo_stack, uc);
1139 undo_content_free (uc);
1140 number--;
1141 }
1142 } else if (g_str_equal (command, "test") || g_str_has_prefix (command, "test:")) {
1143 const UndoContent *uc;
1144
1145 number = test_utils_maybe_extract_undo_number (command);
1146 uc = test_utils_pick_undo_content (fixture->undo_stack, number);
1147 success = uc && undo_content_test (fixture, uc, cc);
1148 } else {
1149 g_warning ("%s: Unknown command 'undo:%s'", G_STRFUNC, command);
1150 success = FALSE;
1151 }
1152
1153 test_utils_wait_milliseconds (event_processing_delay_ms);
1154 } else if (g_str_has_prefix (command, "wait:")) {
1155 test_utils_wait_milliseconds (atoi (command + 5));
1156 } else if (*command) {
1157 g_warning ("%s: Unknown command '%s'", G_STRFUNC, command);
1158 success = FALSE;
1159 }
1160
1161 /* Wait at least 100 ms, to give a chance to move the cursor and
1162 other things for WebKit, for example before executing actions. */
1163 test_utils_wait_milliseconds (MAX (event_processing_delay_ms, 100));
1164 }
1165
1166 g_strfreev (cmds);
1167
1168 if (success) {
1169 /* Give the editor some time to finish any ongoing async operations */
1170 test_utils_wait_milliseconds (MAX (event_processing_delay_ms, 100));
1171 }
1172
1173 return success;
1174 }
1175
1176 gboolean
test_utils_run_simple_test(TestFixture * fixture,const gchar * commands,const gchar * expected_html,const gchar * expected_plain)1177 test_utils_run_simple_test (TestFixture *fixture,
1178 const gchar *commands,
1179 const gchar *expected_html,
1180 const gchar *expected_plain)
1181 {
1182 EContentEditor *cnt_editor;
1183 EContentEditorContentHash *content_hash;
1184 const gchar *text;
1185
1186 g_return_val_if_fail (fixture != NULL, FALSE);
1187 g_return_val_if_fail (E_IS_HTML_EDITOR (fixture->editor), FALSE);
1188 g_return_val_if_fail (commands != NULL, FALSE);
1189
1190 cnt_editor = e_html_editor_get_content_editor (fixture->editor);
1191
1192 if (!test_utils_process_commands (fixture, commands))
1193 return FALSE;
1194
1195 content_hash = test_utils_get_editor_content_hash_sync (cnt_editor, E_CONTENT_EDITOR_GET_TO_SEND_HTML | E_CONTENT_EDITOR_GET_TO_SEND_PLAIN);
1196
1197 if (expected_html) {
1198 text = e_content_editor_util_get_content_data (content_hash, E_CONTENT_EDITOR_GET_TO_SEND_HTML);
1199 g_return_val_if_fail (text != NULL, FALSE);
1200
1201 if (!test_utils_html_equal (fixture, text, expected_html)) {
1202 if (glob_keep_going)
1203 g_printerr ("%s: returned HTML\n---%s---\n and expected HTML\n---%s---\n do not match\n", G_STRFUNC, text, expected_html);
1204 else
1205 g_warning ("%s: returned HTML\n---%s---\n and expected HTML\n---%s---\n do not match", G_STRFUNC, text, expected_html);
1206
1207 e_content_editor_util_free_content_hash (content_hash);
1208
1209 return FALSE;
1210 }
1211 }
1212
1213 if (expected_plain) {
1214 text = e_content_editor_util_get_content_data (content_hash, E_CONTENT_EDITOR_GET_TO_SEND_PLAIN);
1215 g_return_val_if_fail (text != NULL, FALSE);
1216
1217 if (!test_utils_html_equal (fixture, text, expected_plain)) {
1218 if (glob_keep_going)
1219 g_printerr ("%s: returned Plain\n---%s---\n and expected Plain\n---%s---\n do not match\n", G_STRFUNC, text, expected_plain);
1220 else
1221 g_warning ("%s: returned Plain\n---%s---\n and expected Plain\n---%s---\n do not match", G_STRFUNC, text, expected_plain);
1222
1223 e_content_editor_util_free_content_hash (content_hash);
1224
1225 return FALSE;
1226 }
1227 }
1228
1229 e_content_editor_util_free_content_hash (content_hash);
1230
1231 return TRUE;
1232 }
1233
1234 void
test_utils_insert_content(TestFixture * fixture,const gchar * content,EContentEditorInsertContentFlags flags)1235 test_utils_insert_content (TestFixture *fixture,
1236 const gchar *content,
1237 EContentEditorInsertContentFlags flags)
1238 {
1239 EContentEditor *cnt_editor;
1240
1241 g_return_if_fail (fixture != NULL);
1242 g_return_if_fail (E_IS_HTML_EDITOR (fixture->editor));
1243 g_return_if_fail (content != NULL);
1244
1245 cnt_editor = e_html_editor_get_content_editor (fixture->editor);
1246 e_content_editor_insert_content (cnt_editor, content, flags);
1247 }
1248
1249 void
test_utils_set_clipboard_text(const gchar * text,gboolean is_html)1250 test_utils_set_clipboard_text (const gchar *text,
1251 gboolean is_html)
1252 {
1253 GtkClipboard *clipboard;
1254
1255 g_return_if_fail (text != NULL);
1256
1257 clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
1258 g_return_if_fail (clipboard != NULL);
1259
1260 gtk_clipboard_clear (clipboard);
1261
1262 if (is_html) {
1263 e_clipboard_set_html (clipboard, text, -1);
1264 } else {
1265 gtk_clipboard_set_text (clipboard, text, -1);
1266 }
1267 }
1268
1269 gchar *
test_utils_get_clipboard_text(gboolean request_html)1270 test_utils_get_clipboard_text (gboolean request_html)
1271 {
1272 GtkClipboard *clipboard;
1273 gchar *text;
1274
1275 clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
1276 g_return_val_if_fail (clipboard != NULL, NULL);
1277
1278 if (request_html) {
1279 g_return_val_if_fail (e_clipboard_wait_is_html_available (clipboard), NULL);
1280 text = e_clipboard_wait_for_html (clipboard);
1281 } else {
1282 g_return_val_if_fail (gtk_clipboard_wait_is_text_available (clipboard), NULL);
1283 text = gtk_clipboard_wait_for_text (clipboard);
1284 }
1285
1286 g_return_val_if_fail (text != NULL, NULL);
1287
1288 return text;
1289 }
1290
1291 EContentEditor *
test_utils_get_content_editor(TestFixture * fixture)1292 test_utils_get_content_editor (TestFixture *fixture)
1293 {
1294 EContentEditor *cnt_editor;
1295
1296 g_return_val_if_fail (fixture != NULL, NULL);
1297 g_return_val_if_fail (E_IS_HTML_EDITOR (fixture->editor), NULL);
1298
1299 cnt_editor = e_html_editor_get_content_editor (fixture->editor);
1300 return cnt_editor;
1301 }
1302
1303 gchar *
test_utils_dup_image_uri(const gchar * path)1304 test_utils_dup_image_uri (const gchar *path)
1305 {
1306 gchar *image_uri = NULL;
1307 GError *error = NULL;
1308
1309 if (path && strchr (path, G_DIR_SEPARATOR)) {
1310 image_uri = g_filename_to_uri (path, NULL, &error);
1311 } else {
1312 gchar *filename;
1313
1314 filename = e_icon_factory_get_icon_filename (path, GTK_ICON_SIZE_MENU);
1315 if (filename) {
1316 image_uri = g_filename_to_uri (filename, NULL, &error);
1317 g_free (filename);
1318 } else {
1319 g_set_error (&error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "Icon '%s' not found", path);
1320 }
1321 }
1322
1323 if (image_uri) {
1324 gchar *tmp;
1325
1326 tmp = g_strconcat ("evo-", image_uri, NULL);
1327 g_free (image_uri);
1328 image_uri = tmp;
1329 }
1330
1331 g_assert_no_error (error);
1332 g_assert_nonnull (image_uri);
1333
1334 return image_uri;
1335 }
1336
1337 static void
test_utils_insert_signature_done_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)1338 test_utils_insert_signature_done_cb (GObject *source_object,
1339 GAsyncResult *result,
1340 gpointer user_data)
1341 {
1342 gpointer async_data = user_data;
1343 WebKitJavascriptResult *js_result;
1344 JSCException *exception;
1345 JSCValue *js_value;
1346 GError *error = NULL;
1347
1348 g_return_if_fail (async_data != NULL);
1349
1350 js_result = webkit_web_view_run_javascript_finish (WEBKIT_WEB_VIEW (source_object), result, &error);
1351
1352 g_assert_no_error (error);
1353 g_clear_error (&error);
1354
1355 g_assert_nonnull (js_result);
1356
1357 js_value = webkit_javascript_result_get_js_value (js_result);
1358 g_assert_nonnull (js_value);
1359
1360 exception = jsc_context_get_exception (jsc_value_get_context (js_value));
1361
1362 if (exception) {
1363 g_warning ("Failed to call EvoEditor.InsertSignature: %s", jsc_exception_get_message (exception));
1364 jsc_context_clear_exception (jsc_value_get_context (js_value));
1365 }
1366
1367 webkit_javascript_result_unref (js_result);
1368
1369 test_utils_async_call_finish (async_data);
1370 }
1371
1372 void
test_utils_insert_signature(TestFixture * fixture,const gchar * content,gboolean is_html,const gchar * uid,gboolean start_bottom,gboolean top_signature,gboolean add_delimiter)1373 test_utils_insert_signature (TestFixture *fixture,
1374 const gchar *content,
1375 gboolean is_html,
1376 const gchar *uid,
1377 gboolean start_bottom,
1378 gboolean top_signature,
1379 gboolean add_delimiter)
1380 {
1381 EContentEditor *cnt_editor;
1382 gchar *script;
1383 gpointer async_data;
1384
1385 g_return_if_fail (fixture != NULL);
1386 g_return_if_fail (E_IS_HTML_EDITOR (fixture->editor));
1387 g_return_if_fail (content != NULL);
1388 g_return_if_fail (uid != NULL);
1389
1390 cnt_editor = e_html_editor_get_content_editor (fixture->editor);
1391 g_return_if_fail (cnt_editor != NULL);
1392 g_return_if_fail (WEBKIT_IS_WEB_VIEW (cnt_editor));
1393
1394 script = e_web_view_jsc_printf_script (
1395 "EvoEditor.InsertSignature(%s, %x, false, %s, false, false, true, %x, %x, %x);",
1396 content, is_html, uid, start_bottom, top_signature, add_delimiter);
1397
1398 async_data = test_utils_async_call_prepare ();
1399
1400 webkit_web_view_run_javascript (WEBKIT_WEB_VIEW (cnt_editor), script, NULL,
1401 test_utils_insert_signature_done_cb, async_data);
1402
1403 test_utils_async_call_wait (async_data, 10);
1404
1405 g_free (script);
1406 }
1407