summaryrefslogtreecommitdiffstats
path: root/uitest
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:54:39 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:54:39 +0000
commit267c6f2ac71f92999e969232431ba04678e7437e (patch)
tree358c9467650e1d0a1d7227a21dac2e3d08b622b2 /uitest
parentInitial commit. (diff)
downloadlibreoffice-267c6f2ac71f92999e969232431ba04678e7437e.tar.xz
libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.zip
Adding upstream version 4:24.2.0.upstream/4%24.2.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'uitest')
-rw-r--r--uitest/Makefile14
-rw-r--r--uitest/Module_uitest.mk17
-rw-r--r--uitest/README.md3
-rw-r--r--uitest/UITest_demo_ui.mk20
-rw-r--r--uitest/UITest_impress_demo.mk16
-rw-r--r--uitest/UITest_math_demo.mk20
-rw-r--r--uitest/__init__.py0
-rw-r--r--uitest/demo_ui/char_dialog.py26
-rw-r--r--uitest/demo_ui/checkbox.py24
-rw-r--r--uitest/demo_ui/combobox.py26
-rw-r--r--uitest/demo_ui/command_with_parameters.py25
-rw-r--r--uitest/demo_ui/data/test.odsbin0 -> 7598 bytes
-rw-r--r--uitest/demo_ui/data/test2.odsbin0 -> 7339 bytes
-rw-r--r--uitest/demo_ui/edit.py47
-rw-r--r--uitest/demo_ui/gridwin.py50
-rw-r--r--uitest/demo_ui/handle_multiple_files.py47
-rw-r--r--uitest/demo_ui/hierarchy.py31
-rw-r--r--uitest/demo_ui/listbox.py30
-rw-r--r--uitest/demo_ui/radiobutton.py24
-rw-r--r--uitest/demo_ui/spinfield.py63
-rw-r--r--uitest/demo_ui/tabcontrol.py36
-rw-r--r--uitest/demo_ui/tabdialog.py30
-rw-r--r--uitest/demo_ui/treelist.py44
-rw-r--r--uitest/impress_tests/backgrounds.py144
-rw-r--r--uitest/impress_tests/drawinglayer.py133
-rw-r--r--uitest/impress_tests/layouts.py45
-rw-r--r--uitest/impress_tests/start.py54
-rw-r--r--uitest/libreoffice/calc/conditional_format.py17
-rw-r--r--uitest/libreoffice/calc/csv_dialog.py67
-rw-r--r--uitest/libreoffice/calc/document.py63
-rw-r--r--uitest/libreoffice/calc/paste_special.py30
-rw-r--r--uitest/libreoffice/connection.py201
-rw-r--r--uitest/libreoffice/linguistic/linguservice.py27
-rw-r--r--uitest/libreoffice/uno/eventlistener.py55
-rw-r--r--uitest/libreoffice/uno/propertyvalue.py38
-rwxr-xr-xuitest/loginterpreter.py209
-rw-r--r--uitest/math_tests/data/tdf128610.fodt34
-rw-r--r--uitest/math_tests/start.py67
-rw-r--r--uitest/math_tests/tdf128610.py32
-rw-r--r--uitest/math_tests/tdf147755.py32
-rw-r--r--uitest/packaging/Makefile16
-rw-r--r--uitest/packaging/README.md3
-rw-r--r--uitest/packaging/setup.cfg0
-rw-r--r--uitest/packaging/setup.py35
-rw-r--r--uitest/test_main.py150
-rw-r--r--uitest/ui_logger_dsl/General_commands.tx27
-rw-r--r--uitest/ui_logger_dsl/Special_commands.tx238
-rw-r--r--uitest/ui_logger_dsl/UI_Object_commands.tx90
-rw-r--r--uitest/ui_logger_dsl/dialog_commands.tx24
-rw-r--r--uitest/ui_logger_dsl/dsl_core.py1092
-rw-r--r--uitest/ui_logger_dsl/example.ul34
-rw-r--r--uitest/ui_logger_dsl/starter_commands.tx10
-rw-r--r--uitest/ui_logger_dsl/type_options.tx9
-rw-r--r--uitest/ui_logger_dsl/ui_logger_dsl_grammar.tx29
-rw-r--r--uitest/ui_logger_dsl/uno_commands.tx20
-rw-r--r--uitest/uitest/bisecting.py14
-rw-r--r--uitest/uitest/framework.py79
-rw-r--r--uitest/uitest/path.py31
-rw-r--r--uitest/uitest/test.py260
-rw-r--r--uitest/uitest/uihelper/__init__.py0
-rw-r--r--uitest/uitest/uihelper/calc.py16
-rw-r--r--uitest/uitest/uihelper/common.py68
-rw-r--r--uitest/uitest/uihelper/keyboard.py13
-rw-r--r--uitest/uitest/uihelper/testDialog.py32
64 files changed, 4131 insertions, 0 deletions
diff --git a/uitest/Makefile b/uitest/Makefile
new file mode 100644
index 0000000000..0997e62848
--- /dev/null
+++ b/uitest/Makefile
@@ -0,0 +1,14 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+module_directory:=$(dir $(realpath $(firstword $(MAKEFILE_LIST))))
+
+include $(module_directory)/../solenv/gbuild/partial_build.mk
+
+# vim: set noet sw=4 ts=4:
diff --git a/uitest/Module_uitest.mk b/uitest/Module_uitest.mk
new file mode 100644
index 0000000000..0ea8a94cfe
--- /dev/null
+++ b/uitest/Module_uitest.mk
@@ -0,0 +1,17 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_Module_Module,uitest))
+
+$(eval $(call gb_Module_add_uicheck_targets,uitest,\
+ UITest_impress_demo \
+ UITest_demo_ui \
+ UITest_math_demo \
+))
diff --git a/uitest/README.md b/uitest/README.md
new file mode 100644
index 0000000000..8c872bed6f
--- /dev/null
+++ b/uitest/README.md
@@ -0,0 +1,3 @@
+# UI Testing Framework
+
+The code for the UI testing framework and the UI tests.
diff --git a/uitest/UITest_demo_ui.mk b/uitest/UITest_demo_ui.mk
new file mode 100644
index 0000000000..bb67e21b8b
--- /dev/null
+++ b/uitest/UITest_demo_ui.mk
@@ -0,0 +1,20 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_UITest_UITest,demo_ui))
+
+$(eval $(call gb_UITest_add_modules,demo_ui,$(SRCDIR)/uitest,\
+ demo_ui/ \
+))
+
+$(eval $(call gb_UITest_set_defs,demo_ui, \
+ TDOC="$(SRCDIR)/uitest/demo_ui/data" \
+))
+
+# vim: set noet sw=4 ts=4:
diff --git a/uitest/UITest_impress_demo.mk b/uitest/UITest_impress_demo.mk
new file mode 100644
index 0000000000..5b822fe69e
--- /dev/null
+++ b/uitest/UITest_impress_demo.mk
@@ -0,0 +1,16 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_UITest_UITest,impress_demo))
+
+$(eval $(call gb_UITest_add_modules,impress_demo,$(SRCDIR)/uitest,\
+ impress_tests/ \
+))
+
+# vim: set noet sw=4 ts=4:
diff --git a/uitest/UITest_math_demo.mk b/uitest/UITest_math_demo.mk
new file mode 100644
index 0000000000..b66cb87f68
--- /dev/null
+++ b/uitest/UITest_math_demo.mk
@@ -0,0 +1,20 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_UITest_UITest,math_demo))
+
+$(eval $(call gb_UITest_add_modules,math_demo,$(SRCDIR)/uitest,\
+ math_tests/ \
+))
+
+$(eval $(call gb_UITest_set_defs,math_demo, \
+ TDOC="$(SRCDIR)/uitest/math_tests/data" \
+))
+
+# vim: set noet sw=4 ts=4:
diff --git a/uitest/__init__.py b/uitest/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/uitest/__init__.py
diff --git a/uitest/demo_ui/char_dialog.py b/uitest/demo_ui/char_dialog.py
new file mode 100644
index 0000000000..9475f3b16e
--- /dev/null
+++ b/uitest/demo_ui/char_dialog.py
@@ -0,0 +1,26 @@
+# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+from libreoffice.uno.propertyvalue import mkPropertyValues
+from uitest.framework import UITestCase
+
+class CharDialogText(UITestCase):
+
+ def test_select_char(self):
+ with self.ui_test.create_doc_in_start_center("calc"):
+
+ with self.ui_test.execute_dialog_through_command(".uno:InsertSymbol", close_button="cancel") as xCharDialog:
+
+ xCharSet = xCharDialog.getChild("showcharset")
+
+ xCharSet.executeAction("SELECT", mkPropertyValues({"COLUMN": "2", "ROW": "2"}))
+
+
+
+# vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/uitest/demo_ui/checkbox.py b/uitest/demo_ui/checkbox.py
new file mode 100644
index 0000000000..7c83d71f0a
--- /dev/null
+++ b/uitest/demo_ui/checkbox.py
@@ -0,0 +1,24 @@
+# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+from uitest.framework import UITestCase
+
+class CheckBoxTest(UITestCase):
+
+ def test_toggle_checkbox(self):
+
+ with self.ui_test.create_doc_in_start_center("calc"):
+
+ with self.ui_test.execute_dialog_through_command(".uno:FormatCellDialog") as xCellsDlg:
+ xNegativeNumRedCB = xCellsDlg.getChild("negnumred")
+ xNegativeNumRedCB.executeAction("CLICK",tuple())
+
+
+
+# vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/uitest/demo_ui/combobox.py b/uitest/demo_ui/combobox.py
new file mode 100644
index 0000000000..1ec4b30d6f
--- /dev/null
+++ b/uitest/demo_ui/combobox.py
@@ -0,0 +1,26 @@
+# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+from uitest.framework import UITestCase
+from uitest.uihelper.common import select_pos
+
+class ComboBoxTest(UITestCase):
+
+ def test_select_entry_pos(self):
+
+ with self.ui_test.create_doc_in_start_center("calc"):
+
+ with self.ui_test.execute_modeless_dialog_through_command(".uno:AddName", close_button="cancel") as xAddNameDlg:
+
+ scopeCB = xAddNameDlg.getChild("scope")
+ select_pos(scopeCB, "1")
+
+
+
+# vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/uitest/demo_ui/command_with_parameters.py b/uitest/demo_ui/command_with_parameters.py
new file mode 100644
index 0000000000..91d3acec9c
--- /dev/null
+++ b/uitest/demo_ui/command_with_parameters.py
@@ -0,0 +1,25 @@
+# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+from uitest.framework import UITestCase
+from uitest.uihelper.common import type_text
+from libreoffice.uno.propertyvalue import mkPropertyValues
+
+class CommandWithParametersTest(UITestCase):
+
+ def test_text_color_change(self):
+
+ with self.ui_test.create_doc_in_start_center("writer"):
+
+ self.xUITest.executeCommandWithParameters(".uno:Color",
+ mkPropertyValues({"Color": 16776960}))
+ xWriterEdit = self.xUITest.getTopFocusWindow().getChild("writer_edit")
+ type_text(xWriterEdit, "LibreOffice")
+
+
+# vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/uitest/demo_ui/data/test.ods b/uitest/demo_ui/data/test.ods
new file mode 100644
index 0000000000..571291d265
--- /dev/null
+++ b/uitest/demo_ui/data/test.ods
Binary files differ
diff --git a/uitest/demo_ui/data/test2.ods b/uitest/demo_ui/data/test2.ods
new file mode 100644
index 0000000000..550115cb9a
--- /dev/null
+++ b/uitest/demo_ui/data/test2.ods
Binary files differ
diff --git a/uitest/demo_ui/edit.py b/uitest/demo_ui/edit.py
new file mode 100644
index 0000000000..7a0fbb2b8a
--- /dev/null
+++ b/uitest/demo_ui/edit.py
@@ -0,0 +1,47 @@
+# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+from libreoffice.uno.propertyvalue import mkPropertyValues
+
+from uitest.framework import UITestCase
+from uitest.uihelper.common import type_text, get_state_as_dict, select_text
+
+class EditTest(UITestCase):
+
+ def test_type_text(self):
+
+ with self.ui_test.create_doc_in_start_center("calc"):
+
+ with self.ui_test.execute_modeless_dialog_through_command(".uno:AddName", close_button="cancel") as xAddNameDlg:
+
+ xEdit = xAddNameDlg.getChild("edit")
+
+ type_text(xEdit, "simpleRangeName")
+
+
+
+ def test_select_text(self):
+
+ with self.ui_test.create_doc_in_start_center("calc"):
+
+ with self.ui_test.execute_modeless_dialog_through_command(".uno:AddName", close_button="cancel") as xAddNameDlg:
+
+ xEdit = xAddNameDlg.getChild("edit")
+
+ type_text(xEdit, "simpleRangeName")
+ xEdit.executeAction("SELECT", mkPropertyValues({"FROM": "2", "TO": "9"}))
+ type_text(xEdit, "otherChars")
+ self.assertEqual("siotherCharsgeName", get_state_as_dict(xEdit)["Text"])
+
+ select_text(xEdit, from_pos="2", to="12")
+ self.assertEqual("otherChars", get_state_as_dict(xEdit)["SelectedText"])
+
+
+
+# vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/uitest/demo_ui/gridwin.py b/uitest/demo_ui/gridwin.py
new file mode 100644
index 0000000000..272cdffc10
--- /dev/null
+++ b/uitest/demo_ui/gridwin.py
@@ -0,0 +1,50 @@
+# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+from libreoffice.uno.propertyvalue import mkPropertyValues
+
+from uitest.framework import UITestCase
+
+class GridWinTest(UITestCase):
+
+ def test_select_cell(self):
+
+ with self.ui_test.create_doc_in_start_center("calc"):
+ xCalcDoc = self.xUITest.getTopFocusWindow()
+ xGridWindow = xCalcDoc.getChild("grid_window")
+
+ selectProps = mkPropertyValues({"CELL": "B10"})
+ xGridWindow.executeAction("SELECT", selectProps)
+
+
+ def test_select_range(self):
+
+ with self.ui_test.create_doc_in_start_center("calc"):
+ xCalcDoc = self.xUITest.getTopFocusWindow()
+ xGridWindow = xCalcDoc.getChild("grid_window")
+
+ selectProps = mkPropertyValues({"RANGE": "B10:C20"})
+ xGridWindow.executeAction("SELECT", selectProps)
+
+
+ def test_extend_range(self):
+
+ with self.ui_test.create_doc_in_start_center("calc"):
+ xTopWindow = self.xUITest.getTopFocusWindow()
+
+ xGridWindow = xTopWindow.getChild("grid_window")
+
+ selectProps = mkPropertyValues({"RANGE": "B10:C20"})
+ xGridWindow.executeAction("SELECT", selectProps)
+
+ select2Props = mkPropertyValues({"RANGE": "D3:F5", "EXTEND": "true"})
+ xGridWindow.executeAction("SELECT", select2Props)
+
+
+# vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/uitest/demo_ui/handle_multiple_files.py b/uitest/demo_ui/handle_multiple_files.py
new file mode 100644
index 0000000000..a15b92deec
--- /dev/null
+++ b/uitest/demo_ui/handle_multiple_files.py
@@ -0,0 +1,47 @@
+# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+from uitest.framework import UITestCase
+from uitest.uihelper.common import get_url_for_data_file
+import time
+
+class HandleFiles(UITestCase):
+
+ def test_load_file(self):
+
+ with self.ui_test.load_file(get_url_for_data_file("test.ods")):
+
+ with self.ui_test.load_file(get_url_for_data_file("test2.ods")):
+
+ frames = self.ui_test.get_frames()
+ self.assertEqual(len(frames), 2)
+
+ frames = self.ui_test.get_frames()
+ self.assertEqual(len(frames), 1)
+
+ # this is currently still necessary as otherwise
+ # the command is not forwarded to the correct frame
+ # TODO: provide an additional event that we can use
+ # and get rid of the sleep
+ time.sleep(1)
+
+ def test_select_frame(self):
+ with self.ui_test.load_file(get_url_for_data_file("test.ods")):
+
+ with self.ui_test.load_file(get_url_for_data_file("test2.ods")):
+ frames = self.ui_test.get_frames()
+ self.assertEqual(len(frames), 2)
+ frames[0].activate()
+
+ frames = self.ui_test.get_frames()
+ self.assertEqual(len(frames), 1)
+
+ self.assertTrue(frames[0].getTitle().startswith("test2.ods"))
+
+# vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/uitest/demo_ui/hierarchy.py b/uitest/demo_ui/hierarchy.py
new file mode 100644
index 0000000000..a447de563e
--- /dev/null
+++ b/uitest/demo_ui/hierarchy.py
@@ -0,0 +1,31 @@
+# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+from uitest.framework import UITestCase
+
+import json
+
+class CheckBoxTest(UITestCase):
+
+ def test_get_json(self):
+
+ with self.ui_test.create_doc_in_start_center("calc"):
+
+ with self.ui_test.execute_dialog_through_command(".uno:About", close_button="btnClose") as xAboutDlg:
+
+
+ json_string = xAboutDlg.getHierarchy()
+ print(json_string)
+ json_content = json.loads(json_string)
+ print(json_content)
+ print(json.dumps(json_content, indent=4))
+
+
+
+# vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/uitest/demo_ui/listbox.py b/uitest/demo_ui/listbox.py
new file mode 100644
index 0000000000..b59a7b0a0a
--- /dev/null
+++ b/uitest/demo_ui/listbox.py
@@ -0,0 +1,30 @@
+# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+from uitest.framework import UITestCase
+
+class ListBoxTest(UITestCase):
+
+ def test_select_entry_pos(self):
+
+ with self.ui_test.create_doc_in_start_center("calc"):
+
+ with self.ui_test.execute_dialog_through_command(".uno:FormatCellDialog"):
+ pass
+
+
+ def test_select_entry_text(self):
+
+ with self.ui_test.create_doc_in_start_center("calc"):
+
+ with self.ui_test.execute_dialog_through_command(".uno:FormatCellDialog"):
+ pass
+
+
+# vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/uitest/demo_ui/radiobutton.py b/uitest/demo_ui/radiobutton.py
new file mode 100644
index 0000000000..268696e56e
--- /dev/null
+++ b/uitest/demo_ui/radiobutton.py
@@ -0,0 +1,24 @@
+# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+from uitest.framework import UITestCase
+
+class RadioButtonTest(UITestCase):
+
+ def test_toggle_radiobutton(self):
+
+ with self.ui_test.create_doc_in_start_center("calc"):
+
+ with self.ui_test.execute_dialog_through_command(".uno:FormatCellDialog") as xCellsDlg:
+ xNegativeNumRedCB = xCellsDlg.getChild("negnumred")
+ xNegativeNumRedCB.executeAction("CLICK",tuple())
+
+
+
+# vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/uitest/demo_ui/spinfield.py b/uitest/demo_ui/spinfield.py
new file mode 100644
index 0000000000..6b6a3bce0f
--- /dev/null
+++ b/uitest/demo_ui/spinfield.py
@@ -0,0 +1,63 @@
+# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+from uitest.framework import UITestCase
+from uitest.uihelper.common import get_state_as_dict, type_text
+
+class SpinFieldTest(UITestCase):
+
+ def test_up(self):
+
+ with self.ui_test.create_doc_in_start_center("calc"):
+
+ with self.ui_test.execute_dialog_through_command(".uno:FormatCellDialog") as xCellsDlg:
+
+ xDecimalPlaces = xCellsDlg.getChild("leadzerosed")
+ xDecimalPlaces.executeAction("UP", tuple())
+
+ decimal_places_state = get_state_as_dict(xDecimalPlaces)
+ assert(decimal_places_state["Text"] == "2")
+
+
+
+ def test_down(self):
+
+ with self.ui_test.create_doc_in_start_center("calc"):
+
+ with self.ui_test.execute_dialog_through_command(".uno:FormatCellDialog") as xCellsDlg:
+
+ xDecimalPlaces = xCellsDlg.getChild("leadzerosed")
+ xDecimalPlaces.executeAction("UP", tuple())
+ xDecimalPlaces.executeAction("UP", tuple())
+
+ decimal_places_state = get_state_as_dict(xDecimalPlaces)
+ assert(decimal_places_state["Text"] == "3")
+
+ xDecimalPlaces.executeAction("DOWN", tuple())
+
+ decimal_places_state = get_state_as_dict(xDecimalPlaces)
+ assert(decimal_places_state["Text"] == "2")
+
+
+
+ def test_text(self):
+
+ with self.ui_test.create_doc_in_start_center("calc"):
+
+ with self.ui_test.execute_dialog_through_command(".uno:FormatCellDialog") as xCellsDlg:
+
+ xDecimalPlaces = xCellsDlg.getChild("leadzerosed")
+ type_text(xDecimalPlaces, "4")
+
+ decimal_places_state = get_state_as_dict(xDecimalPlaces)
+ assert(decimal_places_state["Text"] == "41")
+
+
+
+# vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/uitest/demo_ui/tabcontrol.py b/uitest/demo_ui/tabcontrol.py
new file mode 100644
index 0000000000..5a1809c98a
--- /dev/null
+++ b/uitest/demo_ui/tabcontrol.py
@@ -0,0 +1,36 @@
+# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+from libreoffice.uno.propertyvalue import mkPropertyValues
+
+from uitest.uihelper.calc import enter_text_to_cell
+from uitest.uihelper.common import select_pos
+
+from uitest.framework import UITestCase
+
+class TabControlTest(UITestCase):
+
+ def test_select_pos(self):
+
+ with self.ui_test.create_doc_in_start_center("calc"):
+
+ xCalcDoc = self.xUITest.getTopFocusWindow()
+ xGridWindow = xCalcDoc.getChild("grid_window")
+ enter_text_to_cell(xGridWindow, "B2", "=2+3+4")
+ xGridWindow.executeAction("SELECT", mkPropertyValues({"CELL": "B2"}))
+
+ with self.ui_test.execute_modeless_dialog_through_command(".uno:FunctionDialog", close_button="cancel") as xFunctionDlg:
+
+
+ xTabs = xFunctionDlg.getChild("tabcontrol")
+ select_pos(xTabs, "1")
+
+
+
+# vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/uitest/demo_ui/tabdialog.py b/uitest/demo_ui/tabdialog.py
new file mode 100644
index 0000000000..6ed5bcc069
--- /dev/null
+++ b/uitest/demo_ui/tabdialog.py
@@ -0,0 +1,30 @@
+# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+from uitest.framework import UITestCase
+
+class TabDialogTest(UITestCase):
+
+ def test_select_tab_page_pos(self):
+
+ with self.ui_test.create_doc_in_start_center("calc"):
+
+ with self.ui_test.execute_dialog_through_command(".uno:FormatCellDialog"):
+ pass
+
+
+ def test_select_tab_page_name(self):
+
+ with self.ui_test.create_doc_in_start_center("calc"):
+
+ with self.ui_test.execute_dialog_through_command(".uno:FormatCellDialog"):
+ pass
+
+
+# vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/uitest/demo_ui/treelist.py b/uitest/demo_ui/treelist.py
new file mode 100644
index 0000000000..214fc4b08e
--- /dev/null
+++ b/uitest/demo_ui/treelist.py
@@ -0,0 +1,44 @@
+# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+from libreoffice.uno.propertyvalue import mkPropertyValues
+from uitest.uihelper.calc import enter_text_to_cell
+
+from uitest.uihelper.common import select_pos
+
+from uitest.framework import UITestCase
+
+class TreeListTest(UITestCase):
+
+ def test_expand(self):
+
+ with self.ui_test.create_doc_in_start_center("calc"):
+
+ xCalcDoc = self.xUITest.getTopFocusWindow()
+ xGridWindow = xCalcDoc.getChild("grid_window")
+ enter_text_to_cell(xGridWindow, "B2", "=2+3+4")
+ xGridWindow.executeAction("SELECT", mkPropertyValues({"CELL": "B2"}))
+
+ with self.ui_test.execute_modeless_dialog_through_command(".uno:FunctionDialog", close_button="cancel") as xFunctionDlg:
+
+
+ xTabs = xFunctionDlg.getChild("tabcontrol")
+ select_pos(xTabs, "1")
+
+ xTreelist = xTabs.getChild("struct")
+
+ xTreeEntry = xTreelist.getChild('0')
+
+ xTreeEntry.executeAction("COLLAPSE", tuple())
+
+ xTreeEntry.executeAction("EXPAND", tuple())
+
+
+
+# vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/uitest/impress_tests/backgrounds.py b/uitest/impress_tests/backgrounds.py
new file mode 100644
index 0000000000..e427995bfa
--- /dev/null
+++ b/uitest/impress_tests/backgrounds.py
@@ -0,0 +1,144 @@
+# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+from uitest.framework import UITestCase
+from uitest.uihelper.common import select_pos
+from com.sun.star.awt.GradientStyle import LINEAR
+from com.sun.star.drawing.HatchStyle import SINGLE
+from com.sun.star.drawing.BitmapMode import REPEAT
+from com.sun.star.drawing.RectanglePoint import MIDDLE_MIDDLE
+
+class ImpressBackgrounds(UITestCase):
+
+ def checkDefaultBackground(self, btn):
+ document = self.ui_test.get_component()
+ if btn == 'btnnone':
+ self.assertEqual(document.DrawPages.getByIndex(0).Background, None)
+ elif btn == 'btncolor':
+ self.assertEqual(
+ hex(document.DrawPages.getByIndex(0).Background.FillColor), '0x729fcf')
+ self.assertEqual(
+ hex(document.DrawPages.getByIndex(0).Background.FillColor), '0x729fcf')
+ elif btn == 'btngradient':
+ self.assertEqual(
+ document.DrawPages.getByIndex(0).Background.FillGradient.Style, LINEAR)
+ self.assertEqual(
+ hex(document.DrawPages.getByIndex(0).Background.FillGradient.StartColor), '0xdde8cb')
+ self.assertEqual(
+ document.DrawPages.getByIndex(0).Background.FillGradient.Angle, 300)
+ self.assertEqual(
+ document.DrawPages.getByIndex(0).Background.FillGradient.Border, 0)
+ self.assertEqual(
+ document.DrawPages.getByIndex(0).Background.FillGradient.XOffset, 0)
+ self.assertEqual(
+ document.DrawPages.getByIndex(0).Background.FillGradient.YOffset, 0)
+ self.assertEqual(
+ document.DrawPages.getByIndex(0).Background.FillGradient.StartIntensity, 100)
+ self.assertEqual(
+ document.DrawPages.getByIndex(0).Background.FillGradient.EndIntensity, 100)
+ self.assertEqual(
+ document.DrawPages.getByIndex(0).Background.FillGradientName, 'Pastel Bouquet')
+ elif btn == 'btnhatch':
+ self.assertEqual(
+ document.DrawPages.getByIndex(0).Background.FillHatch.Style, SINGLE )
+ self.assertEqual(
+ document.DrawPages.getByIndex(0).Background.FillHatch.Color, 0)
+ self.assertEqual(
+ document.DrawPages.getByIndex(0).Background.FillHatch.Distance, 102)
+ self.assertEqual(
+ document.DrawPages.getByIndex(0).Background.FillHatch.Angle, 0)
+ self.assertEqual(
+ document.DrawPages.getByIndex(0).Background.FillHatchName, 'Black 0 Degrees')
+ elif btn == 'btnbitmap':
+ self.assertEqual(
+ document.DrawPages.getByIndex(0).Background.FillBitmapMode, REPEAT)
+ self.assertEqual(
+ document.DrawPages.getByIndex(0).Background.FillBitmapPositionOffsetX, 0)
+ self.assertEqual(
+ document.DrawPages.getByIndex(0).Background.FillBitmapPositionOffsetY, 0)
+ self.assertEqual(
+ document.DrawPages.getByIndex(0).Background.FillBitmapRectanglePoint, MIDDLE_MIDDLE)
+ self.assertEqual(
+ document.DrawPages.getByIndex(0).Background.FillBitmapStretch, False)
+ self.assertEqual(
+ document.DrawPages.getByIndex(0).Background.FillBitmapTile, True)
+ self.assertEqual(
+ document.DrawPages.getByIndex(0).Background.FillBitmapOffsetX, 0)
+ self.assertEqual(
+ document.DrawPages.getByIndex(0).Background.FillBitmapOffsetY, 0)
+ self.assertEqual(
+ document.DrawPages.getByIndex(0).Background.FillBitmapLogicalSize, True)
+ self.assertEqual(
+ document.DrawPages.getByIndex(0).Background.FillBitmapSizeX, 2540)
+ self.assertEqual(
+ document.DrawPages.getByIndex(0).Background.FillBitmapSizeY, 2540)
+ self.assertEqual(document.DrawPages.getByIndex(0).Background.FillBitmapName, 'Painted White')
+ elif btn == 'btnpattern':
+ self.assertEqual(
+ document.DrawPages.getByIndex(0).Background.FillBitmapMode, REPEAT)
+ self.assertEqual(
+ document.DrawPages.getByIndex(0).Background.FillBitmapPositionOffsetX, 0)
+ self.assertEqual(
+ document.DrawPages.getByIndex(0).Background.FillBitmapPositionOffsetY, 0)
+ self.assertEqual(
+ document.DrawPages.getByIndex(0).Background.FillBitmapRectanglePoint, MIDDLE_MIDDLE)
+ self.assertEqual(
+ document.DrawPages.getByIndex(0).Background.FillBitmapStretch, True)
+ self.assertEqual(
+ document.DrawPages.getByIndex(0).Background.FillBitmapTile, True)
+ self.assertEqual(
+ document.DrawPages.getByIndex(0).Background.FillBitmapOffsetX, 0)
+ self.assertEqual(
+ document.DrawPages.getByIndex(0).Background.FillBitmapOffsetY, 0)
+ self.assertEqual(
+ document.DrawPages.getByIndex(0).Background.FillBitmapLogicalSize, True)
+ self.assertEqual(
+ document.DrawPages.getByIndex(0).Background.FillBitmapSizeX, 0)
+ self.assertEqual(
+ document.DrawPages.getByIndex(0).Background.FillBitmapSizeY, 0)
+ self.assertEqual(
+ document.DrawPages.getByIndex(0).Background.FillBitmapName, '5 Percent')
+
+
+ def test_background_dialog(self):
+
+ with self.ui_test.create_doc_in_start_center("impress"):
+
+ xTemplateDlg = self.xUITest.getTopFocusWindow()
+ xCancelBtn = xTemplateDlg.getChild("close")
+ self.ui_test.close_dialog_through_button(xCancelBtn)
+
+ buttons = ['btnbitmap', 'btncolor', 'btngradient', 'btnhatch', 'btnpattern']
+ for index, button in enumerate(buttons):
+ with self.ui_test.execute_dialog_through_command(".uno:PageSetup") as xPageSetupDlg:
+
+ tabcontrol = xPageSetupDlg.getChild("tabcontrol")
+ select_pos(tabcontrol, "1")
+
+ xBtn = xPageSetupDlg.getChild(button)
+ xBtn.executeAction("CLICK", tuple())
+
+ # tdf#100024: Without the fix in place, this test would have crashed here
+ # changing the background to bitmap
+
+ self.checkDefaultBackground(button)
+
+ with self.ui_test.execute_dialog_through_command(".uno:PageSetup") as xPageSetupDlg:
+
+ tabcontrol = xPageSetupDlg.getChild("tabcontrol")
+ select_pos(tabcontrol, "1")
+
+ xBtn = xPageSetupDlg.getChild('btnnone')
+ xBtn.executeAction("CLICK", tuple())
+
+
+ self.checkDefaultBackground('btnnone')
+
+
+# vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/uitest/impress_tests/drawinglayer.py b/uitest/impress_tests/drawinglayer.py
new file mode 100644
index 0000000000..ca9d84bb16
--- /dev/null
+++ b/uitest/impress_tests/drawinglayer.py
@@ -0,0 +1,133 @@
+# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+from uitest.uihelper.common import get_state_as_dict
+from libreoffice.uno.propertyvalue import mkPropertyValues
+from uitest.uihelper.common import change_measurement_unit
+from uitest.framework import UITestCase
+
+class ImpressDrawinglayerTest(UITestCase):
+
+ def test_move_object(self):
+ with self.ui_test.create_doc_in_start_center("impress") as document:
+
+ xTemplateDlg = self.xUITest.getTopFocusWindow()
+ xCancelBtn = xTemplateDlg.getChild("close")
+ self.ui_test.close_dialog_through_button(xCancelBtn)
+
+ with change_measurement_unit(self, 'Centimeter'):
+
+ xImpressDoc = self.xUITest.getTopFocusWindow()
+
+ self.assertIsNone(document.CurrentSelection)
+
+ xEditWin = xImpressDoc.getChild("impress_win")
+ xEditWin.executeAction("SELECT", mkPropertyValues({"OBJECT":"Unnamed Drawinglayer object 1"}))
+ self.assertEqual("com.sun.star.drawing.SvxShapeCollection", document.CurrentSelection.getImplementationName())
+
+ with self.ui_test.execute_dialog_through_command(".uno:Size") as xDialog:
+ self.assertEqual('25.2', get_state_as_dict(xDialog.getChild('MTR_FLD_WIDTH'))['Value'])
+ self.assertEqual('9.13', get_state_as_dict(xDialog.getChild('MTR_FLD_HEIGHT'))['Value'])
+ self.assertEqual('1.4', get_state_as_dict(xDialog.getChild('MTR_FLD_POS_X'))['Value'])
+ self.assertEqual('3.69', get_state_as_dict(xDialog.getChild('MTR_FLD_POS_Y'))['Value'])
+ self.assertEqual('0', get_state_as_dict(xDialog.getChild('NF_ANGLE'))['Value'])
+
+ xDrawinglayerObject = xEditWin.getChild("Unnamed Drawinglayer object 1")
+ xDrawinglayerObject.executeAction("MOVE", mkPropertyValues({"X": "1000", "Y":"1000"}))
+
+ with self.ui_test.execute_dialog_through_command(".uno:Size") as xDialog:
+ self.assertEqual('25.2', get_state_as_dict(xDialog.getChild('MTR_FLD_WIDTH'))['Value'])
+ self.assertEqual('9.13', get_state_as_dict(xDialog.getChild('MTR_FLD_HEIGHT'))['Value'])
+ self.assertEqual('2.4', get_state_as_dict(xDialog.getChild('MTR_FLD_POS_X'))['Value'])
+ self.assertEqual('4.69', get_state_as_dict(xDialog.getChild('MTR_FLD_POS_Y'))['Value'])
+ self.assertEqual('0', get_state_as_dict(xDialog.getChild('NF_ANGLE'))['Value'])
+
+ self.assertEqual("com.sun.star.drawing.SvxShapeCollection", document.CurrentSelection.getImplementationName())
+ xEditWin.executeAction("DESELECT", tuple())
+ self.assertIsNone(document.CurrentSelection)
+
+ def test_resize_object(self):
+ with self.ui_test.create_doc_in_start_center("impress") as document:
+
+ xTemplateDlg = self.xUITest.getTopFocusWindow()
+ xCancelBtn = xTemplateDlg.getChild("close")
+ self.ui_test.close_dialog_through_button(xCancelBtn)
+
+ with change_measurement_unit(self, 'Centimeter'):
+
+ xImpressDoc = self.xUITest.getTopFocusWindow()
+
+ self.assertIsNone(document.CurrentSelection)
+
+ xEditWin = xImpressDoc.getChild("impress_win")
+ xEditWin.executeAction("SELECT", mkPropertyValues({"OBJECT":"Unnamed Drawinglayer object 1"}))
+ self.assertEqual("com.sun.star.drawing.SvxShapeCollection", document.CurrentSelection.getImplementationName())
+
+ with self.ui_test.execute_dialog_through_command(".uno:Size") as xDialog:
+ self.assertEqual('25.2', get_state_as_dict(xDialog.getChild('MTR_FLD_WIDTH'))['Value'])
+ self.assertEqual('9.13', get_state_as_dict(xDialog.getChild('MTR_FLD_HEIGHT'))['Value'])
+ self.assertEqual('1.4', get_state_as_dict(xDialog.getChild('MTR_FLD_POS_X'))['Value'])
+ self.assertEqual('3.69', get_state_as_dict(xDialog.getChild('MTR_FLD_POS_Y'))['Value'])
+ self.assertEqual('0', get_state_as_dict(xDialog.getChild('NF_ANGLE'))['Value'])
+
+ xDrawinglayerObject = xEditWin.getChild("Unnamed Drawinglayer object 1")
+ xDrawinglayerObject.executeAction("RESIZE", mkPropertyValues({"X": "500", "Y":"4000", "FRAC_X": "0.5", "FRAC_Y": "0.5"}))
+
+ xEditWin.executeAction("SELECT", mkPropertyValues({"OBJECT":"Unnamed Drawinglayer object 1"}))
+ self.assertEqual("com.sun.star.drawing.SvxShapeCollection", document.CurrentSelection.getImplementationName())
+
+ with self.ui_test.execute_dialog_through_command(".uno:Size") as xDialog:
+ self.assertEqual('12.6', get_state_as_dict(xDialog.getChild('MTR_FLD_WIDTH'))['Value'])
+ self.assertEqual('4.57', get_state_as_dict(xDialog.getChild('MTR_FLD_HEIGHT'))['Value'])
+ self.assertEqual('0.95', get_state_as_dict(xDialog.getChild('MTR_FLD_POS_X'))['Value'])
+ self.assertEqual('3.84', get_state_as_dict(xDialog.getChild('MTR_FLD_POS_Y'))['Value'])
+ self.assertEqual('0', get_state_as_dict(xDialog.getChild('NF_ANGLE'))['Value'])
+
+ self.assertEqual("com.sun.star.drawing.SvxShapeCollection", document.CurrentSelection.getImplementationName())
+ xEditWin.executeAction("DESELECT", tuple())
+ self.assertIsNone(document.CurrentSelection)
+
+ def test_rotate_object(self):
+ with self.ui_test.create_doc_in_start_center("impress") as document:
+
+ xTemplateDlg = self.xUITest.getTopFocusWindow()
+ xCancelBtn = xTemplateDlg.getChild("close")
+ self.ui_test.close_dialog_through_button(xCancelBtn)
+
+ with change_measurement_unit(self, 'Centimeter'):
+ xImpressDoc = self.xUITest.getTopFocusWindow()
+
+ self.assertIsNone(document.CurrentSelection)
+
+ xEditWin = xImpressDoc.getChild("impress_win")
+ xEditWin.executeAction("SELECT", mkPropertyValues({"OBJECT":"Unnamed Drawinglayer object 1"}))
+ self.assertEqual("com.sun.star.drawing.SvxShapeCollection", document.CurrentSelection.getImplementationName())
+
+ with self.ui_test.execute_dialog_through_command(".uno:Size") as xDialog:
+ self.assertEqual('25.2', get_state_as_dict(xDialog.getChild('MTR_FLD_WIDTH'))['Value'])
+ self.assertEqual('9.13', get_state_as_dict(xDialog.getChild('MTR_FLD_HEIGHT'))['Value'])
+ self.assertEqual('1.4', get_state_as_dict(xDialog.getChild('MTR_FLD_POS_X'))['Value'])
+ self.assertEqual('3.69', get_state_as_dict(xDialog.getChild('MTR_FLD_POS_Y'))['Value'])
+ self.assertEqual('0', get_state_as_dict(xDialog.getChild('NF_ANGLE'))['Value'])
+
+ xDrawinglayerObject = xEditWin.getChild("Unnamed Drawinglayer object 1")
+ xDrawinglayerObject.executeAction("ROTATE", mkPropertyValues({"X": "500", "Y":"4000", "ANGLE": "3000"}))
+
+ xEditWin.executeAction("SELECT", mkPropertyValues({"OBJECT":"Unnamed Drawinglayer object 1"}))
+ self.assertEqual("com.sun.star.drawing.SvxShapeCollection", document.CurrentSelection.getImplementationName())
+
+ with self.ui_test.execute_dialog_through_command(".uno:Size") as xDialog:
+ self.assertEqual('30', get_state_as_dict(xDialog.getChild('NF_ANGLE'))['Value'])
+
+ self.assertEqual("com.sun.star.drawing.SvxShapeCollection", document.CurrentSelection.getImplementationName())
+ xEditWin.executeAction("DESELECT", tuple())
+ self.assertIsNone(document.CurrentSelection)
+
+
+# vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/uitest/impress_tests/layouts.py b/uitest/impress_tests/layouts.py
new file mode 100644
index 0000000000..22c75e504f
--- /dev/null
+++ b/uitest/impress_tests/layouts.py
@@ -0,0 +1,45 @@
+# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+from uitest.framework import UITestCase
+from libreoffice.uno.propertyvalue import mkPropertyValues
+
+class ImpressLayouts(UITestCase):
+
+ def test_impress_layouts(self):
+
+ with self.ui_test.create_doc_in_start_center("impress"):
+
+ xTemplateDlg = self.xUITest.getTopFocusWindow()
+ xCancelBtn = xTemplateDlg.getChild("close")
+ self.ui_test.close_dialog_through_button(xCancelBtn)
+
+ layouts= (".uno:AssignLayout?WhatLayout:long=20", ".uno:AssignLayout?WhatLayout:long=19",
+ ".uno:AssignLayout?WhatLayout:long=0", ".uno:AssignLayout?WhatLayout:long=1",
+ ".uno:AssignLayout?WhatLayout:long=32", ".uno:AssignLayout?WhatLayout:long=3",
+ ".uno:AssignLayout?WhatLayout:long=12", ".uno:AssignLayout?WhatLayout:long=15",
+ ".uno:AssignLayout?WhatLayout:long=14", ".uno:AssignLayout?WhatLayout:long=16",
+ ".uno:AssignLayout?WhatLayout:long=18", ".uno:AssignLayout?WhatLayout:long=34",
+ ".uno:AssignLayout?WhatLayout:long=28", ".uno:AssignLayout?WhatLayout:long=27",
+ ".uno:AssignLayout?WhatLayout:long=29", ".uno:AssignLayout?WhatLayout:long=30")
+
+ for i in layouts:
+ self.xUITest.executeCommand(i)
+
+ xImpressDoc = self.xUITest.getTopFocusWindow()
+
+ xEditWin = xImpressDoc.getChild("impress_win")
+
+ # There's a layout with 7 objects
+ for j in range(0,6):
+ xEditWin.executeAction("SELECT", mkPropertyValues({"OBJECT":"Unnamed Drawinglayer object " + str(j)}))
+ xEditWin.executeAction("DESELECT", tuple())
+
+
+# vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/uitest/impress_tests/start.py b/uitest/impress_tests/start.py
new file mode 100644
index 0000000000..cbf38b88b7
--- /dev/null
+++ b/uitest/impress_tests/start.py
@@ -0,0 +1,54 @@
+# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+from uitest.uihelper.common import get_state_as_dict
+from libreoffice.uno.propertyvalue import mkPropertyValues
+from uitest.framework import UITestCase
+
+class SimpleImpressTest(UITestCase):
+ def test_start_impress(self):
+
+ with self.ui_test.create_doc_in_start_center("impress"):
+
+ xTemplateDlg = self.xUITest.getTopFocusWindow()
+ xCancelBtn = xTemplateDlg.getChild("close")
+ self.ui_test.close_dialog_through_button(xCancelBtn)
+
+ xImpressDoc = self.xUITest.getTopFocusWindow()
+
+ xEditWin = xImpressDoc.getChild("impress_win")
+ xEditWin.executeAction("SET", mkPropertyValues({"ZOOM": "200"}))
+
+ self.assertEqual(get_state_as_dict(xEditWin)["Zoom"], "200")
+
+
+ def test_select_page(self):
+
+ with self.ui_test.create_doc_in_start_center("impress"):
+
+ xTemplateDlg = self.xUITest.getTopFocusWindow()
+ xCancelBtn = xTemplateDlg.getChild("close")
+ self.ui_test.close_dialog_through_button(xCancelBtn)
+
+ xImpressDoc = self.xUITest.getTopFocusWindow()
+
+ xEditWin = xImpressDoc.getChild("impress_win")
+
+ self.assertEqual(get_state_as_dict(xEditWin)["CurrentSlide"], "1")
+
+ self.xUITest.executeCommand(".uno:InsertPage")
+
+ self.assertEqual(get_state_as_dict(xEditWin)["CurrentSlide"], "2")
+
+ xEditWin.executeAction("GOTO", mkPropertyValues({"PAGE": "1"}))
+
+ self.assertEqual(get_state_as_dict(xEditWin)["CurrentSlide"], "1")
+
+
+# vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/uitest/libreoffice/calc/conditional_format.py b/uitest/libreoffice/calc/conditional_format.py
new file mode 100644
index 0000000000..40e7669438
--- /dev/null
+++ b/uitest/libreoffice/calc/conditional_format.py
@@ -0,0 +1,17 @@
+# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*-
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+# this file provides methods to interact with the new conditional format API
+
+def get_conditional_format_from_sheet(sheet):
+ """ Returns a conditional format object belonging to a sheet
+
+ Keyword arguments:
+ sheet -- a XSheet object"""
+ return sheet.getPropertyValue("ConditionalFormats")
+
+# vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/uitest/libreoffice/calc/csv_dialog.py b/uitest/libreoffice/calc/csv_dialog.py
new file mode 100644
index 0000000000..1313903e47
--- /dev/null
+++ b/uitest/libreoffice/calc/csv_dialog.py
@@ -0,0 +1,67 @@
+# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*-
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+from uitest.uihelper.common import get_state_as_dict, get_url_for_data_file
+from libreoffice.uno.propertyvalue import mkPropertyValues
+from contextlib import contextmanager
+
+@contextmanager
+def load_csv_file(UITestCase, fileName, bUseDefaultOptions):
+ with UITestCase.ui_test.execute_dialog_through_command(".uno:Open", close_button="open") as xOpenDialog:
+
+ xFileName = xOpenDialog.getChild("file_name")
+ xFileName.executeAction("TYPE", mkPropertyValues({"TEXT": get_url_for_data_file(fileName)}))
+
+ xDialog = UITestCase.ui_test.wait_for_top_focus_window('TextImportCsvDialog')
+
+ try:
+ if bUseDefaultOptions:
+ xSeparatedBy = xDialog.getChild("toseparatedby")
+ xSeparatedBy.executeAction("CLICK", tuple())
+
+ xTextDelimiter = xDialog.getChild("textdelimiter")
+ xTextDelimiter.executeAction("TYPE", mkPropertyValues({"KEYCODE":"CTRL+A"}))
+ xTextDelimiter.executeAction("TYPE", mkPropertyValues({"KEYCODE":"BACKSPACE"}))
+ xTextDelimiter.executeAction("TYPE", mkPropertyValues({"TEXT": "\""}))
+
+ setToTrue = ['tab', 'comma', 'semicolon']
+ for childName in setToTrue:
+ xChild = xDialog.getChild(childName)
+ if get_state_as_dict(xChild)['Selected'] == 'false':
+ xChild.executeAction("CLICK", tuple())
+ UITestCase.assertEqual('true', get_state_as_dict(xChild)['Selected'])
+
+ setToFalse = ['space', 'other', 'removespace', 'mergedelimiters',
+ 'evaluateformulas', 'quotedfieldastext', 'detectspecialnumbers']
+ for childName in setToFalse:
+ xChild = xDialog.getChild(childName)
+ if get_state_as_dict(xChild)['Selected'] == 'true':
+ xChild.executeAction("CLICK", tuple())
+ UITestCase.assertEqual('false', get_state_as_dict(xChild)['Selected'])
+ # tdf#154131
+ if childName == 'detectspecialnumbers':
+ # if 'Detect special numbers' is false, 'Detect scientific numbers' can be modified
+ xDetectScientific = xDialog.getChild('detectscientificnumbers')
+ if get_state_as_dict(xDetectScientific)['Selected'] == 'false':
+ xDetectScientific.executeAction("CLICK", tuple())
+ UITestCase.assertEqual('true', get_state_as_dict(xDetectScientific)['Selected'])
+ xDetectScientific.executeAction("CLICK", tuple())
+ UITestCase.assertEqual('false', get_state_as_dict(xDetectScientific)['Selected'])
+ # if 'Detect special numbers' is true, 'Detect scientific numbers' is true and disabled
+ xChild.executeAction("CLICK", tuple())
+ UITestCase.assertEqual('true', get_state_as_dict(xChild)['Selected'])
+ UITestCase.assertEqual('true', get_state_as_dict(xDetectScientific)['Selected'])
+ UITestCase.assertEqual('false', get_state_as_dict(xDetectScientific)['Enabled'])
+ xChild.executeAction("CLICK", tuple())
+
+ UITestCase.assertEqual('1', get_state_as_dict(xDialog.getChild("fromrow"))['Text'])
+
+ yield xDialog
+ finally:
+ xOK = xDialog.getChild('ok')
+ with UITestCase.ui_test.wait_until_component_loaded():
+ UITestCase.ui_test.close_dialog_through_button(xOK)
diff --git a/uitest/libreoffice/calc/document.py b/uitest/libreoffice/calc/document.py
new file mode 100644
index 0000000000..b837bda725
--- /dev/null
+++ b/uitest/libreoffice/calc/document.py
@@ -0,0 +1,63 @@
+# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*-
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+def get_sheet_from_doc(document, index=None, name=None):
+ """ Returns a sheet object for a Spreadsheet document
+
+ Keyword arguments:
+ index -- the 0-based index of the sheet (may not be used together with name)
+ name -- the name of the sheet (may not be used together with index)
+ """
+ return document.getSheets().getByIndex(index)
+
+def get_cell_by_position(document, tab, column, row):
+ """ Get the cell object through its position in a document
+
+ Keyword arguments:
+ document -- The document that should be used
+ tab -- The 0-based sheet number
+ column -- The 0-based column number
+ row -- The 0-based row number
+ """
+ sheet = get_sheet_from_doc(document, tab)
+ return sheet[row,column]
+
+def get_column(document, column, tab = 0):
+ """ Get the column object through the column index
+
+ Keyword arguments:
+ document -- The document that should be used
+ tab -- The 0-based sheet number
+ column -- The 0-based column number
+ """
+ sheet = get_sheet_from_doc(document, tab)
+ return sheet.getColumns().getByIndex(column)
+
+def get_row(document, row, tab = 0):
+ """ Get the row object through the row index
+
+ Keyword arguments:
+ document -- The document that should be used
+ tab -- The 0-based sheet number
+ column -- The 0-based row number
+ """
+ sheet = get_sheet_from_doc(document, tab)
+ return sheet.getRows().getByIndex(row)
+
+def is_row_hidden(document, row, tab = 0):
+ """ Check whether a row object is hidden
+
+ Keyword arguments:
+ document -- The document that should be used
+ tab -- The 0-based sheet number
+ column -- The 0-based row number
+ """
+ xRow = get_row(document, row, tab)
+ bVisible = xRow.getPropertyValue("IsVisible")
+ return not bVisible
+
+# vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/uitest/libreoffice/calc/paste_special.py b/uitest/libreoffice/calc/paste_special.py
new file mode 100644
index 0000000000..6f153ad14c
--- /dev/null
+++ b/uitest/libreoffice/calc/paste_special.py
@@ -0,0 +1,30 @@
+# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*-
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+from uitest.uihelper.common import get_state_as_dict
+
+def reset_default_values(UITestCase, xDialog):
+ setToFalse = ['paste_all', 'formats', 'comments', 'objects', 'formulas',
+ 'link', 'transpose', 'skip_empty']
+ for childName in setToFalse:
+ xChild = xDialog.getChild(childName)
+ if get_state_as_dict(xChild)['Selected'] == 'true':
+ xChild.executeAction("CLICK", tuple())
+ UITestCase.assertEqual('false', get_state_as_dict(xChild)['Selected'])
+
+ setToTrue = ['text', 'numbers', 'datetime', 'cbImmediately']
+ for childName in setToTrue:
+ xChild = xDialog.getChild(childName)
+ if get_state_as_dict(xChild)['Selected'] == 'false':
+ xChild.executeAction("CLICK", tuple())
+ UITestCase.assertEqual('true', get_state_as_dict(xChild)['Selected'])
+
+ setToCheck = ['none', 'no_shift']
+ for childName in setToCheck:
+ xChild = xDialog.getChild(childName)
+ xChild.executeAction("CLICK", tuple())
+ UITestCase.assertEqual('true', get_state_as_dict(xChild)['Checked'])
diff --git a/uitest/libreoffice/connection.py b/uitest/libreoffice/connection.py
new file mode 100644
index 0000000000..4f901130f2
--- /dev/null
+++ b/uitest/libreoffice/connection.py
@@ -0,0 +1,201 @@
+# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*-
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+import subprocess
+import time
+import traceback
+import uuid
+import os
+import platform
+import signal
+
+try:
+ import pyuno
+ import uno
+except ImportError:
+ print("pyuno not found: try to set PYTHONPATH and URE_BOOTSTRAP variables", flush=True)
+ print("PYTHONPATH=/installation/opt/program", flush=True)
+ print("URE_BOOTSTRAP=file:///installation/opt/program/fundamentalrc", flush=True)
+ raise
+
+def signal_handler(signal_num, frame):
+ signal_name = signal.Signals(signal_num).name
+ print(f'Signal handler called with signal {signal_name} ({signal_num})', flush=True)
+
+class OfficeConnection:
+ def __init__(self, args):
+ self.args = args
+ self.soffice = None
+ self.xContext = None
+
+ def setUp(self):
+ """ Create a new connection to a LibreOffice process
+
+ If the connection method is path the instance will be created as a
+ new subprocess. If the connection method is connect the instance tries
+ to connect to an existing instance with the specified socket string """
+ if platform.system() != "Windows":
+ signal.signal(signal.SIGCHLD, signal_handler)
+ signal.signal(signal.SIGPIPE, signal_handler)
+
+ (method, sep, rest) = self.args["--soffice"].partition(":")
+ if sep != ":":
+ raise Exception("soffice parameter does not specify method")
+ if method == "path":
+ socket = "pipe,name=pytest" + str(uuid.uuid1())
+ try:
+ userdir = self.args["--userdir"]
+ except KeyError:
+ raise Exception("'path' method requires --userdir")
+ if not(userdir.startswith("file://")):
+ raise Exception("--userdir must be file URL")
+ self.soffice = self.bootstrap(rest, userdir, socket)
+ elif method == "connect":
+ socket = rest
+ else:
+ raise Exception("unsupported connection method: " + method)
+
+ # connect to the soffice instance
+ success = False
+ try:
+ self.xContext = self.connect(socket)
+ success = True
+ finally:
+ if not success and self.soffice:
+ self.soffice.terminate()
+ self.soffice.wait()
+ self.soffice = None
+
+ def bootstrap(self, soffice, userdir, socket):
+ """ Creates a new LibreOffice process
+
+ @param soffice Path to the soffice installation
+ @param userdir Directory of the user profile, only one process per user
+ profile is possible
+ @param socket The socket string used for the PyUNO connection """
+
+ argv = [soffice, "--accept=" + socket + ";urp",
+ "-env:UserInstallation=" + userdir,
+ "--quickstart=no", "--nofirststartwizard",
+ "--norestore", "--nologo"]
+ if "--valgrind" in self.args:
+ argv.append("--valgrind")
+
+ if "--gdb" in self.args:
+ argv.insert(0, "gdb")
+ argv.insert(1, "-ex")
+ argv.insert(2, "run")
+ argv.insert(3, "--args")
+ argv[4] = argv[4].replace("soffice", "soffice.bin")
+
+ env = None
+ environ = dict(os.environ)
+ if 'LIBO_LANG' in environ:
+ env = environ
+ env['LC_ALL'] = environ['LIBO_LANG']
+
+ return subprocess.Popen(argv, env=env)
+
+ def connect(self, socket):
+ """ Tries to connect to the LibreOffice instance through the specified socket"""
+ xLocalContext = uno.getComponentContext()
+ xUnoResolver = xLocalContext.ServiceManager.createInstanceWithContext(
+ "com.sun.star.bridge.UnoUrlResolver", xLocalContext)
+ url = "uno:" + socket + ";urp;StarOffice.ComponentContext"
+ print("OfficeConnection: connecting to: " + url, flush=True)
+ while True:
+ if self.soffice and self.soffice.poll() is not None:
+ raise Exception("soffice has stopped.")
+
+ try:
+ xContext = xUnoResolver.resolve(url)
+ return xContext
+ except pyuno.getClass("com.sun.star.connection.NoConnectException"):
+ print("NoConnectException: sleeping...", flush=True)
+ time.sleep(1)
+
+ def tearDown(self):
+ """Terminate a LibreOffice instance created with the path connection method.
+
+ Tries to terminate the soffice instance through the normal
+ XDesktop::terminate method and waits indefinitely for the subprocess
+ to terminate """
+
+ if self.soffice:
+ if self.xContext:
+ try:
+ print("tearDown: calling terminate()...", flush=True)
+ xMgr = self.xContext.ServiceManager
+ xDesktop = xMgr.createInstanceWithContext(
+ "com.sun.star.frame.Desktop", self.xContext)
+ xDesktop.terminate()
+ print("...done", flush=True)
+ except pyuno.getClass("com.sun.star.beans.UnknownPropertyException"):
+ print("caught while TearDown:\n", traceback.format_exc(), flush=True)
+ pass # ignore, also means disposed
+ except pyuno.getClass("com.sun.star.lang.DisposedException"):
+ print("caught while TearDown:\n", traceback.format_exc(), flush=True)
+ pass # ignore
+ else:
+ self.soffice.terminate()
+
+ ret = self.soffice.wait()
+ self.xContext = None
+ self.soffice = None
+ if ret != 0:
+ raise Exception("Exit status indicates failure: " + str(ret))
+
+ @classmethod
+ def getHelpText(cls):
+ message = """
+ --soffice=method:location
+ specify soffice instance to connect to
+ supported methods: 'path', 'connect'
+ --userdir=URL specify user installation directory for 'path' method
+ --valgrind pass --valgrind to soffice for 'path' method
+
+ 'location' is a pathname, not a URL. 'userdir' is a URL.
+ """
+ return message
+
+
+class PersistentConnection:
+ def __init__(self, args):
+ self.args = args
+ self.connection = None
+
+ def getContext(self):
+ """ Returns the XContext corresponding to the LibreOffice instance
+
+ This is the starting point for any PyUNO access to the LibreOffice
+ instance."""
+ return self.connection.xContext
+
+ def setUp(self):
+ # don't create two connections
+ if self.connection:
+ return
+
+ conn = OfficeConnection(self.args)
+ conn.setUp()
+ self.connection = conn
+
+ def tearDown(self):
+ if self.connection:
+ try:
+ self.connection.tearDown()
+ finally:
+ self.connection = None
+
+ def kill(self):
+ """ Kills the LibreOffice instance if it was created through the connection
+
+ Only works with the connection method path"""
+ if self.connection and self.connection.soffice:
+ self.connection.soffice.kill()
+
+# vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/uitest/libreoffice/linguistic/linguservice.py b/uitest/libreoffice/linguistic/linguservice.py
new file mode 100644
index 0000000000..7ff9c1ac6c
--- /dev/null
+++ b/uitest/libreoffice/linguistic/linguservice.py
@@ -0,0 +1,27 @@
+# -*- Mode: python; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+
+def get_lingu_service_manager(xContext):
+ """ Returns the com.sun.star.linguistic2.LinguServiceManager
+
+ Further information: https://api.libreoffice.org/docs/idl/ref/servicecom_1_1sun_1_1star_1_1linguistic2_1_1LinguServiceManager.html
+ """
+ xServiceManager = xContext.getServiceManager()
+ xLinguServiceManager = xServiceManager.createInstanceWithContext("com.sun.star.linguistic2.LinguServiceManager", xContext)
+ return xLinguServiceManager
+
+
+def get_spellchecker(xContext):
+ """ Returns the com.sun.star.linguistic2.XSpellChecker through the
+ com.sun.star.linguistic2.LinguServiceManager
+
+ Further information: https://api.libreoffice.org/docs/idl/ref/servicecom_1_1sun_1_1star_1_1linguistic2_1_1SpellChecker.html"""
+ xLinguServiceManager = get_lingu_service_manager(xContext)
+ return xLinguServiceManager.getSpellChecker()
+
+# vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/uitest/libreoffice/uno/eventlistener.py b/uitest/libreoffice/uno/eventlistener.py
new file mode 100644
index 0000000000..54076efe33
--- /dev/null
+++ b/uitest/libreoffice/uno/eventlistener.py
@@ -0,0 +1,55 @@
+# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*-
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+try:
+ import unohelper
+ from com.sun.star.document import XDocumentEventListener
+except ImportError:
+ print("pyuno not found: try to set PYTHONPATH and URE_BOOTSTRAP variables")
+ print("PYTHONPATH=/installation/opt/program")
+ print("URE_BOOTSTRAP=file:///installation/opt/program/fundamentalrc")
+ raise
+
+class EventListener(XDocumentEventListener,unohelper.Base):
+
+ def __init__(self, xContext, eventNames, **kwargs):
+ self.xGEB = xContext.ServiceManager.createInstanceWithContext(
+ "com.sun.star.frame.GlobalEventBroadcaster", xContext)
+ self.xContext = xContext
+ self.executed = False
+ self.eventExecuted = []
+ self.printEvents = kwargs.get('printNames', False)
+ if isinstance(eventNames, str):
+ self.eventNames = [eventNames]
+ elif isinstance(eventNames, list):
+ self.eventNames = eventNames
+
+ def __enter__(self):
+ self.xGEB.addDocumentEventListener(self)
+ return self
+
+ def __exit__(self, type, value, traceback):
+ self.xGEB.removeDocumentEventListener(self)
+
+ def documentEventOccured(self, event):
+ if self.printEvents is True:
+ print(event.EventName)
+
+ if event.EventName in self.eventNames:
+ self.executed = True
+ self.eventExecuted.append(event.EventName)
+ else:
+ print(self.eventNames)
+ print(event.EventName)
+
+ def hasExecuted(self, eventName):
+ return eventName in self.eventExecuted
+
+ def disposing(event):
+ pass
+
+# vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/uitest/libreoffice/uno/propertyvalue.py b/uitest/libreoffice/uno/propertyvalue.py
new file mode 100644
index 0000000000..ce8622113a
--- /dev/null
+++ b/uitest/libreoffice/uno/propertyvalue.py
@@ -0,0 +1,38 @@
+# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*-
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+try:
+ import uno
+except ImportError:
+ print("pyuno not found: try to set PYTHONPATH and URE_BOOTSTRAP variables")
+ print("PYTHONPATH=/installation/opt/program")
+ print("URE_BOOTSTRAP=file:///installation/opt/program/fundamentalrc")
+ raise
+
+def mkPropertyValue(name, value):
+ """ Create a UNO PropertyValue from two input values.
+ """
+ return uno.createUnoStruct("com.sun.star.beans.PropertyValue",
+ name, 0, value, 0)
+
+def mkPropertyValues(vals):
+ """ Create UNO property values from a map.
+ """
+ return tuple([mkPropertyValue(name, value) for (name, value) in vals.items()])
+
+def convert_property_values_to_dict(propMap):
+ """ Create a dictionary from a sequence of property values
+ """
+ ret = {}
+ for entry in propMap:
+ name = entry.Name
+ val = entry.Value
+ ret[name] = val
+
+ return ret
+
+# vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/uitest/loginterpreter.py b/uitest/loginterpreter.py
new file mode 100755
index 0000000000..e84d9a0ed7
--- /dev/null
+++ b/uitest/loginterpreter.py
@@ -0,0 +1,209 @@
+#!/usr/bin/env python3
+# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+import os
+import sys
+import argparse
+
+def parse_line(line):
+ """
+ This function parses a line from log file
+ and returns the parsed values as a python dictionary
+ """
+ if (line == ""):
+ return
+ dict = {}
+ if "{" in line:
+ start_index_of_parameters = line.find("{")
+ end_index_of_parameters = line.find("}") + 1
+ parameters = line[start_index_of_parameters:end_index_of_parameters]
+ if parameters != "":
+ dict["parameters"] = parameters
+ line = line[:start_index_of_parameters-1]
+ word_list = line.split()
+ dict["keyword"] = word_list[0]
+
+ for index in range(1,len(word_list)):
+ key, val = word_list[index].split(":",1)
+ dict[key] = val
+ return dict
+
+def parse_args():
+ """
+ This function parses the command-line arguments
+ to get the input and output file details
+ """
+ parser = argparse.ArgumentParser(description = "Generate a UI test file from log")
+ parser.add_argument("input_address", type = str, help = "The log file address")
+ parser.add_argument("output_address", type = str, help = "The test file address")
+ parser.add_argument("-d", "--document", metavar = "", help = "Address of the document to be opened")
+ args = parser.parse_args()
+ return args
+
+def get_log_file(input_address):
+ try:
+ with open(input_address) as f:
+ content = f.readlines()
+ except IOError as err:
+ print("IO error: {0}".format(err))
+ print("Use " + os.path.basename(sys.argv[0]) + " -h to get usage instructions")
+ sys.exit(1)
+
+ content = [x.strip() for x in content if not x.startswith("Action on element")]
+ return content
+
+def initiate_test_generation(address):
+ try:
+ f = open(address,"w")
+ except IOError as err:
+ print("IO error: {0}".format(err))
+ print("Use " + os.path.basename(sys.argv[0]) + " -h to get usage instructions")
+ sys.exit(1)
+ initial_text = \
+ "# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*-\n\n" + \
+ "from uitest.framework import UITestCase\n" + \
+ "from libreoffice.uno.propertyvalue import mkPropertyValues\n" + \
+ "import importlib\n\n" + \
+ "class TestClass(UITestCase):\n" + \
+ " def test_function(self):\n"
+ f.write(initial_text)
+ return f
+
+def get_coupling_type(line1, line2):
+ """
+ This function checks if two consecutive lines of log file
+ refer to the same event
+ """
+ action_dict1 = parse_line(line1)
+ action_dict2 = parse_line(line2)
+
+ if action_dict1["keyword"] == "CommandSent" and \
+ action_dict2["keyword"] == "ModalDialogExecuted":
+ return "COMMAND_MODAL_COUPLE"
+
+ elif action_dict1["keyword"] == "CommandSent" and \
+ action_dict2["keyword"] == "ModelessDialogConstructed":
+ return "COMMAND_MODELESS_COUPLE"
+
+ elif action_dict1["keyword"] == "ButtonUIObject" and \
+ action_dict2["keyword"] == "DialogClosed":
+ return "BUTTON_DIALOGCLOSE_COUPLE"
+
+ elif "parameters" in action_dict1 and \
+ "KEYCODE" in action_dict1["parameters"] and \
+ action_dict2["keyword"] == "CommandSent":
+ return "REDUNDANT_COUPLE"
+
+ return "NOT_A_COUPLE"
+
+def check_app_starting_action(action_dict):
+ app_starter_button_ids = \
+ set(["draw_all", "impress_all", "calc_all" , "writer_all", "database_all", "math_all"])
+
+ if action_dict["keyword"] == "ButtonUIObject" and action_dict["Action"] == "CLICK" and \
+ action_dict["Id"] in app_starter_button_ids:
+ return True
+ return False
+
+def get_test_line_from_one_log_line(log_line):
+ action_dict = parse_line(log_line)
+ test_line = " "
+ if action_dict["keyword"].endswith("UIObject"):
+ parent = action_dict["Parent"]
+ if (check_app_starting_action(action_dict)):
+ test_line +=\
+ "MainDoc = self.ui_test.create_doc_in_start_center(\"" + \
+ action_dict["Id"][:-4] +"\")\n MainWindow = " + \
+ "self.xUITest.getTopFocusWindow()\n"
+ return test_line
+ else:
+ if (parent == ""):
+ parent = "MainWindow"
+ test_line += \
+ action_dict["Id"] + " = " + parent + ".getChild(\"" + \
+ action_dict["Id"] + "\")\n " + \
+ action_dict["Id"] + ".executeAction(\"" + \
+ action_dict["Action"] + "\""
+ if "parameters" in action_dict:
+ test_line += ", mkPropertyValues(" + \
+ action_dict["parameters"] + "))\n"
+ else:
+ test_line += ",tuple())\n"
+ return test_line
+ elif action_dict["keyword"] == "CommandSent":
+ if "parameters" not in action_dict:
+ test_line += "self.xUITest.executeCommand(\"" + \
+ action_dict["Name"] + "\")\n"
+ return test_line
+ else:
+ test_line += "self.xUITest.executeCommandWithParameters(\"" + \
+ action_dict["Name"] + "\", mkPropertyValues(" + action_dict["parameters"] + \
+ "))\n"
+ return test_line
+ elif action_dict["keyword"] == "ModalDialogExecuted" or \
+ action_dict["keyword"] == "ModelessDialogConstructed":
+ test_line += action_dict["Id"] + " = " + "self.xUITest.getTopFocusWindow()\n"
+ return test_line
+
+ return ""
+
+def get_test_line_from_two_log_lines(log_line1,log_line2):
+ coupling_type = get_coupling_type(log_line1, log_line2)
+ action_dict1 = parse_line(log_line1)
+ action_dict2 = parse_line(log_line2)
+ test_line = " "
+ if coupling_type == "COMMAND_MODAL_COUPLE":
+ test_line += \
+ "self.ui_test.execute_dialog_through_command(\"" + \
+ action_dict1["Name"] + "\")\n " + \
+ action_dict2["Id"] + " = self.xUITest.getTopFocusWindow()\n"
+ elif coupling_type == "COMMAND_MODELESS_COUPLE":
+ test_line += \
+ "self.ui_test.execute_modeless_dialog_through_command(\"" + \
+ action_dict1["Name"] + "\")\n " + \
+ action_dict2["Id"] + " = self.xUITest.getTopFocusWindow()\n"
+ elif coupling_type == "BUTTON_DIALOGCLOSE_COUPLE":
+ test_line += \
+ action_dict1["Id"] + " = " + action_dict1["Parent"] + ".getChild(\"" + \
+ action_dict1["Id"] + "\")\n self.ui_test.close_dialog_through_button(" + \
+ action_dict1["Id"] + ")\n"
+ return test_line
+
+def main():
+ args = parse_args()
+ log_lines = get_log_file(args.input_address)
+ output_stream = initiate_test_generation(args.output_address)
+ if args.document is not None:
+ output_line = " pathmodule = importlib.import_module(\"uitest.path\")\n" + \
+ " doc_path = pathmodule.get_srcdir_url() + \"" + args.document + "\"\n" + \
+ " MainDoc = self.ui_test.load_file(doc_path)\n" + \
+ " MainWindow = self.xUITest.getTopFocusWindow()\n"
+ output_stream.write(output_line)
+ line_number = 0
+ while line_number < len(log_lines):
+ if line_number == len(log_lines)-1 or \
+ get_coupling_type(log_lines[line_number],log_lines[line_number + 1]) == "NOT_A_COUPLE":
+ test_line = get_test_line_from_one_log_line(log_lines[line_number])
+ output_stream.write(test_line)
+ line_number += 1
+ elif get_coupling_type(log_lines[line_number],log_lines[line_number + 1]) == "REDUNDANT_COUPLE":
+ line_number += 1
+ else:
+ test_line = get_test_line_from_two_log_lines(log_lines[line_number],log_lines[line_number + 1])
+ output_stream.write(test_line)
+ line_number += 2
+ output_stream.write(" self.ui_test.close_doc()")
+ output_stream.write("\n\n# vim: set shiftwidth=4 softtabstop=4 expandtab:")
+ output_stream.close()
+
+if __name__ == '__main__':
+ main()
+
+# vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/uitest/math_tests/data/tdf128610.fodt b/uitest/math_tests/data/tdf128610.fodt
new file mode 100644
index 0000000000..cee4ed8e59
--- /dev/null
+++ b/uitest/math_tests/data/tdf128610.fodt
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<office:document xmlns:officeooo="http://openoffice.org/2009/office" xmlns:css3t="http://www.w3.org/TR/css3-text/" xmlns:grddl="http://www.w3.org/2003/g/data-view#" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:formx="urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:oooc="http://openoffice.org/2004/calc" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:ooow="http://openoffice.org/2004/writer" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rpt="http://openoffice.org/2005/report" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:config="urn:oasis:names:tc:opendocument:xmlns:config:1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:ooo="http://openoffice.org/2004/office" xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" xmlns:calcext="urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0" xmlns:tableooo="http://openoffice.org/2009/table" xmlns:drawooo="http://openoffice.org/2010/draw" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" xmlns:dom="http://www.w3.org/2001/xml-events" xmlns:field="urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0" xmlns:math="http://www.w3.org/1998/Math/MathML" xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0" xmlns:xforms="http://www.w3.org/2002/xforms" office:version="1.3" office:mimetype="application/vnd.oasis.opendocument.text">
+ <office:body>
+ <office:text>
+ <text:sequence-decls>
+ <text:sequence-decl text:display-outline-level="0" text:name="Illustration"/>
+ <text:sequence-decl text:display-outline-level="0" text:name="Table"/>
+ <text:sequence-decl text:display-outline-level="0" text:name="Text"/>
+ <text:sequence-decl text:display-outline-level="0" text:name="Drawing"/>
+ <text:sequence-decl text:display-outline-level="0" text:name="Figure"/>
+ </text:sequence-decls>
+ <text:p text:style-name="Preformatted_20_Text"><text:bookmark text:name="comment_text_19"/>&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;</text:p>
+ <text:p text:style-name="Preformatted_20_Text">&lt;math xmlns=&quot;<text:a xlink:type="simple" xlink:href="http://www.w3.org/1998/Math/MathML" text:style-name="Internet_20_link" text:visited-style-name="Visited_20_Internet_20_Link">http://www.w3.org/1998/Math/MathML</text:a>&quot; display=&quot;block&quot;&gt;</text:p>
+ <text:p text:style-name="Preformatted_20_Text"><text:s/>&lt;semantics&gt;</text:p>
+ <text:p text:style-name="Preformatted_20_Text"><text:s text:c="2"/>&lt;mrow&gt;</text:p>
+ <text:p text:style-name="Preformatted_20_Text"><text:s text:c="3"/>&lt;msub&gt;</text:p>
+ <text:p text:style-name="Preformatted_20_Text"><text:s text:c="4"/>&lt;mi&gt;f&lt;/mi&gt;</text:p>
+ <text:p text:style-name="Preformatted_20_Text"><text:s text:c="4"/>&lt;mi&gt;c&lt;/mi&gt;</text:p>
+ <text:p text:style-name="Preformatted_20_Text"><text:s text:c="3"/>&lt;/msub&gt;</text:p>
+ <text:p text:style-name="Preformatted_20_Text"><text:s text:c="3"/>&lt;mo stretchy=&quot;false&quot;&gt;=&lt;/mo&gt;</text:p>
+ <text:p text:style-name="Preformatted_20_Text"><text:s text:c="3"/>&lt;mfrac&gt;</text:p>
+ <text:p text:style-name="Preformatted_20_Text"><text:s text:c="4"/>&lt;mn&gt;1&lt;/mn&gt;</text:p>
+ <text:p text:style-name="Preformatted_20_Text"><text:s text:c="4"/>&lt;msub&gt;</text:p>
+ <text:p text:style-name="Preformatted_20_Text"><text:s text:c="5"/>&lt;mi&gt;K&lt;/mi&gt;</text:p>
+ <text:p text:style-name="Preformatted_20_Text"><text:s text:c="5"/>&lt;mi&gt;m&lt;/mi&gt;</text:p>
+ <text:p text:style-name="Preformatted_20_Text"><text:s text:c="4"/>&lt;/msub&gt;</text:p>
+ <text:p text:style-name="Preformatted_20_Text"><text:s text:c="3"/>&lt;/mfrac&gt;</text:p>
+ <text:p text:style-name="Preformatted_20_Text"><text:s text:c="2"/>&lt;/mrow&gt;</text:p>
+ <text:p text:style-name="Preformatted_20_Text"><text:s/>&lt;/semantics&gt;</text:p>
+ <text:p text:style-name="P1">&lt;/math&gt;</text:p>
+ </office:text>
+ </office:body>
+</office:document>
diff --git a/uitest/math_tests/start.py b/uitest/math_tests/start.py
new file mode 100644
index 0000000000..7504387a16
--- /dev/null
+++ b/uitest/math_tests/start.py
@@ -0,0 +1,67 @@
+# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+from uitest.uihelper.common import get_state_as_dict
+
+from libreoffice.uno.propertyvalue import mkPropertyValues
+
+from uitest.framework import UITestCase
+from uitest.uihelper.common import type_text, select_pos
+
+class SimpleMathTest(UITestCase):
+
+ def test_math_unoCommand(self):
+ with self.ui_test.create_doc_in_start_center("math"):
+
+ xMathDoc = self.xUITest.getTopFocusWindow()
+
+ # tdf#140386
+ self.xUITest.executeCommand(".uno:InsertCommandText?Text:string=backepsilon")
+
+ xEditView = xMathDoc.getChild("editview")
+
+ self.assertEqual("backepsilon", get_state_as_dict(xEditView)["Text"])
+
+ def test_math_edit(self):
+ with self.ui_test.create_doc_in_start_center("math"):
+
+ xMathDoc = self.xUITest.getTopFocusWindow()
+
+ xEditView = xMathDoc.getChild("editview")
+
+ type_text(xEditView, "E=mc^2")
+
+ self.assertEqual("E=mc^2", get_state_as_dict(xEditView)["Text"])
+
+ def test_complete_math(self):
+ with self.ui_test.create_doc_in_start_center("math"):
+
+ xMathDoc = self.xUITest.getTopFocusWindow()
+
+ xList = xMathDoc.getChild("categorylist")
+ state = get_state_as_dict(xList)
+ self.assertEqual(state["SelectEntryText"], "Unary/Binary Operators")
+ select_pos(xList, "1")
+ state = get_state_as_dict(xList)
+ self.assertEqual(state["SelectEntryText"], "Relations")
+
+ xMathSelector = xMathDoc.getChild("elements")
+
+ xElement = xMathSelector.getChild("1")
+ xElement.executeAction("DOUBLECLICK", tuple())
+
+ xEditView = xMathDoc.getChild("editview")
+ xEditView.executeAction("TYPE", mkPropertyValues({"KEYCODE":"F4"}))
+ type_text(xEditView, "1")
+ xEditView.executeAction("TYPE", mkPropertyValues({"KEYCODE":"F4"}))
+ type_text(xEditView, "2")
+
+ self.assertEqual("{ 1 <> 2 }", get_state_as_dict(xEditView)["Text"])
+
+# vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/uitest/math_tests/tdf128610.py b/uitest/math_tests/tdf128610.py
new file mode 100644
index 0000000000..29b7d5ac45
--- /dev/null
+++ b/uitest/math_tests/tdf128610.py
@@ -0,0 +1,32 @@
+# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+from uitest.framework import UITestCase
+from uitest.uihelper.common import get_state_as_dict
+from uitest.uihelper.common import get_url_for_data_file
+
+class Tdf128610(UITestCase):
+
+ def test_tdf128610(self):
+ with self.ui_test.load_file(get_url_for_data_file('tdf128610.fodt')):
+ self.xUITest.executeCommand('.uno:SelectAll')
+ self.xUITest.executeCommand('.uno:Copy')
+
+ with self.ui_test.load_empty_file("math"):
+
+ self.xUITest.executeCommand('.uno:ImportMathMLClipboard')
+
+ xMathDoc = self.xUITest.getTopFocusWindow()
+ xEditView = xMathDoc.getChild("editview")
+
+ # Without the fix in place, this test would have failed with
+ # AssertionError: '{ f _ c = frac { 1 } { K _ m } }' != ''
+ self.assertEqual("{ f _ c = { frac { 1 } { K _ m } } }", get_state_as_dict(xEditView)["Text"])
+
+# vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/uitest/math_tests/tdf147755.py b/uitest/math_tests/tdf147755.py
new file mode 100644
index 0000000000..4919747878
--- /dev/null
+++ b/uitest/math_tests/tdf147755.py
@@ -0,0 +1,32 @@
+# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+from uitest.framework import UITestCase
+from uitest.uihelper.common import get_state_as_dict
+
+class Tdf147755(UITestCase):
+
+ def test_tdf147755(self):
+ with self.ui_test.create_doc_in_start_center("math"):
+
+ with self.ui_test.execute_dialog_through_command(".uno:SymbolCatalogue", close_button="close") as xDialog:
+ xSymbolset = xDialog.getChild("symbolset")
+ self.assertEqual("Arabic", get_state_as_dict(xSymbolset)["DisplayText"])
+
+ xOk = xDialog.getChild("ok")
+ xOk.executeAction("CLICK", tuple())
+
+ xMathDoc = self.xUITest.getTopFocusWindow()
+ xEditView = xMathDoc.getChild("editview")
+
+ # Without the fix in place, this test would have failed with
+ # AssertionError: '%arRay' != ''
+ self.assertEqual("%arRay", get_state_as_dict(xEditView)["Text"])
+
+# vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/uitest/packaging/Makefile b/uitest/packaging/Makefile
new file mode 100644
index 0000000000..af63c946e7
--- /dev/null
+++ b/uitest/packaging/Makefile
@@ -0,0 +1,16 @@
+all:
+ @echo "Packaging the libreoffice-connection code"
+ @mkdir -p libreoffice
+ @cp ../libreoffice/connection.py libreoffice/.
+ @touch libreoffice/__init__.py
+ @python3 setup.py sdist
+
+clean:
+ rm -r dist/
+ rm -r libreoffice_connection.egg-info/
+ rm -r libreoffice/
+ rm -r build/
+
+publish:
+ @echo "Uploading the release to pypi"
+ twine upload dist/*
diff --git a/uitest/packaging/README.md b/uitest/packaging/README.md
new file mode 100644
index 0000000000..41dacf5204
--- /dev/null
+++ b/uitest/packaging/README.md
@@ -0,0 +1,3 @@
+# Connection code for LibreOffice's pyUNO
+
+This code allows out-of-process communication with a LibreOffice instance. The instance can either be started by the script or a connection to a running LibreOffice instance. The code contains several safety checks for hanging and crashed LibreOffice instances.
diff --git a/uitest/packaging/setup.cfg b/uitest/packaging/setup.cfg
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/uitest/packaging/setup.cfg
diff --git a/uitest/packaging/setup.py b/uitest/packaging/setup.py
new file mode 100644
index 0000000000..dcaa24512b
--- /dev/null
+++ b/uitest/packaging/setup.py
@@ -0,0 +1,35 @@
+from setuptools import setup, find_packages
+from codecs import open
+from os import path
+
+here = path.abspath(path.dirname(__file__))
+
+# Get the long description from the README file
+with open(path.join(here, 'README.md'), encoding='utf-8') as f:
+ long_description = f.read()
+
+setup(
+ name="libreoffice-connection",
+ version="0.0.1",
+ description="Connection code for LibreOffice's pyUNO",
+ long_description=long_description,
+ long_description_content_type='text/markdown',
+ url="http://www.libreoffice.org",
+ author="The LibreOffice developers",
+ author_email="libreoffice@lists.freedesktop.org",
+ license="MPL2",
+ classifiers=[
+ "Development Status :: 3 - Alpha",
+ "Intended Audience :: Developers",
+ "License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)",
+ "Programming Language :: Python :: 3.2",
+ "Programming Language :: Python :: 3.3",
+ "Programming Language :: Python :: 3.4",
+ "Programming Language :: Python :: 3.5",
+ "Programming Language :: Python :: 3.6",
+ "Topic :: Office/Business :: Office Suites",
+ "Topic :: Software Development :: Libraries",
+ ],
+ keywords="office automation",
+ packages=find_packages(),
+)
diff --git a/uitest/test_main.py b/uitest/test_main.py
new file mode 100644
index 0000000000..1957f54dc3
--- /dev/null
+++ b/uitest/test_main.py
@@ -0,0 +1,150 @@
+# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*-
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+import sys
+import getopt
+import os
+import unittest
+import importlib
+import importlib.machinery
+import types
+
+from uitest.framework import UITestCase
+
+from libreoffice.connection import OfficeConnection
+from libreoffice.connection import PersistentConnection
+
+test_name_limit_found = False
+
+def parseArgs(argv):
+ (optlist,args) = getopt.getopt(argv[1:], "hr",
+ ["help", "soffice=", "oneprocess", "userdir=", "dir=", "file=", "gdb"])
+ return (dict(optlist), args)
+
+def usage():
+ message = """usage: {program} [option]... [task_file]..."
+ -h | --help: print usage information
+ {connection_params}
+ the 'task_file' parameters should be
+ full absolute pathnames, not URLs."""
+ print(message.format(program = os.path.basename(sys.argv[0]), \
+ connection_params = OfficeConnection.getHelpText()))
+
+
+def find_test_files(dir_path):
+ valid_files = []
+ for f in sorted(os.listdir(dir_path)):
+ file_path = os.path.join(dir_path, f)
+
+ # don't go through the sub-directories
+ if not os.path.isfile(file_path):
+ continue
+
+ if os.path.splitext(file_path)[1] == ".swp":
+ continue # ignore VIM swap files
+
+ if file_path[-1:] == "~":
+ continue # ignore backup files
+
+ # fail on any non .py files
+ if not os.path.splitext(file_path)[1] == ".py":
+ raise Exception("file with an extension which is not .py: " + file_path)
+
+ # ignore the __init__.py file
+ # it is obviously not a test file
+ if f == "__init__.py":
+ continue
+
+ valid_files.append(file_path)
+
+ return valid_files
+
+def get_classes_of_module(module):
+ md = module.__dict__
+ return [ md[c] for c in md if (
+ isinstance(md[c], type) and md[c].__module__ == module.__name__ ) ]
+
+def get_test_case_classes_of_module(module):
+ classes = get_classes_of_module(module)
+ return [ c for c in classes if issubclass(c, UITestCase) ]
+
+def add_tests_for_file(test_file, test_suite):
+ test_name_limit = os.environ.get('UITEST_TEST_NAME', '')
+ test_loader = unittest.TestLoader()
+ module_name = os.path.splitext(os.path.split(test_file)[1])[0]
+
+ loader = importlib.machinery.SourceFileLoader(module_name, test_file)
+ # exec_module was only introduced in 3.4
+ if sys.version_info < (3,4):
+ mod = loader.load_module()
+ else:
+ mod = types.ModuleType(loader.name)
+ loader.exec_module(mod)
+ classes = get_test_case_classes_of_module(mod)
+ global test_name_limit_found
+ for c in classes:
+ test_names = test_loader.getTestCaseNames(c)
+ for test_name in test_names:
+ full_name = ".".join([module_name, c.__name__, test_name])
+ if len(test_name_limit) > 0:
+ if test_name_limit != full_name:
+ continue
+ test_name_limit_found = True
+
+ obj = c(test_name, opts, connection)
+ test_suite.addTest(obj)
+
+def get_test_suite_for_dir(opts):
+ test_suite = unittest.TestSuite()
+
+ valid_test_files = find_test_files(opts['--dir'])
+ for test_file in valid_test_files:
+ add_tests_for_file(test_file, test_suite)
+ return test_suite
+
+
+if __name__ == '__main__':
+ (opts,args) = parseArgs(sys.argv)
+ connection = None
+ if "--oneprocess" in opts:
+ connection = PersistentConnection(opts)
+ connection.setUp()
+ if "-h" in opts or "--help" in opts:
+ usage()
+ sys.exit()
+ elif not "--soffice" in opts:
+ usage()
+ sys.exit(1)
+ elif "--dir" in opts:
+ test_suite = get_test_suite_for_dir(opts)
+ test_name_limit = os.environ.get('UITEST_TEST_NAME', '')
+ if len(test_name_limit) > 0:
+ if not test_name_limit_found:
+ print("UITEST_TEST_NAME '%s' does not match any test" % test_name_limit)
+ sys.exit(1)
+ else:
+ print("UITEST_TEST_NAME '%s' active" % test_name_limit)
+ elif "--file" in opts:
+ test_suite = unittest.TestSuite()
+ add_tests_for_file(opts['--file'], test_suite)
+ else:
+ usage()
+ sys.exit()
+
+ result = unittest.TextTestRunner(stream=sys.stdout, verbosity=2).run(test_suite)
+ print("Tests run: %d" % result.testsRun)
+ print("Tests failed: %d" % len(result.failures))
+ print("Tests errors: %d" % len(result.errors))
+ print("Tests skipped: %d" % len(result.skipped))
+ if connection:
+ connection.tearDown()
+ connection.kill()
+ if not result.wasSuccessful():
+ sys.exit(1)
+ sys.exit(0)
+
+# vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/uitest/ui_logger_dsl/General_commands.tx b/uitest/ui_logger_dsl/General_commands.tx
new file mode 100644
index 0000000000..2be59ce75f
--- /dev/null
+++ b/uitest/ui_logger_dsl/General_commands.tx
@@ -0,0 +1,27 @@
+/*
+ This file is for the log statements that are general for all applications.
+ We can use them as general rules for commands and relate them to the
+ applications found in starter_commands.tx
+ Zoom is also general as it is better to treat it that way.
+*/
+
+import type_options
+
+GeneralCommand:
+ SideBar | setZoom_command | Select_command | General_type_command_on_UI_Object
+;
+SideBar:
+ 'From SIDEBAR ' 'Choose ' '{"PANEL":' name=STRING '}'
+;
+setZoom_command:
+ 'Set Zoom to ' zoom_value=INT
+;
+Select_command:
+ 'Select ' '{"OBJECT":' name=STRING '}'
+;
+
+// This part is for typing text in any of these UI elements
+
+General_type_command_on_UI_Object:
+ 'Type on' UI_Obj=STRING what_to_type=Type_options 'from' parent_id=ID
+;
diff --git a/uitest/ui_logger_dsl/Special_commands.tx b/uitest/ui_logger_dsl/Special_commands.tx
new file mode 100644
index 0000000000..fb3a6e235f
--- /dev/null
+++ b/uitest/ui_logger_dsl/Special_commands.tx
@@ -0,0 +1,238 @@
+/*
+ This file is for the log statements that relate to each application.
+ Each grammar rule here is related to a specific application.
+*/
+
+import type_options
+
+SpecialCommand:
+ writer_command | calc_command | impress_command | math_command | draw_command
+;
+
+/*
+ This part is for all the Writer log statements:
+
+ 1) Type
+ 2) Select
+ 3) GOTO page
+ 4) Create Table
+ 5) Copy Text
+ 6) Cut Text
+ 7) Paste Text
+ 8) Insert Break Page
+*/
+writer_command:
+ writer_Type_command | writer_Select_command | writer_GOTO_command |
+ writer_Create_table | writer_Copy_Text | writer_Cut_Text |
+ writer_Paste_Text | writer_Insert_BreakPage | writer_Comment_command
+;
+writer_Type_command:
+ 'Type on writer' what_to_type=Type_options
+;
+writer_Select_command:
+ 'Select from Pos' from_pos=INT 'to Pos' to_pos=INT
+;
+writer_GOTO_command:
+ 'GOTO page number' page_num=INT
+;
+writer_Create_table:
+ 'Create Table with Columns :' cols=INT ', Rows :' rows=INT
+;
+writer_Copy_Text:
+ 'Copy the Selected Text'
+;
+writer_Cut_Text:
+ 'Cut the Selected Text'
+;
+writer_Paste_Text:
+ 'Paste in the Current Cursor Location'
+;
+writer_Insert_BreakPage:
+ 'Insert Break Page'
+;
+
+/*
+ This part is for all the Calc log statements:
+
+ 1) select sheet
+ 2) Select cell or range
+ 3) launch AutoFill
+ 4) launch SELECTMENU
+ 5) Delete Cells
+ 6) Remove Content of a cell
+ 7) Insert new Cells
+ 8) Cut Cells
+ 9) Copy Cells
+ 10) Paste Cells
+ 11) Merge Cells
+ 12) Unmerge Cells
+ 13) Open Comment
+ 14) Close Comment
+*/
+calc_command:
+ calc_Type_command | calc_switch_sheet | calc_Select_cell | calc_AutoFill_filter |
+ calc_SelectMenu_filter | calc_Delete_Cells | calc_Remove_Content | calc_insert_cells |
+ calc_Cut_Cells | calc_Copy_Cells | calc_Paste_Cells | calc_UNMerge_Cells |
+ calc_Merge_Cells | calc_Rename_Sheet | calc_Insert_sheet | calc_Open_Comment |
+ calc_Close_Comment
+;
+calc_Type_command:
+ 'Type on current cell' what_to_type=Type_options
+;
+calc_switch_sheet:
+ 'Switch to sheet number' sheet_num=INT
+;
+calc_Select_cell:
+ 'Select from calc' select_op=select_options
+;
+calc_AutoFill_filter:
+ 'Launch AutoFilter from Col' col_num=INT 'and Row' row_num=INT
+;
+calc_SelectMenu_filter:
+ 'Launch SELECTMENU from Col' col_num=INT 'and Row' row_num=INT
+;
+range_of_cells:
+ '{' '"RANGE":' input_range=STRING '}'
+;
+one_cell:
+ '{' '"CELL":' input_cell=STRING '}'
+;
+calc_Delete_Cells:
+ 'Delete The Cells in' '{' '"RANGE":' input_range=STRING '}'
+;
+calc_Remove_Content:
+ 'Remove Content from This' '{' '"RANGE":' input_range=STRING '}'
+;
+calc_insert_cells:
+ 'Insert Cell around the ' '{' '"RANGE":' input_range=STRING '}'
+;
+calc_Cut_Cells:
+ 'CUT the selected ' '{' '"RANGE":' input_range=STRING '}'
+;
+calc_Copy_Cells:
+ 'COPY the selected ' '{' '"RANGE":' input_range=STRING '}'
+;
+calc_Paste_Cells:
+ 'Paste in the' '{' '"RANGE":' input_range=STRING '}'
+;
+calc_Merge_Cells:
+ 'Merge' '{' '"RANGE":' input_range=STRING '}'
+;
+calc_UNMerge_Cells:
+ 'Delete the merge between' '{' '"CELL":' input_range=STRING '}'
+;
+calc_Rename_Sheet:
+ 'Rename The Selected Tab to ' new_name=STRING
+;
+calc_Insert_sheet:
+ 'Insert New Tab '
+;
+calc_Open_Comment:
+ 'Open Comment' (txt=STRING)?
+;
+calc_Close_Comment:
+ 'Close Comment' (txt=STRING)?
+;
+
+//this is the select options
+select_options:
+ one_cell | range_of_cells
+;
+
+/*
+ This part is for all the Impress log statements:
+
+ 1) Type
+ 2) Insert New Slide
+ 3) Delete Slide
+ 4) Duplicate Slide
+ 5) Rename Slide
+*/
+impress_command:
+ impress_Type_command | impress_Insert_Slide | impress_Delete_Page |
+ impress_Duplicate_Slide | impress_Rename_Slide
+;
+impress_Type_command:
+ 'Type on impress ' what_to_type=Type_options
+;
+impress_Insert_Slide:
+ 'Insert New Slide at Position ' position_num=INT
+;
+impress_Delete_Page:
+ 'Delete Slide number ' position_num=INT
+;
+impress_Duplicate_Slide:
+ 'Duplicate The Selected Slide '
+;
+impress_Rename_Slide:
+ 'Rename The Selected Slide from ' old_name=STRING 'to' new_name=STRING
+;
+/*
+ This part is for all the Math log statements:
+
+ 1) element selector
+ 2) Type
+*/
+math_command:
+ math_element_selector | math_Type_command
+;
+math_element_selector:
+ 'Select element no ' element_no=INT 'From' place=ID
+;
+math_Type_command:
+ 'Type on math ' what_to_type=Type_options
+;
+
+/*
+ This part is for all the Draw log statements:
+
+ 1) Type
+ 2) Insert New Page
+ 3) Delete Page
+ 4) Rename Page
+*/
+draw_command:
+ draw_Type_command | draw_Insert_Page | draw_Delete_Page |
+ draw_Rename_Page
+;
+draw_Type_command:
+ 'Type on draw ' what_to_type=Type_options
+;
+draw_Insert_Page:
+ 'Insert New Page at Position ' position_num=INT
+;
+draw_Delete_Page:
+ 'Delete Page number ' position_num=INT
+;
+draw_Rename_Page:
+ 'Rename The Selected Page from ' old_name=STRING 'to' new_name=STRING
+;
+
+/*
+ This part is for all the Writer Comment log statements:
+
+ 1) Leave
+ 2) Hide
+ 3) Show
+ 4) Delete
+ 5) Set Resolved
+*/
+writer_Comment_command:
+ writer_Comment_leave | writer_Comment_show | writer_Comment_hide |
+ writer_Comment_delete | writer_Comment_setresolved
+;
+writer_Comment_leave:
+ 'Leave ' comment_id=STRING
+;
+writer_Comment_show:
+ 'Show ' comment_id=STRING
+;
+writer_Comment_hide:
+ 'Hide ' comment_id=STRING
+;
+writer_Comment_delete:
+ 'Delete ' comment_id=STRING
+;
+writer_Comment_setresolved:
+ 'Resolve' comment_id=STRING
+;
diff --git a/uitest/ui_logger_dsl/UI_Object_commands.tx b/uitest/ui_logger_dsl/UI_Object_commands.tx
new file mode 100644
index 0000000000..4711ad6ac4
--- /dev/null
+++ b/uitest/ui_logger_dsl/UI_Object_commands.tx
@@ -0,0 +1,90 @@
+/*
+ This file is for the grammar of:
+ 1) ButtonUIObject : ( Click event )
+ 2) EditUIObject : ( Type event - Clear event - Select Text event )
+ 3) CheckBoxUIObject : ( Toggle the value )
+ 4) RadioButtonUIObject : ( Select event )
+ 5) ListBoxUIObject : ( Select event )
+ 6) ComboBoxUIObject ( Select event )
+ 7) SpinUIObject ( Increase event - Decrease event )
+ 8) TabControlUIObject ( Change tab event )
+ 9) ToolBoxUIObject ( Click on item event )
+ 10) ValueSetUIObject (Choose item)
+ 10) MenuBtnUIObject ( Select - Open - Close )
+*/
+
+import type_options
+
+UIObjectCommand:
+ ButtonUIObject | CheckBoxUIObject | EditUIObject |
+ RadioButtonUIObject | ListBoxUIObject | ComboBoxUIObject |
+ SpinFieldUIObject | TabControlUIObject | ToolBoxUIObject |
+ ValueSetUIObject | MenuBtnUIObject
+;
+TabPageNumber:
+ INT | ID
+;
+ButtonUIObject:
+ 'Click on' ui_button=STRING ('from' parent_id=ID)?
+;
+CheckBoxUIObject:
+ 'Toggle' Check_box_id=STRING 'CheckBox' ('from' parent_id=ID)?
+;
+RadioButtonUIObject:
+ 'Select' Radio_button_id=STRING 'RadioButton' ('from' parent_id=ID)?
+;
+ComboBoxUIObject:
+ 'Select in' Combo_box_id=STRING 'ComboBox' 'item number' item_num=INT ('from' parent_id=ID)?
+;
+TabControlUIObject:
+ 'Choose Tab number' tab_page_number=TabPageNumber 'in' tab_id=STRING ('from' parent_id=ID)?
+;
+EditUIObject:
+ action=action_on_UIObject ('from' parent_id=ID)?
+;
+SpinFieldUIObject:
+ change=increase_or_decrease Spin_id=STRING ('from' parent_id=ID)?
+;
+ListBoxUIObject:
+ 'Select element with position ' POS=INT 'in' list_id=STRING ('from' parent_id=ID)?
+;
+ToolBoxUIObject:
+ 'Click on item number' POS=INT 'in' toolbox_id=ID
+;
+ValueSetUIObject:
+ 'Choose element with position ' POS=INT 'in' value_set_id=STRING 'from' parent_id=STRING
+;
+//=============================================================
+MenuBtnUIObject:
+ MenuBtnUIObjectOpen | MenuBtnUIObjectSelect | MenuBtnUIObjectClose
+;
+MenuBtnUIObjectOpen:
+ 'Open List From' + MenuBtn_ID=ID
+;
+MenuBtnUIObjectClose:
+ 'Close List From' + MenuBtn_ID=ID
+;
+MenuBtnUIObjectSelect:
+ 'Select item no' + item_num=INT + 'From List of' + MenuBtn_ID=ID
+;
+//=============================================================
+// Helper grammar for EditUIObject
+action_on_UIObject:
+ Type_action | SELECT | Clear
+;
+Type_action:
+ 'Type on' edit_button=STRING what_to_type=Type_options
+;
+SELECT:
+ 'Select in ' edit_button=STRING
+ '{' + '"FROM"' + ':' + '"' from_pos=INT '"' + ',' + '"TO"' + ':' + '"' to_pos=INT '"'+'}'
+;
+Clear:
+ 'Clear' edit_button=STRING
+;
+
+//=============================================================
+// Helper functions for SpinUIObject
+increase_or_decrease:
+ 'Increase' | 'Decrease'
+;
diff --git a/uitest/ui_logger_dsl/dialog_commands.tx b/uitest/ui_logger_dsl/dialog_commands.tx
new file mode 100644
index 0000000000..637b54f0c9
--- /dev/null
+++ b/uitest/ui_logger_dsl/dialog_commands.tx
@@ -0,0 +1,24 @@
+/*
+ This file is for the Dialog commands.
+ It handles all types of Dialog: the Modeless and the Modal.
+ It also handles the Close Dialog commands.
+*/
+DialogCommand:
+ OpenDialog | CloseDialog
+;
+
+OpenDialog:
+ OpenModalDialog | OpenModelessDialog
+;
+OpenModalDialog :
+ 'Open Modal ' dialog_name=ID
+;
+OpenModelessDialog :
+ 'Open Modeless ' dialog_name=ID
+;
+
+CloseDialog:
+ // If there is a need to match a dialog name in the future, additional_note=STRING? can be used.
+ // It is also used to make an instance of the command of type CloseDialog.
+ 'Close Dialog' additional_note=STRING?
+;
diff --git a/uitest/ui_logger_dsl/dsl_core.py b/uitest/ui_logger_dsl/dsl_core.py
new file mode 100644
index 0000000000..77c75eae9b
--- /dev/null
+++ b/uitest/ui_logger_dsl/dsl_core.py
@@ -0,0 +1,1092 @@
+#!/usr/bin/env python3
+# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*-
+#
+# This file is part of the LibreOffice UI_logger project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This file contains the implementation of the Compiler
+# for the new logger grammar
+#
+# ul stands for Ui_Logger
+
+import os
+import sys
+import argparse
+import keyword
+
+try:
+ from textx.metamodel import metamodel_from_file
+except ImportError:
+ print("textx is a required package.")
+ print('Please install the package for example with "pip3 install --user textx"')
+ sys.exit(1)
+
+tab = " "
+
+def parse_args():
+ """
+ This function parses the command-line arguments
+ to get the input and output file details
+ """
+ parser = argparse.ArgumentParser(description="Generate a UI test file from log")
+ parser.add_argument("input_address", type=str, help="The log file address")
+ parser.add_argument("output_address", type=str, help="The test file address")
+ args = parser.parse_args()
+ return args
+
+
+class ul_Compiler:
+ prev_command = ""
+ variables = []
+ objects = dict()
+ current_app = ""
+ parent_hierarchy_count = 0
+ last_parent = []
+ flag_for_QuerySaveDialog = False
+ math_element_selector_initializer= False;
+
+ def __init__(self, input_address, output_address):
+ self.ui_dsl_mm = metamodel_from_file("ui_logger_dsl_grammar.tx")
+ self.output_stream = self.initiate_test_generation(output_address)
+ self.input_address = input_address
+
+ def get_log_file(self, input_address):
+ try:
+ # load the program
+ content = self.ui_dsl_mm.model_from_file(input_address)
+ except IOError as err:
+ print("IO error: {0}".format(err))
+ print(
+ "Use " + os.path.basename(sys.argv[0]) + " -h to get usage instructions"
+ )
+ sys.exit(1)
+
+ return content
+
+ def initiate_test_generation(self, output_address):
+ self.last_parent.append("MainWindow")
+ try:
+ f = open(output_address, "w")
+ except IOError as err:
+ print("IO error: {0}".format(err))
+ print(
+ "Use " + os.path.basename(sys.argv[0]) + " -h to get usage instructions"
+ )
+ sys.exit(1)
+ line = (
+ "# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*-\n"
+ + "#\n"
+ + "# This file is part of the LibreOffice project.\n"
+ + "#\n"
+ + "# This Source Code Form is subject to the terms of the Mozilla Public\n"
+ + "# License, v. 2.0. If a copy of the MPL was not distributed with this\n"
+ + "# file, You can obtain one at http://mozilla.org/MPL/2.0/\n"
+ + "#\n\n"
+ + "from uitest.framework import UITestCase\n"
+ + "from libreoffice.uno.propertyvalue import mkPropertyValues\n"
+ + "from uitest.uihelper.common import get_state_as_dict\n"
+ + "import importlib\n\n"
+ + "class TestClass(UITestCase):\n"
+ + tab
+ + "def test_function(self):\n"
+ )
+
+ self.variables.append(line)
+
+ return f
+
+ def compile(self):
+ self.ui_dsl_mm.register_obj_processors(
+ {
+ "UNOCommand": self.handle_uno,
+ "StarterCommand": self.handle_start,
+ "CloseDialog": self.handle_Dialog,
+ "OpenModelessDialog": self.handle_Dialog,
+ "OpenModalDialog": self.handle_Dialog,
+ "ButtonUIObject": self.handle_button,
+ "CheckBoxUIObject": self.handle_check_box,
+ "TabControlUIObject": self.handle_tab,
+ "ComboBoxUIObject": self.handle_Combo_box,
+ "RadioButtonUIObject": self.handle_Radio_button,
+ "ListBoxUIObject": self.handle_List_box,
+ "SpinFieldUIObject": self.handle_spin_field,
+ "EditUIObject": self.handle_Edit_uiObject,
+ "ToolBoxUIObject": self.handle_ToolBox_uiObject,
+ "ValueSetUIObject": self.handle_ValueSet_uiObject,
+ "MenuBtnUIObjectOpen":self.handle_MenuBtnUIObjectOpen,
+ "MenuBtnUIObjectSelect":self.handle_MenuBtnUIObjectSelect,
+ "MenuBtnUIObjectClose":self.handle_MenuBtnUIObjectClose,
+ "writer_Type_command": self.handle_writer_type,
+ "writer_Select_command": self.handle_writer_select,
+ "writer_GOTO_command": self.handle_writer_goto,
+ "calc_Select_cell": self.handle_calc_select,
+ "calc_switch_sheet": self.handle_calc_switch_sheet,
+ "calc_Type_command": self.handle_calc_Type_command,
+ "calc_AutoFill_filter": self.handle_calc_AutoFill_filter,
+ "calc_SelectMenu_filter": self.handle_calc_SelectMenu_filter,
+ "calc_Open_Comment": self.handle_calc_Open_Comment,
+ "calc_Close_Comment": self.handle_calc_Close_Comment,
+ "impress_Type_command": self.handle_impress_Type_command,
+ "math_element_selector": self.handle_math_element_selector,
+ "math_Type_command": self.handle_math_Type_command,
+ "setZoom_command": self.handle_setZoom_command,
+ "draw_Type_command": self.handle_draw_Type_command,
+ "SideBar": self.handle_SideBar,
+ "writer_Comment_leave":self.handle_writer_Comment_leave,
+ "writer_Comment_show":self.handle_writer_Comment_show,
+ "writer_Comment_hide":self.handle_writer_Comment_hide,
+ "writer_Comment_delete":self.handle_writer_Comment_delete,
+ "writer_Comment_setresolved":self.handle_writer_Comment_setresolved,
+ "writer_Copy_Text": self.do_nothing,
+ "writer_Cut_Text": self.do_nothing,
+ "writer_Paste_Text": self.do_nothing,
+ "writer_Insert_BreakPage": self.do_nothing,
+ "writer_Create_table": self.do_nothing,
+ "calc_Remove_Content": self.do_nothing,
+ "calc_Delete_Cells": self.do_nothing,
+ "calc_insert_cells": self.do_nothing,
+ "calc_Cut_Cells": self.do_nothing,
+ "calc_Copy_Cells": self.do_nothing,
+ "calc_Merge_Cells": self.do_nothing,
+ "calc_UNMerge_Cells": self.do_nothing,
+ "calc_Rename_Sheet": self.do_nothing,
+ "calc_Insert_sheet": self.do_nothing,
+ "impress_Insert_Slide": self.do_nothing,
+ "impress_Delete_Page": self.do_nothing,
+ "impress_Duplicate_Slide": self.do_nothing,
+ "impress_Rename_Slide": self.do_nothing,
+ "draw_Insert_Page": self.do_nothing,
+ "draw_Delete_Page": self.do_nothing,
+ "draw_Rename_Page": self.do_nothing,
+ }
+ )
+
+ self.log_lines = self.get_log_file(self.input_address)
+
+ def init_app(self):
+ if self.current_app in self.objects:
+ self.objects[self.current_app] += 1
+ else:
+ self.objects[self.current_app] = 1
+ line = (
+ tab * 4
+ + self.current_app
+ + ' = MainWindow.getChild("'
+ + self.current_app
+ + '")\n'
+ )
+ self.variables.append(line)
+
+ def init_Object(self, Id_of_Object, name_of_child, Obj_parent):
+
+ if Id_of_Object in self.objects:
+ self.objects[Id_of_Object] += 1
+ else:
+ self.objects[Id_of_Object] = 1
+ line = (
+ tab * 4
+ + Id_of_Object
+ + " = "
+ + Obj_parent
+ + '.getChild("'
+ + name_of_child
+ + '")\n'
+ )
+
+ self.variables.append(line)
+
+ def write_line_without_parameters(self, Action_holder, Action, Action_type):
+ line = (
+ tab * 4
+ + Action_holder
+ + '.executeAction("'
+ + Action
+ + '",'
+ + Action_type
+ + "())\n"
+ )
+ self.variables.append(line)
+
+ def write_line_with_one_parameters(
+ self, Action_holder, Action, Parameter_name, parameter_value
+ ):
+ line = (
+ tab * 4
+ + Action_holder
+ + '.executeAction("'
+ + Action
+ + '", mkPropertyValues({"'
+ + Parameter_name
+ + '": "'
+ + str(parameter_value)
+ + '"}))\n'
+ )
+ self.variables.append(line)
+
+ def write_line_with_two_parameters(
+ self,
+ Action_holder,
+ Action,
+ Parameter_name_1,
+ parameter_value_1,
+ Parameter_name_2,
+ parameter_value_2,
+ ):
+
+ line = (
+ tab * 3
+ + Action_holder
+ + '.executeAction("'
+ + Action
+ + '", mkPropertyValues({"'
+ + Parameter_name_1
+ + '": "'
+ + str(parameter_value_1)
+ + '", "'
+ + Parameter_name_2
+ + '": "'
+ + str(parameter_value_2)
+ + '"}))\n'
+ )
+ self.variables.append(line)
+
+ def handle_uno(self, UNOCommand):
+ if UNOCommand.parameters == None:
+ line = (
+ tab * 3
+ + 'self.xUITest.executeCommand("'
+ + UNOCommand.uno_command_name
+ + '")\n'
+ )
+ else:
+ parameters = ""
+ for p in UNOCommand.parameters.parameter_data:
+ parameters = parameters + '"' + p.key + '" : ' + str(p.value) + " ,"
+ parameters = parameters[:-1]
+
+ line = (
+ tab * 3
+ + 'self.xUITest.executeCommandWithParameters("'
+ + UNOCommand.uno_command_name
+ + '", mkPropertyValues({'
+ + parameters
+ + "}) )\n"
+ )
+
+ self.variables.append(line)
+ self.prev_command = UNOCommand
+
+ def handle_start(self, StarterCommand):
+ line = (
+ tab * 2
+ + 'with self.ui_test.create_doc_in_start_center("'
+ + StarterCommand.program_name
+ + '") as document:\n'
+ )
+ self.variables.append(line)
+
+ line = tab * 3 + "MainWindow = self.xUITest.getTopFocusWindow()\n"
+ self.variables.append(line)
+ app = {
+ "writer": "writer_edit",
+ "calc": "grid_window",
+ "impress": "impress_win",
+ "math": "math_edit",
+ "draw": "draw_win",
+ }
+ self.current_app = app[StarterCommand.program_name]
+ self.prev_command = StarterCommand
+
+ def handle_SideBar(self, SideBar):
+
+ line = ' self.xUITest.executeCommand(".uno:Sidebar")\n'
+ self.variables.append(line)
+
+ self.write_line_with_one_parameters(
+ "MainWindow", "SIDEBAR", "PANEL", SideBar.name
+ )
+
+ self.prev_command = SideBar
+
+ def handle_Dialog(self, DialogCommand):
+
+ if DialogCommand.__class__.__name__ == "OpenModalDialog":
+
+ if DialogCommand.dialog_name != "QuerySaveDialog":
+ # This part is just to ignore saving the Save dialog while closing the app
+
+ old_line = self.variables.pop()
+ if self.prev_command.__class__.__name__ == "UNOCommand":
+ key_word = self.prev_command.uno_command_name[-6:]
+ else:
+ key_word = old_line[-9:-3]
+
+ if key_word == "Dialog":
+ old_line = (
+ tab * 3
+ + 'with self.ui_test.execute_dialog_through_command("'
+ + self.prev_command.uno_command_name
+ + '") as '
+ + DialogCommand.dialog_name
+ + ':\n'
+ )
+ self.variables.append(old_line)
+ self.last_parent.append(DialogCommand.dialog_name)
+ self.parent_hierarchy_count = self.parent_hierarchy_count + 1
+
+ else:
+ self.flag_for_QuerySaveDialog = True
+
+ elif DialogCommand.__class__.__name__ == "OpenModelessDialog":
+ old_line = self.variables.pop()
+ if self.prev_command.__class__.__name__ == "UNOCommand":
+ key_word = self.prev_command.uno_command_name[-6:]
+ else:
+ key_word = old_line[-9:-3]
+
+ if key_word == "Dialog":
+ old_line = (
+ tab * 3
+ + 'with self.ui_test.execute_modeless_dialog_through_command("'
+ + self.prev_command.uno_command_name
+ + '") as '
+ + DialogCommand.dialog_name
+ + ':\n'
+ )
+ self.variables.append(old_line)
+ self.last_parent.append(DialogCommand.dialog_name)
+ self.parent_hierarchy_count = self.parent_hierarchy_count + 1
+
+ elif DialogCommand.__class__.__name__ == "CloseDialog":
+
+ if not (self.flag_for_QuerySaveDialog):
+ # This part is just to ignore saving the Save dialog while closing the app
+
+ if self.prev_command.__class__.__name__ == "ButtonUIObject":
+ old_line = self.variables.pop()
+ line = ""
+ if keyword.iskeyword(self.prev_command.ui_button):
+ line = (
+ tab * 4
+ + "self.ui_test.close_dialog_through_button(x"
+ + self.prev_command.ui_button
+ + ")\n"
+ )
+ else:
+ line = (
+ tab * 4
+ + "self.ui_test.close_dialog_through_button("
+ + self.prev_command.ui_button
+ + ")\n"
+ )
+ self.variables.append(line)
+ self.last_parent.pop()
+ self.parent_hierarchy_count = self.parent_hierarchy_count - 1
+ else:
+ self.flag_for_QuerySaveDialog = False
+
+ # This is to solve the problem of re-using the same id again in different Dialogs
+
+ self.objects.clear()
+
+ self.prev_command = DialogCommand
+
+ def handle_button(self, ButtonUIObject):
+
+ if ButtonUIObject.parent_id != "QuerySaveDialog":
+ # This part is just to ignore saving the Save dialog while closing the app
+
+ name_of_obj = ""
+ if keyword.iskeyword(ButtonUIObject.ui_button):
+ name_of_obj = "x" + ButtonUIObject.ui_button
+ else:
+ name_of_obj = ButtonUIObject.ui_button
+
+ if ButtonUIObject.parent_id == "":
+ self.init_Object(
+ name_of_obj,
+ ButtonUIObject.ui_button,
+ self.last_parent[self.parent_hierarchy_count],
+ )
+ else:
+ self.init_Object(
+ name_of_obj, ButtonUIObject.ui_button, ButtonUIObject.parent_id
+ )
+
+ self.write_line_without_parameters(name_of_obj, "CLICK", "tuple")
+
+ self.prev_command = ButtonUIObject
+
+ def handle_check_box(self, CheckBoxUIObject):
+
+ name_of_obj = ""
+ if keyword.iskeyword(CheckBoxUIObject.Check_box_id):
+ name_of_obj = "x" + CheckBoxUIObject.Check_box_id
+ else:
+ name_of_obj = CheckBoxUIObject.Check_box_id
+
+ if CheckBoxUIObject.parent_id == "":
+ self.init_Object(
+ name_of_obj,
+ CheckBoxUIObject.Check_box_id,
+ self.last_parent[self.parent_hierarchy_count],
+ )
+ else:
+ self.init_Object(
+ name_of_obj, CheckBoxUIObject.Check_box_id, CheckBoxUIObject.parent_id
+ )
+
+ self.write_line_without_parameters(name_of_obj, "CLICK", "tuple")
+
+ self.prev_command = CheckBoxUIObject
+
+ def handle_tab(self, TabControlUIObject):
+
+ name_of_obj = ""
+ if keyword.iskeyword(TabControlUIObject.tab_id):
+ name_of_obj = "x" + TabControlUIObject.tab_id
+ else:
+ name_of_obj = TabControlUIObject.tab_id
+
+ if TabControlUIObject.parent_id == "":
+ self.init_Object(
+ name_of_obj,
+ TabControlUIObject.tab_id,
+ self.last_parent[self.parent_hierarchy_count],
+ )
+ else:
+ self.init_Object(
+ name_of_obj, TabControlUIObject.tab_id, TabControlUIObject.parent_id
+ )
+
+ self.write_line_with_one_parameters(
+ name_of_obj, "SELECT", "POS", TabControlUIObject.tab_page_number
+ )
+
+ self.prev_command = TabControlUIObject
+
+ def handle_Combo_box(self, ComboBoxUIObject):
+
+ name_of_obj = ""
+ if keyword.iskeyword(ComboBoxUIObject.Combo_box_id):
+ name_of_obj = "x" + ComboBoxUIObject.Combo_box_id
+ else:
+ name_of_obj = ComboBoxUIObject.Combo_box_id
+
+ if ComboBoxUIObject.parent_id == "":
+ self.init_Object(
+ name_of_obj,
+ ComboBoxUIObject.Combo_box_id,
+ self.last_parent[self.parent_hierarchy_count],
+ )
+ else:
+ self.init_Object(
+ name_of_obj, ComboBoxUIObject.Combo_box_id, ComboBoxUIObject.parent_id
+ )
+
+ self.write_line_with_one_parameters(
+ name_of_obj, "SELECT", "POS", ComboBoxUIObject.item_num
+ )
+
+ self.prev_command = ComboBoxUIObject
+
+ def handle_Radio_button(self, RadioButtonUIObject):
+
+ name_of_obj = ""
+ if keyword.iskeyword(RadioButtonUIObject.Radio_button_id):
+ name_of_obj = "x" + RadioButtonUIObject.Radio_button_id
+ else:
+ name_of_obj = RadioButtonUIObject.Radio_button_id
+
+ if RadioButtonUIObject.parent_id == "":
+ self.init_Object(
+ name_of_obj,
+ RadioButtonUIObject.Radio_button_id,
+ self.last_parent[self.parent_hierarchy_count],
+ )
+ else:
+ self.init_Object(
+ name_of_obj,
+ RadioButtonUIObject.Radio_button_id,
+ RadioButtonUIObject.parent_id,
+ )
+
+ self.write_line_without_parameters(name_of_obj, "CLICK", "tuple")
+
+ self.prev_command = RadioButtonUIObject
+
+ def handle_List_box(self, ListBoxUIObject):
+
+ name_of_obj = ""
+ if keyword.iskeyword(ListBoxUIObject.list_id):
+ name_of_obj = "x" + ListBoxUIObject.list_id
+ else:
+ name_of_obj = ListBoxUIObject.list_id
+
+ if ListBoxUIObject.parent_id == "":
+ self.init_Object(
+ name_of_obj,
+ ListBoxUIObject.list_id,
+ self.last_parent[self.parent_hierarchy_count],
+ )
+ else:
+ self.init_Object(
+ name_of_obj, ListBoxUIObject.list_id, ListBoxUIObject.parent_id
+ )
+
+ self.write_line_with_one_parameters(
+ name_of_obj, "SELECT", "POS", ListBoxUIObject.POS
+ )
+
+ self.prev_command = ListBoxUIObject
+
+ def handle_spin_field(self, SpinFieldUIObject):
+
+ name_of_obj = ""
+ if keyword.iskeyword(SpinFieldUIObject.Spin_id):
+ name_of_obj = "x" + SpinFieldUIObject.Spin_id
+ else:
+ name_of_obj = SpinFieldUIObject.Spin_id
+
+ if SpinFieldUIObject.parent_id == "":
+ self.init_Object(
+ name_of_obj,
+ SpinFieldUIObject.Spin_id,
+ self.last_parent[self.parent_hierarchy_count],
+ )
+ else:
+ self.init_Object(
+ name_of_obj, SpinFieldUIObject.Spin_id, SpinFieldUIObject.parent_id
+ )
+
+ if SpinFieldUIObject.change == "Increase":
+ self.write_line_without_parameters(name_of_obj, "UP", "tuple")
+ elif SpinFieldUIObject.change == "Decrease":
+ self.write_line_without_parameters(name_of_obj, "DOWN", "tuple")
+ self.prev_command = SpinFieldUIObject
+
+ def handle_Edit_uiObject(self, EditUIObject):
+
+ name_of_obj = ""
+ if keyword.iskeyword(EditUIObject.action.edit_button):
+ name_of_obj = "x" + EditUIObject.action.edit_button
+ else:
+ name_of_obj = EditUIObject.action.edit_button
+
+ if EditUIObject.parent_id == "":
+ self.init_Object(
+ name_of_obj,
+ EditUIObject.action.edit_button,
+ self.last_parent[self.parent_hierarchy_count],
+ )
+ else:
+ self.init_Object(
+ name_of_obj, EditUIObject.action.edit_button, EditUIObject.parent_id
+ )
+
+ if EditUIObject.action.__class__.__name__ == "Type_action":
+
+ if EditUIObject.action.what_to_type.__class__.__name__ == "char":
+ self.write_line_with_one_parameters(
+ name_of_obj,
+ "TYPE",
+ "TEXT",
+ EditUIObject.action.what_to_type.input_char,
+ )
+
+ elif EditUIObject.action.what_to_type.__class__.__name__ == "KeyCode":
+ self.write_line_with_one_parameters(
+ name_of_obj,
+ "TYPE",
+ "KEYCODE",
+ EditUIObject.action.what_to_type.input_key_code,
+ )
+
+ if EditUIObject.action.__class__.__name__ == "SELECT":
+
+ self.write_line_with_two_parameters(
+ name_of_obj,
+ "SELECT",
+ "FROM",
+ EditUIObject.action.from_pos,
+ "TO",
+ EditUIObject.action.to_pos,
+ )
+
+ if EditUIObject.action.__class__.__name__ == "Clear":
+
+ self.write_line_without_parameters(name_of_obj, "CLEAR", "tuple")
+
+ self.prev_command = EditUIObject
+
+ def handle_ToolBox_uiObject(self, ToolBoxUIObject):
+ name_of_obj = ""
+ if keyword.iskeyword(ToolBoxUIObject.toolbox_id):
+ name_of_obj = "x" + ToolBoxUIObject.toolbox_id
+ else:
+ name_of_obj = ToolBoxUIObject.toolbox_id
+
+ self.init_Object(
+ name_of_obj,
+ ToolBoxUIObject.toolbox_id,
+ self.last_parent[self.parent_hierarchy_count],
+ )
+
+ self.write_line_with_one_parameters(
+ name_of_obj, "CLICK", "POS", ToolBoxUIObject.POS
+ )
+
+ self.prev_command = ToolBoxUIObject
+
+ def handle_ValueSet_uiObject(self, ValueSetUIObject):
+
+ name_of_obj = ""
+ if keyword.iskeyword(ValueSetUIObject.value_set_id):
+ name_of_obj = "x" + ValueSetUIObject.value_set_id
+ else:
+ name_of_obj = ValueSetUIObject.value_set_id
+
+ parent_txt = ValueSetUIObject.parent_id.split("/")
+ parent = parent_txt[len(parent_txt)-2]
+ if( parent.upper() != self.last_parent[self.parent_hierarchy_count].upper()):
+ self.init_Object(
+ parent,
+ parent,
+ self.last_parent[self.parent_hierarchy_count],
+ )
+
+ self.init_Object(
+ name_of_obj, ValueSetUIObject.value_set_id, parent
+ )
+
+ else:
+ self.init_Object(
+ name_of_obj, ValueSetUIObject.value_set_id, self.last_parent[self.parent_hierarchy_count]
+ )
+
+ self.write_line_with_one_parameters(
+ name_of_obj, "CHOOSE", "POS", ValueSetUIObject.POS
+ )
+
+ self.prev_command = ValueSetUIObject
+
+ def handle_MenuBtnUIObjectOpen(self, MenuBtnUIObjectOpen):
+ name_of_obj = ""
+ if keyword.iskeyword(MenuBtnUIObjectOpen.MenuBtn_ID):
+ name_of_obj = "x" + MenuBtnUIObjectOpen.MenuBtn_ID
+ else:
+ name_of_obj = MenuBtnUIObjectOpen.MenuBtn_ID
+
+ self.init_Object(
+ name_of_obj,
+ MenuBtnUIObjectOpen.MenuBtn_ID,
+ self.last_parent[self.parent_hierarchy_count],
+ )
+
+ self.write_line_with_one_parameters(
+ name_of_obj, "OPENLIST", "", ""
+ )
+
+ self.prev_command = MenuBtnUIObjectOpen
+
+ def handle_MenuBtnUIObjectClose(self, MenuBtnUIObjectClose):
+ name_of_obj = ""
+ if keyword.iskeyword(MenuBtnUIObjectClose.MenuBtn_ID):
+ name_of_obj = "x" + MenuBtnUIObjectClose.MenuBtn_ID
+ else:
+ name_of_obj = MenuBtnUIObjectClose.MenuBtn_ID
+
+ self.init_Object(
+ name_of_obj,
+ MenuBtnUIObjectClose.MenuBtn_ID,
+ self.last_parent[self.parent_hierarchy_count],
+ )
+
+ self.write_line_with_one_parameters(
+ name_of_obj, "CLOSELIST", "", ""
+ )
+
+ self.prev_command = MenuBtnUIObjectClose
+
+ def handle_MenuBtnUIObjectSelect(self, MenuBtnUIObjectSelect):
+ name_of_obj = ""
+ if keyword.iskeyword(MenuBtnUIObjectSelect.MenuBtn_ID):
+ name_of_obj = "x" + MenuBtnUIObjectSelect.MenuBtn_ID
+ else:
+ name_of_obj = MenuBtnUIObjectSelect.MenuBtn_ID
+
+ self.init_Object(
+ name_of_obj,
+ MenuBtnUIObjectSelect.MenuBtn_ID,
+ self.last_parent[self.parent_hierarchy_count],
+ )
+
+ self.write_line_with_one_parameters(
+ name_of_obj, "OPENFROMLIST", "POS", MenuBtnUIObjectSelect.item_num[0]
+ )
+
+ self.prev_command = MenuBtnUIObjectSelect
+
+ def handle_writer_type(self, writer_Type_command):
+
+ self.init_app()
+
+ if writer_Type_command.what_to_type.__class__.__name__ == "char":
+ self.write_line_with_one_parameters(
+ self.current_app,
+ "TYPE",
+ "TEXT",
+ writer_Type_command.what_to_type.input_char,
+ )
+
+ elif writer_Type_command.what_to_type.__class__.__name__ == "KeyCode":
+ self.write_line_with_one_parameters(
+ self.current_app,
+ "TYPE",
+ "KEYCODE",
+ writer_Type_command.what_to_type.input_key_code,
+ )
+
+ self.prev_command = writer_Type_command
+
+ def handle_writer_select(self, writer_Select_command):
+
+ self.init_app()
+
+ self.write_line_with_two_parameters(
+ self.current_app,
+ "SELECT",
+ "END_POS",
+ writer_Select_command.from_pos,
+ "START_POS",
+ writer_Select_command.to_pos,
+ )
+
+ self.prev_command = writer_Select_command
+
+ def handle_writer_goto(self, writer_GOTO_command):
+
+ self.init_app()
+
+ self.write_line_with_one_parameters(
+ self.current_app, "GOTO", "PAGE", writer_GOTO_command.page_num
+ )
+
+ self.prev_command = writer_GOTO_command
+
+ def handle_calc_select(self, calc_Select_cell):
+
+ self.init_app()
+
+ if calc_Select_cell.select_op.__class__.__name__ == "range_of_cells":
+ self.write_line_with_one_parameters(
+ self.current_app,
+ "SELECT",
+ "RANGE",
+ calc_Select_cell.select_op.input_range,
+ )
+
+ elif calc_Select_cell.select_op.__class__.__name__ == "one_cell":
+ self.write_line_with_one_parameters(
+ self.current_app,
+ "SELECT",
+ "CELL",
+ calc_Select_cell.select_op.input_cell,
+ )
+
+ self.prev_command = calc_Select_cell
+
+ def handle_calc_switch_sheet(self, calc_switch_sheet):
+
+ self.init_app()
+
+ self.write_line_with_one_parameters(
+ self.current_app, "SELECT", "TABLE", calc_switch_sheet.sheet_num
+ )
+
+ self.prev_command = calc_switch_sheet
+
+ def handle_calc_Type_command(self, calc_Type_command):
+
+ self.init_app()
+
+ if calc_Type_command.what_to_type.__class__.__name__ == "char":
+ self.write_line_with_one_parameters(
+ self.current_app,
+ "TYPE",
+ "TEXT",
+ calc_Type_command.what_to_type.input_char,
+ )
+
+ elif calc_Type_command.what_to_type.__class__.__name__ == "KeyCode":
+ self.write_line_with_one_parameters(
+ self.current_app,
+ "TYPE",
+ "KEYCODE",
+ calc_Type_command.what_to_type.input_key_code,
+ )
+
+ self.prev_command = calc_Type_command
+
+ def handle_calc_AutoFill_filter(self, calc_AutoFill_filter):
+
+ self.init_app()
+
+ line = (
+ tab * 3
+ + self.current_app
+ + '.executeAction("LAUNCH", mkPropertyValues'
+ + '({"AUTOFILTER": "", "COL": "'
+ + str(calc_AutoFill_filter.col_num)
+ + '"'
+ + ', "ROW": "'
+ + str(calc_AutoFill_filter.row_num)
+ + '"}))\n'
+ )
+
+ self.variables.append(line)
+ self.prev_command = calc_AutoFill_filter
+
+ def handle_calc_Open_Comment(self, calc_Open_Comment):
+
+ line = (
+ tab * 3
+ + self.current_app
+ + '.executeAction("COMMENT", mkPropertyValues'
+ + '({"OPEN": " "}))\n'
+ )
+
+ self.variables.append(line)
+
+ self.prev_command = calc_Open_Comment
+
+ def handle_calc_Close_Comment(self, calc_Close_Comment):
+
+ line = (
+ tab * 3
+ + self.current_app
+ + '.executeAction("COMMENT", mkPropertyValues'
+ + '({"CLOSE": " "}))\n'
+ )
+
+ self.variables.append(line)
+
+ self.prev_command = calc_Close_Comment
+
+ def handle_calc_SelectMenu_filter(self, calc_SelectMenu_filter):
+
+ self.init_app()
+
+ line = (
+ tab * 3
+ + self.current_app
+ + '.executeAction("LAUNCH", mkPropertyValues'
+ + '({"SELECTMENU": "", "COL": "'
+ + str(calc_SelectMenu_filter.col_num)
+ + '"'
+ + ', "ROW": "'
+ + str(calc_SelectMenu_filter.row_num)
+ + '"}))\n'
+ )
+
+ self.variables.append(line)
+ self.prev_command = calc_SelectMenu_filter
+
+ def handle_impress_Type_command(self, impress_Type_command):
+
+ self.init_app()
+
+ if impress_Type_command.what_to_type.__class__.__name__ == "char":
+ self.write_line_with_one_parameters(
+ self.current_app,
+ "TYPE",
+ "TEXT",
+ impress_Type_command.what_to_type.input_char,
+ )
+
+ elif impress_Type_command.what_to_type.__class__.__name__ == "KeyCode":
+ self.write_line_with_one_parameters(
+ self.current_app,
+ "TYPE",
+ "KEYCODE",
+ impress_Type_command.what_to_type.input_key_code,
+ )
+
+ self.prev_command = impress_Type_command
+
+ def handle_math_Type_command(self, math_Type_command):
+
+ self.init_app()
+ if math_Type_command.what_to_type.__class__.__name__ == "char":
+ self.write_line_with_one_parameters(
+ self.current_app,
+ "TYPE",
+ "TEXT",
+ math_Type_command.what_to_type.input_char,
+ )
+
+ elif math_Type_command.what_to_type.__class__.__name__ == "KeyCode":
+ self.write_line_with_one_parameters(
+ self.current_app,
+ "TYPE",
+ "KEYCODE",
+ math_Type_command.what_to_type.input_key_code,
+ )
+
+ self.prev_command = math_Type_command
+
+ def handle_draw_Type_command(self, draw_Type_command):
+
+ self.init_app()
+ if draw_Type_command.what_to_type.__class__.__name__ == "char":
+ self.write_line_with_one_parameters(
+ self.current_app,
+ "TYPE",
+ "TEXT",
+ draw_Type_command.what_to_type.input_char,
+ )
+
+ elif draw_Type_command.what_to_type.__class__.__name__ == "KeyCode":
+ self.write_line_with_one_parameters(
+ self.current_app,
+ "TYPE",
+ "KEYCODE",
+ draw_Type_command.what_to_type.input_key_code,
+ )
+
+ self.prev_command = draw_Type_command
+
+ def handle_math_element_selector(self, math_element_selector):
+
+ if( self.math_element_selector_initializer == False ):
+ # This part is for initializing the element selector in the Math application
+ self.math_element_selector_initializer = True
+ line = (
+ tab * 4
+ + "element_selector"
+ + ' = MainWindow.getChild("'
+ + "element_selector"
+ + '")\n'
+ )
+ self.variables.append(line)
+
+ # This inserts a prefix of 'x' to avoid creating variables with only numeric characters
+ element_name="x"+str(math_element_selector.element_no)
+
+ self.init_Object(element_name,str(math_element_selector.element_no),"element_selector")
+
+ self.write_line_without_parameters(
+ str(element_name), "SELECT", "tuple"
+ )
+
+ self.prev_command = math_element_selector
+
+ def handle_writer_Comment_leave(self,writer_Comment_leave):
+
+ self.init_app()
+
+ self.init_Object(
+ writer_Comment_leave.comment_id, writer_Comment_leave.comment_id, "MainWindow"
+ )
+
+ self.write_line_with_one_parameters(
+ writer_Comment_leave.comment_id, "LEAVE", "", ""
+ )
+
+ self.prev_command = writer_Comment_leave
+
+ def handle_writer_Comment_show(self,writer_Comment_show):
+
+ self.init_app()
+
+ self.init_Object(
+ writer_Comment_show.comment_id, writer_Comment_show.comment_id, "MainWindow"
+ )
+
+ self.write_line_with_one_parameters(
+ writer_Comment_show.comment_id, "SHOW", "", ""
+ )
+
+ self.prev_command = writer_Comment_show
+
+ def handle_writer_Comment_hide(self,writer_Comment_hide):
+
+ self.init_app()
+
+ self.init_Object(
+ writer_Comment_hide.comment_id, writer_Comment_hide.comment_id, "MainWindow"
+ )
+
+ self.write_line_with_one_parameters(
+ writer_Comment_hide.comment_id, "HIDE", "", ""
+ )
+
+ self.prev_command = writer_Comment_hide
+
+ def handle_writer_Comment_delete(self,writer_Comment_delete):
+
+ self.init_app()
+
+ self.init_Object(
+ writer_Comment_delete.comment_id, writer_Comment_delete.comment_id, "MainWindow"
+ )
+
+ self.write_line_with_one_parameters(
+ writer_Comment_delete.comment_id, "DELETE", "", ""
+ )
+
+ self.prev_command = writer_Comment_delete
+
+ def handle_writer_Comment_setresolved(self,writer_Comment_setresolved):
+
+ self.init_app()
+
+ self.init_Object(
+ writer_Comment_setresolved.comment_id, writer_Comment_setresolved.comment_id, "MainWindow"
+ )
+
+ self.write_line_with_one_parameters(
+ writer_Comment_setresolved.comment_id, "RESOLVE", "", ""
+ )
+
+ self.prev_command = writer_Comment_setresolved
+
+ def handle_setZoom_command(self, setZoom_command):
+
+ self.init_app()
+
+ self.write_line_with_one_parameters(
+ self.current_app, "SET", "ZOOM", setZoom_command.zoom_value
+ )
+
+ self.prev_command = setZoom_command
+
+ def Generate_UI_test(self):
+ line = "\n\n# vim: set shiftwidth=4 softtabstop=4 expandtab:"
+ self.variables.append(line)
+
+ for line in self.variables:
+ self.output_stream.write(str(line))
+
+ def do_nothing(self, Command):
+ line = "to be added in the future"
+
+ def __del__(self):
+ self.output_stream.close()
+
+
+def main():
+ args = parse_args()
+ ui_logger = ul_Compiler(args.input_address, args.output_address)
+ ui_logger.compile()
+ for statement in ui_logger.log_lines.commands:
+ print(statement)
+ ui_logger.Generate_UI_test()
+ del ui_logger
+
+
+if __name__ == "__main__":
+ main()
diff --git a/uitest/ui_logger_dsl/example.ul b/uitest/ui_logger_dsl/example.ul
new file mode 100644
index 0000000000..d82c71a29f
--- /dev/null
+++ b/uitest/ui_logger_dsl/example.ul
@@ -0,0 +1,34 @@
+Start writer
+Send UNO Command (".uno:UpdateInputFields")
+Send UNO Command (".uno:FontDialog")
+Open Modal CharacterPropertiesDialog
+Choose Tab number 0 in 'tabcontrol' from CharacterPropertiesDialog
+Select in 'westfontnamelb-cjk' ComboBox item number 66 from CharacterPropertiesDialog
+Choose Tab number 1 in 'tabcontrol' from CharacterPropertiesDialog
+Select element with position 3 in 'effectslb' fromCharacterPropertiesDialog
+Toggle 'shadowcb' CheckBox from CharacterPropertiesDialog
+Choose Tab number 2 in 'tabcontrol' from CharacterPropertiesDialog
+Select '270deg' RadioButton from CharacterPropertiesDialog
+Select '90deg' RadioButton from CharacterPropertiesDialog
+Increase 'scalewidthsb' from CharacterPropertiesDialog
+Increase 'scalewidthsb' from CharacterPropertiesDialog
+Increase 'scalewidthsb' from CharacterPropertiesDialog
+Decrease 'scalewidthsb' from CharacterPropertiesDialog
+Decrease 'scalewidthsb' from CharacterPropertiesDialog
+Decrease 'scalewidthsb' from CharacterPropertiesDialog
+Toggle 'pairkerning' CheckBox from CharacterPropertiesDialog
+Click on 'cancel' from CharacterPropertiesDialog
+Close Dialog
+Type on writer {"TEXT": "a"}
+Type on writer {"TEXT": "n"}
+Type on writer {"TEXT": "a"}
+Type on writer {"TEXT": " "}
+Type on writer {"TEXT": "a"}
+Type on writer {"TEXT": "h"}
+Type on writer {"TEXT": "m"}
+Type on writer {"TEXT": "e"}
+Type on writer {"TEXT": "d"}
+Select from Pos 4 to Pos 9
+Open Modal QuerySaveDialog
+Click on 'discard' from QuerySaveDialog
+Close Dialog \ No newline at end of file
diff --git a/uitest/ui_logger_dsl/starter_commands.tx b/uitest/ui_logger_dsl/starter_commands.tx
new file mode 100644
index 0000000000..b581cf7a34
--- /dev/null
+++ b/uitest/ui_logger_dsl/starter_commands.tx
@@ -0,0 +1,10 @@
+// This file is for the starter commands when you open any office application.
+// Button clicks for opening an application are automatically translated as coming from a starter button.
+
+StarterCommand:
+ 'Start' program_name=Program
+;
+
+Program:
+ "writer"|"calc"|"impress"|"draw"|"math"|"database"
+;
diff --git a/uitest/ui_logger_dsl/type_options.tx b/uitest/ui_logger_dsl/type_options.tx
new file mode 100644
index 0000000000..b106e7f038
--- /dev/null
+++ b/uitest/ui_logger_dsl/type_options.tx
@@ -0,0 +1,9 @@
+Type_options:
+ char | KeyCode
+;
+char:
+ '{' '"TEXT":' input_char=STRING '}'
+;
+KeyCode:
+ '{' '"KEYCODE":' input_key_code=STRING '}'
+;
diff --git a/uitest/ui_logger_dsl/ui_logger_dsl_grammar.tx b/uitest/ui_logger_dsl/ui_logger_dsl_grammar.tx
new file mode 100644
index 0000000000..717fb7172f
--- /dev/null
+++ b/uitest/ui_logger_dsl/ui_logger_dsl_grammar.tx
@@ -0,0 +1,29 @@
+/*
+ This file is for defining the DSL grammar.
+ This file imports all grammar rules from all the other files.
+ The compiler works with this file.
+ Each imported file has comments related to its content.
+*/
+
+import dialog_commands
+import starter_commands
+import uno_commands
+import UI_Object_commands
+import Special_commands
+import General_commands
+
+UILogger:
+ commands*=Command
+;
+
+Command:
+ UNOCommand | StarterCommand | UIObjectCommand | DialogCommand |
+ SpecialCommand | GeneralCommand | Comment
+;
+/*
+ The Comment rule is for having an ability to write a comment,
+ if you want to write a test case in the DSL
+*/
+Comment:
+ /\/\/.*$/
+;
diff --git a/uitest/ui_logger_dsl/uno_commands.tx b/uitest/ui_logger_dsl/uno_commands.tx
new file mode 100644
index 0000000000..597c855cb8
--- /dev/null
+++ b/uitest/ui_logger_dsl/uno_commands.tx
@@ -0,0 +1,20 @@
+/*
+ This file is for the grammar of the UNO commands.
+ It has two modes: one with parameters and one without.
+*/
+
+UNOCommand:
+ 'Send UNO Command' '(' uno_command_name=STRING ')' (parameters=parameter)?
+;
+
+parameter:
+ '{' parameter_data *= data ','? '}'
+;
+
+data:
+ ','? key=STRING ':' value= value_type
+;
+
+value_type:
+ INT|ID
+;
diff --git a/uitest/uitest/bisecting.py b/uitest/uitest/bisecting.py
new file mode 100644
index 0000000000..938baaa2f9
--- /dev/null
+++ b/uitest/uitest/bisecting.py
@@ -0,0 +1,14 @@
+# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*-
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+def requires(revision):
+ def decorator(f):
+ f.requires = revision
+ return f
+ return decorator
+
+# vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/uitest/uitest/framework.py b/uitest/uitest/framework.py
new file mode 100644
index 0000000000..0bb6d3ecdb
--- /dev/null
+++ b/uitest/uitest/framework.py
@@ -0,0 +1,79 @@
+# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*-
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+import signal
+import unittest
+import time
+
+from uitest.test import UITest
+
+from libreoffice.connection import PersistentConnection
+
+class UITestCase(unittest.TestCase):
+
+ def __init__(self, test_name, opts, connection=None):
+ unittest.TestCase.__init__(self, test_name)
+ self.opts = opts
+ self.parent_connection = connection
+ self.connection = None
+
+ def getConnection(self):
+ if self.parent_connection:
+ return self.parent_connection
+ return self.connection
+
+ def setUp(self):
+ self.setSignalHandler()
+ if not self.getConnection():
+ self.connection = PersistentConnection(self.opts)
+ self.connection.setUp()
+ self.xContext = self.getConnection().getContext()
+ self.xUITest = self.xContext.ServiceManager.createInstanceWithContext(
+ "org.libreoffice.uitest.UITest", self.xContext)
+
+ self.ui_test = UITest(self.xUITest, self.xContext)
+ if self.parent_connection:
+ self.ui_test.set_use_dispose(False)
+ self.startTime = time.time()
+
+ def tearDown(self):
+ try:
+ t = time.time() - self.startTime
+ print("Execution time for %s: %.3f" % (self.id(), t))
+ if self.xContext is not None:
+ try:
+ desktop = self.ui_test.get_desktop()
+ components = desktop.getComponents()
+ for component in components:
+ component.close(False)
+ except Exception as e:
+ print(e)
+
+ if self.connection:
+ self.connection.tearDown()
+ finally:
+ self.resetSignalHandler()
+ if self.connection:
+ self.connection.kill()
+
+ def signalHandler(self, signum, frame):
+ if self.getConnection():
+ self.getConnection().kill()
+
+ def setSignalHandler(self):
+ signal.signal(signal.SIGABRT, self.signalHandler)
+ signal.signal(signal.SIGSEGV, self.signalHandler)
+ signal.signal(signal.SIGTERM, self.signalHandler)
+ signal.signal(signal.SIGILL, self.signalHandler)
+
+ def resetSignalHandler(self):
+ signal.signal(signal.SIGABRT, signal.SIG_IGN)
+ signal.signal(signal.SIGSEGV, signal.SIG_IGN)
+ signal.signal(signal.SIGTERM, signal.SIG_IGN)
+ signal.signal(signal.SIGILL, signal.SIG_IGN)
+
+# vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/uitest/uitest/path.py b/uitest/uitest/path.py
new file mode 100644
index 0000000000..5a3aeff22c
--- /dev/null
+++ b/uitest/uitest/path.py
@@ -0,0 +1,31 @@
+# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*-
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+import os
+from urllib.parse import urljoin
+from urllib.request import pathname2url
+
+def get_src_dir_fallback():
+ current_dir = os.path.dirname(os.path.realpath(__file__))
+ return os.path.abspath(os.path.join(current_dir, "../../"))
+
+def path2url(path):
+ return urljoin('file:', pathname2url(os.path.normpath(path)))
+
+def get_workdir_url():
+ workdir_path = os.environ.get('WORKDIR', os.path.join(get_src_dir_fallback(), 'workdir'))
+ return path2url(workdir_path)
+
+def get_srcdir_url():
+ srcdir_path = os.environ.get('SRCDIR', get_src_dir_fallback())
+ return path2url(srcdir_path)
+
+def get_instdir_url():
+ instdir_path = os.environ.get('INSTDIR', os.path.join(get_src_dir_fallback(), 'instdir'))
+ return path2url(instdir_path)
+
+# vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/uitest/uitest/test.py b/uitest/uitest/test.py
new file mode 100644
index 0000000000..db2bc1828b
--- /dev/null
+++ b/uitest/uitest/test.py
@@ -0,0 +1,260 @@
+# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*-
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+import time
+import threading
+from contextlib import contextmanager
+from uitest.uihelper.common import get_state_as_dict
+
+from com.sun.star.uno import RuntimeException
+
+from libreoffice.uno.eventlistener import EventListener
+
+DEFAULT_SLEEP = 0.1
+
+class UITest(object):
+
+ def __init__(self, xUITest, xContext):
+ self._xUITest = xUITest
+ self._xContext = xContext
+ self._desktop = None
+ self.use_dispose = True
+
+ def set_use_dispose(self, use_dispose):
+ self.use_dispose = use_dispose
+
+ def get_desktop(self):
+ if self._desktop:
+ return self._desktop
+
+ self._desktop = self._xContext.ServiceManager.createInstanceWithContext("com.sun.star.frame.Desktop", self._xContext)
+ return self._desktop
+
+ def get_frames(self):
+ desktop = self.get_desktop()
+ frames = desktop.getFrames()
+ return frames
+
+ def get_component(self):
+ desktop = self.get_desktop()
+ components = desktop.getComponents()
+ for component in components:
+ if component is not None:
+ return component
+
+ def get_default_sleep(self):
+ return DEFAULT_SLEEP
+
+ def wait_for_top_focus_window(self, id):
+ while True:
+ win = self._xUITest.getTopFocusWindow()
+ if get_state_as_dict(win)['ID'] == id:
+ return win
+ time.sleep(DEFAULT_SLEEP)
+
+ def wait_until_child_is_available(self, childName):
+ while True:
+ xDialog = self._xUITest.getTopFocusWindow()
+ if childName in xDialog.getChildren():
+ return xDialog.getChild(childName)
+ else:
+ time.sleep(DEFAULT_SLEEP)
+
+ def wait_until_property_is_updated(self, element, propertyName, value):
+ while True:
+ if get_state_as_dict(element)[propertyName] == value:
+ return
+ else:
+ time.sleep(DEFAULT_SLEEP)
+
+ @contextmanager
+ def wait_until_component_loaded(self):
+ with EventListener(self._xContext, "OnLoad") as event:
+ yield
+ while True:
+ if event.executed:
+ frames = self.get_frames()
+ if len(frames) == 1:
+ self.get_desktop().setActiveFrame(frames[0])
+ time.sleep(DEFAULT_SLEEP)
+ return
+ time.sleep(DEFAULT_SLEEP)
+
+ def load_component_from_url(self, url, eventName="OnLoad", load_props=()):
+ with EventListener(self._xContext, eventName) as event:
+ component = self.get_desktop().loadComponentFromURL(url, "_default", 0, load_props)
+ while True:
+ if event.executed:
+ frames = self.get_frames()
+ #activate the newest frame
+ self.get_desktop().setActiveFrame(frames[-1])
+ return component
+ time.sleep(DEFAULT_SLEEP)
+
+ # Calls UITest.close_doc at exit
+ @contextmanager
+ def load_file(self, url, load_props=()):
+ try:
+ yield self.load_component_from_url(url, "OnLoad", load_props)
+ finally:
+ self.close_doc()
+
+ # Resets the setting to the old value at exit
+ @contextmanager
+ def set_config(self, path, new_value):
+ xChanges = self._xContext.ServiceManager.createInstanceWithArgumentsAndContext('com.sun.star.configuration.ReadWriteAccess', ("",), self._xContext)
+ try:
+ old_value = xChanges.getByHierarchicalName(path)
+ xChanges.replaceByHierarchicalName(path, new_value)
+ xChanges.commitChanges()
+ yield
+ finally:
+ xChanges.replaceByHierarchicalName(path, old_value)
+ xChanges.commitChanges()
+
+ # Calls UITest.close_doc at exit
+ @contextmanager
+ def load_empty_file(self, app):
+ try:
+ yield self.load_component_from_url("private:factory/s" + app, "OnNew")
+ finally:
+ self.close_doc()
+
+ def wait_and_yield_dialog(self, event, parent, close_button):
+ while not event.executed:
+ time.sleep(DEFAULT_SLEEP)
+ dialog = self._xUITest.getTopFocusWindow()
+ if parent == dialog:
+ raise Exception("executing the action did not open the dialog")
+ try:
+ yield dialog
+ except:
+ if not close_button:
+ if 'cancel' in dialog.getChildren():
+ self.close_dialog_through_button(dialog.getChild("cancel"))
+ raise
+ finally:
+ if close_button:
+ self.close_dialog_through_button(dialog.getChild(close_button))
+
+ # Calls UITest.close_dialog_through_button at exit
+ @contextmanager
+ def execute_dialog_through_command(self, command, printNames=False, close_button = "ok", eventName = "DialogExecute"):
+ with EventListener(self._xContext, eventName, printNames=printNames) as event:
+ xDialogParent = self._xUITest.getTopFocusWindow()
+ if not self._xUITest.executeDialog(command):
+ raise Exception("Dialog not executed for: " + command)
+ yield from self.wait_and_yield_dialog(event, xDialogParent, close_button)
+
+ @contextmanager
+ def execute_modeless_dialog_through_command(self, command, printNames=False, close_button = "ok"):
+ with self.execute_dialog_through_command(command, printNames, close_button, "ModelessDialogVisible") as xDialog:
+ yield xDialog
+
+ # Calls UITest.close_dialog_through_button at exit
+ @contextmanager
+ def execute_dialog_through_action(self, ui_object, action, parameters = None, event_name = "DialogExecute", close_button = "ok"):
+ if parameters is None:
+ parameters = tuple()
+
+ xDialogParent = self._xUITest.getTopFocusWindow()
+ with EventListener(self._xContext, event_name) as event:
+ ui_object.executeAction(action, parameters)
+ yield from self.wait_and_yield_dialog(event, xDialogParent, close_button)
+
+ # Calls UITest.close_doc at exit
+ @contextmanager
+ def create_doc_in_start_center(self, app):
+ xStartCenter = self._xUITest.getTopFocusWindow()
+ try:
+ xBtn = xStartCenter.getChild(app + "_all")
+ except RuntimeException:
+ raise
+
+ with EventListener(self._xContext, "OnNew") as event:
+ xBtn.executeAction("CLICK", tuple())
+ while True:
+ if event.executed:
+ frames = self.get_frames()
+ self.get_desktop().setActiveFrame(frames[0])
+ component = self.get_component()
+ try:
+ yield component
+ finally:
+ self.close_doc()
+ return
+ time.sleep(DEFAULT_SLEEP)
+
+ def close_dialog_through_button(self, button):
+ with EventListener(self._xContext, "DialogClosed" ) as event:
+ button.executeAction("CLICK", tuple())
+ while True:
+ if event.executed:
+ time.sleep(DEFAULT_SLEEP)
+ return
+ time.sleep(DEFAULT_SLEEP)
+
+ def close_doc(self):
+ desktop = self.get_desktop()
+ active_frame = desktop.getActiveFrame()
+ if not active_frame:
+ print("close_doc: no active frame")
+ return
+ component = active_frame.getController().getModel()
+ if not component:
+ print("close_doc: active frame has no component")
+ return
+ if self.use_dispose:
+ component.dispose()
+ else:
+ if component.isModified():
+ with self.execute_dialog_through_command('.uno:CloseDoc', close_button="discard") as xConfirmationDialog:
+ pass
+ else:
+ self._xUITest.executeCommand(".uno:CloseDoc")
+ frames = desktop.getFrames()
+ if frames:
+ frames[0].activate()
+
+ @contextmanager
+ def execute_blocking_action(self, action, args=(), close_button="ok", printNames=False):
+ """Executes an action which blocks while a dialog is shown.
+
+ Click a button or perform some other action on the dialog when it
+ is shown.
+
+ Args:
+ action(callable): Will be called to show a dialog, and is expected
+ to block while the dialog is shown.
+ close_button(str): The name of a button which will be clicked to close
+ the dialog. if it's empty, the dialog won't be closed from here.
+ This is useful when consecutive dialogs are open one after the other.
+ args(tuple, optional): The arguments to be passed to `action`
+ printNames: print all received event names
+ """
+
+ thread = threading.Thread(target=action, args=args)
+ with EventListener(self._xContext, ["DialogExecute", "ModelessDialogExecute", "ModelessDialogVisible"], printNames=printNames) as event:
+ thread.start()
+ while True:
+ if event.executed:
+ xDialog = self._xUITest.getTopFocusWindow()
+ try:
+ yield xDialog
+ except:
+ if not close_button:
+ if 'cancel' in xDialog.getChildren():
+ self.close_dialog_through_button(xDialog.getChild("cancel"))
+ raise
+ finally:
+ if close_button:
+ self.close_dialog_through_button(xDialog.getChild(close_button))
+ thread.join()
+ return
+ time.sleep(DEFAULT_SLEEP)
+
+# vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/uitest/uitest/uihelper/__init__.py b/uitest/uitest/uihelper/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/uitest/uitest/uihelper/__init__.py
diff --git a/uitest/uitest/uihelper/calc.py b/uitest/uitest/uihelper/calc.py
new file mode 100644
index 0000000000..e34304c690
--- /dev/null
+++ b/uitest/uitest/uihelper/calc.py
@@ -0,0 +1,16 @@
+# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*-
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+from libreoffice.uno.propertyvalue import mkPropertyValues
+from uitest.uihelper.common import type_text
+
+def enter_text_to_cell(gridwin, cell, text):
+ gridwin.executeAction("SELECT", mkPropertyValues({"CELL": cell}))
+ type_text(gridwin, text)
+ gridwin.executeAction("TYPE", mkPropertyValues({"KEYCODE": "RETURN"}))
+
+# vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/uitest/uitest/uihelper/common.py b/uitest/uitest/uihelper/common.py
new file mode 100644
index 0000000000..17b682b0e0
--- /dev/null
+++ b/uitest/uitest/uihelper/common.py
@@ -0,0 +1,68 @@
+# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*-
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+from libreoffice.uno.propertyvalue import convert_property_values_to_dict, mkPropertyValues
+from contextlib import contextmanager
+import org.libreoffice.unotest
+import pathlib
+
+def get_state_as_dict(ui_object):
+ return convert_property_values_to_dict(ui_object.getState())
+
+def type_text(ui_object, text):
+ ui_object.executeAction("TYPE", mkPropertyValues({"TEXT": text}))
+
+def select_pos(ui_object, pos):
+ assert isinstance(pos, str), "select_pos: POS must be of type str"
+ ui_object.executeAction("SELECT", mkPropertyValues({"POS": pos}))
+
+def select_by_text(ui_object, text):
+ ui_object.executeAction("SELECT", mkPropertyValues({"TEXT": text}))
+
+def select_text(ui_object, from_pos, to):
+ ui_object.executeAction("SELECT", mkPropertyValues({"FROM": from_pos, "TO": to}))
+
+def get_url_for_data_file(file_name):
+ return pathlib.Path(org.libreoffice.unotest.makeCopyFromTDOC(file_name)).as_uri()
+
+@contextmanager
+def change_measurement_unit(UITestCase, unit):
+ try:
+ launch_option_dialog(UITestCase, unit)
+ yield
+ finally:
+ # change to default value
+ launch_option_dialog(UITestCase, 'Inch')
+
+def launch_option_dialog(UITestCase, unit):
+ with UITestCase.ui_test.execute_dialog_through_command(".uno:OptionsTreeDialog") as xDialogOpt:
+ xPages = xDialogOpt.getChild("pages")
+ xAppEntry = xPages.getChild('3')
+ xAppEntry.executeAction("EXPAND", tuple())
+ xGeneralEntry = xAppEntry.getChild('0')
+ xGeneralEntry.executeAction("SELECT", tuple())
+
+ # Calc
+ if 'unitlb' in xDialogOpt.getChildren():
+ xUnit = xDialogOpt.getChild("unitlb")
+
+ # Writer
+ elif 'metric' in xDialogOpt.getChildren():
+ xUnit = xDialogOpt.getChild("metric")
+
+ # Impress
+ elif 'units' in xDialogOpt.getChildren():
+ xUnit = xDialogOpt.getChild("units")
+
+ select_by_text(xUnit, unit)
+
+ # tdf#137930: Check apply button doesn't reset the value
+ xApplyBtn = xDialogOpt.getChild("apply")
+ xApplyBtn.executeAction("CLICK", tuple())
+ UITestCase.assertEqual(unit, get_state_as_dict(xUnit)['SelectEntryText'])
+
+# vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/uitest/uitest/uihelper/keyboard.py b/uitest/uitest/uihelper/keyboard.py
new file mode 100644
index 0000000000..f2be76de40
--- /dev/null
+++ b/uitest/uitest/uihelper/keyboard.py
@@ -0,0 +1,13 @@
+# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*-
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+from libreoffice.uno.propertyvalue import mkPropertyValues
+
+def select_all(ui_object):
+ ui_object.executeAction("TYPE", mkPropertyValues({"KEYCODE":"CTRL+A"}))
+
+# vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/uitest/uitest/uihelper/testDialog.py b/uitest/uitest/uihelper/testDialog.py
new file mode 100644
index 0000000000..488b202f60
--- /dev/null
+++ b/uitest/uitest/uihelper/testDialog.py
@@ -0,0 +1,32 @@
+# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*-
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+from uitest.uihelper.common import get_state_as_dict
+
+
+# opens the dialogs, closes it with the given close button
+# and if there is an "OK" button open the dialog again and close it by using the OK button
+# the test only checks if LibreOffice crashes by opening the dialog
+def testDialog(UITestCase, app, dialog):
+ with UITestCase.ui_test.create_doc_in_start_center(app):
+ with UITestCase.ui_test.execute_dialog_through_command(dialog['command'], close_button=dialog['closeButton']) as xDialog:
+ if 'skipTestOK' in dialog and dialog['skipTestOK'] == True:
+ xOKBtn = None
+ else:
+ try:
+ xOKBtn = xDialog.getChild("ok")
+ if (get_state_as_dict(xOKBtn)["Enabled"] != "true"):
+ xOKBtn = None
+ except:
+ xOKBtn = None
+
+ if (xOKBtn != None):
+ print("check also OK button")
+ with UITestCase.ui_test.execute_dialog_through_command(dialog['command']):
+ pass
+
+# vim: set shiftwidth=4 softtabstop=4 expandtab: