paul 4 years ago
parent
commit
3997a1ef1d
100 changed files with 33 additions and 7003 deletions
  1. 0 15
      .gitignore
  2. 12 0
      .idea/baangt-Docker-Appium.iml
  3. 7 0
      .idea/misc.xml
  4. 8 0
      .idea/modules.xml
  5. 6 0
      .idea/vcs.xml
  6. 0 674
      LICENSE
  7. 0 1
      MANIFEST.in
  8. 0 9
      MakePackage.sh
  9. 0 5
      README.md
  10. 0 8
      TechSpecs/30 Ready/CREATE_EXECUTABLE.md
  11. 0 44
      TechSpecs/30 Ready/UI_ShowStatusOfTestrun.md
  12. 0 56
      TechSpecs/30 Ready/randomValues.md
  13. 0 32
      TechSpecs/60 InProgress/PYSIMPLEGUI_UI_ADDITONS_1.md
  14. 0 126
      TechSpecs/70 Impl Done/CONCEPT_DB_UI.md
  15. 0 41
      TechSpecs/70 Impl Done/DATABASE_AND_UI.md
  16. 0 19
      TechSpecs/70 Impl Done/MULTIPROCESSING_REFACTOR.md
  17. 0 15
      TechSpecs/70 Impl Done/Plugin-Readyness.md
  18. 0 10
      TechSpecs/70 Impl Done/UnitTests_Part1.md
  19. 0 39
      TechSpecs/70 Impl Done/Versioning.md
  20. 0 62
      TechSpecs/70 Impl Done/XLSX2BaangtDBAndViceVersa.md
  21. 0 6
      TechSpecs/80 Merged/DOWNLOAD_BROWSER_DRIVERS.md
  22. 0 64
      TechSpecs/80 Merged/KatalonRecorderImporter.md
  23. 0 16
      TechSpecs/80 Merged/LogNetworkTraffic.md
  24. 0 62
      TechSpecs/80 Merged/addressCreation.md
  25. 0 26
      TechSpecs/80 Merged/assertions.md
  26. 0 13
      TechSpecs/99 Done/READ_THE_DOCS.md
  27. 0 18
      TechSpecs/99 Done/TECHSPEC_RUNLOG.md
  28. 0 9
      TechSpecs/_ProcessWithContributers.md
  29. 0 6
      baangt.py
  30. 0 83
      baangt/TestCase/TestCaseMaster.py
  31. 0 0
      baangt/TestCase/__init__.py
  32. 0 151
      baangt/TestCaseSequence/TestCaseSequenceMaster.py
  33. 0 52
      baangt/TestCaseSequence/TestCaseSequenceParallel.py
  34. 0 0
      baangt/TestCaseSequence/__init__.py
  35. 0 133
      baangt/TestSteps/AddressCreation.py
  36. 0 22
      baangt/TestSteps/DropsApp/Login_API.py
  37. 0 0
      baangt/TestSteps/DropsApp/__init__.py
  38. 0 8
      baangt/TestSteps/Exceptions.py
  39. 0 315
      baangt/TestSteps/TestStepMaster.py
  40. 0 0
      baangt/TestSteps/__init__.py
  41. 0 71
      baangt/__init__.py
  42. 0 39
      baangt/base/AddressCreate.py
  43. 0 110
      baangt/base/ApiHandling.py
  44. 0 855
      baangt/base/BrowserHandling/BrowserHandling.py
  45. 0 0
      baangt/base/BrowserHandling/__init__.py
  46. 0 141
      baangt/base/BrowserHandling/hookImpls.py
  47. 0 9
      baangt/base/BrowserHandling/logs/20200318_145218.log
  48. 0 9
      baangt/base/BrowserHandling/logs/20200318_145232.log
  49. 0 11
      baangt/base/BrowserHandling/logs/20200318_145255.log
  50. 0 13
      baangt/base/BrowserHandling/logs/20200318_145335.log
  51. 0 12
      baangt/base/BrowserHandling/logs/20200318_145337.log
  52. 0 12
      baangt/base/BrowserHandling/logs/20200318_145352.log
  53. 0 13
      baangt/base/BrowserHandling/logs/20200318_145407.log
  54. 0 12
      baangt/base/BrowserHandling/logs/20200318_145411.log
  55. 0 12
      baangt/base/BrowserHandling/logs/20200318_145440.log
  56. 0 11
      baangt/base/BrowserHandling/logs/20200318_145533.log
  57. 0 12
      baangt/base/BrowserHandling/logs/20200318_145546.log
  58. 0 12
      baangt/base/BrowserHandling/logs/20200318_145549.log
  59. 0 11
      baangt/base/BrowserHandling/logs/20200318_145642.log
  60. 0 12
      baangt/base/BrowserHandling/logs/20200318_145655.log
  61. 0 12
      baangt/base/BrowserHandling/logs/20200318_145701.log
  62. 0 12
      baangt/base/BrowserHandling/logs/20200318_145733.log
  63. 0 12
      baangt/base/BrowserHandling/logs/20200318_145737.log
  64. 0 12
      baangt/base/BrowserHandling/logs/20200318_145752.log
  65. 0 12
      baangt/base/BrowserHandling/logs/20200318_145755.log
  66. 0 73
      baangt/base/CliAndInteractive.py
  67. 0 13
      baangt/base/CustGlobalConstants.py
  68. 0 25
      baangt/base/DataBaseORM.py
  69. 0 528
      baangt/base/ExportResults/ExportResults.py
  70. 0 0
      baangt/base/ExportResults/__init__.py
  71. 0 73
      baangt/base/ExportResults/hookImpls.py
  72. 0 98
      baangt/base/GlobalConstants.py
  73. 0 153
      baangt/base/HandleDatabase.py
  74. 0 36
      baangt/base/IBAN.py
  75. 0 9
      baangt/base/ODataHandling.py
  76. 0 9
      baangt/base/SOAPHandling.py
  77. 0 332
      baangt/base/TestRun/TestRun.py
  78. 0 4
      baangt/base/TestRun/__init__.py
  79. 0 50
      baangt/base/TestRun/hookImpls.py
  80. 0 283
      baangt/base/TestRunDatabaseCreate.py
  81. 0 243
      baangt/base/TestRunExcelImporter.py
  82. 0 48
      baangt/base/TestRunUtils.py
  83. 0 73
      baangt/base/Timing/Timing.py
  84. 0 0
      baangt/base/Timing/__init__.py
  85. 0 43
      baangt/base/Timing/hookImpls.py
  86. 0 134
      baangt/base/Utils.py
  87. 0 0
      baangt/base/__init__.py
  88. BIN
      baangt/chromedriver.exe
  89. 0 409
      baangt/hookSpecs.py
  90. 0 0
      baangt/katalonImporter/__init__.py
  91. 0 493
      baangt/katalonImporter/katalonImport.py
  92. BIN
      baangt/ressources/baangtIcon.png
  93. BIN
      baangt/ressources/baangtLogo.png
  94. BIN
      baangt/ressources/baangtLogo.snagproj
  95. BIN
      baangt/ressources/baangtLogo2020.png
  96. BIN
      baangt/ressources/baangtLogo2020Small.png
  97. BIN
      baangt/ressources/favicon.ico
  98. 0 297
      baangt/ui/ImportKatalonRecorder.py
  99. 0 0
      baangt/ui/__init__.py
  100. 0 0
      baangt/ui/logs/20200129_174558.log

+ 0 - 15
.gitignore

@@ -1,15 +0,0 @@
-.idea
-/logs
-/build
-/dist
-/baangt/logs
-geckodriver.log
-/Screenshots/*.png
-baangt.ini
-/Screenshots
-/docs/logs
-__pycache__
-*.pyc
-*.db
-venv
-application.sublime-workspace

+ 12 - 0
.idea/baangt-Docker-Appium.iml

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="PYTHON_MODULE" version="4">
+  <component name="NewModuleRootManager">
+    <content url="file://$MODULE_DIR$" />
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+  </component>
+  <component name="TestRunnerService">
+    <option name="projectConfiguration" value="pytest" />
+    <option name="PROJECT_TEST_RUNNER" value="pytest" />
+  </component>
+</module>

+ 7 - 0
.idea/misc.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="JavaScriptSettings">
+    <option name="languageLevel" value="ES6" />
+  </component>
+  <component name="ProjectRootManager" version="2" project-jdk-name="Python 3.7" project-jdk-type="Python SDK" />
+</project>

+ 8 - 0
.idea/modules.xml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectModuleManager">
+    <modules>
+      <module fileurl="file://$PROJECT_DIR$/.idea/baangt-Docker-Appium.iml" filepath="$PROJECT_DIR$/.idea/baangt-Docker-Appium.iml" />
+    </modules>
+  </component>
+</project>

+ 6 - 0
.idea/vcs.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="VcsDirectoryMappings">
+    <mapping directory="$PROJECT_DIR$" vcs="Git" />
+  </component>
+</project>

+ 0 - 674
LICENSE

@@ -1,674 +0,0 @@
-                    GNU GENERAL PUBLIC LICENSE
-                       Version 3, 29 June 2007
-
- Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
-                            Preamble
-
-  The GNU General Public License is a free, copyleft license for
-software and other kinds of works.
-
-  The licenses for most software and other practical works are designed
-to take away your freedom to share and change the works.  By contrast,
-the GNU General Public License is intended to guarantee your freedom to
-share and change all versions of a program--to make sure it remains free
-software for all its users.  We, the Free Software Foundation, use the
-GNU General Public License for most of our software; it applies also to
-any other work released this way by its authors.  You can apply it to
-your programs, too.
-
-  When we speak of free software, we are referring to freedom, not
-price.  Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-them if you wish), that you receive source code or can get it if you
-want it, that you can change the software or use pieces of it in new
-free programs, and that you know you can do these things.
-
-  To protect your rights, we need to prevent others from denying you
-these rights or asking you to surrender the rights.  Therefore, you have
-certain responsibilities if you distribute copies of the software, or if
-you modify it: responsibilities to respect the freedom of others.
-
-  For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must pass on to the recipients the same
-freedoms that you received.  You must make sure that they, too, receive
-or can get the source code.  And you must show them these terms so they
-know their rights.
-
-  Developers that use the GNU GPL protect your rights with two steps:
-(1) assert copyright on the software, and (2) offer you this License
-giving you legal permission to copy, distribute and/or modify it.
-
-  For the developers' and authors' protection, the GPL clearly explains
-that there is no warranty for this free software.  For both users' and
-authors' sake, the GPL requires that modified versions be marked as
-changed, so that their problems will not be attributed erroneously to
-authors of previous versions.
-
-  Some devices are designed to deny users access to install or run
-modified versions of the software inside them, although the manufacturer
-can do so.  This is fundamentally incompatible with the aim of
-protecting users' freedom to change the software.  The systematic
-pattern of such abuse occurs in the area of products for individuals to
-use, which is precisely where it is most unacceptable.  Therefore, we
-have designed this version of the GPL to prohibit the practice for those
-products.  If such problems arise substantially in other domains, we
-stand ready to extend this provision to those domains in future versions
-of the GPL, as needed to protect the freedom of users.
-
-  Finally, every program is threatened constantly by software patents.
-States should not allow patents to restrict development and use of
-software on general-purpose computers, but in those that do, we wish to
-avoid the special danger that patents applied to a free program could
-make it effectively proprietary.  To prevent this, the GPL assures that
-patents cannot be used to render the program non-free.
-
-  The precise terms and conditions for copying, distribution and
-modification follow.
-
-                       TERMS AND CONDITIONS
-
-  0. Definitions.
-
-  "This License" refers to version 3 of the GNU General Public License.
-
-  "Copyright" also means copyright-like laws that apply to other kinds of
-works, such as semiconductor masks.
-
-  "The Program" refers to any copyrightable work licensed under this
-License.  Each licensee is addressed as "you".  "Licensees" and
-"recipients" may be individuals or organizations.
-
-  To "modify" a work means to copy from or adapt all or part of the work
-in a fashion requiring copyright permission, other than the making of an
-exact copy.  The resulting work is called a "modified version" of the
-earlier work or a work "based on" the earlier work.
-
-  A "covered work" means either the unmodified Program or a work based
-on the Program.
-
-  To "propagate" a work means to do anything with it that, without
-permission, would make you directly or secondarily liable for
-infringement under applicable copyright law, except executing it on a
-computer or modifying a private copy.  Propagation includes copying,
-distribution (with or without modification), making available to the
-public, and in some countries other activities as well.
-
-  To "convey" a work means any kind of propagation that enables other
-parties to make or receive copies.  Mere interaction with a user through
-a computer network, with no transfer of a copy, is not conveying.
-
-  An interactive user interface displays "Appropriate Legal Notices"
-to the extent that it includes a convenient and prominently visible
-feature that (1) displays an appropriate copyright notice, and (2)
-tells the user that there is no warranty for the work (except to the
-extent that warranties are provided), that licensees may convey the
-work under this License, and how to view a copy of this License.  If
-the interface presents a list of user commands or options, such as a
-menu, a prominent item in the list meets this criterion.
-
-  1. Source Code.
-
-  The "source code" for a work means the preferred form of the work
-for making modifications to it.  "Object code" means any non-source
-form of a work.
-
-  A "Standard Interface" means an interface that either is an official
-standard defined by a recognized standards body, or, in the case of
-interfaces specified for a particular programming language, one that
-is widely used among developers working in that language.
-
-  The "System Libraries" of an executable work include anything, other
-than the work as a whole, that (a) is included in the normal form of
-packaging a Major Component, but which is not part of that Major
-Component, and (b) serves only to enable use of the work with that
-Major Component, or to implement a Standard Interface for which an
-implementation is available to the public in source code form.  A
-"Major Component", in this context, means a major essential component
-(kernel, window system, and so on) of the specific operating system
-(if any) on which the executable work runs, or a compiler used to
-produce the work, or an object code interpreter used to run it.
-
-  The "Corresponding Source" for a work in object code form means all
-the source code needed to generate, install, and (for an executable
-work) run the object code and to modify the work, including scripts to
-control those activities.  However, it does not include the work's
-System Libraries, or general-purpose tools or generally available free
-programs which are used unmodified in performing those activities but
-which are not part of the work.  For example, Corresponding Source
-includes interface definition files associated with source files for
-the work, and the source code for shared libraries and dynamically
-linked subprograms that the work is specifically designed to require,
-such as by intimate data communication or control flow between those
-subprograms and other parts of the work.
-
-  The Corresponding Source need not include anything that users
-can regenerate automatically from other parts of the Corresponding
-Source.
-
-  The Corresponding Source for a work in source code form is that
-same work.
-
-  2. Basic Permissions.
-
-  All rights granted under this License are granted for the term of
-copyright on the Program, and are irrevocable provided the stated
-conditions are met.  This License explicitly affirms your unlimited
-permission to run the unmodified Program.  The output from running a
-covered work is covered by this License only if the output, given its
-content, constitutes a covered work.  This License acknowledges your
-rights of fair use or other equivalent, as provided by copyright law.
-
-  You may make, run and propagate covered works that you do not
-convey, without conditions so long as your license otherwise remains
-in force.  You may convey covered works to others for the sole purpose
-of having them make modifications exclusively for you, or provide you
-with facilities for running those works, provided that you comply with
-the terms of this License in conveying all material for which you do
-not control copyright.  Those thus making or running the covered works
-for you must do so exclusively on your behalf, under your direction
-and control, on terms that prohibit them from making any copies of
-your copyrighted material outside their relationship with you.
-
-  Conveying under any other circumstances is permitted solely under
-the conditions stated below.  Sublicensing is not allowed; section 10
-makes it unnecessary.
-
-  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
-
-  No covered work shall be deemed part of an effective technological
-measure under any applicable law fulfilling obligations under article
-11 of the WIPO copyright treaty adopted on 20 December 1996, or
-similar laws prohibiting or restricting circumvention of such
-measures.
-
-  When you convey a covered work, you waive any legal power to forbid
-circumvention of technological measures to the extent such circumvention
-is effected by exercising rights under this License with respect to
-the covered work, and you disclaim any intention to limit operation or
-modification of the work as a means of enforcing, against the work's
-users, your or third parties' legal rights to forbid circumvention of
-technological measures.
-
-  4. Conveying Verbatim Copies.
-
-  You may convey verbatim copies of the Program's source code as you
-receive it, in any medium, provided that you conspicuously and
-appropriately publish on each copy an appropriate copyright notice;
-keep intact all notices stating that this License and any
-non-permissive terms added in accord with section 7 apply to the code;
-keep intact all notices of the absence of any warranty; and give all
-recipients a copy of this License along with the Program.
-
-  You may charge any price or no price for each copy that you convey,
-and you may offer support or warranty protection for a fee.
-
-  5. Conveying Modified Source Versions.
-
-  You may convey a work based on the Program, or the modifications to
-produce it from the Program, in the form of source code under the
-terms of section 4, provided that you also meet all of these conditions:
-
-    a) The work must carry prominent notices stating that you modified
-    it, and giving a relevant date.
-
-    b) The work must carry prominent notices stating that it is
-    released under this License and any conditions added under section
-    7.  This requirement modifies the requirement in section 4 to
-    "keep intact all notices".
-
-    c) You must license the entire work, as a whole, under this
-    License to anyone who comes into possession of a copy.  This
-    License will therefore apply, along with any applicable section 7
-    additional terms, to the whole of the work, and all its parts,
-    regardless of how they are packaged.  This License gives no
-    permission to license the work in any other way, but it does not
-    invalidate such permission if you have separately received it.
-
-    d) If the work has interactive user interfaces, each must display
-    Appropriate Legal Notices; however, if the Program has interactive
-    interfaces that do not display Appropriate Legal Notices, your
-    work need not make them do so.
-
-  A compilation of a covered work with other separate and independent
-works, which are not by their nature extensions of the covered work,
-and which are not combined with it such as to form a larger program,
-in or on a volume of a storage or distribution medium, is called an
-"aggregate" if the compilation and its resulting copyright are not
-used to limit the access or legal rights of the compilation's users
-beyond what the individual works permit.  Inclusion of a covered work
-in an aggregate does not cause this License to apply to the other
-parts of the aggregate.
-
-  6. Conveying Non-Source Forms.
-
-  You may convey a covered work in object code form under the terms
-of sections 4 and 5, provided that you also convey the
-machine-readable Corresponding Source under the terms of this License,
-in one of these ways:
-
-    a) Convey the object code in, or embodied in, a physical product
-    (including a physical distribution medium), accompanied by the
-    Corresponding Source fixed on a durable physical medium
-    customarily used for software interchange.
-
-    b) Convey the object code in, or embodied in, a physical product
-    (including a physical distribution medium), accompanied by a
-    written offer, valid for at least three years and valid for as
-    long as you offer spare parts or customer support for that product
-    model, to give anyone who possesses the object code either (1) a
-    copy of the Corresponding Source for all the software in the
-    product that is covered by this License, on a durable physical
-    medium customarily used for software interchange, for a price no
-    more than your reasonable cost of physically performing this
-    conveying of source, or (2) access to copy the
-    Corresponding Source from a network server at no charge.
-
-    c) Convey individual copies of the object code with a copy of the
-    written offer to provide the Corresponding Source.  This
-    alternative is allowed only occasionally and noncommercially, and
-    only if you received the object code with such an offer, in accord
-    with subsection 6b.
-
-    d) Convey the object code by offering access from a designated
-    place (gratis or for a charge), and offer equivalent access to the
-    Corresponding Source in the same way through the same place at no
-    further charge.  You need not require recipients to copy the
-    Corresponding Source along with the object code.  If the place to
-    copy the object code is a network server, the Corresponding Source
-    may be on a different server (operated by you or a third party)
-    that supports equivalent copying facilities, provided you maintain
-    clear directions next to the object code saying where to find the
-    Corresponding Source.  Regardless of what server hosts the
-    Corresponding Source, you remain obligated to ensure that it is
-    available for as long as needed to satisfy these requirements.
-
-    e) Convey the object code using peer-to-peer transmission, provided
-    you inform other peers where the object code and Corresponding
-    Source of the work are being offered to the general public at no
-    charge under subsection 6d.
-
-  A separable portion of the object code, whose source code is excluded
-from the Corresponding Source as a System Library, need not be
-included in conveying the object code work.
-
-  A "User Product" is either (1) a "consumer product", which means any
-tangible personal property which is normally used for personal, family,
-or household purposes, or (2) anything designed or sold for incorporation
-into a dwelling.  In determining whether a product is a consumer product,
-doubtful cases shall be resolved in favor of coverage.  For a particular
-product received by a particular user, "normally used" refers to a
-typical or common use of that class of product, regardless of the status
-of the particular user or of the way in which the particular user
-actually uses, or expects or is expected to use, the product.  A product
-is a consumer product regardless of whether the product has substantial
-commercial, industrial or non-consumer uses, unless such uses represent
-the only significant mode of use of the product.
-
-  "Installation Information" for a User Product means any methods,
-procedures, authorization keys, or other information required to install
-and execute modified versions of a covered work in that User Product from
-a modified version of its Corresponding Source.  The information must
-suffice to ensure that the continued functioning of the modified object
-code is in no case prevented or interfered with solely because
-modification has been made.
-
-  If you convey an object code work under this section in, or with, or
-specifically for use in, a User Product, and the conveying occurs as
-part of a transaction in which the right of possession and use of the
-User Product is transferred to the recipient in perpetuity or for a
-fixed term (regardless of how the transaction is characterized), the
-Corresponding Source conveyed under this section must be accompanied
-by the Installation Information.  But this requirement does not apply
-if neither you nor any third party retains the ability to install
-modified object code on the User Product (for example, the work has
-been installed in ROM).
-
-  The requirement to provide Installation Information does not include a
-requirement to continue to provide support service, warranty, or updates
-for a work that has been modified or installed by the recipient, or for
-the User Product in which it has been modified or installed.  Access to a
-network may be denied when the modification itself materially and
-adversely affects the operation of the network or violates the rules and
-protocols for communication across the network.
-
-  Corresponding Source conveyed, and Installation Information provided,
-in accord with this section must be in a format that is publicly
-documented (and with an implementation available to the public in
-source code form), and must require no special password or key for
-unpacking, reading or copying.
-
-  7. Additional Terms.
-
-  "Additional permissions" are terms that supplement the terms of this
-License by making exceptions from one or more of its conditions.
-Additional permissions that are applicable to the entire Program shall
-be treated as though they were included in this License, to the extent
-that they are valid under applicable law.  If additional permissions
-apply only to part of the Program, that part may be used separately
-under those permissions, but the entire Program remains governed by
-this License without regard to the additional permissions.
-
-  When you convey a copy of a covered work, you may at your option
-remove any additional permissions from that copy, or from any part of
-it.  (Additional permissions may be written to require their own
-removal in certain cases when you modify the work.)  You may place
-additional permissions on material, added by you to a covered work,
-for which you have or can give appropriate copyright permission.
-
-  Notwithstanding any other provision of this License, for material you
-add to a covered work, you may (if authorized by the copyright holders of
-that material) supplement the terms of this License with terms:
-
-    a) Disclaiming warranty or limiting liability differently from the
-    terms of sections 15 and 16 of this License; or
-
-    b) Requiring preservation of specified reasonable legal notices or
-    author attributions in that material or in the Appropriate Legal
-    Notices displayed by works containing it; or
-
-    c) Prohibiting misrepresentation of the origin of that material, or
-    requiring that modified versions of such material be marked in
-    reasonable ways as different from the original version; or
-
-    d) Limiting the use for publicity purposes of names of licensors or
-    authors of the material; or
-
-    e) Declining to grant rights under trademark law for use of some
-    trade names, trademarks, or service marks; or
-
-    f) Requiring indemnification of licensors and authors of that
-    material by anyone who conveys the material (or modified versions of
-    it) with contractual assumptions of liability to the recipient, for
-    any liability that these contractual assumptions directly impose on
-    those licensors and authors.
-
-  All other non-permissive additional terms are considered "further
-restrictions" within the meaning of section 10.  If the Program as you
-received it, or any part of it, contains a notice stating that it is
-governed by this License along with a term that is a further
-restriction, you may remove that term.  If a license document contains
-a further restriction but permits relicensing or conveying under this
-License, you may add to a covered work material governed by the terms
-of that license document, provided that the further restriction does
-not survive such relicensing or conveying.
-
-  If you add terms to a covered work in accord with this section, you
-must place, in the relevant source files, a statement of the
-additional terms that apply to those files, or a notice indicating
-where to find the applicable terms.
-
-  Additional terms, permissive or non-permissive, may be stated in the
-form of a separately written license, or stated as exceptions;
-the above requirements apply either way.
-
-  8. Termination.
-
-  You may not propagate or modify a covered work except as expressly
-provided under this License.  Any attempt otherwise to propagate or
-modify it is void, and will automatically terminate your rights under
-this License (including any patent licenses granted under the third
-paragraph of section 11).
-
-  However, if you cease all violation of this License, then your
-license from a particular copyright holder is reinstated (a)
-provisionally, unless and until the copyright holder explicitly and
-finally terminates your license, and (b) permanently, if the copyright
-holder fails to notify you of the violation by some reasonable means
-prior to 60 days after the cessation.
-
-  Moreover, your license from a particular copyright holder is
-reinstated permanently if the copyright holder notifies you of the
-violation by some reasonable means, this is the first time you have
-received notice of violation of this License (for any work) from that
-copyright holder, and you cure the violation prior to 30 days after
-your receipt of the notice.
-
-  Termination of your rights under this section does not terminate the
-licenses of parties who have received copies or rights from you under
-this License.  If your rights have been terminated and not permanently
-reinstated, you do not qualify to receive new licenses for the same
-material under section 10.
-
-  9. Acceptance Not Required for Having Copies.
-
-  You are not required to accept this License in order to receive or
-run a copy of the Program.  Ancillary propagation of a covered work
-occurring solely as a consequence of using peer-to-peer transmission
-to receive a copy likewise does not require acceptance.  However,
-nothing other than this License grants you permission to propagate or
-modify any covered work.  These actions infringe copyright if you do
-not accept this License.  Therefore, by modifying or propagating a
-covered work, you indicate your acceptance of this License to do so.
-
-  10. Automatic Licensing of Downstream Recipients.
-
-  Each time you convey a covered work, the recipient automatically
-receives a license from the original licensors, to run, modify and
-propagate that work, subject to this License.  You are not responsible
-for enforcing compliance by third parties with this License.
-
-  An "entity transaction" is a transaction transferring control of an
-organization, or substantially all assets of one, or subdividing an
-organization, or merging organizations.  If propagation of a covered
-work results from an entity transaction, each party to that
-transaction who receives a copy of the work also receives whatever
-licenses to the work the party's predecessor in interest had or could
-give under the previous paragraph, plus a right to possession of the
-Corresponding Source of the work from the predecessor in interest, if
-the predecessor has it or can get it with reasonable efforts.
-
-  You may not impose any further restrictions on the exercise of the
-rights granted or affirmed under this License.  For example, you may
-not impose a license fee, royalty, or other charge for exercise of
-rights granted under this License, and you may not initiate litigation
-(including a cross-claim or counterclaim in a lawsuit) alleging that
-any patent claim is infringed by making, using, selling, offering for
-sale, or importing the Program or any portion of it.
-
-  11. Patents.
-
-  A "contributor" is a copyright holder who authorizes use under this
-License of the Program or a work on which the Program is based.  The
-work thus licensed is called the contributor's "contributor version".
-
-  A contributor's "essential patent claims" are all patent claims
-owned or controlled by the contributor, whether already acquired or
-hereafter acquired, that would be infringed by some manner, permitted
-by this License, of making, using, or selling its contributor version,
-but do not include claims that would be infringed only as a
-consequence of further modification of the contributor version.  For
-purposes of this definition, "control" includes the right to grant
-patent sublicenses in a manner consistent with the requirements of
-this License.
-
-  Each contributor grants you a non-exclusive, worldwide, royalty-free
-patent license under the contributor's essential patent claims, to
-make, use, sell, offer for sale, import and otherwise run, modify and
-propagate the contents of its contributor version.
-
-  In the following three paragraphs, a "patent license" is any express
-agreement or commitment, however denominated, not to enforce a patent
-(such as an express permission to practice a patent or covenant not to
-sue for patent infringement).  To "grant" such a patent license to a
-party means to make such an agreement or commitment not to enforce a
-patent against the party.
-
-  If you convey a covered work, knowingly relying on a patent license,
-and the Corresponding Source of the work is not available for anyone
-to copy, free of charge and under the terms of this License, through a
-publicly available network server or other readily accessible means,
-then you must either (1) cause the Corresponding Source to be so
-available, or (2) arrange to deprive yourself of the benefit of the
-patent license for this particular work, or (3) arrange, in a manner
-consistent with the requirements of this License, to extend the patent
-license to downstream recipients.  "Knowingly relying" means you have
-actual knowledge that, but for the patent license, your conveying the
-covered work in a country, or your recipient's use of the covered work
-in a country, would infringe one or more identifiable patents in that
-country that you have reason to believe are valid.
-
-  If, pursuant to or in connection with a single transaction or
-arrangement, you convey, or propagate by procuring conveyance of, a
-covered work, and grant a patent license to some of the parties
-receiving the covered work authorizing them to use, propagate, modify
-or convey a specific copy of the covered work, then the patent license
-you grant is automatically extended to all recipients of the covered
-work and works based on it.
-
-  A patent license is "discriminatory" if it does not include within
-the scope of its coverage, prohibits the exercise of, or is
-conditioned on the non-exercise of one or more of the rights that are
-specifically granted under this License.  You may not convey a covered
-work if you are a party to an arrangement with a third party that is
-in the business of distributing software, under which you make payment
-to the third party based on the extent of your activity of conveying
-the work, and under which the third party grants, to any of the
-parties who would receive the covered work from you, a discriminatory
-patent license (a) in connection with copies of the covered work
-conveyed by you (or copies made from those copies), or (b) primarily
-for and in connection with specific products or compilations that
-contain the covered work, unless you entered into that arrangement,
-or that patent license was granted, prior to 28 March 2007.
-
-  Nothing in this License shall be construed as excluding or limiting
-any implied license or other defenses to infringement that may
-otherwise be available to you under applicable patent law.
-
-  12. No Surrender of Others' Freedom.
-
-  If conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License.  If you cannot convey a
-covered work so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you may
-not convey it at all.  For example, if you agree to terms that obligate you
-to collect a royalty for further conveying from those to whom you convey
-the Program, the only way you could satisfy both those terms and this
-License would be to refrain entirely from conveying the Program.
-
-  13. Use with the GNU Affero General Public License.
-
-  Notwithstanding any other provision of this License, you have
-permission to link or combine any covered work with a work licensed
-under version 3 of the GNU Affero General Public License into a single
-combined work, and to convey the resulting work.  The terms of this
-License will continue to apply to the part which is the covered work,
-but the special requirements of the GNU Affero General Public License,
-section 13, concerning interaction through a network will apply to the
-combination as such.
-
-  14. Revised Versions of this License.
-
-  The Free Software Foundation may publish revised and/or new versions of
-the GNU General Public License from time to time.  Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
-  Each version is given a distinguishing version number.  If the
-Program specifies that a certain numbered version of the GNU General
-Public License "or any later version" applies to it, you have the
-option of following the terms and conditions either of that numbered
-version or of any later version published by the Free Software
-Foundation.  If the Program does not specify a version number of the
-GNU General Public License, you may choose any version ever published
-by the Free Software Foundation.
-
-  If the Program specifies that a proxy can decide which future
-versions of the GNU General Public License can be used, that proxy's
-public statement of acceptance of a version permanently authorizes you
-to choose that version for the Program.
-
-  Later license versions may give you additional or different
-permissions.  However, no additional obligations are imposed on any
-author or copyright holder as a result of your choosing to follow a
-later version.
-
-  15. Disclaimer of Warranty.
-
-  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
-APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
-HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
-OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
-THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
-IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
-ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
-
-  16. Limitation of Liability.
-
-  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
-THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
-GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
-USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
-DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
-PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
-EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGES.
-
-  17. Interpretation of Sections 15 and 16.
-
-  If the disclaimer of warranty and limitation of liability provided
-above cannot be given local legal effect according to their terms,
-reviewing courts shall apply local law that most closely approximates
-an absolute waiver of all civil liability in connection with the
-Program, unless a warranty or assumption of liability accompanies a
-copy of the Program in return for a fee.
-
-                     END OF TERMS AND CONDITIONS
-
-            How to Apply These Terms to Your New Programs
-
-  If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
-  To do so, attach the following notices to the program.  It is safest
-to attach them to the start of each source file to most effectively
-state the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
-    <one line to give the program's name and a brief idea of what it does.>
-    Copyright (C) <year>  <name of author>
-
-    This program is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation, either version 3 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program.  If not, see <https://www.gnu.org/licenses/>.
-
-Also add information on how to contact you by electronic and paper mail.
-
-  If the program does terminal interaction, make it output a short
-notice like this when it starts in an interactive mode:
-
-    <program>  Copyright (C) <year>  <name of author>
-    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
-    This is free software, and you are welcome to redistribute it
-    under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License.  Of course, your program's commands
-might be different; for a GUI interface, you would use an "about box".
-
-  You should also get your employer (if you work as a programmer) or school,
-if any, to sign a "copyright disclaimer" for the program, if necessary.
-For more information on this, and how to apply and follow the GNU GPL, see
-<https://www.gnu.org/licenses/>.
-
-  The GNU General Public License does not permit incorporating your program
-into proprietary programs.  If your program is a subroutine library, you
-may consider it more useful to permit linking proprietary applications with
-the library.  If this is what you want to do, use the GNU Lesser General
-Public License instead of this License.  But first, please read
-<https://www.gnu.org/licenses/why-not-lgpl.html>.

+ 0 - 1
MANIFEST.in

@@ -1 +0,0 @@
-recursive include baangt/ressources *.*

+ 0 - 9
MakePackage.sh

@@ -1,9 +0,0 @@
-rm -R dist/baangt-*
-rm -R dist/baangt
-rm -R dist/baangtIA
-rm -R dist/baangt.app
-rm -R dist/baangtIA.app
-python3 setup.py sdist bdist_wheel
-python3 -m pip install --user --upgrade twine
-python3 -m twine upload dist/*
-rm -R build/

+ 0 - 5
README.md

@@ -1,5 +0,0 @@
-# Welcome
-Please find the full documentation on [ReadTheDocs](https://baangt.readthedocs.io)
-
-# Further reading:
-Latest news on http://baangt.org for community edition and http://baangt.com for corporate environments.

+ 0 - 8
TechSpecs/30 Ready/CREATE_EXECUTABLE.md

@@ -1,8 +0,0 @@
-# Create executable using pyinstaller
-Apart from installing baangt via pip, cloning pip-repository and using the [docker container](https://gogs.earthsquad.global/athos/baangt-Docker)
-baangt should also be available as executable on Mac, Windows and Linux.
-
-# DoD:
-* Baangt executable was created and tested for either Mac or Windows (depending on your development system) as well as Linux (ubuntu)
-    * Versions: Ubuntu 64bit, Windows 10, Mac OS 10.13
-* Necessary adjustments to code (if any) were done (I only expect problems with Paths (either reading or writing) and tested.

+ 0 - 44
TechSpecs/30 Ready/UI_ShowStatusOfTestrun.md

@@ -1,44 +0,0 @@
-# Situation
-
-Right now when you start ``baangt`` interactive starter, choose a testrun and globals file and execute the testrun there's
-a log either in the console or in your IDE (depending from where you start it), but directly in the UI you don't see
-anything except an hourglass.
-
-# Aim
-
-## Statistics
-In the right area of the UI (under the logo), we should have a section showing statistical information (number of testcases
-to execute, count of testcases executed, count of testcases successful, count of testcases paused, count of testcases failed
-and overall duration since test case start).
-
-Under this, the count of executed TestCaseSequences, TestCases, TestStepSequences and TestSteps should be displayed.
-
-## Logs
-Additionally in the lower part of the window a section should appear (and disappear when the test run is finished), that
-shows the Info/Error/Warning-Messages that are logged.
-
-# Additional information
-
-Apart from using these statistical information in the current UI, the flask-Implementation will also use the same information.
-To be efficient, we'll need to have data gathering in one separate class, that can be used by current UI as well as flask. 
-Current flask implementation is in directory flask.
-
-# Implementation
-
-Majority of the data points can be found/derived from ``baangt.base.TestRun.TestRun`` in the method ``executeDictSequenceOfClasses``
-(see documentation there). For single runs (one browser or one API-Session), that should work fine.
-
-Parallel runs are different/difficult (``TestCaseSequence.TestCaseSequenceParallel``) as they run in a different Python instance 
-than the UI. This means, that during runtime of the parallel sequence a Queue must be written from the parallel runs
-and read from within the UI.
-
-Most probably to display the logs inside UI the logger needs to be changed. It's defined in ``baangt.__init__.py``
-
-## Scope / DOD
-This task is considered completed after:
-* Implementation provided (in line with coding standards and PEP-8 conformity)
-* Functional test executed and passed
-* Existing functionality not compromised - baangt background mode works as before this change
-* Enhance existing documentation in docs-Folder in RST-Format
-* Unit-Tests in tests-folder providing reasonable coverage of the newly created functionality
-* git commit to feature branch and pull request on Gogs created

+ 0 - 56
TechSpecs/30 Ready/randomValues.md

@@ -1,56 +0,0 @@
-# Provision of intelligent random values
-
-## Aim
-
-In Test data creation despite having often very specific test data requirements to facilitiate reproducable outcome both
-in exploratory testing but also in regression testing sometimes we want random values.
-
-Example:
-* Names of business partners
-* Random text
-
-We want to provide an easily accessable feature for users of ```baangt``` to deal with this requirement without needing
-to develop code.
-
-## Implementation
-
-Implement a class (RandomValues), activity (Random) in API and SimpleFormat as well as variable replacement (``$(RANDOM)``). 
-The implementation of the activity and variable replacement needs to be done in ``TestStepMaster``.
-
-### Class RandomValues
-
-Parameters for the class should be none in init, and the following in Method ``retrieveRandomValue``:
-* RandomizationType (default: "String". Other values: Int)
-* Min (default: 3)
-* Max (default: 10)
-
-### Activity "Random"
-
-Parameters of the activity ``Random`` are defined in field ``value`` with JSON-Format (e.g. ``{Type:String,Min:5,Max:40}``) 
-and must be mapped to the method parameters (Type->RandomizationType, Min->Min, Max->Max).
-
-In this case the return value must be stored to testDataDict into the variable defined in column ``value2``
-
-### Variable replacement
-
-When ``$(RANDOM)`` is used in e.g. Activitiy ``SETTEXT`` in the column "Value", then we should execute method
-``retrieveRandomValue``. The logic must also be implemented in ``TestStepMaster`` in method ```replaceVariables```. 
-
-Also in variable replacement additional parameters can be mentioned, e.g. ``$(RANDOM{Min:10,Max:60})`` 
-
-### Randomization
-Implement one String and one Integer generation of random values and return according to length definition in min/max.
-
-### Storing 
-In Activity "Random" we need to store the resulting value in the field given in ```value2``` from the TestStep.
-
-## Example File
-An example File with explanations can be found in folder ``Examples``, filename ``Random.xlsx``
-
-## Scope / DOD
-* Implementation provided (in line with coding standards and PEP-8 conformity)
-* Functional test executed and passed (theoretically if values from ``Random.xlsx`` work, this should be enough)
-* Enhance existing documentation in docs-Folder in RST-Format
-    * in this case in simpleExample.rst and SimpleAPI.rst
-* Unit-Tests in tests-folder providing reasonable coverage of the provided functionality
-* git commit to feature branch and pull request on Gogs created

+ 0 - 32
TechSpecs/60 InProgress/PYSIMPLEGUI_UI_ADDITONS_1.md

@@ -1,32 +0,0 @@
-# Scope of this enhancement
-
-UI has currently just very basic functionality. Enhancements are needed in the following areas:
-
-## More comfortable handling of Global Parameters
-Right now in baangt.UI.UI when you chose any global*.JSON the parameters and values from this file are displayed. 
-4 empty lines are added to give the user a chance to add more parameters/values. In long configuration files, this
-leads to an overflowing window.
-
-### Solution approach
-Keep the global parameters/values in a sorted dict. Keep for instance 10 parameter/value-pairs on the UI (just as
-parameter-01, value-01, parameter-02, value-02 and so on). Add a vertical scroll-bar on the UI (if there are more 
-than 10 parameters (including the 4 empty ones)). 
-
-In the initialization loop over the first 10 entries of the globals-Dict and fill in parameter-01, value-01 until 
-parameter-10 and value-10. 
-
-When the user scrolls on the vertical scroll-bar, e.g. to position 4: Read the globals-Dict from position 4. Fill in
-UI-Element parameter-01 with parameter 4 from globals-Dict, value-01 with value 4 from globals-Dict, and so on.
-
-## Global Parameters as dropdown with additional values:
-Right now the user must know the allowed parameter names in order to tune the globals-file. That's not ideal. It would
-be better if at least standard values are in a dropdown and only customer specific values must be known.
-
-### Solution approach:
-For empty entries (Currently 4 at the end of the list):
-Change the Parameter-fields in the UI to be Dropdowns. Have a method to set the default entries. Enable manual 
-addition of values by the user. 
-
-For filled entries:
-No dropdown. Instead show a "delete"-Button for each row. After pressing the delete-button remove the entry and reload
-the Window (hide/unhide on Mac doesn't really work. It destroys the layout. Reloading works well)

File diff suppressed because it is too large
+ 0 - 126
TechSpecs/70 Impl Done/CONCEPT_DB_UI.md


+ 0 - 41
TechSpecs/70 Impl Done/DATABASE_AND_UI.md

@@ -1,41 +0,0 @@
-# TechSpec TestRun-Database
-The database is a an alternative to creating testrun settings for baangt in XLSX. 
-Working with the database provides - other than XLSX - also the option to reuse elements (TestCaseSequence, TestCase, TestStepSequence) between testruns, while in XLSX-Format there is no referencing, only Copy+Paste (and resulting maintenance issues)
-
-## Create Database and UI for Testrun Definitions
-Database and UI should be implemented using FLASK and ORM. Database SQLite is enough for now.
-### Main entities
-* Testrun
-* TestCaseSequence (n:m to TestRun)
-* DataFiles (n:m) to TestCaseSequence 
-(For now baangt.py supports only 1 dataFile. Later this will be refactored to support multiple files. Also there will be an option to connect to a database and use Query as input)
-* TestCase (n:m) to TestCaseSequence
-* TestStepSequence (n:m) to TestCase
-* TestStepExecution (1:n) to TestStepSequence
-
-### Supporting entities
-When a new database is created all entries in supporting entities shall be created (by ORM - not any db-specific command)
-* GlobalTestStepExecution (identical to TestStepExecution table but for reusable TestSteps)
-* ClassNames (Value table for Classnames in TestCaseSequence, TestCase, TestStepSequence)
-* BrowserTypes (Value table for TestCase->BrowserType).
-  * Values: FF, Chrome, IE, Safari, Edge
-* TestCaseType (Value table for Testcase->TestCaseType)
-  * Values: Browser, API-Rest, API-SOAP, API-oDataV2, API-oDataV4
-* ActivityType (Value table for TestStepExecution->Activity)
-  * Values: lots - see Note in Tab `TestStepExecution` in column `Activity`
-* LocatorType (Value table for TestStepExecution->LocatorType)
-  * Values: xpath, css, id
-
-Supporting entities shall have language/locale depending descriptions, that will be used in the UI to display tooltips and/or explanations in Dropdown-Fields.
-  
-## Create the UI
-Hierarchical display of testruns and all their subsequent entities. Most probably something like a Tree would be good with +/- Buttons to add/remove elements. This UI-Element must be searchable and show filtered search result after a few characters are typed.
-
-### Special treatment of Global Variables
-GlobalVariables are stored in `baangt.base.GlobalConstants.py` - these variables shall be available at several places, additionally to manually entered values (see excel-sheet `DropsTestRunDefinition`)
-
-### Testdatafiles:
-Headers of testdatafiles must be read, so that the column names are available for selection in TestStepExecution-Steps for use in Column `Value` or `Value2` 
-
-### Execution
-There should be a "Run"-Button, which can be pressed whenever the user is inside a testrun (or any level below). When the button is clicke, all changes shall be saved to the database. `baangt.py` shall be called with the testrun-name of the currently active testrun in the UI. Further parameters need to be discussed.

+ 0 - 19
TechSpecs/70 Impl Done/MULTIPROCESSING_REFACTOR.md

@@ -1,19 +0,0 @@
-# Multiprocessing
-In baangt.TestCaseSequenceMaster.py in method execute_parallel the class TestCaseSequenceParallel is called and executed.
-
-This works well on Mac and Ubuntu. But doesn't work on Windows.
-
-Also on Mac and Ubuntu the performance using this technique is not ideal, as all executations wait for the last task to 
-finish before they start a new iteration.
-
-# Goals
-Change from process based multiprocessing to thread based parallel processing (using standard module Threads instead of 
-Multiprocessing)
-
-# DoD:
-* Usage of `threading` and `Queue` instead of `multiprocessing` library to start parallel browsers and execute testcases
-in parallel.
-* After execution of Testcase latest data from Testcase (`TestdataDict`) is updated in `TestRun.py`
-* Functionality was tested locally on either Linux and/or Windows and/or Mac and results documented (e.g. Log-File 
-showing successful execution)
-* Pull request was created

+ 0 - 15
TechSpecs/70 Impl Done/Plugin-Readyness.md

@@ -1,15 +0,0 @@
-# Current situation
-baangt classes are well structure and generally follow the principle of separations of concern. Users can easily subclass
-existing baangt-classes. Depending on the requirements the user might end up with subclassing a lot of classes and overwrite
-a lot of methods.
-
-Each method, that doesn't call super().<method>() means danger of upcoming breaking changes.
-
-Sometimes it would be easier to implement a Plugin than subclassing.
-
-# Aim of this task
-Prepare baangt classes and methods for usage of [pluggy](https://pluggy.readthedocs.io/en/latest/). Implement pluggy-entry points in 
-* baangt.base.BrowserHandling
-* baangt.base.TestRun
-* baangt.base.Timing
-* baangt.base.ExportResults

+ 0 - 10
TechSpecs/70 Impl Done/UnitTests_Part1.md

@@ -1,10 +0,0 @@
-# Aim
-So far there were no unit tests implemented in ``baangt``. Aim of this task is to provide unit tests for the following 
-classes/methods using pytest:
-
-* baangt.base.TestRun getSuccessAndError
-* baangt.base.TestRun setResult
-* baangt.base.IBAN getRandomIBAN
-* baangt.base.BrowserHandling slowExecutionToggle
-* baangt.base.BrowserHandling takeScreenshot
-

+ 0 - 39
TechSpecs/70 Impl Done/Versioning.md

@@ -1,39 +0,0 @@
-# Aim
-
-In ``baangt`` simple XLSX and in complete XLSX-Formats there's a field ``Release`` for each TestStep. Right now this field
-is not interpreted in the program.
-
-The intended use of the field is described in the Docs in "SimpleExample" toward the end. Basically we should have an option
-to conditionally (per stage) activate/deactivate testSteps (Example: In DEV-Stage you need to fill a field, which doesn't
-(yet) exist in Quality-Stage. Instead of copying the test case and adding one statement in the Dev-Copy you'd add one TestStep
-in the Testcase and put ">= " + the proper version of the Dev-Stage in the field "Release").
-
-# Prerequisits
-
-PyPi already supports several different approaches to software version numbering. We should implement the same logic in 
-``baangt`` standard. If the user needs something else, they'll need to subclass the method. It's a prerequisit for this task
-to understand the existing version logic used by PyPi.
-
-# Field characteristics
-* The field may be empty. In this case the TestStep will run no matter which version was set in Globals
-* The field may have a condition in the first two characters ("<", ">", "=", "<>", "<=", ">=") followed by blank and 
-the version number (e.g. ``>= 2019.05b``)
-
-# Implementation
-
-* First we need to extend BrowserDriver-Methods (``findBy`` and all callers) with a new optional variable ``release``. 
-* A new parameter ``Release`` needs to be added to UI.py with a default value of None and stored in all globals.json. (Same logic
-as was already added for "TC." + GC.DATABASELINES). 
-    * If this parameter is set in Globals and a TestStep's line has the
-field ``release`` filled, we shall use the PyPi-Logic to see, if the current version qualifies to run this line (this method
-should be encapsulated, so that it can be easily subclassed by users).
-    * If the line doesn't quality for execution, we shall document in the logs (Level=Debug), that we skipped this line due to 
-{version_line} disqualifies according to {version_globals} and return to the caller.
-* Additionally check, if the field is included in flask implementation. If not, add it to the model and prepare migration.
-* Also make sure the field "Release" is correctly mapped in TestStepMaster.py, so that the value reaches the ``findBy`` methods.
-
-# Test
-
-* Write unit tests to show behaviour of comparison of version between globals and TestSteps.
-* Write unit tests to show behaviour if no value was set in globals, but value in TestStep.
-* Write unit zest to show behaviour if value is set in globals, but no value in TestStep.

+ 0 - 62
TechSpecs/70 Impl Done/XLSX2BaangtDBAndViceVersa.md

@@ -1,62 +0,0 @@
-# Current Situation
-
-We've currently 3 loosely connected ways how to define test runs (and testCaseSequences, testCases, TestStepSequences 
-and teststeps) in baangt:
-
-* SimpleFormat (XLSX): Has only 2 tabs ("TestStepExecution" and "data"). Very fast, very simple. No options for further
-configuration except the options that come from "overriding" in globals. When interpreted by baangt interactive starter
-or baangt CLI we actually create a complex XLSX with default values.
-* Complex XLSX: Has a lot of tabs and all options, that we have in baangtDB. Multiple TestCases, Browsers, data files, 
-and so on.
-* BaangtDB: The flask app, that enables the users to have real modular test sequences and reuse sequences across all 
-structural elements (e.g. Login-Page, commonly used "cards" in multiple test cases, etc.).
-
-Directly connected to these definitions are the two following options to create customer/installation specific 
-functionality:
-* Subclassing: Some or all of the structural items of baangt standard are being subclassed and used instead of the standard
-classes (only possible in Complex XLSX and in BaangtDB - where the executing class is a parameter).
-* Plugins: Methods of the standard baangt can be redefined by using and activating plugins. These changes are also 
-effective when using SimpleFormat (e.g. one could override the data source of SimpleFormat-XLSX to be something else)
-
-# Problem
-
-There is no support for switching between the options. Users need to be able to export and import when baangtDB is the
-center of the installation.
-
-## Processes
-
-### Update of existing test cases:
-Assume baangtDB is run by the central testmanagement department of the organization. baangt simple format is used by the business
-departments all over the world. A new release of their software is about to come out and they need test cases to be 
-adjusted to the local/regional specialities in pre-production stage. Central testmanagement department doesn't know, which specifics these are, 
-but they know, which test cases aren't working on pre-production stage. Central testmanagement department wants to
-generate SimpleFormat XLSX or Complex XLSX from a baangtDB-Testcase and send this XLSX to business departments in order to fix the 
-definitions. (= Export from baangtDB to Complex XLSX).
-
-### Adding new test cases:
-Business department tests a new functionality and is happy with it. They want the testcase to be included to regression
-test set. They send the SimpleFormat XLSX or Complex XLSX to central testmanagement department, who need to add those
-test cases to certain test runs, defined in baangtDB. (= Import Complex XLSX to baangtDB).
-
-# Implementation
-
-## Flask import
-* In Flask app create an import button on level Testrun. 
-* When button is pressed upload XLSX, run through converter and save resulting objects in 
-testrun-Database. 
-* Show log of import (especially the object names, that were created)
-
-## Flask export
-* In Flask app create an export button on level Testrun.
-* When button is pressed, create complex XLSX-Format. Filename = <Testrun-name><timestamp>.xlsx
-* Download resulting XLSX to Frontend
-
-# Scope / DOD
-This task is considered completed after:
-* Implementation provided (in line with coding standards and PEP-8 conformity)
-* Functional test executed and passed
-* Existing functionality not compromised - baangt Interactive Starter, baangt CLI and baangtDB work as before this change
-* Enhance existing documentation in docs-Folder in RST-Format
-    * For this task there will be a new page in Docs (Import/Export in baangtDB)
-* Unit-Tests in tests-folder providing reasonable coverage of the newly created functionality
-* git commit to feature branch and pull request on Gogs created

+ 0 - 6
TechSpecs/80 Merged/DOWNLOAD_BROWSER_DRIVERS.md

@@ -1,6 +0,0 @@
-# Result
-If the browser drivers (Chromedriver, Geckodriver) for the current operating system can't be found in 
-`Path(os.getcwd()).joinpath("browserDrivers")` download latest Chromedriver and Geckodriver and unpack them, so that the application can use them.
-
-# Implementation
-In class `baangt.base.BrowserHandling.BrowserDriver` in Method `createNewBrowser` call a new method to identify, whether the browserDrivers are in the expected location and if not, download them accordingly.

+ 0 - 64
TechSpecs/80 Merged/KatalonRecorderImporter.md

@@ -1,64 +0,0 @@
-# Situation
-
-In UI (baangtIA.py without any parameters opens the simple starter UI) there's a button with text "Import Katalon Recorder". 
-On pressing the button another pySimpleGui Window opens. In this window one can import (from clipboard) the exported result
-from Katalon Recorder (= Plugin in Chrome/FF to record browser interaction) and translate the contents to ```baangt``` format.
-
-On "Save as"-Button a new XLSX Testrun-Definition in Simple format is created. All Teststeps from the recording are included
-in the XLSX.
-
-This works quite well. But it's not pretty (the UI) and the functionality is not complete or at least hard to use: 
-
-In order to use the resulting
-XLSX, the users have to do tedious manual steps (Create variable names (=columns) in tab "data", move entered data from TestStep into
-tab "data" into each column and finally replace cells in column "Value" of TestSteps with the variable names, they just
-created (=the columns in tab "Data")).
-
-# Aim
-
-When a recording is imported, all Values should be extracted as columns in the data-tab. For instance, if you have a recording
-
-```markdown
-gotoUrl | http://franzi.com 
-Click   | //@ID='Button1'
-SetText | //@ID='TextInput2' | testTextTestText
-```
-
-right now we translate this into correct simpleFormat, but we'll copy the value "testTextTestText" into the field "value"
-of the Teststep. This is not practical. Users will have entered this text just as an example and want to use a variable to
-dynamically replace this fixed text.
-
-We shall extract all those variables, store them as columns in the Tab ```data```, set the field "Value" of the TestStep
-to the variable name (e.g. ```$(TextValue1)```) and store the value from the recording in the proper field of the tab data in Line 1.
-
-We shall do the same for Clicks. Create column "Button<n>" in tab data, set the testStepActivity to "ClickIF" and place 
-column-name in the Value-field of the TestStep (e.g. ``$(Button001)``)
-
-From the above example the tab data would look as follows:
-
-```
-Url               | Button001 | TextValue1       |
-http://franzi.com |   X       | testTextTestText |
-```
-
-The Tab ```TestStepExecution``` would look as follows:
-
-```
-Activity  | LocatorType | Locator             | Value 
-GOTOURL   |             |                     | $(Url) <-- Column in tab 'data'
-CLICKIF   | XPATH       | //*[@id=Button1]    | $(Button001)   <-- Column in tab 'data'
-SETTEXT   | XPATH       | //*[@id=TextInput2] | $(TextValue1) <-- Column in tab 'data'
-```
-
-# UI
-
-If you have any suggestions, how to improve the UI of the Katalon Recorder Import dialogue, please get in contact.
-
-# Output
-
-The resulting XLSX is not prettyfied, e.g. column widths are standard and not formatted, the header lines are not in bold, etc.
-Please see methods in ``baangt.base.ExportResults.ExcelSheetHelperFunctions`` and apply here too.
-
-# Test
-
-Create unit tests for new functionality

+ 0 - 16
TechSpecs/80 Merged/LogNetworkTraffic.md

@@ -1,16 +0,0 @@
-# Aim
-So far via class ```timing``` the durations of webpages are logged. In some performance testing and analysis jobs this
-is not enough. Additionally we need the network traffic stored for each request.
-
-# Vision
-Activated via TestRun-Parameter or in Globalsettings the network traffic needs to be stored. In `ExportResults` the 
-network traffic should be stored in a separate Tab in the output-XLSX (Status, Method, URI, Type, Size, Headers, Params, Response)
-for each activity done in the browser
-
-# Implementation idea:
-Usage of https://github.com/browserup/browserup-proxy and the corresponding Python Package. Dynamically create a proxy 
-for each active browser (and pass the new proxy-URL to the browser in ``baangt.base.BrowserHandling``. 
-Read the logs of browserup-proxy after finished requests and store result together with the
-```Teststep```. Other ideas welcome.
-
- 

+ 0 - 62
TechSpecs/80 Merged/addressCreation.md

@@ -1,62 +0,0 @@
-# Creation of address data
-
-## Prerequisits
-
-In many cases test master data (in this case addresses) needs to be created before any transactional data (e.g. sales orders,
-user accounts, shipping information) can be processed.
-
-Depending on the industry and use-case generation of this data might be of critical importance for the subsequent processes,
-e.g. to test shipping fees for an international forwarding company you'll likely need test data in different countries.
-
-When testing e.g. for property insurance you'll even need more distinct data to test business logic, that derives premium 
-discounts and surcharges based on risk attributes of a certain address.
-
-The more complex the data requirement is, the more likely testers start to prepare "their" own syntectic test data set (e.g.
-1 address per parameter to test) and re-use this data over and over again for manual and automated tests. Very often this
-leads to undetected errors (this one combination obviously works, but a slight deviation with real world data brings 
-errors, that linger undetected in productive system) or false errors (e.g. a customer, who has 10.000 insurance contracts 
-in Test-system and brings this in turn leads to wrongfully reported performance bottlenecks (which will never happen in 
-production). This happens over and over again in many organizations. Time and money spent for wrong analysis hurts double! 
-Once because it's money spent on activities, that don't deliver value. Second because time spent on those activities means 
-less time for important improvements.). 
-
-## Aim
-
-``baangt`` should provide an easy and easily extendable way to generate address data for a test case. 
-
-## Implementation
-
-There needs to be a new activity "ADDRESS_CREATE" in SimpleFormat (``TestStepMaster.py``). Field ``value`` = optional list of 
-attributes (see below), ``value2`` = prefix for fieldnames (optional).
-
-When this activity is called, we shall call a singleton class ``AddressCreate`` using the (optional) parameters from the
-Value-Field. This class is **not** in scope of the current specification. The method ``returnAddress`` provides a dict of 
-fields and values, which shall be stored in TestDataDict (with (optional) prefix taken from value2).
-
-After the fields were filled, they'll be used by the test case to fill values for the UI or API.
-
-### Fields
-The following fields are part of the return value of method ``returnAddress``:
-* CountryCode**
-* PostlCode**
-* CityName
-* StreetName
-* HouseNumber
-* AdditionalData1
-* AdditionalData2
-
-**These fields can be used as filter criteria in field ``value``. JSON-Format should be supported. Example of field ``Value``: {CountryCode:CY, PostlCode: 7*}.
-Values must be mapped into a ``Dict``.
-
-If a prefix was povided in field ``Value2``, the fieldnames shall be concatenated with this prefix, e.g. if 
-prefix = ``PremiumPayer_``, then the resulting field for ``CountryCode`` in testDataDict would become 
-``PremiumPayer_CountryCode``.
-
-## Scope / DOD
-* Implementation provided (in line with coding standards and PEP-8 conformity)
-* Functional test executed and passed (in this case it means to also create a new simpleXLS-Format for a site where
-address data can be entered)
-* Enhance existing documentation in docs-Folder in RST-Format
-    * in this case in simpleExample.rst and SimpleAPI.rst
-* Unit-Tests in tests-folder providing reasonable coverage of the provided functionality
-* git commit to feature branch and pull request on Gogs created

+ 0 - 26
TechSpecs/80 Merged/assertions.md

@@ -1,26 +0,0 @@
-# Situation
-Currently ``baangt`` supports on the WEB click, settext, iframes, windowhandling and so on. There's also a way to access
-values from elements and write them back to ``testDataDict``. But there are no classical assertions implemented.
-
-# Aim
-Assertions should be available in all test technologies (currently API and Browser). Assertions should follow the 
-existing logic by using method ```findBy``` (similar to current implementation in ``findByAndWaitForValue``). This method
-retrieves the value of an element but doesn't compare to a reference value. It could be a good base though for the assert
-method (just call ``findByAndWaitForValue`` and compare result to given reference parameter in ``testdataDict``)
-
-The simplest type of assertions are to read the attribute or text of an element and compare to a variable from ``testDataDict``.
-They should also be available in Excel Simple Format (implementation in ``TestStepMaster`` in method ``executeDirect``). 
-
-A more complex situation comes up, when the assertion is checked against an API. The sequence would be
-* Execute SetText or Click or any sequence of activities
-* Execute an API-Call, receive result
-* Compare specific part of the result with a field value from ``testDataDict``
-
-The most complex functionality is needed in asynchronous assertions, either by callback or from batch processing. This 
-needs to be implemented by customer specific routines. The main point here is to set the Testcase into condition 
-```GC.TESTCASESTATUS_PENDING``` and have a unique ID of the testrun and the TestCase-ID, that can be matched/found by 
-asynchronous trigger, which would later set Testcasestatus accordingly (failed, OK).
-
-# Implementation
-For now we'll only implement the simplest type of assertions for Browsers and later tend to more complex implementations.
-

+ 0 - 13
TechSpecs/99 Done/READ_THE_DOCS.md

@@ -1,13 +0,0 @@
-# Integration with ReadTheDocs.io
-The aim of this task is to provide automatically generated documentation from source code using ``Sphinx`` so that we
-can automatically update [ReadTheDocs](http://readthedocs.io) and docs-pages on [Gogs](https://gogs.earthsquad.global) and later on Github.
-
-Implementation should follow roughly the steps from [this tutorial](https://daler.github.io/sphinxdoc-test/includeme.html).
-
-# DoD:
-* Sphinx was configured properly in ``conf.py`` so that generation of extracted documentation from the source code happens
-* `index.rst` and *.rst-Files for all relevant paths were built properly (using `sphinx-apidoc ../ --output-dir docs`)
-* Necessary GIT-Branch for `gh-pages` was created and `Makefile` updated accordingly.
-* Contents of ``README.md`` were transformed into `Readme.rst`, properly formatted and all necessary steps taken to ensure
-readability on github pages. 
-* Pull request created and result of local test attached to pull request

+ 0 - 18
TechSpecs/99 Done/TECHSPEC_RUNLOG.md

@@ -1,18 +0,0 @@
-# Testrun Log and Reporting
-For easy comparison between stages, software versions and testcases we want to have a database of testruns
-
-## Phase 1
-### Create Database and entities
-create (if not exisits) database and entities to save:
-* *Testrun-Name (from `baangt.base.TestRun.testRunName`)
-* *Logfile-Name (from `__init__.py->logFilename`)
-* *Start-Timestamp/End-Timestamp (Call to `Timing.takeTime` when TestRun starts and stops)
-* values from globals.json (if used)
-* *Datafile(s) used
-* *Count of Testcases in each status (OK, Failed, Paused --> Values from `baangt.base.GlobalConstants.py` )
-
-*This logic  is already implemented in ExportResults.py and can be reused for the planned functionality.
-
-### Extend TestRun.py to write into the database
-* In the method `tearDown` of `baangt.base.TestRun.py` add a call to store the testrun execution data into the database.
-* Alternatively use the existing (and already called) ExportResults.py class directly

+ 0 - 9
TechSpecs/_ProcessWithContributers.md

@@ -1,9 +0,0 @@
-# Process to deal with TechSpecs:
-
-* New ideas --> Folder "01 in Creation"
-* Functional part speficied --> Folder "30 Ready"
-* Technial concept --> Folder "40 Ready for Implementation"
-* currently implementing --> "60 InProgress"
-* Pull Request created --> "70 Impl Done"
-* Merged to Candiate or Release branch --> "80 Merged"
-* Tested and deployed --> "99 Done"

+ 0 - 6
baangt.py

@@ -1,6 +0,0 @@
-from baangt.base.CliAndInteractive import run
-from multiprocessing import freeze_support
-
-if __name__ == '__main__':
-    freeze_support()
-    run()

+ 0 - 83
baangt/TestCase/TestCaseMaster.py

@@ -1,83 +0,0 @@
-from baangt.base import GlobalConstants as GC
-from baangt.base.Timing.Timing import Timing
-from baangt.TestSteps.Exceptions import *
-
-class TestCaseMaster:
-    def __init__(self, **kwargs):
-        self.name = None
-        self.description = None
-        self.testSteps = {}
-        self.apiInstance = None
-        self.numberOfParallelRuns = None
-        self.testRunInstance = kwargs.get(GC.KWARGS_TESTRUNINSTANCE)
-        self.testRunUtils = self.testRunInstance.testRunUtils
-        self.testSequence = self.testRunUtils.getSequenceByNumber(testRunName=self.testRunInstance.testRunName,
-                                                                  sequence=kwargs.get(GC.STRUCTURE_TESTCASESEQUENCE))
-        self.testCaseSettings = self.testRunUtils.getTestCaseByNumber(self.testSequence,
-                                                                      kwargs.get(GC.STRUCTURE_TESTCASE))
-        self.testSteps = self.testCaseSettings[2][GC.STRUCTURE_TESTSTEP]
-        self.testCaseType = self.testCaseSettings[1][GC.KWARGS_TESTCASETYPE]
-        self.kwargs = kwargs
-        self.timing : Timing = self.kwargs.get(GC.KWARGS_TIMING)
-        self.timingName = self.timing.takeTime(self.__class__.__name__, forceNew=True)
-        if self.testCaseType == GC.KWARGS_BROWSER:
-            if self.kwargs.get(GC.KWARGS_BROWSER):
-                self.browser = self.kwargs[GC.KWARGS_BROWSER] # Browser already set - most probably from parallel run
-            else:
-                self.browserType = self.testCaseSettings[1][GC.KWARGS_BROWSER]
-                self.browserSettings = self.testCaseSettings[1][GC.BROWSER_ATTRIBUTES]
-                self.mobileType = self.testCaseSettings[1][GC.KWARGS_MOBILE]
-                self.browser = self.testRunInstance.getBrowser(browserName=self.browserType, browserAttributes=self.browserSettings, mobileType=self.mobileType)
-                self.kwargs[GC.KWARGS_BROWSER] = self.browser
-        elif self.testCaseType == GC.KWARGS_API_SESSION:
-            # FIXME: For now we're using session_number always = 1. We need to be able to run e.g. 10 sessions with
-            # FIXME: Parallel API-Test.
-            self.apiInstance = self.testRunInstance.getAPI()
-            self.kwargs[GC.KWARGS_API_SESSION] = self.apiInstance
-
-            # For API-Tests we assume status = Passed and actively set it to error if not.
-            self.kwargs[GC.KWARGS_DATA][GC.TESTCASESTATUS] = GC.TESTCASESTATUS_SUCCESS
-        self.execute()
-        self.tearDown()
-
-    def execute(self):
-        try:
-            self.testRunInstance.executeDictSequenceOfClasses(self.testSteps, GC.STRUCTURE_TESTSTEP, **self.kwargs)
-        except baangtTestStepException as e:
-            logger.info(f"Testcase {self.kwargs.get(GC.STRUCTURE_TESTSTEP,'')} failed")
-            self.kwargs[GC.KWARGS_DATA][GC.TESTCASEERRORLOG] += '\n' + "Exception-Text: " + str(e)
-            self.kwargs[GC.KWARGS_DATA][GC.TESTCASESTATUS] = GC.TESTCASESTATUS_ERROR
-        finally:
-            self._finalizeTestCase()
-
-    def _finalizeTestCase(self):
-        tcData = self.kwargs[GC.KWARGS_DATA]
-        tcData[GC.TIMING_DURATION] = self.timing.takeTime(self.timingName)   # Write the End-Record for this Testcase
-
-        tcData[GC.TIMELOG] = self.timing.returnTime()
-
-        self.timing.resetTime()
-
-    def _checkAndSetTestcaseStatusIfFailExpected(self):
-        """
-        If this Testcase is supposed to fail and failed, he's actually successful.
-        If this Testcase is supposed to fail and doesn't, he's actually failed.
-        @return: Directly sets the Testcasestatus accordingly.
-        """
-        tcData = self.kwargs[GC.KWARGS_DATA]
-
-        if tcData.get(GC.TESTCASE_EXPECTED_ERROR_FIELD) == 'X':
-            if tcData[GC.TESTCASESTATUS] == GC.TESTCASESTATUS_ERROR:
-                tcData[GC.TESTCASESTATUS] = GC.TESTCASESTATUS_SUCCESS
-            elif tcData[GC.TESTCASESTATUS] == GC.TESTCASESTATUS_SUCCESS:
-                tcData[GC.TESTCASESTATUS] = GC.TESTCASESTATUS_ERROR
-
-    def tearDown(self):
-        if self.kwargs[GC.KWARGS_DATA][GC.TESTCASESTATUS] == GC.TESTCASESTATUS_ERROR:
-            # Try taking a Screenshot
-            if self.testCaseType == GC.KWARGS_BROWSER:
-                self.kwargs[GC.KWARGS_DATA][GC.SCREENSHOTS] = self.kwargs[GC.KWARGS_DATA][GC.SCREENSHOTS] + '\n' +\
-                                                              self.browser.takeScreenshot()
-
-        self._checkAndSetTestcaseStatusIfFailExpected()
-

+ 0 - 0
baangt/TestCase/__init__.py


+ 0 - 151
baangt/TestCaseSequence/TestCaseSequenceMaster.py

@@ -1,151 +0,0 @@
-from baangt.base.HandleDatabase import HandleDatabase
-from baangt.TestCaseSequence.TestCaseSequenceParallel import TestCaseSequenceParallel
-from baangt.base.Timing.Timing import Timing
-from baangt.base.Utils import utils
-import baangt.base.GlobalConstants as GC
-import multiprocessing
-from pathlib import Path
-import time
-from datetime import datetime
-import sys
-import logging
-
-logger = logging.getLogger("pyC")
-
-
-class TestCaseSequenceMaster:
-    def __init__(self, **kwargs):
-        self.name = None
-        self.description = None
-        self.timing : Timing = kwargs.get(GC.KWARGS_TIMING)
-        self.testdataDataBase = None
-        self.testrunAttributes = kwargs.get(GC.KWARGS_TESTRUNATTRIBUTES)
-        self.testRunInstance = kwargs.get(GC.KWARGS_TESTRUNINSTANCE)
-        self.testRunName = self.testRunInstance.testRunName
-        self.dataRecords = {}
-        self.recordCounter = 0
-        # Extract relevant data for this TestSequence:
-        self.testSequenceData = self.testrunAttributes[GC.STRUCTURE_TESTCASESEQUENCE].get(
-            kwargs.get(GC.STRUCTURE_TESTCASESEQUENCE))[1]
-        self.testCases = self.testSequenceData[GC.STRUCTURE_TESTCASE]
-        self.kwargs = kwargs
-        self.timingName = self.timing.takeTime(self.__class__.__name__,forceNew=True)
-        self.prepareExecution()
-        if int(self.testSequenceData.get(GC.EXECUTION_PARALLEL, 0)) > 1:
-            self.execute_parallel(self.testSequenceData.get(GC.EXECUTION_PARALLEL, 0))
-        else:
-            self.execute()
-
-    def prepareExecution(self):
-        if self.testSequenceData.get(GC.DATABASE_FROM_LINE) and not self.testSequenceData.get(GC.DATABASE_LINES, None):
-            # Change old line selection format into new format:
-            self.testSequenceData[GC.DATABASE_LINES] = f"{self.testSequenceData.get(GC.DATABASE_FROM_LINE)}-{self.testSequenceData.get(GC.DATABASE_TO_LINE)}"
-            self.testSequenceData.pop(GC.DATABASE_FROM_LINE)
-            self.testSequenceData.pop(GC.DATABASE_TO_LINE)
-        self.__getDatabase()
-        recordPointer = 0
-        # Read all Testrecords into l_testRecords:
-        while True:
-            self.dataRecords[recordPointer] = self.getNextRecord()
-            if not self.dataRecords[recordPointer]:
-                self.dataRecords.pop(recordPointer)
-                recordPointer -= 1
-                break
-            recordPointer += 1
-        logger.info(f"{recordPointer + 1} test records read for processing")
-
-    def execute_parallel(self, parallelInstances):
-        # Usually the Testcases themselves would request Browser from Testrun
-        # In this case we need to request them, because the Testcases will run in their own
-        # Processes
-        parallelInstances = int(parallelInstances)
-        browserInstances = {}
-        for n in range(0, int(parallelInstances)):
-            # fixme: Browser should come from Testcase definition - not hardcoded. It's not that easy, as we might have many
-            # fixme: Testcases, some Browser, some API and we might even have different browsers. For now we'll only
-            # fixme: take Browser from globals-file
-            lBrowserName = self.testRunInstance.globalSettings.get("TC.Browser", GC.BROWSER_FIREFOX)
-            lBrowserAttributes = self.testRunInstance.globalSettings.get("TC." + GC.BROWSER_ATTRIBUTES, None)
-            browserInstances[n] = self.testRunInstance.getBrowser(browserInstance=n,
-                                                                  browserName=lBrowserName,
-                                                                  browserAttributes=lBrowserAttributes)
-
-        processes = {}
-        processExecutions = {}
-        resultQueue = multiprocessing.Queue()
-
-        numberOfRecords = len(self.dataRecords)
-        for n in range(0, numberOfRecords, parallelInstances):
-            for x in range(0, parallelInstances):
-                if self.dataRecords.get(n + x):
-                    logger.debug(f"starting Process and Executions {x}. Value of n+x is {n + x}, "
-                                 f"Record = {str(self.dataRecords[n + x])[0:50]}")
-                    self.kwargs[GC.KWARGS_DATA] = self.dataRecords[n+x]
-                    # Prints the first 5 fields of the data record into the log:
-                    logger.info(f"Starting parallel execution with TestRecord {n+x}, Details: " +
-                        str({k: self.kwargs[GC.KWARGS_DATA][k] for k in list(self.kwargs[GC.KWARGS_DATA])[0:5]}))
-                    self.kwargs[GC.KWARGS_BROWSER] = browserInstances[x]
-                    processes[x] = TestCaseSequenceParallel(sequenceNumber=x,
-                                                            tcNumber=n + x,
-                                                            testcaseSequence=self.testCases,
-                                                            **self.kwargs)
-                    processExecutions[x] = multiprocessing.Process(target=processes[x].one_sequence,
-                                                                   args=(resultQueue,))
-                else:
-                    # This is the case when we have e.g. 4 parallel runs and 5 testcases,
-                    # First iteration: all 4 are used. Second iteration: only 1 used, 3 are empty.
-                    if processExecutions.get(x):
-                        processExecutions.pop(x)
-
-            for x in range(0, parallelInstances):
-                logger.info(f"starting execution of parallel instance {x}")
-                if processExecutions.get(x):
-                    processExecutions[x].start()
-
-            for x in range(0, parallelInstances):
-                if processExecutions.get(x):
-                    # Queue should be filled by now - take entries into Testrun-instance:
-                    while not resultQueue.empty():
-                        resultDict = resultQueue.get()
-                        for recordNumber, dataRecordAfterExecution in resultDict.items():
-                            self.testRunInstance.setResult(recordNumber, dataRecordAfterExecution)
-                    # Quit the running parallel process:
-                    logger.info(f"Stopping parallel instance {x}")
-                    processExecutions[x].join()
-
-    def execute(self):
-        # Execute all Testcases:
-        for key, value in self.dataRecords.items():
-            self.kwargs[GC.KWARGS_DATA] = value
-            logger.info(f"Starting execution with TestRecord {key}, Details: " +
-                        str({k: self.kwargs[GC.KWARGS_DATA][k] for k in list(self.kwargs[GC.KWARGS_DATA])[0:5]}))
-            self.testRunInstance.executeDictSequenceOfClasses(self.testCases, GC.STRUCTURE_TESTCASE,
-                                                              **self.kwargs)
-            d_t = datetime.fromtimestamp(time.time())
-            self.testRunInstance.append1DTestCaseEndDateTimes(d_t)
-            logger.info("execute append1DTestCaseEndDateTimes, param is {}".format(d_t))
-            # Write Result back to TestRun for later saving in export format
-            self.testRunInstance.setResult(key, value)
-
-    def getNextRecord(self):
-        self.recordCounter += 1
-
-        if not self.testdataDataBase:
-            self.__getDatabase()
-
-        lDataRecord=self.testdataDataBase.readNextRecord()
-        return lDataRecord
-
-    def __getDatabase(self):
-        if not self.testdataDataBase:
-            self.testdataDataBase = HandleDatabase(globalSettings=self.testRunInstance.globalSettings,
-                                                   linesToRead=self.testSequenceData.get(GC.DATABASE_LINES))
-            self.testdataDataBase.read_excel(
-                fileName=utils.findFileAndPathFromPath(
-                    self.testSequenceData[GC.DATABASE_FILENAME],
-                    basePath=str(Path(self.testRunInstance.globalSettingsFileNameAndPath).parent)),
-                sheetName=self.testSequenceData[GC.DATABASE_SHEETNAME])
-        return self.testdataDataBase
-
-    def tearDown(self):
-        self.timing.takeTime(self.timingName)

+ 0 - 52
baangt/TestCaseSequence/TestCaseSequenceParallel.py

@@ -1,52 +0,0 @@
-import multiprocessing
-import logging
-from baangt.TestSteps import Exceptions
-from baangt.base import GlobalConstants as GC
-from datetime import datetime
-import time
-logger = logging.getLogger("pyC")
-
-
-class TestCaseSequenceParallel:
-    def __init__(self, sequenceNumber, tcNumber, testcaseSequence=None, **kwargs):
-        self.manager = multiprocessing.Manager()
-        self.process_list = self.manager.list()
-        self.sequenceNumber = sequenceNumber
-        self.dataRecord = kwargs.get(GC.KWARGS_DATA)
-        self.tcNumber = tcNumber
-        self.testcaseSequence = testcaseSequence
-        self.kwargs = kwargs
-
-    def one_sequence(self, resultQueue: multiprocessing.Queue):
-        dataRecord = self.dataRecord
-        currentRecordNumber = self.tcNumber
-        testcaseSequence = self.testcaseSequence
-        parallelizationSequenceNumber = self.sequenceNumber
-        logger.info(f"Starting one_sequence with SequenceNumber = {parallelizationSequenceNumber}, "
-                    f"CurrentRecordNumber is {currentRecordNumber}")
-
-        if not dataRecord:
-            logger.warning("dataRecord was empty - doing nothing")
-            return
-
-        try:
-            self.kwargs[GC.KWARGS_TESTRUNINSTANCE].executeDictSequenceOfClasses(testcaseSequence,
-                                                                                GC.STRUCTURE_TESTCASE,
-                                                                                **self.kwargs)
-
-            d_t = datetime.fromtimestamp(time.time())
-            self.kwargs[GC.KWARGS_TESTRUNINSTANCE].append2DTestCaseEndDateTimes(self.sequenceNumber,
-                                                                                d_t)
-
-            logger.info("execute append2DTestCaseEndDateTimes, testrun id is {} , params are {}, {}".
-                        format(id(self.kwargs[GC.KWARGS_TESTRUNINSTANCE]), self.sequenceNumber, d_t))
-
-        except Exceptions.baangtTestStepException as e:
-            logger.critical(f"Unhandled Error happened in parallel run {parallelizationSequenceNumber}: " + str(e))
-            dataRecord[GC.TESTCASESTATUS] = GC.TESTCASESTATUS_ERROR
-        finally:
-            # the result must be pushed into the queue:
-            logger.debug(
-                f"Starting to Put value in Queue {currentRecordNumber}. Len of datarecord: {len(str(dataRecord))}")
-            resultQueue.put({self.tcNumber: dataRecord})
-            logger.debug(f"Finished putting Value i Queue for TC {currentRecordNumber}")

+ 0 - 0
baangt/TestCaseSequence/__init__.py


+ 0 - 133
baangt/TestSteps/AddressCreation.py

@@ -1,133 +0,0 @@
-class AddressCreate:
-    """
-    This file Define a Singleton Class AddressCreate which will have static
-        method returnAddress to return the address in format.
-
-
-        CountryCode**
-        PostlCode**
-        CityName
-        StreetName
-        HouseNumber
-        AdditionalData1
-        AdditionalData2
-
-        Input: value1 = {'CountryCode':"IN",'CityName':"Mohali"} [optional]
-               value2 = "sales_"
-
-        Output: {'sales_CountryCode':"IN","sales_PostalCode":"19883",
-              "sales_CityName":"Mohali","sales_StreeName":"32HB",
-               "sales_AdditionalData1":"Near Hospital",
-               "sales_AdditionData2":"Ajanta Mandis Park"}
-
-
-        Sample Usage:
-                Default Value
-                >> AddressCreate.returnAddress()
-
-                Pass value of ('HouseNumber', 'AdditionalData1',
-                 'AdditionalData2', 'StreetName', 'CityName', 'PostalCode',
-                 'CountryCode' ) to change.
-
-               For example:
-                >> Update City Name
-                  AddressCreate(value1={"CityName":"Something"})
-
-                  or
-                  AddressCreate({"CityName":"Something"})
-
-                >> Update Country Name
-                  AddressCreate({"CountryCode":"US"})
-
-
-
-    """
-
-    __instance = None
-
-    class __AddressCreate:
-        """ Inner class   """
-
-        def __init__(self, value1={}, value2=""):
-            addressData = {"HouseNumber": "6",
-                           "AdditionalData1": "Near MahavirChowk",
-                           "AdditionalData2": "Opposite St. Marish Church",
-                           "StreetName": "GoreGaon",
-                           "CityName": "Ambala",
-                           "PostalCode": "160055",
-                           "CountryCode": "India",
-                           }
-            prefixData = ""  # empty initially
-            
-            self.value1 = addressData
-            self.value2 = prefixData
-
-            # Now update the value
-            self.updateValue(value1, value2)
-
-        def updateValue(self, value1, value2):
-            """ This function will process value1,value2 and
-                prepare dictionary data
-            """
-            # value1
-            try:
-                value1 = dict(value1)
-                value2 = str(value2)
-
-            except Exception as e:
-                print("Error Occured :  ", e)
-            # process only dictionary data, ignore everything else
-            if isinstance(value1, type({})):
-                for key, value in value1.items():
-                    if key in self.value1:
-                        self.value1[key] = value
-
-            if value2.strip():
-                self.value2 = value2.strip()
-
-        def __str__(self):
-            """ AddressCreation print information"""
-            return "AddressCreate Instance id :{} ".format(id(self))
-
-        def __repr__(self):
-            return "AddressCreate Instance id:{}\nvalue1: {}\nvalue2: {}".format(
-                id(self), self.value1, self.value2)
-
-    @staticmethod
-    def getInstance():
-        if AddressCreate.__instance is None:
-            AddressCreate.__instance = AddressCreate.__AddressCreate()
-        return AddressCreate.__instance
-
-    def __new__(cls, value1={}, value2=""):
-        """ Get the instance and update the value """
-        if not AddressCreate.__instance:
-            # Create new instance
-            AddressCreate.__instance = AddressCreate.__AddressCreate(
-                value1, value2)
-        else:
-            # Update old instance
-            AddressCreate.__instance.updateValue(value1, value2)
-        return AddressCreate.__instance
-
-    @staticmethod
-    def returnAddress():
-        """This function will use value1 and value2 to prepare structured
-            Address Data
-        """
-        # get the instance
-        a = AddressCreate.getInstance()
-        data = {}
-
-        for key, val in a.value1.items():
-            # prefix_value + addresskey = addresskey_value
-            # if prefix = "Sales" and "CountryCode" = "India"
-            # then final data will be data['Sales_CountryCode'] = India
-            if a.value2:
-                # prefix is given
-                data[a.value2 + key] = val
-            else:
-                data[key] = val
-        # finally return the processed dataDict
-
-        return data

+ 0 - 22
baangt/TestSteps/DropsApp/Login_API.py

@@ -1,22 +0,0 @@
-from baangt.base.ApiHandling import ApiHandling
-from baangt.TestSteps.TestStepMaster import TestStepMaster
-import baangt.base.GlobalConstants as GC
-
-
-class Login_API(TestStepMaster):
-    def __init__(self, **kwargs):
-        super().__init__(**kwargs)
-        self.execute()
-
-    def execute(self):
-        lUrl = self.testcaseDataDict["baseURL"]
-        session : ApiHandling = self.apiSession
-        content = {"username": self.testcaseDataDict["Username"],
-                   "password": self.testcaseDataDict["Password"]}
-        resultStatus, resultDict, resultHeader = session.postURL(url=lUrl, content=content)
-
-        self.testcaseDataDict["resultStatus"] = resultStatus
-        self.testcaseDataDict["resultDict"] = resultDict
-        self.testcaseDataDict["resultHeader"] = resultHeader
-
-        self.testcaseDataDict[GC.TESTCASESTATUS] = session.returnTestCaseStatus(resultStatus)

+ 0 - 0
baangt/TestSteps/DropsApp/__init__.py


+ 0 - 8
baangt/TestSteps/Exceptions.py

@@ -1,8 +0,0 @@
-import logging
-
-logger = logging.getLogger("pyC")
-
-
-class baangtTestStepException(Exception):
-    def __init__(self, *args, **kwargs):
-        logger.exception(f"Exception occured - aborting. Args: {args}, KWARGS: {kwargs}")

+ 0 - 315
baangt/TestSteps/TestStepMaster.py

@@ -1,315 +0,0 @@
-import baangt.base.GlobalConstants as GC
-from baangt.base.Timing.Timing import Timing
-from baangt.base.BrowserHandling.BrowserHandling import BrowserDriver
-from baangt.base.ApiHandling import ApiHandling
-import sys
-from pkg_resources import parse_version
-import logging
-from baangt.TestSteps.Exceptions import baangtTestStepException
-from baangt.TestSteps.AddressCreation import AddressCreate
-
-logger = logging.getLogger("pyC")
-
-
-class TestStepMaster:
-    def __init__(self, **kwargs):
-        self.kwargs = kwargs
-        self.testRunInstance = kwargs.get(GC.KWARGS_TESTRUNINSTANCE)
-        self.testcaseDataDict = kwargs.get(GC.KWARGS_DATA)
-        self.timing: Timing = kwargs.get(GC.KWARGS_TIMING)
-        self.timingName = self.timing.takeTime(self.__class__.__name__, forceNew=True)
-        self.browserSession: BrowserDriver = kwargs.get(GC.KWARGS_BROWSER)
-        self.apiSession: ApiHandling = kwargs.get(GC.KWARGS_API_SESSION)
-        self.testCaseStatus = None
-        self.testStepNumber = kwargs.get(GC.STRUCTURE_TESTSTEP)    # Set in TestRun by TestCaseMaster
-        self.testRunUtil = self.testRunInstance.testRunUtils
-        # check, if this TestStep has additional Parameters and if so, execute
-        lSequence = self.testRunUtil.getSequenceByNumber(testRunName=self.testRunInstance.testRunName,
-                                                         sequence=kwargs.get(GC.STRUCTURE_TESTCASESEQUENCE))
-        lTestCase = self.testRunUtil.getTestCaseByNumber(lSequence, kwargs.get(GC.STRUCTURE_TESTCASE))
-        lTestStep = self.testRunUtil.getTestStepByNumber(lTestCase, kwargs.get(GC.STRUCTURE_TESTSTEP))
-        self.globalRelease = self.testRunInstance.globalSettings.get("Release", "")
-        self.ifActive = False
-        self.ifIsTrue = True
-
-        if not isinstance(lTestStep, str):
-            # This TestStepMaster-Instance should actually do something - activitites are described
-            # in the TestExecutionSteps
-            self.executeDirect(lTestStep[1][GC.STRUCTURE_TESTSTEPEXECUTION])
-
-    def executeDirect(self, executionCommands):
-        """
-        This will execute single Operations directly
-        """
-        for key, command in executionCommands.items():
-            # when we have an IF-condition and it's condition was not TRUE, then skip whatever comes here until we
-            # reach Endif
-            if not self.ifIsTrue and command["Activity"] != "ENDIF":
-                continue
-
-            lActivity = command["Activity"].upper()
-            if lActivity == "COMMENT":
-                continue     # Comment's are ignored
-
-            lLocatorType = command["LocatorType"].upper()
-            lLocator = self.replaceVariables(command["Locator"])
-
-            if lLocator and not lLocatorType:   # If locatorType is empty, default it to XPATH
-                lLocatorType = 'XPATH'
-
-            xpath, css, id = self.__setLocator(lLocatorType, lLocator)
-
-            lValue = str(command["Value"])
-            lValue2 = str(command["Value2"])
-            lComparison = command["Comparison"]
-            lOptional = TestStepMaster._sanitizeXField(command["Optional"])
-
-            # check release line
-            lRelease = command["Release"]
-
-            lTimeout = TestStepMaster.__setTimeout(command["Timeout"])
-
-            # Replace variables from data file
-            if len(lValue) > 0:
-                lValue = self.replaceVariables(lValue)
-            if len(lValue2) > 0:
-                lValue2 = self.replaceVariables(lValue2)
-
-            if not TestStepMaster.ifQualifyForExecution(self.globalRelease, lRelease):
-                logger.debug(f"we skipped this line due to {lRelease} disqualifies according to {self.globalRelease} ")
-                continue  # We ignored the steps as it doesn't qualify
-
-            if lActivity == "GOTOURL":
-                print('-----------------this is the url--------------------')
-                print(self.browserSession)
-                print('----------------------this is the url--------------------------')
-                lValue ='https://drops.earthsquad.global'
-                self.browserSession.goToUrl(lValue)
-            elif lActivity == "SETTEXT":
-                self.browserSession.findByAndSetText(xpath=xpath, css=css, id=id, value=lValue, timeout=lTimeout)
-            elif lActivity == 'HANDLEIFRAME':
-                self.browserSession.handleIframe(lLocator)
-            elif lActivity == "CLICK":
-                self.browserSession.findByAndClick(xpath=xpath, css=css, id=id, timeout=lTimeout)
-            elif lActivity == "IF":
-                if self.ifActive:
-                    raise BaseException("No nested IFs at this point, sorry...")
-                self.ifActive = True
-                # Originally we had only Comparisons. Now we also want to check for existance of Field
-                if not lValue and lLocatorType and lLocator:
-                    lValue = self.browserSession.findBy(xpath=xpath, css=css, id=id, optional=lOptional,
-                                                        timeout=lTimeout)
-
-                self.__doComparisons(lComparison=lComparison, value1=lValue, value2=lValue2)
-            elif lActivity == "ENDIF":
-                if not self.ifActive:
-                    raise BaseException("ENDIF without IF")
-                self.ifActive = False
-                self.ifIsTrue = True
-            elif lActivity == 'GOBACK':
-                self.browserSession.goBack()
-            elif lActivity == 'APIURL':
-                self.apiSession.setBaseURL(lValue)
-            elif lActivity == 'ENDPOINT':
-                self.apiSession.setEndPoint(lValue)
-            elif lActivity == 'POST':
-                self.apiSession.postURL(content=lValue)
-            elif lActivity == 'GET':
-                self.apiSession.getURL()
-            elif lActivity == 'HEADER':
-                self.apiSession.setHeaders(setHeaderData=lValue)
-            elif lActivity == 'SAVE':
-                self.doSaveData(lValue, lValue2)
-            elif lActivity == "ADDRESS_CREATE":
-                # Create Address with option lValue and lValue2
-                AddressCreate(lValue,lValue2)
-                # Update testcaseDataDict with addressDict returned from
-                AddressCreate.returnAddress()
-                self.testcaseDataDict.update(AddressCreate.returnAddress())
-            elif lActivity == 'ASSERT':
-                value_found = self.browserSession.findByAndWaitForValue(xpath=xpath, css=css, id=id, optional=lOptional,
-                                                        timeout=lTimeout)
-                value_expected = lValue
-                if value_expected != value_found:
-                    raise baangtTestStepException(f"Expected Value: {value_expected}, Value found :{value_found} ")
-            else:
-                raise BaseException(f"Unknown command in TestStep {lActivity}")
-
-    @staticmethod
-    def _sanitizeXField(inField):
-        """
-        When "X" or "True" is sent, then use this
-        @param inField:
-        @return:
-        """
-        lXField = True
-
-        if not inField:
-            lXField = False
-
-        if inField.upper() == 'FALSE':
-            lXField = False
-
-        return lXField
-
-    @staticmethod
-    def ifQualifyForExecution(version_global, version_line):
-        """ This function will test version_global and version_line
-            @return True or False
-        """
-        if not version_global.strip():
-            # No value is defined in Release, return True
-            return True
-        if not version_line.strip():
-            # we skipped this line
-            return True
-
-        # split the version line
-        if not len(version_line.strip().split(" ")) == 2:
-            logger.debug(f"Invalid release format {version_line} ")
-            return True
-        comp_operator, version = version_line.strip().split(" ")
-        if comp_operator == "<":
-            return parse_version(version_global) < parse_version(version)
-        elif comp_operator == ">":
-            return parse_version(version_global) > parse_version(version)
-        elif comp_operator == ">=":
-            return parse_version(version_global) >= parse_version(version)
-        elif comp_operator == "<=":
-            return parse_version(version_global) <= parse_version(version)
-        elif comp_operator == "=" or comp_operator == "==":
-            return parse_version(version_global) == parse_version(version)
-        else:
-            logger.debug(f"Global version {version_global}, line version {version_line} ")
-            return False
-
-    def doSaveData(self, toField, valueForField):
-        self.testcaseDataDict[toField] = valueForField
-
-    @staticmethod
-    def __setLocator(lLocatorType, lLocator):
-        """
-
-        @param lLocatorType: XPATH, CSS, ID, etc.
-        @param lLocator: Value of the locator
-        @return:
-        """
-        xpath = None
-        css = None
-        lId = None
-
-        if lLocatorType:
-            if lLocatorType == 'XPATH':
-                xpath = lLocator
-            elif lLocatorType == 'CSS':
-                css = lLocator
-            elif lLocatorType == 'ID':
-                lId = lLocator
-
-        return xpath, css, lId
-
-    @staticmethod
-    def __setTimeout(lTimeout):
-        if lTimeout:
-            lTimeout = float(lTimeout)
-        else:
-            lTimeout = 20
-        return lTimeout
-
-    @staticmethod
-    def anyting2Boolean(valueIn):
-        if isinstance(valueIn, bool):
-            return valueIn
-
-        if isinstance(valueIn, int):
-            return bool(valueIn)
-
-        if isinstance(valueIn, str):
-            if valueIn.lower() in ("yes", "true", "1", "ok"):
-                return True
-            else:
-                return False
-
-        raise TypeError(f"Anything2Boolean had a wrong value: {valueIn}. Don't know how to convert that to boolean")
-
-    def __doComparisons(self, lComparison, value1, value2):
-        if isinstance(value1, bool) or isinstance(value2, bool):
-            value1 = TestStepMaster.anyting2Boolean(value1)
-            value2 = TestStepMaster.anyting2Boolean(value2)
-
-        if lComparison == "=":
-            if value1 == value2:
-                self.ifIsTrue = True
-            else:
-                self.ifIsTrue = False
-        elif lComparison == ">":
-            if value1 > value2:
-                self.ifIsTrue = True
-            else:
-                self.ifIsTrue = False
-        elif lComparison == "<":
-            if value1 < value2:
-                self.ifIsTrue = True
-            else:
-                self.ifIsTrue = False
-        else:
-            raise BaseException(f"Comparison Operator not supported/unknown {lComparison}")
-
-    def execute(self):
-        """Method is overwritten in all children"""
-        pass
-
-    def teardown(self):
-        if self.testCaseStatus:
-            if not self.testcaseDataDict[GC.TESTCASESTATUS]:
-                self.testcaseDataDict[GC.TESTCASESTATUS] = self.testCaseStatus
-            elif self.testCaseStatus == GC.TESTCASESTATUS_SUCCESS:
-                # We'll not overwrite a potential Error Status of the testcase
-                pass
-            elif self.testCaseStatus == GC.TESTCASESTATUS_ERROR:
-                self.testcaseDataDict = GC.TESTCASESTATUS_ERROR
-            else:
-                sys.exit("No idea, what happened here. Unknown condition appeared")
-        self.timing.takeTime(self.timingName)   # Why does this not work?
-
-    def replaceVariables(self, expression):
-        """
-        The syntax for variables is currently $(<column_name_from_data_file>). Multiple variables can be assigned
-        in one cell, for instance perfectly fine: "http://$(BASEURL)/$(ENDPOINT)"
-
-        @param expression: the current cell, either as fixed value, e.g. "Franzi" or with a varible $(DATE)
-        @return: the replaced value, e.g. if expression was $(DATE) and the value in column "DATE" of data-file was
-            "01.01.2020" then return will be "01.01.2020"
-        """
-        if "$(" not in expression:
-            return expression
-
-        while "$(" in expression:
-            if expression[0:2] == "$(":
-                left_part = ""
-            else:
-                left_part = expression.split("$(")[0]
-
-            center = expression[len(left_part)+2:]
-            center = center.split(")")[0]
-
-            right_part = expression[len(left_part)+len(center)+3:]
-
-            if "." not in center:
-                # Replace the variable with the value from data structure
-                center = self.testcaseDataDict.get(center)
-            else:
-                # This is a reference to a DICT with ".": for instance APIHandling.AnswerContent("<bla>")
-                dictVariable = center.split(".")[0]
-                dictValue = center.split(".")[1]
-
-                if dictVariable == 'ANSWER_CONTENT':
-                    center = self.apiSession.session[1].answerJSON.get(dictValue, "Empty")
-                else:
-                    raise BaseException(f"Missing code to replace value for: {center}")
-
-            if not center:
-                raise BaseException(f"Variable not found: {center}")
-
-            expression = "".join([left_part, center, right_part])
-        return expression

+ 0 - 0
baangt/TestSteps/__init__.py


+ 0 - 71
baangt/__init__.py

@@ -1,71 +0,0 @@
-import logging
-from datetime import datetime
-import pathlib
-import sys
-import os
-from pathlib import Path
-from pluggy import HookspecMarker, HookimplMarker, PluginManager
-
-hook_spec = HookspecMarker("baangt")
-hook_impl = HookimplMarker("baangt")
-plugin_manager = PluginManager("baangt")
-
-from baangt.hookSpecs import baangtHookSpec
-
-plugin_manager.add_hookspecs(baangtHookSpec)
-
-from baangt.base.TestRun.hookImpls import TestRunHookImpl
-from baangt.base.BrowserHandling.hookImpls import BrowserDriverHookImpl
-from baangt.base.Timing.hookImpls import TimingHookImpl
-from baangt.base.ExportResults.hookImpls import \
-    (ExportResultsHookImpl, ExcelSheetHelperFunctionsHookImpl, ExportTimingHookImpl)
-
-
-plugin_manager.register(plugin=TestRunHookImpl())
-plugin_manager.register(plugin=BrowserDriverHookImpl())
-plugin_manager.register(plugin=TimingHookImpl())
-plugin_manager.register(plugin=ExportResultsHookImpl())
-plugin_manager.register(plugin=ExcelSheetHelperFunctionsHookImpl())
-plugin_manager.register(plugin=ExportTimingHookImpl())
-
-
-# fixme: Parameter für Logfile should include stage and browser()
-logFilename:pathlib.Path = Path(os.getcwd())
-logFilename = logFilename.joinpath('logs')
-pathlib.Path(logFilename).mkdir(parents=True, exist_ok=True)
-logFilename = logFilename.joinpath(datetime.now().strftime("%Y%m%d_%H%M%S") + '.log')
-
-print(f"Logfile used: {logFilename}")
-
-# Bit more advanced logging
-logger = logging.getLogger('pyC')
-logger.setLevel(logging.DEBUG)
-# create file handler which logs even debug messages
-fileHandler = logging.FileHandler(logFilename, encoding ="UTF-8")
-fileHandler.setLevel(level=logging.DEBUG)
-# create console handler with a higher log level
-channelHandler = logging.StreamHandler()
-channelHandler.setLevel(logging.INFO)
-# create formatter and add it to the handlers
-formatter = logging.Formatter('%(asctime)s _ %(levelname)s _ %(module)s _ %(funcName)s : %(message)s')
-channelHandler.setFormatter(formatter)
-fileHandler.setFormatter(formatter)
-# add the handlers to logger
-logger.addHandler(channelHandler)
-logger.addHandler(fileHandler)
-
-# Some general debug info:
-logger.debug(f"Value of sys._MEIPASS {getattr(sys, '_MEIPASS', 'None')}")
-logger.debug(f"Value of __file__: {__file__}")
-logger.debug(f"Value of sys.executable: {sys.executable}")
-logger.debug(f"Value of sys.argv[0]: {sys.argv[0]}")
-logger.debug(f"Value of sys.path: {sys.path}")
-logger.debug(f"Value of os.path: {os.path}")
-logger.debug(f"Value of os.getcwd(): {os.getcwd()}")
-logger.debug(f"Value of Path.cwd(): {Path.cwd()}")
-
-if hasattr(sys, 'frozen') and hasattr(sys, '_MEIPASS'):
-    logger.debug('running in a PyInstaller bundle')
-else:
-    logger.debug('running in a normal Python process')
-

+ 0 - 39
baangt/base/AddressCreate.py

@@ -1,39 +0,0 @@
-from baangt.base import GlobalConstants as GC
-
-class AddressCreate:
-
-    returnDict = {
-        GC.ADDRESS_COUNTRYCODE: "",
-        GC.ADDRESS_POSTLCODE: "",
-        GC.ADDRESS_CITYNAME: "",
-        GC.ADDRESS_STREETNAME: "",
-        GC.ADDRESS_HOUSENUMBER: "",
-        GC.ADDRESS_ADDITION1: "",
-        GC.ADDRESS_ADDITION2: ""
-    }
-
-    def __init__(self, addressFilterCriteria:dict):
-        self.filterCriteria = addressFilterCriteria
-        pass
-
-    def returnAddress(self):
-        # fixme: Implement the functionality.
-
-        result = self.returnDict
-        if self.filterCriteria.get(GC.ADDRESS_COUNTRYCODE) == 'AT' or not self.filterCriteria:
-            result[GC.ADDRESS_COUNTRYCODE] = "AT"
-            result[GC.ADDRESS_CITYNAME] = "Wien"
-            result[GC.ADDRESS_POSTLCODE] = "1020"
-            result[GC.ADDRESS_STREETNAME] = "Castellezgasse"
-            result[GC.ADDRESS_HOUSENUMBER] = "32"
-            result[GC.ADDRESS_ADDITION1] = "5"
-        elif self.filterCriteria.get(GC.ADDRESS_COUNTRYCODE) == 'CY':
-            result[GC.ADDRESS_COUNTRYCODE] = "CY"
-            result[GC.ADDRESS_CITYNAME] = "Larnaca"
-            result[GC.ADDRESS_POSTLCODE] = "6020"
-            result[GC.ADDRESS_STREETNAME] = "Pavlou Valsamaki"
-            result[GC.ADDRESS_HOUSENUMBER] = "1"
-            result[GC.ADDRESS_ADDITION1] = "Apt 202"
-
-        return self.returnDict
-

+ 0 - 110
baangt/base/ApiHandling.py

@@ -1,110 +0,0 @@
-import requests
-from baangt.base import GlobalConstants as GC
-from baangt.TestSteps import Exceptions
-import logging
-import json
-
-logger = logging.getLogger("pyC")
-
-
-class ApiHandling:
-    def __init__(self):
-        """
-        This class handles Session Management for API-Calls
-        """
-        self.session = {}
-        self.baseURL = None
-        self.endPoint = None
-        self.answerStatusCode = None
-        self.answerHeaders = None
-        self.answerJSON = None
-
-    def getSession(self, sessionNumber=1):
-        if not self.session.get(sessionNumber):
-            self.session[sessionNumber] = requests.Session()
-        return self.session[sessionNumber]
-
-    def getNewSession(self, sessionNumber=None):
-        if not sessionNumber:
-            # fixme: Get highest Session Number, add 1, etc.
-            logger.critical("Method not implemented - aborting")
-            raise Exceptions.baangtTestStepException("Method not properly implemented")
-        else:
-            self.session[sessionNumber] = requests.session()
-
-    def getURL(self, url=None, sessionNumber=1):
-        url = self.__retrieveURL(url)
-        answer = self.getSession(sessionNumber=sessionNumber).get(url=url)
-
-        self.__saveAnswerLocally(answer=answer, sessionNumber=sessionNumber)
-
-        return self.session[sessionNumber].answerStatusCode, \
-               self.session[sessionNumber].answerJSON, \
-               self.session[sessionNumber].answerHeaders
-
-    @staticmethod
-    def returnTestCaseStatus(status_code):
-        if status_code < 300:
-            return GC.TESTCASESTATUS_SUCCESS
-        else:
-            return GC.TESTCASESTATUS_ERROR
-
-    def __saveAnswerLocally(self, answer, sessionNumber):
-        self.session[sessionNumber].answerStatusCode = answer.status_code
-        self.session[sessionNumber].answerJSON = answer.json()
-        self.session[sessionNumber].answerHeaders = dict(answer.headers)
-
-    def postURL(self, url=None, content=None, sessionNumber=1):
-        url = self.__retrieveURL(url)
-        logger.info(f"POST to url: {url} with content: {content}")
-        content = ApiHandling.__cnvtString2Dict(content)
-        if isinstance(content, str):
-            # Convert content into JSON-Format, if leading and trailing {}
-            if content[0] == "{" and content[-1] == "}":
-                content = json.loads(content)
-        answer = self.getSession(sessionNumber=sessionNumber).post(url=url, data=content)
-
-        self.__saveAnswerLocally(answer=answer, sessionNumber=sessionNumber)
-
-        return self.session[sessionNumber].answerStatusCode, \
-               self.session[sessionNumber].answerJSON, \
-               self.session[sessionNumber].answerHeaders
-
-    @staticmethod
-    def __cnvtString2Dict(content):
-        if isinstance(content, str):
-            # Convert content into JSON-Format, if leading and trailing {}
-            if content[0] == "{" and content[-1] == "}":
-                content = json.loads(content)
-
-        return content
-
-    def setLoginData(self, userName, password, sessionNumber=1):
-        self.session[sessionNumber].auth = (userName, password)
-
-    def setHeaders(self, sessionNumber=1, setHeaderData=None):
-        setHeaderData = ApiHandling.__cnvtString2Dict(setHeaderData)
-        self.session[sessionNumber].headers.update(setHeaderData)
-
-    def tearDown(self, sessionNumber=None):
-        if not sessionNumber:
-            for key, value in self.session.items():
-                self.session[key].close()
-
-    def __retrieveURL(self, url):
-        """
-
-        @param url: Importing parameter - maybe an Endpoint-URL
-        @return: either the passed URL or the combination of BaseURL + Endpoint.
-        """
-        if url:
-            return url
-
-        url = self.baseURL + self.endPoint
-        return url
-
-    def setBaseURL(self, url):
-        self.baseURL = url
-
-    def setEndPoint(self, endpoint):
-        self.endPoint = endpoint

+ 0 - 855
baangt/base/BrowserHandling/BrowserHandling.py

@@ -1,855 +0,0 @@
-import os
-from selenium import webdriver
-from appium import webdriver as Appiumwebdriver
-from selenium.webdriver.support import expected_conditions as ec
-from selenium.webdriver.common.by import By
-from selenium.webdriver.support.ui import WebDriverWait
-from selenium.webdriver.chrome.options import Options as ChromeOptions
-from selenium.webdriver.firefox.options import Options as ffOptions
-from selenium.common.exceptions import *
-from selenium.webdriver.common import keys
-from baangt.base import GlobalConstants as GC
-from baangt.base.Timing.Timing import Timing
-from baangt.TestSteps import Exceptions
-import uuid
-import time
-import logging
-from pathlib import Path
-import json
-import sys
-import platform
-import ctypes
-from urllib.request import urlretrieve
-import tarfile
-import zipfile
-import requests
-import time
-
-logger = logging.getLogger("pyC")
-
-
-class BrowserDriver:
-    """
-    The main class for baangt-Elements to interact with a browser.
-    Main Methods:
-    - createNewBrowser: Create one instance of a Browser
-    - findBy*-Methods: e.g. findByAndClick
-    - URL-Methods: To navigate to an URL
-    - handleIframe and handleWindow: To navigate between Windows (=tabs) and Iframes
-    - javaScript: to pass JS directly to the browser
-    - takeScreenshot: yes, that.
-    """
-    def __init__(self, timing=None, screenshotPath=None):
-        self.driver = None
-        self.iFrame = None
-        self.element = None
-        self.locatorType = None
-        self.locator = None
-        self.slowExecution = False
-        self.slowExecutionTimeoutInSeconds = 1
-
-        if timing:
-            self.timing = timing
-        else:
-            self.timing = Timing()
-
-        self.takeTime = self.timing.takeTime
-
-        if screenshotPath:
-            self.screenshotPath = screenshotPath
-            Path(self.screenshotPath).mkdir(exist_ok=True)
-        else:
-            self.screenshotPath = os.getcwd()
-
-    def createNewBrowser(self, mobileType=None, browserName=GC.BROWSER_FIREFOX, desiredCapabilities={}, **kwargs):
-        """
-        Will find the specified executables of the desired browser and start it with the given capabilities.
-
-        @param browserName: one of GC_BROWSER_*-Browsernames, e.g. GC_BROWSER_FIREFOX
-        @param desiredCapabilities: DICT of desiredCapabilities for this browser
-        @param kwargs: Currently (Jan2020) not used
-        """
-        self.takeTime("Browser Start")
-        browserNames = {
-            GC.BROWSER_FIREFOX: webdriver.Firefox,
-            GC.BROWSER_CHROME: webdriver.Chrome,
-            GC.BROWSER_SAFARI: webdriver.Safari,
-            GC.BROWSER_EDGE: webdriver.Edge,
-            GC.BROWSER_REMOTE: webdriver.Remote}
-        if browserName in browserNames:
-            GeckoExecutable = "geckodriver"
-            ChromeExecutable = "chromedriver"
-
-            if 'NT' in os.name.upper():
-                GeckoExecutable = GeckoExecutable + ".exe"
-                ChromeExecutable = ChromeExecutable + ".exe"
-
-
-            lCurPath = Path(os.getcwd())
-            lCurPath = lCurPath.joinpath("browserDrivers")
-
-            browserProxy = kwargs.get('browserProxy')
-            browserInstance = kwargs.get('browserInstance', 'unknown')
-
-
-            if browserName == GC.BROWSER_FIREFOX:
-                lCurPath = lCurPath.joinpath(GeckoExecutable)
-                if mobileType == 'True':
-                    if not (os.path.isfile(str(lCurPath))):
-                        self.downloadDriver(browserName)
-                    desired_cap = {}
-                    desired_cap['platformName'] = "Android"
-                    desired_cap['deviceName'] = "emulator-5554"
-                    desired_cap['browserName'] = browserName
-                    desired_cap['chromedriverExecutable'] = self.__findBrowserDriverPaths(ChromeExecutable)
-                    desired_cap['noReset'] = False
-                    self.driver = Appiumwebdriver.Remote("http://localhost:4723/wd/hub", desired_cap)
-                else:
-                    if not(os.path.isfile(str(lCurPath))):
-                        self.downloadDriver(browserName)
-                    profile = None
-                    firefox_proxy = browserProxy.selenium_proxy() if browserProxy else None
-                    if firefox_proxy:
-                        profile = webdriver.FirefoxProfile()
-                        profile.set_proxy(firefox_proxy)
-                    self.driver = browserNames[browserName](options=self.__createBrowserOptions(browserName=browserName,
-                                                                                                desiredCapabilities=desiredCapabilities),
-                                                            executable_path=self.__findBrowserDriverPaths(GeckoExecutable),
-                                                            firefox_profile=profile)
-                    browserProxy.new_har("baangt-firefox-{}".format(browserInstance),
-                                         options={'captureHeaders': True, 'captureContent': True}) \
-                        if firefox_proxy else None
-            elif browserName == GC.BROWSER_CHROME:
-                lCurPath = lCurPath.joinpath(ChromeExecutable)
-                if mobileType == 'True':
-                    if not (os.path.isfile(str(lCurPath))):
-                        self.downloadDriver(browserName)
-                    desired_cap = {}
-                    desired_cap['platformName'] = "Android"
-                    desired_cap['deviceName'] = "emulator-5554"
-                    desired_cap['browserName'] = browserName
-                    desired_cap['chromedriverExecutable'] = self.__findBrowserDriverPaths(ChromeExecutable)
-                    desired_cap['noReset'] = False
-                    self.driver = Appiumwebdriver.Remote("http://localhost:4723/wd/hub", desired_cap)
-                else:
-                    if not (os.path.isfile(str(lCurPath))):
-                        self.downloadDriver(browserName)
-                    self.driver = browserNames[browserName](chrome_options=self.__createBrowserOptions(browserName=browserName,
-                                                                                                desiredCapabilities=desiredCapabilities,
-                                                                                                browserProxy=browserProxy),
-                                                            executable_path=self.__findBrowserDriverPaths(ChromeExecutable))
-                    browserProxy.new_har("baangt-chrome-{}".format(browserInstance),
-                                         options={'captureHeaders': True, 'captureContent': True}) if browserProxy else None
-            elif browserName == GC.BROWSER_EDGE:
-                if mobileType == 'True':
-                    if not (os.path.isfile(str(lCurPath))):
-                        self.downloadDriver(browserName)
-                    desired_cap = {}
-                    desired_cap['platformName'] = "Android"
-                    desired_cap['deviceName'] = "emulator-5554"
-                    desired_cap['browserName'] = browserName
-                    desired_cap['chromedriverExecutable'] = self.__findBrowserDriverPaths(ChromeExecutable)
-                    desired_cap['noReset'] = False
-                    self.driver = Appiumwebdriver.Remote("http://localhost:4723/wd/hub", desired_cap)
-                else:
-                    self.driver = browserNames[browserName](executable_path=self.__findBrowserDriverPaths("msedgedriver.exe"))
-            elif browserName == GC.BROWSER_SAFARI:
-                # SAFARI doesn't provide any options, but desired_capabilities.
-                # Executable_path = the standard safaridriver path.
-                if mobileType == 'True':
-                    if not (os.path.isfile(str(lCurPath))):
-                        self.downloadDriver(browserName)
-                    desired_cap = {}
-                    desired_cap['platformName'] = "Android"
-                    desired_cap['deviceName'] = "emulator-5554"
-                    desired_cap['browserName'] = browserName
-                    desired_cap['chromedriverExecutable'] = self.__findBrowserDriverPaths(ChromeExecutable)
-                    desired_cap['noReset'] = False
-                    self.driver = Appiumwebdriver.Remote("http://localhost:4723/wd/hub", desired_cap)
-                else:
-                    if len(desiredCapabilities) == 0:
-                        desiredCapabilities = {}
-                    self.driver = browserNames[browserName](desired_capabilities=desiredCapabilities)
-
-            elif browserName == GC.BROWSER_REMOTE:
-                if mobileType == 'True':
-                    if not (os.path.isfile(str(lCurPath))):
-                        self.downloadDriver(browserName)
-                    desired_cap = {}
-                    desired_cap['platformName'] = "Android"
-                    desired_cap['deviceName'] = "emulator-5554"
-                    desired_cap['browserName'] = browserName
-                    desired_cap['chromedriverExecutable'] = self.__findBrowserDriverPaths(ChromeExecutable)
-                    desired_cap['noReset'] = False
-                    self.driver = Appiumwebdriver.Remote("http://localhost:4723/wd/hub", desired_cap)
-                else:
-                    self.driver = browserNames[browserName](options=self.__createBrowserOptions(browserName=browserName,
-                                                                                            desiredCapabilities=desiredCapabilities),
-                                                        command_executor='http://localhost:4444/wd/hub',
-                                                        desired_capabilities=desiredCapabilities)
-        else:
-            raise SystemExit("Browsername unknown")
-
-        self.takeTime("Browser Start")
-
-    def __findBrowserDriverPaths(self, filename):
-        if hasattr(sys, 'frozen') and hasattr(sys, '_MEIPASS'):
-            # We're in pyinstaller. Chromedriver and Geckodriver are in the executable-directory
-            # in the subdirectory /chromedriver/ or /geckodriver for Linux und MAC,
-            # directly in the exeuctable directory for the windows.exe
-            if platform.system().lower() == 'windows':
-                lCurPath = Path(sys.executable).parent.joinpath(filename)
-            else:
-                lCurPath = Path(sys.executable).parent.joinpath(filename).joinpath(filename)
-
-        else:
-            lCurPath = Path(os.getcwd())
-            lCurPath = lCurPath.joinpath("browserDrivers")
-            lCurPath = lCurPath.joinpath(filename)
-
-        logger.debug(f"Path for BrowserDrivers: {lCurPath}")
-        return str(lCurPath)
-
-    def slowExecutionToggle(self, newSlowExecutionWaitTimeInSeconds = None):
-        """
-        SlowExecution can be set in globals or by the teststep. It's intended use is debugging or showcasing a testcases
-        functionality.
-
-        @param newSlowExecutionWaitTimeInSeconds: Optional. If set, it will change the default value of WaitTime, when SlowExecution is active
-        @return: Returns the state of sloeExecution toggle after toggling was done.
-        """
-
-        if self.slowExecution:
-            self.slowExecution = False
-        else:
-            self.slowExecution = True
-
-        if newSlowExecutionWaitTimeInSeconds:
-            self.slowExecutionTimeoutInSeconds = newSlowExecutionWaitTimeInSeconds
-
-        return self.slowExecution
-
-
-    def __createBrowserOptions(self, browserName, desiredCapabilities, browserProxy=None):
-        """
-        Translates desired capabilities from the Testrun (or globals) into specific BrowserOptions for the
-        currently active browser
-
-        @param browserName: any of the GC.BROWSER*
-        @param desiredCapabilities: Settings from TestRun or globals
-        @param browserProxy: Proxy-Server IP+Port
-        @return: the proper BrowserOptions for the currently active browser.
-        """
-        if browserName == GC.BROWSER_CHROME:
-            lOptions = ChromeOptions()
-            lOptions.add_argument('--proxy-server={0}'.format(browserProxy.proxy)) if browserProxy else None
-        elif browserName == GC.BROWSER_FIREFOX:
-            lOptions = ffOptions()
-        else:
-            return None
-
-        if not desiredCapabilities and not browserProxy:
-            return None
-
-        # sometimes instead of DICT comes a string with DICT-Format
-        if isinstance(desiredCapabilities, str) and "{" in desiredCapabilities and "}" in desiredCapabilities:
-            desiredCapabilities = json.loads(desiredCapabilities.replace("'", '"'))
-
-        if not isinstance(desiredCapabilities, dict) and not browserProxy:
-            return None
-
-        if isinstance(desiredCapabilities, dict) and desiredCapabilities.get(GC.BROWSER_MODE_HEADLESS):
-            logger.debug("Starting in Headless mode")
-            lOptions.headless = True
-            lOptions.add_argument("--window-size=1920,1080")
-
-        return lOptions
-
-    def closeBrowser(self):
-        self.driver.quit()
-
-    def _log(self, logType, logText, **kwargs):
-        """
-        Interal wrapper of Browser-Class for Logging. Takes a screenshot on Error and Warning.
-
-        @param logType: any of logging.ERROR, logging.WARN, INFO, etc.
-        @param logText: Text to log
-        @param kwargs: Additional Arguments to be logged
-        """
-        argsString = ""
-        for key, value in kwargs.items():
-            if value:
-                argsString = argsString + f" {key}: {value}"
-
-        if self.locator:
-            argsString = argsString + f" Locator: {self.locatorType} = {self.locator}"
-
-        if logType == logging.DEBUG:
-            logger.debug(logText + argsString)
-        elif logType == logging.ERROR:
-            logger.error(logText + argsString)
-            self.takeScreenshot()
-        elif logType == logging.WARN:
-            logger.warning(logText + argsString)
-            self.takeScreenshot()
-        elif logType == logging.INFO:
-            logger.info(logText + argsString)
-        elif logType == logging.CRITICAL:
-            logger.critical(logText + argsString)
-            self.takeScreenshot()
-        else:
-            print(f"Unknown call to Logger: {logType}")
-            self._log(logging.CRITICAL, f"Unknown type in call to logger: {logType}")
-
-    def takeScreenshot(self, screenShotPath=None):
-        driver = self.driver
-        # Filename must have ".png" inside
-        lFile = str(uuid.uuid4()) + ".png"
-
-        if screenShotPath:
-            lFile = Path(screenShotPath).joinpath(lFile)
-        else:
-            lFile = Path(self.screenshotPath).joinpath(lFile)
-
-        try:
-            lFile = str(lFile)
-            driver.save_screenshot(lFile)
-            self._log(logging.DEBUG, f"Stored Screenshot: {lFile}")
-        except Exception as e:
-            self._log(logging.INFO, f"Screenshot not possible. Error: {e}")
-
-        return lFile
-
-    def handleIframe(self, iframe=None):
-        """
-        Give an IFRAME and it will try to go into.
-        If you're inside an iframe it will go out of the iframe
-        """
-        if iframe:
-            self._log(logging.DEBUG, "Going into Iframe: ", **{"iframe": iframe})
-            # frame_to_be_availble_and_switch_to_it doesn't work.
-            mustEnd = time.time() + 30
-            while time.time() < mustEnd:
-                try:
-                    self.driver.switch_to.default_content()
-                    self.iFrame = self.driver.switch_to.frame(iframe)
-                    break
-                except WebDriverException as e:
-                    self._log(logging.DEBUG, f"IFrame {iframe} not there yet - waiting 1 second")
-                    time.sleep(1)
-            if time.time() > mustEnd:
-                raise TimeoutError
-
-        elif self.iFrame:
-            self._log(logging.DEBUG, f"Leaving Iframe: {self.iFrame}")
-            self.driver.switch_to.default_content()
-            self.iFrame = None
-
-    def handleWindow(self, windowNumber=None, function=None):
-        """
-        Interations with Windows (=BrowserTabs).
-
-        @param windowNumber: Number of the windowHandle inside this browser session (0 = startwindow(=Tab), 1=Next window
-        @param function: "CLOSE", "CLOSEALL"
-        """
-        if function:
-            if function.lower() == "close":
-                self.driver.close()
-                self.driver.switch_to.window(self.driver.window_handles[0])
-            elif "closeall" in function.lower():
-                exceptHandles = function.lower().replace("closeall", "")
-                exceptHandles = exceptHandles.replace("-", "")
-                # WindowHandles based on 0.. Value "let 2 windows open" means to close everything except 0 and 1:
-                exceptHandles = int(exceptHandles.strip()) - 1
-                totalWindows = len(self.driver.window_handles)
-                for windowHandle in self.driver.window_handles[-1:exceptHandles:-1]:
-                    self.driver.switch_to.window(windowHandle)
-                    self.driver.close()
-                self.driver.switch_to.window(self.driver.window_handles[exceptHandles])
-        else:
-            try:
-                self.driver.switch_to.window(self.driver.window_handles[windowNumber])
-            except Exception as e:
-                logger.critical(f"Tried to switch to Window {windowNumber} but it's not there")
-                raise Exceptions.baangtTestStepException(f"Window {windowNumber} doesn't exist")
-
-    def findByAndWaitForValue(self, id=None, css=None, xpath=None, class_name=None, iframe=None, timeout=20,
-                              optional=False):
-        """
-
-        @param id: ID of the element
-        @param css: CSS-Locator
-        @param xpath: XPATH-Locator
-        @param class_name: Class-Name
-        @param iframe: Iframe to use (use only if changed. If you set an iframe before, you don't need to set it again!)
-        @param timeout: Timeout in Seconds before raising an error or returning back (depending on "optional")
-        @param optional: If set to "True" and the operation can not be executed, just a log entry is written but no error raised
-        @return: the text of the element, if element was found
-        """
-        start = time.time()
-        found = False
-        duration = 0
-
-        while not found and duration < timeout:
-            self.element = None
-            self.findBy(id=id, css=css, xpath=xpath, class_name=class_name, iframe=iframe, timeout=timeout / 3,
-                        optional=optional)
-            try:
-                if len(self.element.text) > 0:
-                    return self.element.text
-                elif self.element.tag_name == 'input':
-                    #  element is of type <input />
-                    return self.element.get_property('value')
-                
-            except Exception as e:
-                logger.debug(f"Exception during findByAndWaitForValue, but continuing {str(e)}, "
-                             f"Locator: {self.locatorType} = {self.locator}")
-                pass
-            time.sleep(0.5)
-            duration = time.time() - start
-
-        logger.info(f"Couldn't find value for element {self.locatorType}:{self.locator}")
-        return None
-
-    def findByAndSetText(self, id=None, css=None, xpath=None, class_name=None, value=None, iframe=None,
-                         timeout=60, optional=False):
-        """
-        Please see documentation in findBy and __doSomething
-        """
-        self.findBy(id=id,
-                    css=css,
-                    xpath=xpath,
-                    class_name=class_name,
-                    iframe=iframe,
-                    timeout=timeout)
-
-        self.__doSomething(GC.CMD_SETTEXT, value=value, timeout=timeout, xpath=xpath, optional=optional)
-
-    def findByAndSetTextIf(self, id=None, css=None, xpath=None, class_name=None, value=None, iframe=None,
-                           timeout=60):
-        """
-        Helper function to not have to write:
-        If <condition>:
-            findByAndSetText(locator)
-
-        instead use:
-        findByAndSetTextIf(locator, value).
-
-        If value is evaluated into "True" the Text is set.
-
-        """
-        if not value:
-            return True
-
-        if len(value) == 0:
-            return
-
-        return self.findByAndSetText(id=id, css=css, xpath=xpath, class_name=class_name, value=value, iframe=iframe,
-                                     timeout=timeout)
-
-    def findByAndSetTextValidated(self,id = None,
-                       css = None,
-                       xpath = None,
-                       class_name = None,
-                       value = None,
-                       iframe = None,
-                       timeout = 60,
-                       retries = 5):
-        """
-        This is a method not recommended to be used regularly. Sometimes (especially with Angular Frontends) it gets
-        pretty hard to set a value into a field. Chrome, but also FF will show the value, but the DOM will not have it.
-        Ths Method should be your last ressort. Here we try <retries> time to set a value. Then we read the element again
-        and compare value to what we'd expect. If value is different and we're less than <retries>-Times, we'll try again.
-        """
-
-        tries = 0
-
-        self.findBy(id=id, css=css, xpath=xpath, class_name=class_name, iframe=iframe, timeout=timeout)
-
-        while self.element.text != value and self.element.get_property("value") != value and tries < retries:
-
-            self._log(logging.DEBUG, f"Verified trying of SetText - iteration {tries} of {retries}")
-
-            self.findByAndForceText(id=id, css=css, xpath=xpath, class_name=class_name, iframe=iframe,
-                                    value=value, timeout=timeout)
-
-            self.findBy(id=id, css=css, xpath=xpath, class_name=class_name, iframe=iframe, timeout=timeout)
-
-            tries += 1
-
-    def submit(self):
-        """
-        Used for forms to call the standard submit-function (similar to pressing "Enter" in Dialogue)
-        @return:
-        """
-        self.element.submit()
-
-    def findByAndClick(self, id = None, css=None, xpath=None, class_name=None, iframe=None, timeout=20, optional=False):
-        """
-        Execute a Click on an element identified by it's locator.
-        @return wasSuccessful says, whether the element was found.
-        """
-        wasSuccessful = self.findBy(id=id, css=css, xpath=xpath, class_name=class_name, iframe=iframe, timeout=timeout,
-                                    optional=optional)
-
-        if not wasSuccessful:
-            logger.debug("findBy didn't work in findByAndClick")
-            return wasSuccessful
-
-        self.__doSomething(GC.CMD_CLICK, xpath=xpath, timeout=timeout, optional=optional)
-
-        return wasSuccessful
-
-    def findByAndClickIf(self, id=None, css=None, xpath=None, class_name=None, iframe=None, timeout=60,
-                         value=None, optional=False):
-        """
-        Convenience method to not have to write:
-        if <condition>:
-            findByAndClick(locator)
-
-        instead write:
-        findByAndClickIf(locator, value).
-
-        If value is evaluated to "True", the click-event is executed.
-        """
-        if not value:
-            return True
-
-        if len(value) == 0:
-            return True
-
-        return self.findByAndClick(id=id, css=css, xpath=xpath, class_name=class_name, iframe=iframe, timeout=timeout,
-                                   optional=optional)
-
-    def findByAndForceText(self, id=None, css=None, xpath=None, class_name=None, value=None,
-                           iframe=None, timeout=60, optional=False):
-        """
-        Convenience Method. Please see documentation in findBy and __doSomething.
-
-        """
-
-        self.findBy(id=id, css=css, xpath=xpath, class_name=class_name, iframe=iframe, timeout=timeout)
-
-        self.__doSomething(GC.CMD_FORCETEXT, value=value, timeout=timeout, xpath=xpath, optional=optional)
-
-    def findBy(self, id=None, css=None, xpath=None, class_name=None, iframe=None, timeout=60, loggingOn=True,
-               optional=False):
-        """
-        chose any single locator (ID, CSS, XPATH, CLASS_NAME) to identify an element within the page. if slowExectuion
-        is set, we'll pause for slowExecutionTimeoutInSeconds.
-
-        @param id: ID of the element
-        @param css: CSS-Locator
-        @param xpath: XPATH
-        @param class_name: Class-Name
-        @param iframe: Name of an Iframe. Use only, if you didn't set the Iframe previously already!
-        @param timeout: How many seconds shall we try/retry, default = 60 Seconds
-        @param loggingOn: Shall this request be logged? Default = Yes
-        @param optional: If set to true and within Timeout we can't find the element, we just return this info. If set to False (=default), an Exception is raised
-        @return: True if element was located, False if element couldn't be found.
-        """
-
-        if self.slowExecution:
-            time.sleep(self.slowExecutionTimeoutInSeconds)
-
-        if self.slowExecution:
-            time.sleep(self.slowExecutionTimeoutInSeconds)
-
-        if iframe:
-            self.handleIframe(iframe)
-
-        # Set class variables for potential logging of problems.
-        if xpath:
-            self.locatorType = 'XPATH'
-            self.locator = xpath
-        elif css:
-            self.locatorType = 'CSS'
-            self.locator = css
-        elif class_name:
-            self.locatorType = 'ClassName'
-            self.locator = class_name
-        elif id:
-            self.locatorType = 'ID'
-            self.locator = id
-
-        if loggingOn:
-            self._log(logging.DEBUG, f"Locating Element {self.locatorType} = {self.locator}")
-
-        successful = self.__tryAndRetry(id, css, xpath, class_name, timeout=timeout)
-
-        if not successful and not optional:
-            raise Exceptions.baangtTestStepException(f"Element {self.locatorType} = {self.locator} could not be found "
-                                                     f"within timeout of {timeout}")
-        return successful
-
-    def getURL(self):
-        """
-
-        @return: the current URL/URI of the current Tab of the current Browser
-        """
-        return self.driver.current_url
-
-    def __tryAndRetry(self, id=None, css=None, xpath=None, class_name=None, timeout=20):
-        """
-        In: Locator
-        Out: Boolean whether the element was found or not.
-
-        Also sets the self.element for further use by other Methods (for instance to setText or read existing value)
-
-        The method is resistant to common timing problems (can't work 100% of the time but will remove at least 80%
-        of your pain compared to directly calling Selenium Methods).
-        """
-
-        wasSuccessful = False
-        begin = time.time()
-        elapsed = 0
-        if timeout < 1.5:
-            pollFrequency = timeout / 3
-        else:
-            pollFrequency = 0.5
-
-        internalTimeout = timeout / 3
-
-        lLoopCount = 0
-
-        while not wasSuccessful and elapsed < timeout:
-            lLoopCount += 1
-            try:
-                driverWait = WebDriverWait(self.driver, timeout=internalTimeout, poll_frequency=pollFrequency)
-
-                if id:
-                    self.element = driverWait.until(ec.visibility_of_element_located((By.ID, id)))
-                elif css:
-                    self.element = driverWait.until(ec.visibility_of_element_located((By.CSS_SELECTOR, css)))
-                elif class_name:
-                    self.element = self.driver.find_element_by_class_name(class_name)
-                elif xpath:
-                    # visibility of element sometimes not true, but still clickable. If we tried already
-                    # 2 times with visibility, let's give it one more try with Presence of element
-                    if lLoopCount > 2:
-                        self._log(logging.INFO, "Tried 2 times to find visible element, now trying presence "
-                                                f"of element instead, XPATH = {xpath}")
-                        self.element = driverWait.until(ec.presence_of_element_located((By.XPATH, xpath)))
-                    else:
-                        self.element = driverWait.until(ec.visibility_of_element_located((By.XPATH, xpath)))
-
-                wasSuccessful = True
-            except StaleElementReferenceException as e:
-                self._log(logging.DEBUG, "Stale Element Exception - retrying " + str(e))
-                time.sleep(pollFrequency)
-            except ElementClickInterceptedException as e:
-                self._log(logging.DEBUG, "ElementClickIntercepted - retrying " + str(e))
-                time.sleep(pollFrequency)
-            except TimeoutException as e:
-                self._log(logging.WARNING, "TimoutException - retrying " + str(e))
-                time.sleep(pollFrequency)
-            except NoSuchElementException as e:
-                self._log(logging.WARNING, "Retrying Webdriver Exception: " + str(e))
-                time.sleep(pollFrequency)
-            except InvalidSessionIdException as e:
-                self._log(logging.CRITICAL, "WebDriver Exception - terminating testrun: " + str(e))
-                raise Exceptions.baangtTestStepException
-            except NoSuchWindowException as e:
-                self._log(logging.CRITICAL, "WebDriver Exception - terminating testrun: " + str(e))
-                raise Exceptions.baangtTestStepException
-            except ElementNotInteractableException as e:
-                self._log(logging.DEBUG, "Most probably timeout exception - retrying: " + str(e))
-            except WebDriverException as e:
-                self._log(logging.ERROR, "Retrying WebDriver Exception: " + str(e))
-                time.sleep(2)
-
-            elapsed = time.time() - begin
-
-        return wasSuccessful
-
-    def findWaitNotVisible(self, css=None, xpath=None, id=None, timeout = 90, optional = False):
-        """
-        You'd use this method when you wait for an element to disappear, for instance Angular Spinner or a popup
-        to disapear before you continue with your script in the main screen.
-
-        """
-        self._log(logging.DEBUG, "Waiting for Element to disappear", **{"xpath":xpath, "timeout":timeout})
-        time.sleep(0.5)
-
-        stillHere = True
-        elapsed = 0
-        begin = time.time()
-
-        while stillHere and elapsed < timeout:
-            try:
-                if xpath:
-                    self.element = self.driver.find_element_by_xpath(xpath)
-                elif id:
-                    self.element = self.driver.find_element_by_id(id)
-                elif css:
-                    self.element = self.driver.find_element_by_css_selector(css)
-                time.sleep(0.1)
-                elapsed = time.time() - begin
-            except Exception as e:
-                # Element gone - exit
-                stillHere = False
-                self._log(logging.DEBUG, f"Element was gone after {format(elapsed, '.2f')} seconds")
-                return
-
-        raise Exceptions.baangtTestStepException(f"Element still here after {timeout} seconds. Locator: xpath={xpath}, id={id}")
-
-    @staticmethod
-    def sleep(sleepTimeinSeconds):
-        time.sleep(sleepTimeinSeconds)
-
-    def __doSomething(self, command, value=None, timeout=20, xpath=None, optional=False):
-        """
-        Will interact in an element (that was found before by findBy-Method and stored in self.element) as defined by
-        ``command``.
-
-        Command can be "SETTEXT" (GC.CMD_SETTEXT), "CLICK" (GC.CMD_CLICK), "FORCETEXT" (GC.CMD_FORCETEXT).
-
-        Similarly to __try_and_retry the method is pretty robust when it comes to error handling of timing issues.
-
-        """
-        didWork = False
-        elapsed = 0
-        begin = time.time()
-
-        while not didWork and elapsed < timeout:
-            try:
-                self._log(logging.DEBUG, f"Do_something {command} with {value}")
-                if command.upper() == GC.CMD_SETTEXT:
-                    self.element.send_keys(value)
-                elif command.upper() == GC.CMD_CLICK:
-                    self.element.click()
-                elif command.upper() == GC.CMD_FORCETEXT:
-                    self.element.clear()
-                    for i in range(0, 10):
-                        self.element.send_keys(keys.Keys.BACKSPACE)
-                    time.sleep(0.1)
-                    self.element.send_keys(value)
-                didWork = True
-                return
-            except ElementClickInterceptedException as e:
-                self._log(logging.DEBUG, "doSomething: Element intercepted - retry")
-                time.sleep(0.2)
-            except StaleElementReferenceException as e:
-                self._log(logging.DEBUG, "doSomething: Element stale - retry")
-                time.sleep(0.2)
-            except NoSuchElementException as e:
-                self._log(logging.DEBUG, "doSomething: Element not there yet - retry")
-                time.sleep(0.5)
-            except InvalidSessionIdException as e:
-                self._log(logging.ERROR, f"Invalid Session ID Exception caught - aborting... {e} ")
-                raise Exceptions.baangtTestStepException(e)
-            except ElementNotInteractableException as e:
-                self._log(logging.ERROR, f"Element not interactable {e}")
-                raise Exceptions.baangtTestStepException(e)
-            except NoSuchWindowException as e:
-                raise Exceptions.baangtTestStepException(e)
-            elapsed = time.time()-begin
-
-        if optional:
-            logger.debug(f"Action not possible after {timeout} s, Locator: {self.locatorType}: {self.locator}, but flag 'optional' is set")
-        else:
-            raise Exceptions.baangtTestStepException(f"Action not possible after {timeout} s, Locator: {self.locatorType}: {self.locator}")
-
-    def goToUrl(self, url):
-        self._log(logging.INFO, f'GoToUrl:{url}')
-        try:
-            self.driver.get(url)
-        except WebDriverException as e:
-            self._log(logging.ERROR, f"Webpage {url} not reached. Error was: {e}")
-            raise Exceptions.baangtTestStepException
-        pass
-
-    def goBack(self):
-        """
-        Method to go 1 step back in current tab's browse history
-        @return:
-        """
-        try:
-            self.javaScript("window.history.go(-1)")
-        except Exception as e:
-            self._log(logging.WARNING, f"Tried to go back in history, didn't work with error {e}")
-
-
-    def javaScript(self, jsText):
-        """Execute a given JavaScript in the current Session"""
-        self.driver.execute_script(jsText)
-
-    def downloadDriver(self,browserName):
-        path = Path(os.getcwd())
-        path = path.joinpath("browserDrivers")
-        tar_url = ''
-        url = ''
-        if str(browserName) == GC.BROWSER_FIREFOX:
-            response = requests.get(GC.GECKO_URL)
-            gecko = response.json()
-            gecko = gecko['assets']
-            gecko_length_results = len(gecko)
-            drivers_url_dict = []
-
-            for i in range(gecko_length_results):
-                drivers_url_dict.append(gecko[i]['browser_download_url'])
-
-            zipbObj = zip(GC.OS_list, drivers_url_dict)
-            geckoDriversDict = dict(zipbObj)
-            if platform.system().lower() == GC.WIN_PLATFORM:
-                if ctypes.sizeof(ctypes.c_voidp) == GC.BIT_64:
-                    url = geckoDriversDict[GC.OS_list[4]]
-                else:
-                    url = geckoDriversDict[GC.OS_list[3]]
-            elif platform.system().lower() == GC.LINUX_PLATFORM:
-                if ctypes.sizeof(ctypes.c_voidp) == GC.BIT_64:
-                    tar_url = geckoDriversDict[GC.OS_list[1]]
-                else:
-                    tar_url = geckoDriversDict[GC.OS_list[0]]
-            else:
-                tar_url = geckoDriversDict[GC.OS_list[2]]
-
-            if tar_url != '':
-                path_zip = path.joinpath(GC.GECKO_DRIVER.replace('exe', 'tar.gz'))
-                filename, headers = urlretrieve(tar_url,path_zip)
-                tar = tarfile.open(filename, "r:gz")
-                tar.extractall(path)
-                tar.close()
-
-
-            else:
-                file = requests.get(url)
-                path_zip = path.joinpath(GC.GECKO_DRIVER.replace('exe', 'zip'))
-                open(path_zip, 'wb').write(file.content)
-                with zipfile.ZipFile(path_zip, 'r') as zip_ref:
-                    zip_ref.extractall(path)
-
-
-        else:
-            response = requests.get(GC.CHROME_URL)
-            chromeversion = response.text
-            chromedriver_url_dict = []
-
-            for i in range(len(GC.OS_list_chrome)):
-                OS = GC.OS_list_chrome[i]
-                chrome = 'http://chromedriver.storage.googleapis.com/{ver}/chromedriver_{os}.zip'.format(
-                    ver=chromeversion,
-                    os=OS)
-
-                chromedriver_url_dict.append(chrome)
-
-            zipbObjChrome = zip(GC.OS_list, chromedriver_url_dict)
-            chromeDriversDict = dict(zipbObjChrome)
-            if platform.system().lower() == GC.WIN_PLATFORM:
-                url = chromeDriversDict[GC.OS_list[3]]
-            elif platform.system().lower() == GC.LINUX_PLATFORM:
-                url = chromeDriversDict[GC.OS_list[1]]
-            else:
-                url = chromeDriversDict[GC.OS_list[2]]
-            file = requests.get(url)
-            path_zip = path.joinpath(GC.CHROME_DRIVER.replace('exe', 'zip'))
-            open(path_zip, 'wb').write(file.content)
-            with zipfile.ZipFile(path_zip, 'r') as zip_ref:
-                zip_ref.extractall(path)
-
-                # permissions
-
-            if platform.system().lower() != GC.WIN_PLATFORM:
-                file_path = path.joinpath(GC.CHROME_DRIVER.replace('.exe', ''))
-                os.chmod(file_path, 0o777)
-        os.remove(path_zip)

+ 0 - 0
baangt/base/BrowserHandling/__init__.py


+ 0 - 141
baangt/base/BrowserHandling/hookImpls.py

@@ -1,141 +0,0 @@
-import baangt
-
-from baangt.base.BrowserHandling.BrowserHandling import BrowserDriver
-from baangt.base import GlobalConstants as GC
-import logging
-
-logger = logging.getLogger("pyC")
-
-
-class BrowserDriverHookImpl:
-    @baangt.hook_impl
-    def browserDriver_init(self, timing=None, screenshotPath=None):
-        return BrowserDriver(timing, screenshotPath)
-
-    @baangt.hook_impl
-    def browserDriver_createNewBrowser(self, browserDriverObject, browserName=GC.BROWSER_FIREFOX,
-                                       desiredCapabilities=None, **kwargs):
-        return browserDriverObject.createNewBrowser(browserName=browserName,
-                                                    desiredCapabilities=desiredCapabilities, **kwargs)
-
-    @baangt.hook_impl
-    def browserDriver_slowExecutionToggle(self, browserDriverObject, newSlowExecutionWaitTimeInSeconds=None):
-        return browserDriverObject.slowExecutionToggle(newSlowExecutionWaitTimeInSeconds)
-
-    @baangt.hook_impl
-    def browserDriver_closeBrowser(self, browserDriverObject):
-        return browserDriverObject.closeBrowser()
-
-    @baangt.hook_impl
-    def browserDriver_takeScreenshot(self, browserDriverObject, screenShotPath=None):
-        return browserDriverObject.takeScreenshot(screenShotPath)
-
-    @baangt.hook_impl
-    def browserDriver_handleIframe(self, browserDriverObject, iframe=None):
-        return browserDriverObject.handleIframe(iframe)
-
-    @baangt.hook_impl
-    def browserDriver_handleWindow(self, browserDriverObject, windowNumber=None, function=None):
-        return browserDriverObject.handleWindow(windowNumber, function)
-
-    @baangt.hook_impl
-    def browserDriver_findByAndWaitForValue(self, browserDriverObject, id=None, css=None, xpath=None, class_name=None,
-                                            iframe=None, timeout=20,
-                                            optional=False):
-        return browserDriverObject.findByAndWaitForValue(id, css, xpath, class_name,
-                                            iframe, timeout,
-                                            optional)
-
-    @baangt.hook_impl
-    def browserDriver_findByAndSetText(self, browserDriverObject, id=None, css=None, xpath=None, class_name=None,
-                                       value=None, iframe=None,
-                                       timeout=60, optional=False):
-        return browserDriverObject.findByAndSetText(id, css, xpath, class_name, value,
-                                                    iframe, timeout,
-                                                    optional)
-
-    @baangt.hook_impl
-    def browserDriver_findByAndSetTextIf(self, browserDriverObject, id=None, css=None, xpath=None, class_name=None,
-                                         value=None, iframe=None,
-                                         timeout=60):
-        return browserDriverObject.findByAndSetTextIf(id, css, xpath, class_name,
-                                         value, iframe,
-                                         timeout)
-
-    @baangt.hook_impl
-    def browserDriver_findByAndSetTextValidated(self, browserDriverObject, id=None,
-                                                css=None,
-                                                xpath=None,
-                                                class_name=None,
-                                                value=None,
-                                                iframe=None,
-                                                timeout=60,
-                                                retries=5):
-        return browserDriverObject.findByAndSetTextValidated(id,
-                                                css,
-                                                xpath,
-                                                class_name,
-                                                value,
-                                                iframe,
-                                                timeout,
-                                                retries)
-
-    @baangt.hook_impl
-    def browserDriver_submit(self, browserDriverObject):
-        return browserDriverObject.submit()
-
-    @baangt.hook_impl
-    def browserDriver_findByAndClick(self, browserDriverObject, id=None, css=None, xpath=None, class_name=None,
-                                     iframe=None, timeout=20, optional=False):
-        return browserDriverObject.findByAndClick(id, css, xpath, class_name,
-                                     iframe, timeout, optional)
-
-    @baangt.hook_impl
-    def browserDriver_findByAndClickIf(self, browserDriverObject, id=None, css=None, xpath=None, class_name=None,
-                                       iframe=None, timeout=60,
-                                       value=None, optional=False):
-        return browserDriverObject.findByAndClickIf(id, css, xpath, class_name,
-                                       iframe, timeout,
-                                       value, optional)
-
-    @baangt.hook_impl
-    def browserDriver_findByAndForceText(self, browserDriverObject, id=None, css=None, xpath=None, class_name=None,
-                                         value=None,
-                                         iframe=None, timeout=60, optional=False):
-        return browserDriverObject.findByAndForceText(id, css, xpath, class_name,
-                                         value,
-                                         iframe, timeout, optional)
-
-    @baangt.hook_impl
-    def browserDriver_findBy(self, browserDriverObject, id=None, css=None, xpath=None, class_name=None, iframe=None,
-                             timeout=60, loggingOn=True,
-                             optional=False):
-        return browserDriverObject.findBy(id, css, xpath, class_name, iframe,
-                             timeout, loggingOn,
-                             optional)
-
-    @baangt.hook_impl
-    def browserDriver_getURL(self, browserDriverObject):
-        return browserDriverObject.getURL()
-
-    @baangt.hook_impl
-    def browserDriver_findWaitNotVisible(self, browserDriverObject, xpath=None, id=None, timeout=90):
-        return browserDriverObject.findWaitNotVisible(xpath, id, timeout)
-
-    @baangt.hook_impl
-    def browserDriver_sleep(self, browserDriverObject, sleepTimeinSeconds):
-        return browserDriverObject.sleep(sleepTimeinSeconds)
-
-    @baangt.hook_impl
-    def browserDriver_goToUrl(self, browserDriverObject, url):
-        return browserDriverObject.goToUrl(url)
-
-    @baangt.hook_impl
-    def browserDriver_goBack(self, browserDriverObject):
-        return browserDriverObject.goBack()
-
-    @baangt.hook_impl
-    def browserDriver_javaScript(self, browserDriverObject, jsText):
-        return browserDriverObject.javaScript(jsText)
-
-

File diff suppressed because it is too large
+ 0 - 9
baangt/base/BrowserHandling/logs/20200318_145218.log


File diff suppressed because it is too large
+ 0 - 9
baangt/base/BrowserHandling/logs/20200318_145232.log


File diff suppressed because it is too large
+ 0 - 11
baangt/base/BrowserHandling/logs/20200318_145255.log


File diff suppressed because it is too large
+ 0 - 13
baangt/base/BrowserHandling/logs/20200318_145335.log


File diff suppressed because it is too large
+ 0 - 12
baangt/base/BrowserHandling/logs/20200318_145337.log


File diff suppressed because it is too large
+ 0 - 12
baangt/base/BrowserHandling/logs/20200318_145352.log


File diff suppressed because it is too large
+ 0 - 13
baangt/base/BrowserHandling/logs/20200318_145407.log


File diff suppressed because it is too large
+ 0 - 12
baangt/base/BrowserHandling/logs/20200318_145411.log


File diff suppressed because it is too large
+ 0 - 12
baangt/base/BrowserHandling/logs/20200318_145440.log


File diff suppressed because it is too large
+ 0 - 11
baangt/base/BrowserHandling/logs/20200318_145533.log


File diff suppressed because it is too large
+ 0 - 12
baangt/base/BrowserHandling/logs/20200318_145546.log


File diff suppressed because it is too large
+ 0 - 12
baangt/base/BrowserHandling/logs/20200318_145549.log


File diff suppressed because it is too large
+ 0 - 11
baangt/base/BrowserHandling/logs/20200318_145642.log


File diff suppressed because it is too large
+ 0 - 12
baangt/base/BrowserHandling/logs/20200318_145655.log


File diff suppressed because it is too large
+ 0 - 12
baangt/base/BrowserHandling/logs/20200318_145701.log


File diff suppressed because it is too large
+ 0 - 12
baangt/base/BrowserHandling/logs/20200318_145733.log


File diff suppressed because it is too large
+ 0 - 12
baangt/base/BrowserHandling/logs/20200318_145737.log


File diff suppressed because it is too large
+ 0 - 12
baangt/base/BrowserHandling/logs/20200318_145752.log


File diff suppressed because it is too large
+ 0 - 12
baangt/base/BrowserHandling/logs/20200318_145755.log


+ 0 - 73
baangt/base/CliAndInteractive.py

@@ -1,73 +0,0 @@
-import sys, getopt
-from baangt.base.Utils import utils
-from baangt.ui.ui import UI
-from baangt import plugin_manager
-
-def print_args():
-    print("""
-Call: python baangt.py --parameters 
-       --run=<Existing, predefined Name of a TestRun (XLSX or .JSON-File incl. Path)>
-       --globals=<path to JSON-File containing global Settings. If omitted, will look for globals.json in the current directory>
-
- Suggested for standard use:
-   python baangt.py --run="Franzi4711.xlsx": Will run a Testrun Franzi4711.xlsx
-   python baangt.py --run="runProducts.json": Will execute a Testrun as specified in runProducts.json and use default globals.json, if exists
-   python baangt.py --run="runProducts.json" --globals="production.json" will use settings in production.json
-   python baangt.py --run="runProducts.json" --globals="qa.json" will use settings in qa.json
-   
-   If run without parameters you'll find a simple interactive Window
-   """)
-
-def args_read(l_search_parameter):
-    l_args = sys.argv[1:]
-
-    try:
-        opts, args = getopt.getopt(l_args, "", ["run=",
-                                                "globals="
-                                                ])
-    except getopt.GetoptError as err_det:
-        print("Error in reading parameters:" + str(err_det))
-        print_args()
-        sys.exit("Wrong parameters - exiting")
-    if opts:
-        for opt, arg in opts:
-            if l_search_parameter == opt:  # in ("-u", "--usage"):
-                return arg
-            if "--" + l_search_parameter == opt:
-                return arg
-    return None
-
-
-def getGlobalSettings():
-    lGlobals = args_read("globals")
-    if not lGlobals:
-        lGlobals = "globals.json"
-    return lGlobals
-
-
-def callTestrun(testRunFile):
-    if ".XLSX" in testRunFile.upper() or ".JSON" in testRunFile.upper():
-
-        plugin_manager.hook.testRun_init(testRunName=utils.sanitizeFileName(testRunFile),
-                                         globalSettingsFileNameAndPath=utils.sanitizeFileName(getGlobalSettings()))
-
-    else:
-        sys.exit(f"Unknown Filetype - should be XLSX or JSON: {testRunFile}")
-
-
-
-
-def run():
-
-    print_args()
-
-    testRunFile = args_read("run")
-    if testRunFile:
-        print(f"Starting Testrun: {testRunFile}")
-        callTestrun(testRunFile)
-    else:
-        UI()
-
-
-
-

+ 0 - 13
baangt/base/CustGlobalConstants.py

@@ -1,13 +0,0 @@
-CUST_TOASTS = "Toasts"
-# CUST_TOASTS_ERROR = "ToastsError" --> Replaced by GC.TESTCASEERRORLOG
-VIGOGFNUMMER = "VIGOGF#"
-SAPPOLNR = "SAP Polizzennr"
-PRAEMIE = "Prämie"
-POLNRHOST = "PolNR Host"
-VERMITTLER = "vermittler"
-MANDANT = "Mandant"
-DONAU = "DON"
-WSTV = "WSTV"
-VN = "VN"
-
-CLASSES_TESTCASE = "baangtVIG.CustTestCaseMaster"

+ 0 - 25
baangt/base/DataBaseORM.py

@@ -1,25 +0,0 @@
-from sqlalchemy import Column, String, Integer, DateTime
-from sqlalchemy import create_engine
-from sqlalchemy.ext.declarative import declarative_base
-
-DATABASE_URL = 'testrun.db'
-
-engine = create_engine(f'sqlite:///{DATABASE_URL}')
-base = declarative_base()
-
-# declare models
-class TestrunLog(base):
-	__tablename__ = "testrunLog"
-	id = Column(Integer, primary_key=True)
-	testrunName = Column(String, nullable=False)
-	logfileName = Column(String, nullable=False)
-	startTime = Column(DateTime, nullable=False)
-	endTime = Column(DateTime, nullable=False)
-	globalVars = Column(String, nullable=True)
-	dataFile = Column(String, nullable=True)
-	statusOk = Column(Integer, nullable=False)
-	statusFailed = Column(Integer, nullable=False)
-	statusPaused = Column(Integer, nullable=False)
-
-# create tables
-base.metadata.create_all(engine)

+ 0 - 528
baangt/base/ExportResults/ExportResults.py

@@ -1,528 +0,0 @@
-import xlsxwriter
-import logging
-import json
-import baangt.base.GlobalConstants as GC
-from baangt.base.Timing.Timing import Timing
-import sys
-from baangt.base.Utils import utils
-from pathlib import Path
-from typing import Optional
-from xlsxwriter.worksheet import (
-    Worksheet, cell_number_tuple, cell_string_tuple)
-from sqlalchemy import create_engine
-from sqlalchemy.orm import sessionmaker
-from baangt.base.DataBaseORM import DATABASE_URL, TestrunLog
-from datetime import datetime
-import time
-from baangt import plugin_manager
-import re
-import csv
-from dateutil.parser import parse
-
-logger = logging.getLogger("pyC")
-
-
-class ExportResults:
-    def __init__(self, **kwargs):
-        self.testList = []
-        self.testRunInstance = kwargs.get(GC.KWARGS_TESTRUNINSTANCE)
-        self.networkInfo = kwargs.get('networkInfo')
-        self.testCasesEndDateTimes_1D = kwargs.get('testCasesEndDateTimes_1D')
-
-        logger.info('init ExportResults, testCasesEndDateTimes_2D: {}'.format(kwargs.get('testCasesEndDateTimes_2D')))
-        self.testCasesEndDateTimes_2D = kwargs.get('testCasesEndDateTimes_2D')
-        self.testRunName = self.testRunInstance.testRunName
-        self.dataRecords = self.testRunInstance.dataRecords
-
-        try:
-            self.exportFormat = kwargs.get(GC.KWARGS_TESTRUNATTRIBUTES).get(GC.EXPORT_FORMAT)[GC.EXPORT_FORMAT]
-        except KeyError:
-            self.exportFormat = GC.EXP_XLSX
-
-        self.fileName = self.__getOutputFileName()
-        logger.info("Export-Sheet for results: " + self.fileName)
-
-        if self.exportFormat == GC.EXP_XLSX:
-            self.fieldListExport = kwargs.get(GC.KWARGS_TESTRUNATTRIBUTES).get(GC.EXPORT_FORMAT)["Fieldlist"]
-            self.workbook = xlsxwriter.Workbook(self.fileName)
-            self.summarySheet = self.workbook.add_worksheet("Summary")
-            self.worksheet = self.workbook.add_worksheet("Output")
-            self.timingSheet = self.workbook.add_worksheet("Timing")
-            self.networkSheet = self.workbook.add_worksheet("Network")
-            self.cellFormatGreen = self.workbook.add_format()
-            self.cellFormatGreen.set_bg_color('green')
-            self.cellFormatRed = self.workbook.add_format()
-            self.cellFormatRed.set_bg_color('red')
-            self.cellFormatBold = self.workbook.add_format()
-            self.cellFormatBold.set_bold(bold=True)
-            self.summaryRow = 0
-            self.__setHeaderDetailSheetExcel()
-            self.makeSummaryExcel()
-            self.exportResultExcel()
-            self.exportTiming = ExportTiming(self.dataRecords,
-                                            self.timingSheet)
-            self.exportNetWork = ExportNetWork(self.networkInfo,
-                                               self.testCasesEndDateTimes_1D,
-                                               self.testCasesEndDateTimes_2D,
-                                               self.workbook,
-                                               self.networkSheet)
-            self.closeExcel()
-        elif self.exportFormat == GC.EXP_CSV:
-            self.export2CSV()
-        self.exportToDataBase()
-
-    def export2CSV(self):
-        """
-        Writes CSV-File of datarecords
-
-        """
-        f = open(self.fileName, 'w')
-        writer = csv.DictWriter(f, self.dataRecords[0].keys())
-        writer.writeheader()
-        for i in range(0, len(self.dataRecords)-1):
-            writer.writerow(self.dataRecords[i])
-        f.close()
-
-    def exportToDataBase(self):
-        engine = create_engine(f'sqlite:///{DATABASE_URL}')
-
-        # create a Session
-        Session = sessionmaker(bind=engine)
-        session = Session()
-
-        # get timings
-        timing: Timing = self.testRunInstance.timing
-        start, end, duration = timing.returnTimeSegment(GC.TIMING_TESTRUN)
-
-        # get status
-        success = 0
-        error = 0
-        waiting = 0
-        for value in self.dataRecords.values():
-            if value[GC.TESTCASESTATUS] == GC.TESTCASESTATUS_SUCCESS:
-                success += 1
-            elif value[GC.TESTCASESTATUS] == GC.TESTCASESTATUS_ERROR:
-                error += 1
-            if value[GC.TESTCASESTATUS] == GC.TESTCASESTATUS_WAITING:
-                waiting += 1
-
-        # get globals
-        globalString = '{'
-        for key, value in self.testRunInstance.globalSettings.items():
-            if len(globalString) > 1:
-                globalString += ', '
-            globalString += f'{key}: {value}'
-        globalString += '}'
-
-        # get documents
-        datafiles = self.fileName
-
-        # create object
-        log = TestrunLog(
-            testrunName = self.testRunName,
-            logfileName = logger.handlers[1].baseFilename,
-            startTime = datetime.strptime(start, "%H:%M:%S"),
-            endTime = datetime.strptime(end, "%H:%M:%S"),
-            statusOk = success,
-            statusFailed = error,
-            statusPaused = waiting,
-            globalVars = globalString,
-            dataFile = datafiles,
-        )
-        # write to DataBase
-        session.add(log)
-        session.commit()
-
-    def exportResultExcel(self, **kwargs):
-        self._exportData()
-
-    def makeSummaryExcel(self):
-
-        self.summarySheet.write(0,0, f"Testreport for {self.testRunName}", self.cellFormatBold)
-        self.summarySheet.set_column(0, last_col=0, width=15)
-        # get testrunname my
-        self.testList.append(self.testRunName)
-        # Testrecords
-        self.__writeSummaryCell("Testrecords", len(self.dataRecords), row=2, format=self.cellFormatBold)
-        value = len([x for x in self.dataRecords.values()
-                                                   if x[GC.TESTCASESTATUS] == GC.TESTCASESTATUS_SUCCESS])
-        self.testList.append(value) # Ok my
-        if not value:
-            value = ""
-        self.__writeSummaryCell("Successful", value, format=self.cellFormatGreen)
-        self.testList.append(value)  # paused my
-        self.__writeSummaryCell("Paused", len([x for x in self.dataRecords.values()
-                                                   if x[GC.TESTCASESTATUS] == GC.TESTCASESTATUS_WAITING]))
-        value = len([x for x in self.dataRecords.values()
-                                               if x[GC.TESTCASESTATUS] == GC.TESTCASESTATUS_ERROR])
-        self.testList.append(value)  # error my
-        if not value:
-            value = ""
-        self.__writeSummaryCell("Error", value, format=self.cellFormatRed)
-
-        # Logfile
-        self.__writeSummaryCell("Logfile", logger.handlers[1].baseFilename, row=7)
-        # get logfilename for database my
-        self.testList.append(logger.handlers[1].baseFilename)
-        # Timing
-        timing:Timing = self.testRunInstance.timing
-        start, end, duration = timing.returnTimeSegment(GC.TIMING_TESTRUN)
-        self.__writeSummaryCell("Starttime", start, row=9)
-        # get start end during time my
-        self.testList.append(start)
-        self.testList.append(end)
-
-        self.__writeSummaryCell("Endtime", end)
-        self.__writeSummaryCell("Duration", duration, format=self.cellFormatBold )
-        self.__writeSummaryCell("Avg. Dur", "")
-        # Globals:
-        self.__writeSummaryCell("Global settings for this testrun", "", format=self.cellFormatBold, row=14)
-        for key, value in self.testRunInstance.globalSettings.items():
-            self.__writeSummaryCell(key, str(value))
-            # get global data my
-            self.testList.append(str(value))
-        # Testcase and Testsequence setting
-        self.__writeSummaryCell("TestSequence settings follow:", "", row=16+len(self.testRunInstance.globalSettings),
-                                format=self.cellFormatBold)
-        lSequence = self.testRunInstance.testRunUtils.getSequenceByNumber(testRunName=self.testRunName, sequence="1")
-        if lSequence:
-            for key, value in lSequence[1].items():
-                if isinstance(value, list) or isinstance(value, dict):
-                    continue
-                self.__writeSummaryCell(key, str(value))
-
-    def __writeSummaryCell(self, lineHeader, lineText, row=None, format=None):
-        if not row:
-            self.summaryRow += 1
-        else:
-            self.summaryRow = row
-
-        if not lineText:
-            # If we have no lineText we want to apply format to the Header
-            self.summarySheet.write(self.summaryRow, 0, lineHeader, format)
-        else:
-            self.summarySheet.write(self.summaryRow, 0, lineHeader)
-            self.summarySheet.write(self.summaryRow, 1, lineText, format)
-
-    def __getOutputFileName(self):
-        if self.testRunInstance.globalSettings[GC.PATH_ROOT]:
-            basePath = Path(self.testRunInstance.globalSettings[GC.PATH_ROOT])
-        elif "/" not in self.testRunInstance.globalSettings[GC.DATABASE_EXPORTFILENAMEANDPATH][0:1]:
-            basePath = Path(sys.modules['__main__'].__file__).parent
-        else:
-            basePath = ""
-        l_file: Path = Path(basePath).joinpath(self.testRunInstance.globalSettings[GC.DATABASE_EXPORTFILENAMEANDPATH])
-        if "~" in str(l_file.absolute()):
-            l_file = l_file.expanduser()
-        if not Path(l_file).is_dir():
-            logger.info(f"Create directory {l_file}")
-            Path(l_file).mkdir(parents=True, exist_ok=True)
-
-        if self.exportFormat == GC.EXP_XLSX:
-            lExtension = '.xlsx'
-        elif self.exportFormat == GC.EXP_CSV:
-            lExtension = '.csv'
-        else:
-            logger.critical(f"wrong export file format: {self.exportFormat}, using 'xlsx' instead")
-            lExtension = '.xlsx'
-
-        l_file = l_file.joinpath("baangt_" + self.testRunName + "_" + utils.datetime_return() + lExtension)
-        logger.debug(f"Filename for export: {str(l_file)}")
-        return str(l_file)
-
-    def __setHeaderDetailSheetExcel(self):
-        i = 0
-        self.__extendFieldList()  # Add fields with name "RESULT_*" to output fields.
-        for column in self.fieldListExport:
-            self.worksheet.write(0, i, column)
-            i += 1
-        self.worksheet.write(0, len(self.fieldListExport), "JSON")
-
-    def __extendFieldList(self):
-        """
-        Fields, that start with "RESULT_" shall always be exported.
-
-        @return:
-        """
-        for key in self.dataRecords[0].keys():
-            if "RESULT_" in key:
-                if not key in self.fieldListExport:
-                    self.fieldListExport.append(key)
-
-    def _exportData(self):
-        for key, value in self.dataRecords.items():
-            for (n, column) in enumerate(self.fieldListExport):
-                self.__writeCell(key+1, n, value, column)
-            # Also write everything as JSON-String into the last column
-            self.worksheet.write(key+1, len(self.fieldListExport), json.dumps(value))
-
-        # Create autofilter
-        self.worksheet.autofilter(0,0,len(self.dataRecords.items()),len(self.fieldListExport)-1)
-
-        # Make cells wide enough
-        for n in range(0,len(self.fieldListExport)):
-            ExcelSheetHelperFunctions.set_column_autowidth(self.worksheet, n)
-
-    def __writeCell(self, line, cellNumber, testRecordDict, fieldName, strip=False):
-        if fieldName in testRecordDict.keys() and testRecordDict[fieldName]:
-            if '\n' in testRecordDict[fieldName][0:5] or strip:
-                testRecordDict[fieldName] = testRecordDict[fieldName].strip()
-            if isinstance(testRecordDict[fieldName], dict) or isinstance(testRecordDict[fieldName], list):
-                self.worksheet.write(line, cellNumber, testRecordDict[fieldName].strip())
-            else:
-                if fieldName == GC.TESTCASESTATUS:
-                    if testRecordDict[GC.TESTCASESTATUS] == GC.TESTCASESTATUS_SUCCESS:
-                        self.worksheet.write(line, cellNumber, testRecordDict[fieldName], self.cellFormatGreen)
-                    elif testRecordDict[GC.TESTCASESTATUS] == GC.TESTCASESTATUS_ERROR:
-                        self.worksheet.write(line, cellNumber, testRecordDict[fieldName], self.cellFormatRed)
-                else:
-                    self.worksheet.write(line, cellNumber, testRecordDict[fieldName])
-
-    def closeExcel(self):
-        self.workbook.close()
-        # Next line doesn't work on MAC. Returns "not authorized"
-        # subprocess.Popen([self.filename], shell=True)
-
-
-class ExcelSheetHelperFunctions:
-    def __init__(self):
-        pass
-
-    @staticmethod
-    def set_column_autowidth(worksheet: Worksheet, column: int):
-        """
-        Set the width automatically on a column in the `Worksheet`.
-        !!! Make sure you run this function AFTER having all cells filled in
-        the worksheet!
-        """
-        maxwidth = ExcelSheetHelperFunctions.get_column_width(worksheet=worksheet, column=column)
-        if maxwidth is None:
-            return
-        elif maxwidth > 45:
-            maxwidth = 45
-        worksheet.set_column(first_col=column, last_col=column, width=maxwidth)
-
-    @staticmethod
-    def get_column_width(worksheet: Worksheet, column: int) -> Optional[int]:
-        """Get the max column width in a `Worksheet` column."""
-        strings = getattr(worksheet, '_ts_all_strings', None)
-        if strings is None:
-            strings = worksheet._ts_all_strings = sorted(
-                worksheet.str_table.string_table,
-                key=worksheet.str_table.string_table.__getitem__)
-        lengths = set()
-        for row_id, colums_dict in worksheet.table.items():  # type: int, dict
-            data = colums_dict.get(column)
-            if not data:
-                continue
-            if type(data) is cell_string_tuple:
-                iter_length = len(strings[data.string])
-                if not iter_length:
-                    continue
-                lengths.add(iter_length)
-                continue
-            if type(data) is cell_number_tuple:
-                iter_length = len(str(data.number))
-                if not iter_length:
-                    continue
-                lengths.add(iter_length)
-        if not lengths:
-            return None
-        return max(lengths)
-
-
-class ExportNetWork:
-
-    headers = ['BrowserName', 'TestCaseNum', 'Status', 'Method', 'URL', 'ContentType', 'ContentSize', 'Headers',
-               'Params', 'Response', 'startDateTime', 'Duration/ms']
-
-    def __init__(self, networkInfo: dict, testCasesEndDateTimes_1D: list,
-                 testCasesEndDateTimes_2D: list, workbook: xlsxwriter.Workbook, sheet: xlsxwriter.worksheet):
-
-        self.networkInfo = networkInfo
-        self.testCasesEndDateTimes_1D = testCasesEndDateTimes_1D
-        logger.info('init ExportNetWork, testCasesEndDateTimes_2D: {}'.format(testCasesEndDateTimes_2D))
-        self.testCasesEndDateTimes_2D = testCasesEndDateTimes_2D
-        self.workbook = workbook
-        self.sheet = sheet
-        header_style = self.get_header_style()
-        self.write_header(style=header_style)
-        self.set_column_align()
-        self.write_content()
-        self.set_column_width()
-
-    def set_column_align(self):
-        right_align_indexes = list()
-        right_align_indexes.append(ExportNetWork.headers.index('ContentSize'))
-        right_align_indexes.append(ExportNetWork.headers.index('Duration/ms'))
-        right_align_style = self.get_column_style(alignment='right')
-        left_align_style = self.get_column_style(alignment='left')
-        [self.sheet.set_column(i, i, cell_format=right_align_style) if i in right_align_indexes else
-         self.sheet.set_column(i, i, cell_format=left_align_style) for i in range(len(ExportNetWork.headers))]
-
-    def set_column_width(self):
-        [ExcelSheetHelperFunctions.set_column_autowidth(self.sheet, i) for i in range(len(ExportNetWork.headers))]
-
-    def get_header_style(self):
-        header_style = self.workbook.add_format()
-        header_style.set_bg_color("#00CCFF")
-        header_style.set_color("#FFFFFF")
-        header_style.set_bold()
-        header_style.set_border()
-        return header_style
-
-    def get_column_style(self, alignment=None):
-        column_style = self.workbook.add_format()
-        column_style.set_color("black")
-        column_style.set_align('right') if alignment == 'right'\
-            else column_style.set_align('left') if alignment == 'left' else None
-        column_style.set_border()
-        return column_style
-
-    def write_header(self, style=None):
-        for index, value in enumerate(ExportNetWork.headers):
-            self.sheet.write(0, index, value, style)
-
-    def _get_test_case_num(self, start_date_time, browser_name):
-        d_t = parse(start_date_time)
-        d_t = d_t.replace(tzinfo=None)
-        logger.info('get in to _get_test_case_num, testCasesEndDateTimes_1D: {}, testCasesEndDateTimes_2D: {}'
-              .format(self.testCasesEndDateTimes_1D, self.testCasesEndDateTimes_2D))
-        if self.testCasesEndDateTimes_1D:
-            for index, dt_end in enumerate(self.testCasesEndDateTimes_1D):
-                if d_t < dt_end:
-                    return index
-                elif dt_end < d_t < self.testCasesEndDateTimes_1D[index + 1] \
-                        if index + 1 < len(self.testCasesEndDateTimes_1D) else dt_end < d_t:
-                    return index + 1
-        elif self.testCasesEndDateTimes_2D:
-            browser_num = re.findall(r"\d+\.?\d*", str(browser_name))[-1] \
-                if re.findall(r"\d+\.?\d*", str(browser_name)) else 0
-            logger.info('get browser_num: {}'.format(browser_num))
-            dt_list_index = int(browser_num) - 1 if int(browser_num) > 0 else 0
-            logger.info('get dt_list_index: {}'.format(dt_list_index))
-            for i, dt_end in enumerate(self.testCasesEndDateTimes_2D[dt_list_index]):
-                logger.info('i: {}, dt_end: {}'.format(i, dt_end))
-                if d_t < dt_end:
-                    logger.info('get into d_t < dt_end')
-                    return i
-                elif dt_end < d_t < self.testCasesEndDateTimes_2D[dt_list_index][i + 1] \
-                        if i + 1 < len(self.testCasesEndDateTimes_2D[dt_list_index]) else dt_end < d_t:
-                    logger.info('get into else')
-                    return i + 1
-        return 'unknown'
-
-    def write_content(self):
-        if not self.networkInfo:
-            return
-
-        for index, entry in enumerate(self.networkInfo['log']['entries']):
-            browser_name = entry['pageref']
-            status = entry['response']['status']
-            method = entry['request']['method']
-            url = entry['request']['url']
-            content_type = entry['response']['content']['mimeType']
-            content_size = entry['response']['content']['size']
-            headers = entry['response']['headers']
-            params = entry['request']['queryString']
-            response = entry['response']['content']['text'] if 'text' in entry['response']['content'] else ''
-            start_date_time = entry['startedDateTime']
-            duration = entry['time']
-            test_case_num = self._get_test_case_num(start_date_time, browser_name)
-
-            data_list = [browser_name, test_case_num, status, method, url, content_type, content_size,
-                         headers, params, response, start_date_time, duration]
-
-            [self.sheet.write(index + 1, i, str(data_list[i]) or 'null') for i in range(len(data_list))]
-
-
-class ExportTiming:
-    def __init__(self, testdataRecords:dict, sheet:xlsxwriter.worksheet):
-        self.testdataRecords = testdataRecords
-        self.sheet:xlsxwriter.worksheet = sheet
-
-        self.sections = {}
-
-        self.findAllTimingSections()
-        self.writeHeader()
-        self.writeLines()
-
-        # Autowidth
-        for n in range(0,len(self.sections)+1):
-            ExcelSheetHelperFunctions.set_column_autowidth(self.sheet, n)
-
-    def writeHeader(self):
-        self.wc(0,0,"Testcase#")
-        for index, key in enumerate(self.sections.keys(), start=1):
-            self.wc(0, index, key)
-
-    def writeLines(self):
-        for tcNumber, (key, line) in enumerate(self.testdataRecords.items(),start=1):
-            self.wc(tcNumber, 0, tcNumber)
-            lSections = self.interpretTimeLog(line[GC.TIMELOG])
-            for section, timingValue in lSections.items():
-                # find, in which column this section should be written:
-                for column, key in enumerate(self.sections.keys(),1):
-                    if key == section:
-                        self.wc(tcNumber, column,
-                                ExportTiming.shortenTimingValue(timingValue[GC.TIMING_DURATION]))
-                        continue
-
-    @staticmethod
-    def shortenTimingValue(timingValue):
-        # TimingValue is seconds in Float. 2 decimals is enough:
-        timingValue = int(float(timingValue) * 100)
-        return timingValue/100
-
-    def writeCell(self, row, col, content, format=None):
-        self.sheet.write(row, col, content, format)
-
-    wc = writeCell
-
-    def findAllTimingSections(self):
-        """
-        We try to have an ordered list of Timing Sequences. As each Testcase might have different sections we'll have
-        to make guesses
-
-        @return:
-        """
-        lSections = {}
-        for key, line in self.testdataRecords.items():
-            lTiming:dict = ExportTiming.interpretTimeLog(line[GC.TIMELOG])
-            for key in lTiming.keys():
-                if lSections.get(key):
-                    continue
-                else:
-                    lSections[key] = None
-
-        self.sections = lSections
-
-    @staticmethod
-    def interpretTimeLog(lTimeLog):
-        """Example Time Log:
-        Complete Testrun: Start: 1579553837.241974 - no end recorded
-        TestCaseSequenceMaster: Start: 1579553837.243414 - no end recorded
-        CustTestCaseMaster: Start: 1579553838.97329 - no end recorded
-        Browser Start: , since last call: 2.3161418437957764
-        Empfehlungen: , since last call: 6.440968036651611, ZIDs:[175aeac023237a73], TS:2020-01-20 21:57:46.525577
-        Annahme_RABAZ: , since last call: 2.002716064453125e-05, ZIDs:[6be7d0a44e59acf6], TS:2020-01-20 21:58:37.203583
-        Antrag drucken: , since last call: 9.075241088867188, ZIDs:[6be7d0a44e59acf6, b27c3875ddcbb4fa], TS:2020-01-20 21:58:38.040137
-        Warten auf Senden an Bestand Button: , since last call: 1.3927149772644043
-        Senden an Bestand: , since last call: 9.60469913482666, ZIDs:[66b12fa4869cf8a0, ad1f3d47c4694e26], TS:2020-01-20 21:58:49.472288
-
-        where the first part before ":" is the section, "since last call:" is the duration, TS: is the timestamp"""
-        lExport = {}
-        lLines = lTimeLog.split("\n")
-        for line in lLines:
-            parts = line.split(",")
-            if len(parts) < 2:
-                continue
-            if "Start:" in line:
-                # Format <sequence>: <Start>: <time.loctime>
-                continue
-            else:
-                lSection = parts[0].replace(":","").strip()
-                lDuration = parts[1].split(":")[1]
-                lExport[lSection] = {GC.TIMING_DURATION: lDuration}
-        return lExport
-

+ 0 - 0
baangt/base/ExportResults/__init__.py


+ 0 - 73
baangt/base/ExportResults/hookImpls.py

@@ -1,73 +0,0 @@
-import baangt
-from baangt.base.ExportResults.ExportResults import\
-    (ExportResults, ExportTiming, ExcelSheetHelperFunctions)
-import logging
-
-logger = logging.getLogger("pyC")
-
-
-class ExportResultsHookImpl:
-    @baangt.hook_impl
-    def exportResults_init(self, **kwargs):
-        return ExportResults(**kwargs)
-
-    @baangt.hook_impl
-    def exportResults_exportToDataBase(self, exportResultsObject):
-        return exportResultsObject.exportToDataBase()
-
-    @baangt.hook_impl
-    def exportResults_exportResult(self, exportResultsObject, **kwargs):
-        return exportResultsObject.exportResultExcel(**kwargs)
-
-    @baangt.hook_impl
-    def exportResults_makeSummary(self, exportResultsObject):
-        return exportResultsObject.makeSummaryExcel()
-
-    @baangt.hook_impl
-    def exportResults_closeExcel(self, exportResultsObject, line, cellNumber, testRecordDict, fieldName, strip=False):
-        return exportResultsObject.closeExcel(line, cellNumber, testRecordDict, fieldName, strip)
-
-
-class ExcelSheetHelperFunctionsHookImpl:
-    
-    @baangt.hook_impl
-    def excelSheetHelperFunctions_init(self):
-        return ExcelSheetHelperFunctions()
-
-    @baangt.hook_impl
-    def excelSheetHelperFunctions_set_column_autowidth(self, excelSheetHelperFunctionsObject, worksheet, column):
-        return excelSheetHelperFunctionsObject.set_column_autowidth(worksheet, column)
-
-    @baangt.hook_impl
-    def excelSheetHelperFunctions_get_column_width(self, excelSheetHelperFunctionsObject, worksheet, column):
-        return excelSheetHelperFunctionsObject.get_column_width(worksheet, column)
-
-
-class ExportTimingHookImpl:
-    @baangt.hook_impl
-    def exportTiming_init(self, testdataRecords, sheet):
-        return ExportTiming(testdataRecords, sheet)
-
-    @baangt.hook_impl
-    def exportTiming_writeHeader(self, exportTimingObject):
-        return exportTimingObject.writeHeader()
-
-    @baangt.hook_impl
-    def exportTiming_writeLines(self, exportTimingObject):
-        return exportTimingObject.writeLines()
-
-    @baangt.hook_impl
-    def exportTiming_shortenTimingValue(self, exportTimingObject, timingValue):
-        return exportTimingObject.shortenTimingValue(timingValue)
-
-    @baangt.hook_impl
-    def exportTiming_writeCell(self, exportTimingObject, row, col, content, format=None):
-        return exportTimingObject.writeCell(row, col, content, format)
-
-    @baangt.hook_impl
-    def exportTiming_findAllTimingSections(self, exportTimingObject):
-        return exportTimingObject.findAllTimingSections()
-
-    @baangt.hook_impl
-    def exportTiming_interpretTimeLog(self, exportTimingObject, lTimeLog):
-        return exportTimingObject.interpretTimeLog(lTimeLog)

+ 0 - 98
baangt/base/GlobalConstants.py

@@ -1,98 +0,0 @@
-KWARGS_DATA = "data"
-KWARGS_MOBILE = "Mobile"
-KWARGS_BROWSER = "Browser"
-KWARGS_API_SESSION = "api"
-KWARGS_APPIUM = 'Appium'
-KWARGS_TESTCASETYPE = "TestCaseType"
-KWARGS_TESTRUNATTRIBUTES = "TESTRUNEXECUTIONPARAMETERS"
-KWARGS_TESTRUNINSTANCE = "TESTRUNINSTANCE"
-KWARGS_TIMING = "TimingClassInstance"
-
-CLASSES_TESTCASESEQUENCE_OLD = "baangt.TestCaseSequence.TestCaseSequenceMaster.TestCaseSequenceMaster"
-CLASSES_TESTCASESEQUENCE = "TestCaseSequenceMaster"
-CLASSES_TESTCASE_OLD = "baangt.TestCase.TestCaseMaster.TestCaseMaster"
-CLASSES_TESTCASE = 'TestCaseMaster'
-CLASSES_TESTSTEPMASTER_OLD = 'baangt.TestSteps.TestStepMaster'
-CLASSES_TESTSTEPMASTER = 'TestStepMaster'
-
-TIMING_END = "end"
-TIMING_START = "start"
-TIMING_TESTRUN = "Complete Testrun"
-TIMING_DURATION = "Duration"
-TIMESTAMP = "timestamp"
-TIMELOG = "timelog"
-
-GECKO_DRIVER = "geckodriver.exe"
-CHROME_DRIVER = "chromedriver.exe"
-
-BROWSER_FIREFOX = "FF"
-BROWSER_CHROME = "CHROME"
-BROWSER_SAFARI = "SAFARI"
-BROWSER_EDGE = "EDGE"
-BROWSER_REMOTE = 'REMOTE'
-BROWSER_APPIUM = 'APPIUM'
-BROWSER_MODE_HEADLESS = "HEADLESS"
-BROWSER_ATTRIBUTES = "BrowserAttributes"
-
-CMD_CLICK = "CLICK"
-CMD_SETTEXT = "SETTEXT"
-CMD_FORCETEXT = "FORCETEXT"
-
-TESTCASESTATUS = "TestCaseStatus"
-TESTCASESTATUS_SUCCESS = "OK"
-TESTCASESTATUS_ERROR = "Failed"
-TESTCASESTATUS_WAITING = "Paused"
-TESTCASEERRORLOG = "TCErrorLog"
-TESTCASE_EXPECTED_ERROR_FIELD = "TC Expected Error"
-
-DATABASE_FROM_LINE = "FromLine"
-DATABASE_TO_LINE = "ToLine"
-DATABASE_LINES = "Lines"
-DATABASE_FILENAME = "TestDataFileName"
-DATABASE_SHEETNAME = "Sheetname"
-DATABASE_EXPORTFILENAMEANDPATH = "exportFilesBasePath"
-
-STRUCTURE_TESTCASESEQUENCE = "TESTSEQUENCE"
-STRUCTURE_TESTCASE = "TESTCASE"
-STRUCTURE_TESTSTEP = "TestStep"
-STRUCTURE_TESTSTEPEXECUTION = "TestStepExecutionParameters"
-
-EXECUTION_PARALLEL = "ParallelRuns"
-SCREENSHOTS = "Screenshots"
-EXECUTION_DONTCLOSEBROWSER = "dontCloseBrowser"
-EXECUTION_SLOW = "slowExecution"
-EXECUTION_NETWORK_INFO = 'NetworkInfo'
-
-EXPORT_FORMAT = "Export Format"
-EXP_FIELDLIST = "Fieldlist"
-EXP_XLSX = "XLSX"
-EXP_CSV = "CSV"
-
-PATH_EXPORT = 'ExportPath'
-PATH_IMPORT = 'ImportPath'
-PATH_SCREENSHOTS = 'ScreenshotPath'
-PATH_ROOT = 'RootPath'
-
-ADDRESS_COUNTRYCODE = "CountryCode"
-ADDRESS_POSTLCODE = "PostlCode"
-ADDRESS_CITYNAME = "CityName"
-ADDRESS_STREETNAME = "StreetName"
-ADDRESS_HOUSENUMBER = "HouseNumber"
-ADDRESS_ADDITION1 = "Addition1"
-ADDRESS_ADDITION2 = "Addition2"
-
-WIN_PLATFORM = 'windows'
-LINUX_PLATFORM = 'linux'
-
-BIT_64 = 8
-BIT_32 = 4
-
-OS_list = ["Linux-32", "Linux-64", "MacOS", "Windows-32", "Windows-64"]
-OS_list_chrome = ['linux32', 'linux64', 'mac64', 'win32']
-
-GECKO_URL = 'https://api.github.com/repos/mozilla/geckodriver/releases/latest'
-CHROME_URL = 'https://chromedriver.storage.googleapis.com/LATEST_RELEASE'
-
-
-BROWSER_PROXY_PATH = '/browsermob-proxy/bin/browsermob-proxy'
-BROWSER_PROXY_URL = 'https://github.com/lightbody/browsermob-proxy/releases/download/browsermob-proxy/browsermob-proxy-bin.zip'

+ 0 - 153
baangt/base/HandleDatabase.py

@@ -1,153 +0,0 @@
-import logging
-import pandas as pd
-import itertools
-import json
-import baangt.base.CustGlobalConstants as CGC
-import baangt.base.GlobalConstants as GC
-from baangt.base.Utils import utils
-import baangt.TestSteps.Exceptions
-from pathlib import Path
-
-logger = logging.getLogger("pyC")
-
-
-class HandleDatabase:
-    def __init__(self, linesToRead, globalSettings=None):
-        self.lineNumber = 3
-        # FIXME: This is still not clean. GlobalSettings shouldn't be predefined in CustomConstants-Class
-        self.globals = {
-            CGC.CUST_TOASTS: "",
-            GC.TESTCASEERRORLOG: "",
-            CGC.VIGOGFNUMMER: "",
-            CGC.SAPPOLNR: "",
-            CGC.PRAEMIE: "",
-            CGC.POLNRHOST: "",
-            GC.TESTCASESTATUS: "",
-            GC.TIMING_DURATION: "",
-            GC.SCREENSHOTS: "",
-            GC.TIMELOG: ""
-        }
-        if globalSettings:
-            for setting, value in globalSettings.items():
-                self.globals[setting] = value
-        self.range = self.__buildRangeOfRecords(linesToRead)
-        self.rangeDict = {}
-        self.__buildRangeDict()
-        self.df_json = None
-        self.dataDict = {}
-        self.recordPointer = 0
-
-    def __buildRangeDict(self):
-        """
-        Interprets the Range and creates a DICT of values, that we can loop over later
-
-        @return: Creates empty self.rangeDict
-        """
-        for lRangeLine in self.range:
-            for x in range(lRangeLine[0], lRangeLine[1]+1):
-                self.rangeDict[x] = ""
-
-    def __buildRangeOfRecords(self, rangeFromConfigFile):
-        lRange = []
-        if not rangeFromConfigFile:
-            # No selection - means all records
-            return [[0,9999]]
-        else:
-            # Format: 4;6-99;17-200;203
-            if ";" in rangeFromConfigFile:
-                for kombination in rangeFromConfigFile.split(";"):
-                    lRange.append(HandleDatabase.__buildRangeOfRecordsOneEntry(kombination))
-                    pass
-            else:
-                lRange.append(HandleDatabase.__buildRangeOfRecordsOneEntry(rangeFromConfigFile))
-
-        # Make sure these are numbers:
-        for lRangeLine in lRange:
-            lRangeLine[0] = HandleDatabase.__sanitizeNumbers(lRangeLine[0])
-            lRangeLine[1] = HandleDatabase.__sanitizeNumbers(lRangeLine[1])
-
-        return lRange
-
-    @staticmethod
-    def __sanitizeNumbers(numberIn):
-        numberIn = numberIn.strip()
-        return int(numberIn)
-
-    @staticmethod
-    def __buildRangeOfRecordsOneEntry(rangeIn):
-        if "-" in rangeIn:
-            # This is a range (17-22)
-            return HandleDatabase.__buildRangeOfRecordsSingleRange(rangeIn)
-        else:
-            # This is a single entry:
-            return [rangeIn, rangeIn]
-
-    @staticmethod
-    def __buildRangeOfRecordsSingleRange(rangeIn):
-        lSplit = rangeIn.split("-")
-        return [lSplit[0], lSplit[1]]
-
-    def read_excel(self, fileName, sheetName):
-        fileName = utils.findFileAndPathFromPath(fileName)
-        if not fileName:
-            logger.critical(f"Can't open file: {fileName}")
-            return
-
-        # FIXME: Sooner or later replace Pandas with direct XLSX-Import
-        xl = pd.ExcelFile(fileName)
-        ncols = xl.book.sheet_by_name(sheet_name=sheetName).ncols
-        # Read all columns as strings:
-        df = xl.parse(sheet_name=sheetName, converters={i: str for i in range(ncols)})
-
-        # Set all Nan to empty String:
-        df = df.where((pd.notnull(df)), '')
-        # Create Dict of Header + item:
-        self.dataDict = df.to_dict(orient="records")
-
-    def readNextRecord(self):
-        """
-        We built self.range during init. Now we need to iterate over the range(s) in range,
-        find appropriate record and return that - one at a time
-
-        @return:
-        """
-        if len(self.rangeDict) == 0:
-            # All records were processed
-            return None
-        try:
-            # the topmost record of the RangeDict (RangeDict was built by the range(s) from the TestRun
-            # - 1 because there's a header line in the Excel-Sheet.
-            lRecord = self.dataDict[(list(self.rangeDict.keys())[0])-1]
-        except Exception as e:
-            logger.debug(f"Couldn't read record from database: {list(self.rangeDict.keys())[0]}")
-            self.rangeDict.pop(list(self.rangeDict.keys())[0])
-            return None
-
-        self.rangeDict.pop(list(self.rangeDict.keys())[0])
-        return self.updateGlobals(lRecord)
-
-    def readTestRecord(self, lineNumber=None):
-        if lineNumber:
-            self.lineNumber = lineNumber -1  # Base 0 vs. Base 1
-        else:
-            self.lineNumber += 1 # add 1 to read next line number
-
-        try:
-            record = self.dataDict[self.lineNumber]
-            logger.info(f"Starting with Testrecord {self.lineNumber}, Details: " +
-                        str({k: record[k] for k in list(record)[0:5]}))
-            return self.updateGlobals(record)
-        except Exception as e:
-            logger.critical(f"Couldn't read record# {self.lineNumber}")
-
-    def updateGlobals(self, record):
-
-        self.globals[CGC.CUST_TOASTS] = ""
-        self.globals[GC.TESTCASEERRORLOG] = ""
-        self.globals[GC.TIMING_DURATION] = ""
-        self.globals[GC.TIMELOG] = ""
-        self.globals[GC.TESTCASESTATUS] = ""
-        self.globals[GC.SCREENSHOTS] = ""
-
-        record.update(self.globals)
-        return record

+ 0 - 36
baangt/base/IBAN.py

@@ -1,36 +0,0 @@
-import schwifty
-import random
-
-
-class IBAN:
-    def __init__(self, bankLeitZahl='20151', bankLand='AT'):
-        """
-        Class to generate random IBAN-Numbers. Mainly used in test data generation either on-the-fly or upfront
-        during creation of a testdata XLSX.
-
-        @param bankLeitZahl: Valid Bankcode (old format)
-        @param bankLand: Valid Bank country in ISO-Format
-        """
-        self.bankLeitZahl = bankLeitZahl
-        self.bankLand = bankLand
-        pass
-
-    def getRandomIBAN(self):
-        """
-        Generates a random IBAN based on bankLand and bankLeitzahl as well as a random account number
-
-        @return: gives a String of IBAN
-        """
-        laenge = random.randrange(6, 10)
-        digits = []
-        for n in range(laenge):
-            digits.append(random.randrange(0, 10))
-        digits = "".join(str(x) for x in digits)
-        return str(schwifty.IBAN.generate(country_code=self.bankLand,
-                                          bank_code=self.bankLeitZahl,
-                                          account_code=digits))
-
-
-if __name__ == '__main__':
-    l = IBAN()
-    print(l.getRandomIBAN())

+ 0 - 9
baangt/base/ODataHandling.py

@@ -1,9 +0,0 @@
-from baangt.base.ApiHandling import ApiHandling
-
-class ODataHandling(ApiHandling):
-    """
-    This is the Class to handle OData-specific functionality of baangt. It inherits all methods and parameters from
-    ApiHandling
-    """
-    pass
-

+ 0 - 9
baangt/base/SOAPHandling.py

@@ -1,9 +0,0 @@
-from baangt.base.ApiHandling import ApiHandling
-
-class SOAPHandling(ApiHandling):
-    """
-    This is the Class to handle SOAP-Specific functionality of baangt. It inherits all methods and parameters from
-    ApiHandling
-    """
-    pass
-

+ 0 - 332
baangt/base/TestRun/TestRun.py

@@ -1,332 +0,0 @@
-from baangt.base.BrowserHandling.BrowserHandling import BrowserDriver
-from baangt.base.ApiHandling import ApiHandling
-from baangt.base.ExportResults.ExportResults import ExportResults
-from baangt.base.Utils import utils
-from baangt.base import GlobalConstants as GC
-from baangt.base.TestRunExcelImporter import TestRunExcelImporter
-# needed - they'll be used dynamically later
-from baangt.TestSteps.TestStepMaster import TestStepMaster
-from baangt.TestCase.TestCaseMaster import TestCaseMaster
-from baangt.TestCaseSequence.TestCaseSequenceMaster import TestCaseSequenceMaster
-import logging
-from pathlib import Path
-import sys
-import os
-from baangt.base.Timing.Timing import Timing
-from baangt.base.TestRunUtils import TestRunUtils
-import time
-logger = logging.getLogger("pyC")
-
-
-class TestRun:
-    """
-    This is the main Class of Testexecution in the baangt Framework. It is usually started
-    from baangt.py
-    """
-    def __init__(self, testRunName, globalSettingsFileNameAndPath=None):
-        """
-        @param testRunName: The name of the TestRun to be executed.
-        @param globalSettingsFileNameAndPath: from where to read the <globals>.json
-        """
-        logger.info('init Testrun, id is {}'.format(id(self)))
-        self.browser = {}
-        self.apiInstance = None
-        self.testType = None
-        self.networkInfo = None
-        self.kwargs = {}
-        self.dataRecords = {}
-        self.globalSettingsFileNameAndPath = globalSettingsFileNameAndPath
-        self.globalSettings = {}
-        self.testRunName, self.testRunFileName = \
-            self._sanitizeTestRunNameAndFileName(testRunName)
-        self.timing = Timing()
-        self.timing.takeTime(GC.TIMING_TESTRUN)  # Initialize Testrun Duration
-        self.testRunUtils = TestRunUtils()
-        self._initTestRun()
-
-        self.browserProxyAndServer = self.getBrowserProxyAndServer() \
-            if self.globalSettings.get('TC.' + GC.EXECUTION_NETWORK_INFO) == 'True' else None
-        self.testCasesEndDateTimes_1D = []  # refer to single execution
-        self.testCasesEndDateTimes_2D = [[]]  # refer to parallel execution
-        self._loadJSONTestRunDefinitions()
-        self._loadExcelTestRunDefinitions()
-        self.executeTestRun()
-        self.tearDown()
-
-    def append1DTestCaseEndDateTimes(self, dt):
-        self.testCasesEndDateTimes_1D.append(dt)
-
-    def append2DTestCaseEndDateTimes(self, index, dt):
-        [self.testCasesEndDateTimes_2D.append([]) for i in range(
-            index + 1 - len(self.testCasesEndDateTimes_2D))] if index + 1 > len(
-                self.testCasesEndDateTimes_2D) else None
-        logger.info('before append index: {}, dt: {},  testCasesEndDateTimes_2D:{}'.format(index, dt, self.testCasesEndDateTimes_2D))
-        self.testCasesEndDateTimes_2D[index].append(dt)
-        logger.info('after append index: {}, dt: {},  testCasesEndDateTimes_2D:{}'.format(index, dt, self.testCasesEndDateTimes_2D))
-
-    def tearDown(self):
-        """
-        Close browser (unless stated in the Globals to not do so) and API-Instances
-        Take overall Time spent for the complete TestRun
-        Write results of TestRun to output channel(s)
-        """
-        if not self.globalSettings.get("TC." + GC.EXECUTION_DONTCLOSEBROWSER):
-            for browserInstance in self.browser.keys():
-                self.browser[browserInstance].closeBrowser()
-
-        self.timing.takeTime(GC.TIMING_TESTRUN)
-        self.timing.takeTimeSumOutput()
-
-        if self.apiInstance:
-            self.apiInstance.tearDown()
-
-        if self.browserProxyAndServer:
-            network_info = self.browserProxyAndServer[0].har
-            self.browserProxyAndServer[1].stop()
-            self.kwargs['networkInfo'] = network_info
-
-        if self.testCasesEndDateTimes_1D:
-            self.kwargs['testCasesEndDateTimes_1D'] = self.testCasesEndDateTimes_1D
-
-        logger.info('before prepared testCasesEndDateTimes_2D: {}'.format(self.testCasesEndDateTimes_2D))
-        if self.testCasesEndDateTimes_2D and self.testCasesEndDateTimes_2D[0]:
-            self.kwargs['testCasesEndDateTimes_2D'] = self.testCasesEndDateTimes_2D
-        logger.info('after prepared testCasesEndDateTimes_2D: {}'.format(self.testCasesEndDateTimes_2D))
-        ExportResults(**self.kwargs)
-        successful, error = self.getSuccessAndError()
-        logger.info(f"Finished execution of Testrun {self.testRunName}. "
-                    f"{successful} Testcases successfully executed, {error} errors")
-        print(f"Finished execution of Testrun {self.testRunName}. "
-              f"{successful} Testcases successfully executed, {error} errors")
-
-    def getSuccessAndError(self):
-        """
-        Returns number of successful and number of error test cases of the current test run
-        @rtype: object
-        """
-        lError = 0
-        lSuccess = 0
-        for value in self.dataRecords.values():
-            if value[GC.TESTCASESTATUS] == GC.TESTCASESTATUS_ERROR:
-                lError += 1
-            elif value[GC.TESTCASESTATUS] == GC.TESTCASESTATUS_SUCCESS:
-                lSuccess += 1
-        return lSuccess, lError
-
-    def getAllTestRunAttributes(self):
-        return self.testRunUtils.getCompleteTestRunAttributes(self.testRunName)
-
-    def getBrowser(self, browserInstance=1, browserName=None, browserAttributes=None, mobileType=None):
-        """
-        This method is called whenever a browser instance (existing or new) is needed. If called without
-        parameters it will create one instance of Firefox (geckodriver).
-
-        if global setting TC.EXECUTION_SLOW is set, inform the browser instance about it.
-
-        @param browserInstance: Number of the requested browser instance. If none is provided, always the default
-        browser instance will be returned
-        @param browserName: one of the browser names (e.g. FF, Chrome) from GC.BROWSER*
-        @param browserAttributes: optional Browser Attributes
-        @return: the browser instance of base class BrowserDriver
-
-        """
-        if mobileType == 'True' :
-            logger.info(f"opening new Appium instance {browserInstance} of Appium browser {browserName}")
-            self._getBrowserInstance(browserInstance=browserInstance)
-            browser_proxy = self.browserProxyAndServer[0] if self.browserProxyAndServer else None
-            self.browser[browserInstance].createNewBrowser(mobileType=mobileType,
-                                                           browserName=browserName,
-                                                           desiredCapabilities=browserAttributes,
-                                                           browserProxy=browser_proxy,
-                                                           browserInstance=browserInstance)
-            if self.globalSettings.get("TC." + GC.EXECUTION_SLOW):
-                self.browser[browserInstance].slowExecutionToggle()
-            return self.browser[browserInstance]
-        else:
-            if browserInstance not in self.browser.keys():
-                logger.info(f"opening new instance {browserInstance} of browser {browserName}")
-                self._getBrowserInstance(browserInstance=browserInstance)
-                browser_proxy = self.browserProxyAndServer[0] if self.browserProxyAndServer else None
-                self.browser[browserInstance].createNewBrowser(mobileType=mobileType,
-                                                               browserName=browserName,
-                                                               desiredCapabilities=browserAttributes,
-                                                               browserProxy=browser_proxy,
-                                                               browserInstance=browserInstance)
-                if self.globalSettings.get("TC." + GC.EXECUTION_SLOW):
-                    self.browser[browserInstance].slowExecutionToggle()
-            else:
-                logger.debug(f"Using existing instance of browser {browserInstance}")
-            return self.browser[browserInstance]
-
-    def _getBrowserInstance(self, browserInstance):
-        self.browser[browserInstance] = BrowserDriver(timing=self.timing,
-                                                      screenshotPath=self.globalSettings[GC.PATH_SCREENSHOTS])
-
-    def downloadBrowserProxy(self):
-        pass
-
-    def getBrowserProxyAndServer(self):
-        from browsermobproxy import Server
-        server = Server(os.getcwd() + GC.BROWSER_PROXY_PATH)
-        logger.info("Starting browsermob proxy")
-        server.start()
-        time.sleep(1)
-        proxy = server.create_proxy()
-        time.sleep(1)
-        return proxy, server
-
-    def getAPI(self):
-        if not self.apiInstance:
-            self.apiInstance = ApiHandling()
-        return self.apiInstance
-
-    def setResult(self, recordNumber, dataRecordResult):
-        logger.debug(f"Received new result for Testrecord {recordNumber}")
-        self.dataRecords[recordNumber] = dataRecordResult
-
-    def executeTestRun(self):
-        """
-        Start TestcaseSequence
-
-        TestCaseSequence is a sequence of Testcases. In the TestcaseSequence there's a sequential List of
-        Testcases to be executed.
-
-        Before the loop (executeDictSequenceOfClasses) variables inside the the testrun-definition are replaced
-        by values from the globals-file (e.g. if you want to generally run with FF, but in a certain case you want to
-        run with Chrome, you'd have FF in the Testrundefinition, but set parameter in globals_chrome.json accordingly
-        (in this case {"TC.Browser": "CHROME"}. TC.-Prefix signals the logic to look for this variable ("Browser")
-        inside the testcase definitions and replace it with value "CHROME".
-
-        """
-        self.testRunUtils.replaceGlobals(self.globalSettings)
-        self.executeDictSequenceOfClasses(
-            self.testRunUtils.getCompleteTestRunAttributes(self.testRunName)[GC.STRUCTURE_TESTCASESEQUENCE],
-            counterName=GC.STRUCTURE_TESTCASESEQUENCE)
-
-    def executeDictSequenceOfClasses(self, dictSequenceOfClasses, counterName, **kwargs):
-        """
-        This is the main loop of the TestCaseSequence, TestCases, TestStepSequences and TestSteps.
-        The Sequence of which class instance to create is defined by the TestRunAttributes.
-
-        Before instancgetBrowsering the class it is checked, whether the class was loaded already and if not, will be loaded
-        (only if the classname is fully qualified (e.g baangt<projectname>.TestSteps.myTestStep).
-        If the testcase-Status is already "error" (GC.TESTCASESTATUS_ERROR) we'll stop the loop.
-
-        @param dictSequenceOfClasses: The list of classes to be instanced. Must be a dict of {Enum, Classname},
-        can be also {enum: [classname, <whatEverElse>]}
-        @param counterName: Which Structure element we're currently looping, e.g. "TestStep" (GC.STRUCTURE_TESTSTEP)
-        @param kwargs: TestrunAttributes, this TestRun, the Timings-Instance, the datarecord
-
-        """
-        if not kwargs.get(GC.KWARGS_TESTRUNATTRIBUTES):
-            kwargs[GC.KWARGS_TESTRUNATTRIBUTES] = self.getAllTestRunAttributes()
-        if not kwargs.get(GC.KWARGS_TESTRUNINSTANCE):
-            kwargs[GC.KWARGS_TESTRUNINSTANCE] = self
-            logger.info('get into not kwargs.getGC.KWARGS_TESTRUNINSTANCE, id is {}'.format(id(self)))
-        if not kwargs.get(GC.KWARGS_TIMING):
-            kwargs[GC.KWARGS_TIMING] = self.timing
-        for key, value in dictSequenceOfClasses.items():
-            # If any of the previous steps set the testcase to "Error" - exit here.
-            if kwargs.get(GC.KWARGS_DATA):
-                if kwargs[GC.KWARGS_DATA][GC.TESTCASESTATUS] == GC.TESTCASESTATUS_ERROR:
-                    logger.info(f"TC is already in status Error - not processing steps {counterName}: {key}, {value}"
-                                f"and everything behind this step")
-                    return
-            logger.info(f"Starting {counterName}: {key}, {value} ")
-            kwargs[counterName] = key
-
-            if isinstance(value, list):
-                lFullQualified = value[0]  # First List-Entry must hold the ClassName
-            else:
-                lFullQualified = value
-
-            if "." in lFullQualified:
-                l_class = TestRun.__dynamicImportClasses(lFullQualified)
-            else:
-                l_class = globals()[lFullQualified]
-            l_class(**kwargs)  # Executes the class __init__
-        self.kwargs = kwargs
-
-
-    def _initTestRun(self):
-        self.loadJSONGlobals()
-        if not self.globalSettings.get(GC.PATH_SCREENSHOTS,None):
-            self.globalSettings[GC.PATH_SCREENSHOTS] = str(Path(self.globalSettingsFileNameAndPath
-                                                                ).parent.joinpath("Screenshots").expanduser())
-            self.globalSettings[GC.PATH_EXPORT] = str(Path(self.globalSettingsFileNameAndPath
-                                                           ).parent.joinpath("1testoutput").expanduser())
-            self.globalSettings[GC.PATH_IMPORT] = str(Path(self.globalSettingsFileNameAndPath
-                                                           ).parent.joinpath("0testdateninput").expanduser())
-            self.globalSettings[GC.PATH_ROOT] = str(Path(self.globalSettingsFileNameAndPath
-                                                         ).parent.expanduser())
-
-    def loadJSONGlobals(self):
-        if self.globalSettingsFileNameAndPath:
-            self.globalSettings = utils.openJson(self.globalSettingsFileNameAndPath)
-
-    def _loadJSONTestRunDefinitions(self):
-        if not self.testRunFileName:
-            return
-
-        if ".JSON" in self.testRunFileName.upper():
-            data = utils.replaceAllGlobalConstantsInDict(utils.openJson(self.testRunFileName))
-            self.testRunUtils.setCompleteTestRunAttributes(testRunName=self.testRunName,
-                                                           testRunAttributes=data)
-
-    def _loadExcelTestRunDefinitions(self):
-        if not self.testRunFileName:
-            return
-
-        if ".XLSX" in self.testRunFileName.upper():
-            logger.info(f"Reading Definition from {self.testRunFileName}")
-            lExcelImport = TestRunExcelImporter(FileNameAndPath=self.testRunFileName, testRunUtils=self.testRunUtils)
-            lExcelImport.importConfig(self.globalSettings)
-
-    @staticmethod
-    def __dynamicImportClasses(fullQualifiedImportName):
-        """
-        Requires fully qualified Name of Import-Class and module,
-        e.g. TestCaseSequence.TCS_VIGO.TCS_VIGO if the class TCS_VIGO
-                                                is inside the Python-File TCS_VIGO
-                                                which is inside the Module TestCaseSequence
-        if name is not fully qualified, the ClassName must be identical with the Python-File-Name,
-        e.g. TestSteps.Franzi will try to import TestSteps.Franzi.Franzi
-        @param fullQualifiedImportName:
-        @return: The class instance. If no class instance can be found the TestRun aborts hard with sys.exit
-        """
-        importClass = fullQualifiedImportName.split(".")[-1]
-        if globals().get(importClass):
-            return globals()[importClass]  # Class already imported
-
-        if fullQualifiedImportName.split(".")[-2:-1][0] == fullQualifiedImportName.split(".")[-1]:
-            moduleToImport = ".".join(fullQualifiedImportName.split(".")[0:-1])
-        else:
-            moduleToImport = fullQualifiedImportName
-
-        mod = __import__(moduleToImport, fromlist=importClass)
-        logger.debug(f"Imported module {fullQualifiedImportName}, result was {str(mod)}")
-        retClass = getattr(mod, importClass)
-        if not retClass:
-            logger.critical(f"Can't import module: {fullQualifiedImportName}")
-            sys.exit("Critical Error in Class import - can't continue. "
-                     "Please maintain proper classnames in Testrundefinition.")
-        return retClass
-
-    @staticmethod
-    def _sanitizeTestRunNameAndFileName(TestRunNameInput):
-        """
-        @param TestRunNameInput: The complete File and Path of the TestRun definition (JSON or XLSX).
-        @return: TestRunName and FileName (if definition of testrun comes from a file (JSON or XLSX)
-        """
-        if ".XLSX" in TestRunNameInput.upper() or ".JSON" in TestRunNameInput.upper():
-            lRunName = utils.extractFileNameFromFullPath(TestRunNameInput)
-            lFileName = TestRunNameInput
-        else:
-            lRunName = TestRunNameInput
-            lFileName = None
-
-        return lRunName, lFileName
-
-
-if __name__ == '__main__':
-    print(1)

+ 0 - 4
baangt/base/TestRun/__init__.py

@@ -1,4 +0,0 @@
-
-
-
-

+ 0 - 50
baangt/base/TestRun/hookImpls.py

@@ -1,50 +0,0 @@
-import baangt
-
-from baangt.base.TestRun.TestRun import TestRun
-
-
-class TestRunHookImpl(object):
-
-    @baangt.hook_impl
-    def testRun_init(self, testRunName, globalSettingsFileNameAndPath):
-        return TestRun(testRunName=testRunName, globalSettingsFileNameAndPath=globalSettingsFileNameAndPath)
-
-    @baangt.hook_impl
-    def testRun_tearDown(self, testRunObject):
-        return testRunObject.tearDown()
-
-    @baangt.hook_impl
-    def testRun_getSuccessAndError(self, testRunObject):
-        return testRunObject.testRun_getSuccessAndError()
-
-    @baangt.hook_impl
-    def testRun_getAllTestRunAttributes(self, testRunObject):
-        return testRunObject.getAllTestRunAttributes()
-
-    @baangt.hook_impl
-    def testRun_getBrowser(self, testRunObject, browserInstance=1, browserName=None, browserAttributes=None):
-        return testRunObject.getBrowser(browserInstance, browserName, browserAttributes)
-
-    @baangt.hook_impl
-    def testRun_getAPI(self, testRunObject):
-        return testRunObject._getAPI()
-
-    @baangt.hook_impl
-    def testRun_setResult(self, testRunObject, recordNumber, dataRecordResult):
-        return testRunObject.setResult(recordNumber, dataRecordResult)
-
-    @baangt.hook_impl
-    def testRun_executeTestRun(self, testRunObject):
-        return testRunObject.executeTestRun()
-
-    @baangt.hook_impl
-    def testRun_executeDictSequenceOfClasses(self, testRunObject, dictSequenceOfClasses, counterName, **kwargs):
-        return testRunObject.executeDictSequenceOfClasses(dictSequenceOfClasses, counterName, **kwargs)
-
-    @baangt.hook_impl
-    def testRun_loadJSONGlobals(self, testRunObject):
-        return testRunObject._loadJSONGlobals()
-
-
-
-

+ 0 - 283
baangt/base/TestRunDatabaseCreate.py

@@ -1,283 +0,0 @@
-import sqlite3
-
-class TestRunDatabaseFill:
-    def __init__(self, cursor):
-        self.cursor = cursor
-        self.fill()
-
-
-    def fill(self):
-        self.cursor.execute('insert into executionMethod (name, description, created, createdBy)'
-                            'values ("Browser", "Execute with a Browser", "", "Franzi")')
-
-        self.cursor.execute("""
-        INSERT INTO "main"."locatorTypes" ("ID", "name", "description", "created", "createdBy", "changed", "changedBy") 
-                VALUES ('1', 'XPATH', 'Locate via XPATH-Expression', '', 'Franzi', '', '');""")
-        self.cursor.execute("""
-        INSERT INTO "main"."locatorTypes" ("ID", "name", "description", "created", "createdBy", "changed", "changedBy") 
-                VALUES ('2', 'ID', 'Locate via ID of the Element', '', 'Franzi', '', '');""")
-        self.cursor.execute("""
-        INSERT INTO "main"."locatorTypes" ("ID", "name", "description", "created", "createdBy", "changed", "changedBy") 
-                VALUES ('3', 'CSS', 'Locate via CSS-Path of the Element', '', 'Franzi', '', '');
-        """)
-
-        self.cursor.execute("""
-        INSERT INTO "main"."activityTypes" ("ID", "name", "description", "created", "createdBy", "changed", "changedBy") 
-                VALUES ('1', 'GoToURL', 'Go to an URL', '', 'Franzi', '', '');""")
-        self.cursor.execute("""INSERT INTO "main"."activityTypes" ("ID", "name", "description", "created", "createdBy", "changed", "changedBy") 
-                VALUES ('2', 'SetText', 'Set Text of an Element', '', 'Franzi', '', '');""")
-        self.cursor.execute("""INSERT INTO "main"."activityTypes" ("ID", "name", "description", "created", "createdBy", "changed", "changedBy") 
-                VALUES ('3', 'Click', 'Click on an Element', '', 'Franzi', '', '');
-        """)
-
-        self.cursor.execute('insert into testrun (name, description, created, createdBy) '
-                            'values ("Test1", "Test1", "", "Franzi")')
-
-        self.cursor.execute('insert into testCaseSequence (name, description, testrunID, created, createdBy)'
-                            'values ("Dummy", "Dummy", 1, "", "Franzi")')
-
-        self.cursor.execute('insert into testCase (name, description, executionMethodID, methodParameters, testCaseSequenceID, created, createdBy)'
-                            'values ("Dummy", "Dummy", 1, "FF", 1, "", "Franzi")')
-
-        self.cursor.execute('insert into testStepSequence (name, description, testCaseID, created, createdBy)'
-                            'values ("Dummy", "Dummy", 1, "", "Franzi")')
-
-        self.cursor.executemany("""
-        INSERT INTO "main"."testStep" ("ID", "name", "sequenceNumber", "description", "locatorTypeID", "locator", "activityTypeID", "value", "testStepSequenceID", "created", "createdBy", "changed", "changedBy") VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?)
-        """, [('1', 'OpenURL', '1', 'Open EarthSquad Drops', '', '', '1', 'https://drops.earthsquad.global', '1', '', 'Franzi', '', ''),
-                ('2', 'ENTER Username', '2', 'Enter Username', '2', '(//input[@step=''any''])[1]', '2', 'test12', '1', '', 'Franzi', '', ''),
-                ('3', 'Enter Password', '3', 'Enter the password', '2', '(//input[contains(@step,''any'')])[2]', '2', 'franzi12', '1', '', 'Franzi', '', ''),
-                ('4', 'Submit', '4', 'Click Submit', '2', '//div[@class=''q-btn-inner row col items-center justify-center''][contains(.,''(//div[contains(@class,"q-btn-inner row col items-center justify-center")])[5]'')]', '3', '', '1', ' ', 'Franzi', '', '')
-                ])
-
-        self.cursor.execute("""INSERT INTO "main"."testDataInput" 
-                            ("name", "description", "path", "pathIsRelative", "tabName", "created", "createdBy", "changed", "changedBy") 
-                            VALUES ('EarthSquad Test', 'Testfile for Earthsquad', '/earthsquad.xlsx', 'True', 'testdata', '', 'Franzi', '', '');""")
-
-        self.cursor.execute("""INSERT INTO "main"."testDataInput" ("name", "description", "path", "pathIsRelative", "tabName", "created", "createdBy", "changed", "changedBy")
-                            VALUES ('EarthSquad Test', 'Testfile for Earthsquad', '/earthsquad.xlsx', 'True', 'testdata2', '', 'Franzi', '', '');
-        """)
-
-class TestRunDatabaseCreate:
-    """
-    Initializes the Database and loads some sample data
-    """
-    def __init__(self, dbConnection=None):
-        if dbConnection:
-            self.dbCon = dbConnection
-        else:
-            self.dbCon = sqlite3.connect("test.db")
-        self.cursor = self.dbCon.cursor()
-        self.createStructures()
-
-    def getCursor(self):
-        return self.cursor
-
-    def commit(self):
-        self.dbCon.commit()
-
-    def createStructures(self):
-        self.createExectionMethods()
-        self.createActivityTypes()
-        self.createLocatorTypes()
-        self.createTestRun()
-        self.createTestCaseSequence()
-        self.createTestDataDefinition()
-        self.createTestCase()
-        self.createTestCase2TestData()
-        self.createValidator()
-        self.createTestStepSequence()
-        self.createTestStep()
-        pass
-
-    def createMasterClasses(self):
-        self.cursor.execute("""
-        CREATE TABLE if not exists masterClasses (
-            ID integer PRIMARY KEY,
-            name text NOT NULL,
-            description text NOT NULL,
-            CustomBrowserHandling text NOT NULL,
-            CustomTestStep text NOT NULL,
-            CustomGlobalConstants text,
-            created text NOT NULL,
-            createdBy text NOT NULL,
-            changed text,
-            changedBy text
-        )
-        """)
-
-    def createExectionMethods(self):
-        self.cursor.execute("""
-        CREATE TABLE if not exists executionMethod (
-            ID integer PRIMARY KEY,
-            name text NOT NULL,
-            description text NOT NULL,
-            created text NOT NULL,
-            createdBy text NOT NULL,
-            changed text,
-            changedBy text
-        )""")
-
-    def createTestRun(self):
-        self.cursor.execute("""
-        CREATE TABLE if not exists testrun (
-            ID integer PRIMARY KEY,
-            name text NOT NULL,
-            description text NOT NULL,
-            created text NOT NULL,
-            createdBy text NOT NULL,
-            changed text,
-            changedBy text
-        )""")
-
-    def createTestCaseSequence(self):
-        self.cursor.execute("""
-        CREATE TABLE if not exists testCaseSequence (
-            ID integer PRIMARY KEY,
-            name text NOT NULL,
-            description text NOT NULL,
-            testrunID integer,
-            created text NOT NULL,
-            createdBy text NOT NULL,
-            changed text,
-            changedBy text,
-            FOREIGN KEY (testrunID) REFERENCES testrun (ID)
-        )        
-        """)
-
-    def createTestCase(self):
-        # Method_parameters = "FF", "CHROME", etc.
-        self.cursor.execute("""
-        CREATE TABLE if not exists testCase (
-            ID integer PRIMARY KEY,
-            name text NOT NULL,
-            description text NOT NULL,
-            testCaseSequenceID integer,
-            executionMethodID integer NOT NULL,
-            methodParameters text NOT NULL, 
-            created text NOT NULL,
-            createdBy text NOT NULL,
-            changed text,
-            changedBy text,
-            FOREIGN KEY (testCaseSequenceID) REFERENCES testCaseSequence (ID),
-            FOREIGN KEY (executionMethodID) REFERENCES executionMethod (ID)
-        )        
-        """)
-
-    def createTestDataDefinition(self):
-        self.cursor.execute("""
-         CREATE TABLE if not exists testDataInput (
-            ID integer PRIMARY KEY,
-            name text NOT NULL,
-            description text NOT NULL,
-            path text,
-            pathIsRelative boolean,
-            tabName text,
-            created text NOT NULL,
-            createdBy text NOT NULL,
-            changed text,
-            changedBy text
-        )               
-        """)
-
-    def createTestCase2TestData(self):
-        self.cursor.execute("""
-          CREATE TABLE if not exists testCaseData (
-            ID integer PRIMARY KEY,
-            name text NOT NULL,
-            testDataInputID integer,
-            testCaseID integer,
-            created text NOT NULL,
-            createdBy text NOT NULL,
-            changed text ,
-            changedBy text ,
-            FOREIGN KEY (testDataInputID) REFERENCES testDataInput (ID),
-            FOREIGN KEY (testCaseID) REFERENCES testCase (ID)
-        )         
-        """)
-
-    def createTestStepSequence(self):
-        self.cursor.execute("""
-          CREATE TABLE if not exists testStepSequence (
-            ID integer PRIMARY KEY,
-            name text NOT NULL,
-            description text NOT NULL,
-            testCaseID integer,
-            created text NOT NULL,
-            createdBy text NOT NULL,
-            changed text ,
-            changedBy text ,
-            FOREIGN KEY (testCaseID) REFERENCES testCase (ID)
-        )         
-        """)
-
-    def createLocatorTypes(self):
-        self.cursor.execute("""
-          CREATE TABLE if not exists locatorTypes (
-            ID integer PRIMARY KEY,
-            name text NOT NULL,
-            description text NOT NULL,
-            created text NOT NULL,
-            createdBy text NOT NULL,
-            changed text ,
-            changedBy text
-        )         
-        """)
-
-    def createActivityTypes(self):
-        self.cursor.execute("""
-          CREATE TABLE if not exists activityTypes (
-            ID integer PRIMARY KEY,
-            name text NOT NULL,
-            description text NOT NULL,
-            created text NOT NULL,
-            createdBy text NOT NULL,
-            changed text ,
-            changedBy text
-        )         
-        """)
-
-    def createTestStep(self):
-        self.cursor.execute("""
-          CREATE TABLE if not exists testStep (
-            ID integer PRIMARY KEY,
-            name text NOT NULL,
-            sequenceNumber int NOT NULL,
-            description text NOT NULL,
-            locatorTypeID integer,
-            locator text ,
-            activityTypeID integer NOT NULL,
-            value text,
-            testStepSequenceID integer,
-            created text NOT NULL,
-            createdBy text NOT NULL,
-            changed text ,
-            changedBy text ,
-            FOREIGN KEY (testStepSequenceID) REFERENCES testStepSequence (ID),
-            FOREIGN KEY (locatorTypeID) REFERENCES locatorTypes (ID),
-            FOREIGN KEY (activityTypeID) REFERENCES activityTypes (ID)
-        )         
-        """)
-
-    def createValidator(self):
-        self.cursor.execute("""
-        CREATE TABLE if not exists testValidator (
-            ID integer PRIMARY KEY,
-            name text NOT NULL,
-            validatorType text NOT NULL,
-            locatorType text NOT NULL,
-            locator text NOT NULL,
-            compareValue text NOT NULL,
-            compareOption text NOT NULL,
-            created text NOT NULL,
-            createdBy text NOT NULL,
-            changed text ,
-            changedBy text 
-        ) 
-        """)
-
-if __name__ == '__main__':
-    lDb = TestRunDatabaseCreate()
-    lCursor = lDb.getCursor()
-    lInsert = TestRunDatabaseFill(lCursor)
-    lDb.commit()

+ 0 - 243
baangt/base/TestRunExcelImporter.py

@@ -1,243 +0,0 @@
-from baangt.base.Utils import utils
-from baangt.base.TestRunUtils import TestRunUtils
-import baangt.base.GlobalConstants as GC
-import baangt.base.CustGlobalConstants as CGC
-import xlrd
-import logging
-
-logger = logging.getLogger("pyC")
-
-
-class TestRunExcelImporter:
-    """
-    The TestrunSettings are in class TestRunUtils and expected to be a deep dict. For details see documentation there.
-
-    This class will migrate data from an excel sheet (either simple format with only 1 tab or complex format with all
-    structural elements) into the deep dict.
-
-    In case the XLSX is simple format, all missing data is "predicted"/assumed.
-
-    """
-    def __init__(self, FileNameAndPath, testRunUtils: TestRunUtils):
-        self.testStepExecutionNumber = 0
-        # self.testRunAttributes = testRunUtils.testRunAttributes
-        self.testRunUtils = testRunUtils
-
-        try:
-            self.excelFile = xlrd.open_workbook(FileNameAndPath)
-        except FileNotFoundError as e:
-            raise BaseException(f"File not found - exiting {e}")
-        self.fileName = utils.extractFileNameFromFullPath(FileNameAndPath)
-
-    def importConfig(self, global_settings):
-        self._initTestRun(global_settings)
-        return self.testRunUtils.getCompleteTestRunAttributes(self.fileName)
-
-    def _initTestRun(self, global_settings):
-        """
-        Writes a new entry to testRunUtils --> = this current TestRunDefinition (Filename)
-        Then loops through the tabs "TestRun", "ExportFieldList", "TestCaseSequence" and so on,
-        reads the lines/columns and writes them the the deep dict of testRunUtils accordingly (TestCaseSequence,
-        TestCase, TestStepSequence, TestSteps, etc.)
-        @return:
-        """
-        self.testRunUtils.testRunAttributes = \
-            {
-                self.fileName: {
-                    GC.KWARGS_TESTRUNATTRIBUTES: {
-                    },
-                },
-            }
-        testRunAttributes = self.testRunUtils.testRunAttributes[self.fileName][GC.KWARGS_TESTRUNATTRIBUTES]
-
-        xlsTab = self._getTab("TestRun")
-        if xlsTab:
-            testRunAttributes[GC.EXPORT_FORMAT] = \
-                {GC.EXPORT_FORMAT: self._getValueFromList(xlsTab=xlsTab,
-                                                          searchField=GC.EXPORT_FORMAT)}
-        else:
-            # Default Value
-            testRunAttributes[GC.EXPORT_FORMAT] = {GC.EXPORT_FORMAT: GC.EXP_XLSX}
-
-        xlsTab = self._getTab("ExportFieldList")
-        if xlsTab:
-            testRunAttributes[GC.EXPORT_FORMAT][GC.EXP_FIELDLIST] = self._getRowAsList(xlsTab)
-        else:
-            # Default Value as there are no explicit values from the TestRunDefinition:
-            testRunAttributes[GC.EXPORT_FORMAT][GC.EXP_FIELDLIST] = [GC.TESTCASESTATUS,
-                                                                     GC.TIMING_DURATION,
-                                                                     GC.TIMELOG]
-
-        testRunAttributes[GC.STRUCTURE_TESTCASESEQUENCE] = {}
-        testrunSequence = testRunAttributes[GC.STRUCTURE_TESTCASESEQUENCE]
-
-        xlsTab = self._getTab("TestCaseSequence")
-        if xlsTab:
-            lSequenceDict = self.getRowsWithHeadersAsDict(xlsTab=xlsTab)
-        else:
-            lSequenceDict = {1:
-                {
-                "SequenceClass": GC.CLASSES_TESTCASESEQUENCE,
-                "TestDataFileName": self.fileName,
-                "Sheetname": "data",
-                "ParallelRuns": 1,
-                "FromLine": 0,
-                "ToLine": 999999
-                },
-            }
-        for key, sequence in lSequenceDict.items():
-            testrunSequence[key] = [sequence["SequenceClass"], {}]
-            for field, value in sequence.items():
-                testRunAttributes[GC.STRUCTURE_TESTCASESEQUENCE][key][1][field] = value
-
-
-        xlsTab = self._getTab("TestCase")
-        # if Tab "TestCase" exists, then take the definitions from there. Otherwise (means simpleFormat)
-        # we need to create "dummy" data ourselves.
-        if xlsTab:
-            lTestCaseDict = self.getRowsWithHeadersAsDict(xlsTab=xlsTab)
-        else:
-            # Dirty, but not possible in any other way in API-Simple-Format:
-            if "API" in self.fileName.upper():
-                    lTestCaseDict = {1: {"TestCaseSequenceNumber": 1,
-                                     "TestCaseNumber": 1,
-                                     "TestCaseType": GC.KWARGS_API_SESSION,
-                                     "TestCaseClass": GC.CLASSES_TESTCASE}}
-            else:
-                if global_settings['TC.Mobile'] == 'True':
-                    lTestCaseDict = {1: {"TestCaseSequenceNumber": 1,
-                                         "TestCaseNumber": 1,
-                                         "TestCaseType": GC.KWARGS_BROWSER,
-                                         "TestCaseClass": GC.CLASSES_TESTCASE,
-                                         GC.KWARGS_BROWSER: GC.BROWSER_FIREFOX,
-                                         GC.BROWSER_ATTRIBUTES: "",
-                                         GC.KWARGS_MOBILE: ""}}
-                else:
-                    lTestCaseDict = {1: {"TestCaseSequenceNumber": 1,
-                                     "TestCaseNumber": 1,
-                                     "TestCaseType": GC.KWARGS_BROWSER,
-                                     "TestCaseClass": GC.CLASSES_TESTCASE,
-                                     GC.KWARGS_BROWSER: GC.BROWSER_FIREFOX,
-                                     GC.BROWSER_ATTRIBUTES: ""}}
-        for key, testCase in lTestCaseDict.items():
-            testSequenceRoot = testrunSequence[testCase["TestCaseSequenceNumber"]][1]
-            testSequenceRoot[GC.STRUCTURE_TESTCASE] = {testCase["TestCaseNumber"]: []}
-            testSequenceRoot[GC.STRUCTURE_TESTCASE][testCase["TestCaseNumber"]].append(testCase["TestCaseClass"])
-            testSequenceRoot[GC.STRUCTURE_TESTCASE][testCase["TestCaseNumber"]].append(
-                {"TestCaseType": testCase["TestCaseType"],
-                 GC.KWARGS_BROWSER: testCase.get("Browser"),
-                 GC.BROWSER_ATTRIBUTES: testCase.get("BrowserAttributes"),
-                 GC.KWARGS_MOBILE: testCase.get("Mobile")
-                 })
-
-        xlsTab = self._getTab("TestStep")
-        if xlsTab:
-            lStepDict = self.getRowsWithHeadersAsDict(xlsTab=xlsTab)
-        else:
-            lStepDict = {1: {"TestCaseSequenceNumber": 1,
-                         "TestCaseNumber": 1,
-                         "TestStepNumber": 1,
-                         "TestStepClass": GC.CLASSES_TESTSTEPMASTER}
-                         }
-        for key, testStep in lStepDict.items():
-            testStepRoot = testrunSequence[testStep["TestCaseSequenceNumber"]][1]
-            testStepRoot = testStepRoot[GC.STRUCTURE_TESTCASE][testStep["TestCaseNumber"]]
-            if len(testStepRoot) == 2:
-                testStepRoot.append({GC.STRUCTURE_TESTSTEP: {}})
-
-            testStepRoot = testStepRoot[2][GC.STRUCTURE_TESTSTEP]
-            testStepRoot[testStep["TestStepNumber"]] = testStep["TestStepClass"]
-
-
-        xlsTab = self._getTab("TestStepExecution")
-        lExecDict = self.getRowsWithHeadersAsDict(xlsTab=xlsTab)
-        for key, execLine in lExecDict.items():
-            lSequence = self.testRunUtils.getSequenceByNumber(testRunName=self.fileName,
-                                                              sequence=execLine.get("TestCaseSequenceNumber",1))
-            lTestCase = self.testRunUtils.getTestCaseByNumber(sequence=lSequence,
-                                                              testcaseNumber=execLine.get("TestCaseNumber",1))
-            lTestStep = self.testRunUtils.getTestStepByNumber(testCase=lTestCase,
-                                                              testStepNumber=execLine.get("TestStepNumber",1))
-            # if this TestStep is still just a String, then it needs to be converted into a List with Dict
-            # to hold the TestexecutionSteps in this Dict.
-            if isinstance(lTestStep, str):
-                lTestCase[2][GC.STRUCTURE_TESTSTEP][execLine.get("TestStepNumber",1)] = \
-                    [lTestStep, {GC.STRUCTURE_TESTSTEPEXECUTION: {}}]
-                lTestStep = self.testRunUtils.getTestStepByNumber(testCase=lTestCase,
-                                                                  testStepNumber=execLine.get("TestStepNumber",1))
-
-            lStepExecutionNumber = self.__iterateStepExecutionNumber(execLine.get("TestStepExecutionNumber"))
-            lTestStep[1][GC.STRUCTURE_TESTSTEPEXECUTION][lStepExecutionNumber] = {}
-            for lkey, value in execLine.items():
-                lTestStep[1][GC.STRUCTURE_TESTSTEPEXECUTION][lStepExecutionNumber][lkey] = value
-
-    def __iterateStepExecutionNumber(self, numberFromDefinition):
-        if numberFromDefinition:
-            return numberFromDefinition
-
-        # This is a teststep in simple format, without an execution number column in the XLS
-        # we need to iterate ourselves and simply add 1 to each new TestStep
-        self.testStepExecutionNumber += 1
-        return self.testStepExecutionNumber
-
-    def getRowsWithHeadersAsDict(self, xlsTab):
-        lRetDict = {}
-
-        for row in range(1, xlsTab.nrows):
-            lRetDict[row] = {}
-            for col in range(0, xlsTab.ncols):
-                lRetDict[row][xlsTab.cell_value(0, col)] = self.replaceFieldValueWithValueOfConstant(
-                    xlsTab.cell_value(row, col))
-        return lRetDict
-
-    def _getRowAsList(self, xlsTab, colNumber=0):
-        l_fields = []
-        for row in range(1, xlsTab.nrows):
-            l_fields.append(self.replaceFieldValueWithValueOfConstant(xlsTab.cell_value(row, colNumber)))
-        return l_fields
-
-    def _getValueFromList(self, xlsTab, searchField):
-        # Searches for Value in Column 1 and returns Value from Column 2 if found.
-        # This is used for XLSX-Tabs with this format:
-        # column:value
-        # TestlineStart:10
-        # TestlineEnd:20
-        # foo:bar
-        for row in range(1, xlsTab.nrows):
-            if xlsTab.cell_value(row, 1) == searchField:
-                return self.replaceFieldValueWithValueOfConstant(xlsTab.cell_value(row, 2))
-
-    def _getTab(self, tabName):
-        try:
-            worksheet = self.excelFile.sheet_by_name(tabName)
-        except Exception as e:
-            return None
-        return worksheet
-
-    def replaceFieldValueWithValueOfConstant(self, value):
-        """
-        baangt Global constants (baangt.base.GlobalConstants) are available everywhere as GC., e.g. the variable
-        "BROWSER" defined in GlobalConstants can be accessed from everywhere within baangt by using "GC.BROWSER".
-
-        The variables can also be used in configuration files (would be very stupid if we need to change a constant and
-        then 100s of Config-Files need adjustment - worst case the testruns behave unexepected). This applies both to
-        XLSX and JSON Config files.
-
-        The CGC.-Part is still needs fixing. It shouldn't appear in Baangt base, but currently still needed.
-
-        @param value: potentially convertable value (e.g. GC.BROWSER)
-        @return: potentially converted value (e.g. "Browser")
-        """
-        if isinstance(value, float):
-            if value % 1 == 0:
-                return int(value)
-        elif isinstance(value, int):
-            return value
-
-        value = value.strip()
-        value = utils.replaceFieldValueWithValueOfConstant(value)
-
-        if value[0:5] == "CGC.":
-            value = getattr(globals()[value.split(".")[0]], value.split(".")[1])
-
-        return value

+ 0 - 48
baangt/base/TestRunUtils.py

@@ -1,48 +0,0 @@
-import baangt.base.GlobalConstants as GC
-import logging
-
-logger = logging.getLogger("pyC")
-
-class TestRunUtils():
-    def __init__(self):
-        self.testRunAttributes = {}
-
-    def setCompleteTestRunAttributes(self, testRunName:str, testRunAttributes: dict):
-        self.testRunAttributes[testRunName] = testRunAttributes
-
-    def getCompleteTestRunAttributes(self, testRunName):
-        logger.info('get into getCompleteTestRunAttributes, testRunName is {}'.format(testRunName))
-        return self.testRunAttributes[testRunName][GC.KWARGS_TESTRUNATTRIBUTES]
-
-    def getSequenceByNumber(self, sequence, testRunName):
-        return self.testRunAttributes[testRunName][GC.KWARGS_TESTRUNATTRIBUTES][GC.STRUCTURE_TESTCASESEQUENCE].get(sequence)
-
-    def getTestCaseByNumber(self, sequence, testcaseNumber):
-        return sequence[1][GC.STRUCTURE_TESTCASE][testcaseNumber]
-
-    def getTestStepByNumber(self, testCase, testStepNumber):
-        return testCase[2][GC.STRUCTURE_TESTSTEP].get(testStepNumber)
-
-    def replaceGlobals(self, globals):
-        """
-        Will go through all testcase-Settings and replace values with values from global settings, if matched
-        """
-        for key, value in globals.items():
-            if not "TC." in key[0:3] or len(value) == 0:
-                continue
-            self.testRunAttributes = TestRunUtils._recursive_replace(self.testRunAttributes, key.replace("TC.",""), value)
-
-    @staticmethod
-    def _recursive_replace(dictToBeReplaced, lKey, lValue):
-        if isinstance(dictToBeReplaced, list):
-            for entry in dictToBeReplaced:
-                TestRunUtils._recursive_replace(entry, lKey, lValue)
-        elif isinstance(dictToBeReplaced, dict):
-            if lKey in dictToBeReplaced:
-                logger.info(f"Due to Globals replaced value {dictToBeReplaced[lKey]} of {lKey} with value {lValue}")
-                dictToBeReplaced[lKey] = lValue
-            for k,v in dictToBeReplaced.items():
-                TestRunUtils._recursive_replace(v, lKey, lValue)
-        else:
-            pass
-        return dictToBeReplaced

+ 0 - 73
baangt/base/Timing/Timing.py

@@ -1,73 +0,0 @@
-from baangt.base import GlobalConstants as GC
-from datetime import timedelta
-import time
-import logging
-
-logger = logging.getLogger("pyC")
-
-class Timing:
-    def __init__(self):
-        self.timing = {}
-        self.currentTimingSection = None
-
-    def takeTime(self, timingName, forceNew=False):
-        if forceNew and timingName in self.timing.keys():
-            for x in range(0,1000000):
-                if not timingName + "_" + str(x) in self.timing.keys():
-                    timingName = timingName + "_" + str(x)
-                    break
-        if timingName in self.timing:
-            self.timing[timingName][GC.TIMING_END] = time.time()
-            return str(timedelta(seconds=self.timing[timingName][GC.TIMING_END] - self.timing[timingName][GC.TIMING_START]))
-        else:
-            self.timing[timingName] = {}
-            self.timing[timingName][GC.TIMING_START] = time.time()
-            self.currentTimingSection = timingName
-        return timingName
-
-    def addAttribute(self, attribute, value, timingSection=None):
-        if not timingSection:
-            lSection = self.currentTimingSection
-        else:
-            lSection = timingSection
-
-        self.timing[lSection][attribute]=value
-
-    def takeTimeSumOutput(self):
-        logger.info("Timing follows:")
-        for key, value in self.timing.items():
-            if "end" in value.keys():
-                logger.info(f'{key} : {Timing.__format_time(value)}')
-
-    def returnTime(self):
-        timingString = ""
-        for key,value in self.timing.items():
-            if GC.TIMING_END in value.keys():
-                timingString = timingString + "\n" + f'{key}: , since last call: ' \
-                                                     f'{Timing.__format_time(value)}'
-                if "timestamp" in value.keys():
-                    timingString = timingString + ", TS:" + str(value[GC.TIMESTAMP])
-        return timingString
-
-    def returnTimeSegment(self, segment):
-        if self.timing.get(segment):
-            lStart =  time.strftime("%H:%M:%S", time.localtime(self.timing[segment][GC.TIMING_START]))
-            lEnd = time.strftime("%H:%M:%S", time.localtime(self.timing[segment][GC.TIMING_END]))
-            lDuration = self.__format_time(self.timing[segment])
-            return lStart, lEnd, lDuration
-        return None
-
-    def resetTime(self):
-        if GC.TIMING_TESTRUN in self.timing:
-            buffer = self.timing.get(GC.TIMING_TESTRUN)
-            self.timing = {GC.TIMING_TESTRUN: buffer}
-        else:
-            self.timing = {}
-
-    @staticmethod
-    def __format_time(startAndEndTimeAsDict):
-        return time.strftime("%H:%M:%S", time.gmtime(startAndEndTimeAsDict[GC.TIMING_END] -
-                                                        startAndEndTimeAsDict[GC.TIMING_START]))
-
-if __name__ == '__main__':
-    test = Timing()

+ 0 - 0
baangt/base/Timing/__init__.py


+ 0 - 43
baangt/base/Timing/hookImpls.py

@@ -1,43 +0,0 @@
-import baangt
-
-from baangt.base.Timing.Timing import Timing
-
-import logging
-
-logger = logging.getLogger("pyC")
-
-
-class TimingHookImpl:
-    
-    @baangt.hook_impl
-    def timing_init(self):
-        return Timing()
-
-    @baangt.hook_impl
-    def timing_takeTime(self, timingObject, timingName, forceNew=False):
-        return timingObject.takeTime(timingName, forceNew)
-
-    @baangt.hook_impl
-    def timing_addAttribute(self, timingObject, attribute, value, timingSection=None):
-        return timingObject.addAttribute(attribute, value, timingSection)
-
-    @baangt.hook_impl
-    def timing_takeTimeSumOutput(self, timingObject):
-        return timingObject.takeTimeSumOutput()
-
-    @baangt.hook_impl
-    def timing_returnTime(self, timingObject):
-        return timingObject.returnTime()
-
-    @baangt.hook_impl
-    def timing_returnTimeSegment(self, timingObject, segment):
-        return timingObject.returnTimeSegment(segment)
-
-    @baangt.hook_impl
-    def timing_resetTime(self, timingObject):
-        return timingObject.resetTime()
-
-
-
-
-

+ 0 - 134
baangt/base/Utils.py

@@ -1,134 +0,0 @@
-from datetime import datetime
-import baangt.base.GlobalConstants as GC
-import baangt.base.CustGlobalConstants as CGC
-import ntpath
-import logging
-import json
-import sys
-from pathlib import Path
-
-logger = logging.getLogger("pyC")
-
-
-class utils:
-    def __init__(self):
-        self.__perf_trace = {}
-
-    @staticmethod
-    def datetime_return():
-        # needed, so that the datetime-module is called newly
-        t = datetime.now().strftime("%Y%m%d_%H%M%S")
-        return t
-
-    @staticmethod
-    def extractFileNameFromFullPath(fileAndPathName):
-        return ntpath.basename(fileAndPathName)
-        pass
-
-    @staticmethod
-    def sanitizeFileName(value):
-        value = value.replace("'", "")
-        value = value.replace('"', "")
-
-        return value
-
-    @staticmethod
-    def replaceFieldValueWithValueOfConstant(value):
-        """
-        If a String reference to global Constant (e.g. GC.BROWSER_FF) is
-        given, this function will replace it with the actual value (e.g. FIREFOX)
-        """
-        if value[0:3] == "GC." or value[0:4] == 'CGC.':
-            if value[0:3] == 'GC.':
-                try:
-                    value = getattr(globals()[value.split(".")[0]], value.split(".")[1])
-                except Exception as e:
-                    logger.warning(f"Referenced variable doesn't exist: {value}")
-            elif value[0:4] == 'CGC.':
-                value = getattr(globals()[value.split(".")[0]], value.split(".")[1])
-        return value
-
-    @staticmethod
-    def replaceAllGlobalConstantsInDict(lDict: dict):
-        lDictOut = {}
-        for key, value in lDict.items():
-            lKey = utils.replaceFieldValueWithValueOfConstant(key)
-            if isinstance(value, str):
-                lDictOut[lKey] = utils.replaceFieldValueWithValueOfConstant(value)
-            elif isinstance(value, dict):
-                lDictOut[lKey] = utils.replaceAllGlobalConstantsInDict(value)
-            elif isinstance(value, list):
-                lDictOut[lKey] = utils._loopList(value)
-            else:
-                lDictOut[lKey] = value
-
-        return lDictOut
-
-    @staticmethod
-    def _loopList(listIn):
-        listOut = []
-        for item in listIn:
-            if isinstance(item, str):
-                item = utils.replaceFieldValueWithValueOfConstant(item)
-            elif isinstance(item, dict):
-                item = utils.replaceAllGlobalConstantsInDict(item)
-            elif isinstance(item, list):
-                item = utils._loopList(item)
-            listOut.append(item)
-        return listOut
-
-    @staticmethod
-    def openJson(fileNameAndPath):
-        logger.info(f"Reading Definition from {fileNameAndPath}")
-        data = None
-        fileNameAndPath = utils.findFileAndPathFromPath(fileNameAndPath)
-
-        with open(fileNameAndPath) as json_file:
-            data = json.load(json_file)
-        return data
-
-    @staticmethod
-    def findFileAndPathFromPath(fileNameAndPath, basePath=None):
-        """
-        Tries different approaches to locate a file
-        lBasePath = the Path where the script is run
-
-        @param fileNameAndPath: Filename and potentially relative path
-        @param basePath (optional): Optional basePath to look at
-        @return:
-        """
-        lFileNameAndPath = fileNameAndPath
-        if basePath:
-            lBasePath = Path(basePath)
-            if "~" in str(lBasePath):
-                lBasePath = lBasePath.expanduser()
-        else:
-            lBasePath = Path(sys.argv[0]).parent    # Works in Windows
-            logger.debug(f"Main Path to search for files: {lBasePath}")
-            if len(str(lBasePath)) < 3:
-                # Most probaby we're in pyinstaller. Let's try to find executable path
-                lBasePath = Path(sys.executable).parent
-                logger.debug(f"New Main Path to search for files: {lBasePath}")
-
-        if not Path(lFileNameAndPath).exists():
-            if "~" in lFileNameAndPath:
-                lFileNameAndPath = Path(lFileNameAndPath).expanduser()
-            elif Path(lBasePath).joinpath(fileNameAndPath).exists():
-                lFileNameAndPath = Path(lBasePath).joinpath(lFileNameAndPath)
-                logger.debug(f"Found file via BasePath {str(lFileNameAndPath)}")
-            elif len(Path(lFileNameAndPath).parents) == 0:
-                # This is only the filename. Try with current path and a bit up
-                if Path(utils.__file__).joinpath(lFileNameAndPath).exists:
-                    lFileNameAndPath = Path(utils.__file__).joinpath(lFileNameAndPath)
-                elif Path(utils.__file__).parent.joinpath(lFileNameAndPath).exists:
-                    lFileNameAndPath = Path(utils.__file__).parent.joinpath(lFileNameAndPath)
-                elif Path(utils.__file__).parent.parent.joinpath(lFileNameAndPath).exists:
-                    lFileNameAndPath = Path(utils.__file__).parent.parent.joinpath(lFileNameAndPath)
-                else:
-                    raise Exception(f"Can't find file {fileNameAndPath}")
-            else:
-                raise Exception(f"Can't find file {fileNameAndPath}")
-        else:
-            lFileNameAndPath = Path(lFileNameAndPath)
-
-        return str(lFileNameAndPath.absolute())

+ 0 - 0
baangt/base/__init__.py


BIN
baangt/chromedriver.exe


+ 0 - 409
baangt/hookSpecs.py

@@ -1,409 +0,0 @@
-from baangt import hook_spec
-from baangt.base import GlobalConstants as GC
-
-# -------------------------------------------------------------------
-
-# correspond to baangt/base/TestRun
-
-# -------------------------------------------------------------------
-
-
-class baangtHookSpec(object):
-
-    @hook_spec
-    def testRun_init(self, testRunName, globalSettingsFileNameAndPath):
-        pass
-
-
-    @hook_spec
-    def testRun_tearDown(self, testRunObject):
-        pass
-
-
-    @hook_spec
-    def testRun_getSuccessAndError(self, testRunObject):
-        pass
-
-
-    @hook_spec
-    def testRun_getAllTestRunAttributes(self, testRunObject):
-        pass
-
-
-    @hook_spec
-    def testRun_getBrowser(self, testRunObject, browserInstance=1, browserName=None, browserAttributes=None):
-        pass
-
-
-    @hook_spec
-    def testRun__getBrowserInstance(self, testRunObject, browserInstance):
-        pass
-
-
-    @hook_spec
-    def testRun_getAPI(self, testRunObject):
-        pass
-
-
-    @hook_spec
-    def testRun_setResult(self, testRunObject, recordNumber, dataRecordResult):
-        pass
-
-
-    @hook_spec
-    def testRun_executeTestRun(self, testRunObject):
-        pass
-
-
-    @hook_spec
-    def testRun_executeDictSequenceOfClasses(self, testRunObject, dictSequenceOfClasses, counterName, **kwargs):
-        pass
-
-
-    @hook_spec
-    def testRun__initTestRun(self, testRunObject):
-        pass
-
-
-    @hook_spec
-    def testRun_loadJSONGlobals(self, testRunObject):
-        pass
-
-
-    @hook_spec
-    def testRun__loadJSONTestRunDefinitions(self, testRunObject):
-        pass
-
-
-    @hook_spec
-    def testRun__loadExcelTestRunDefinitions(self, testRunObject):
-        pass
-
-
-    @hook_spec
-    def testRun___dynamicImportClasses(self, fullQualifiedImportName):
-        pass
-
-
-    @hook_spec
-    def testRun__sanitizeTestRunNameAndFileName(self, TestRunNameInput):
-        pass
-
-    # -------------------------------------------------------------------
-
-    # correspond to baangt/base/Timing
-
-    # -------------------------------------------------------------------
-
-    @hook_spec
-    def timing_init(self):
-        pass
-
-    @hook_spec
-    def timing_takeTime(self, timingObject, timingName, forceNew=False):
-        pass
-
-    @hook_spec
-    def timing_addAttribute(self, timingObject, attribute, value, timingSection=None):
-        pass
-
-    @hook_spec
-    def timing_takeTimeSumOutput(self, timingObject):
-        pass
-
-    @hook_spec
-    def timing_returnTime(self, timingObject):
-        pass
-
-    @hook_spec
-    def timing_returnTimeSegment(self, timingObject, segment):
-        pass
-
-    @hook_spec
-    def timing_resetTime(self, timingObject):
-        pass
-
-    @hook_spec
-    def timing___format_time(self, startAndEndTimeAsDict):
-        pass
-
-    # -------------------------------------------------------------------
-
-    # correspond to baangt/base/ExportResults
-
-    # -------------------------------------------------------------------
-
-    # ExportResults
-
-    # -------------------------------------------------------------------
-    @hook_spec
-    def exportResults_init(self,  **kwargs):
-        pass
-
-
-    @hook_spec
-    def exportResults_exportToDataBase(self, exportResultsObject):
-        pass
-
-
-    @hook_spec
-    def exportResults_exportResult(self, exportResultsObject, **kwargs):
-        pass
-
-
-    @hook_spec
-    def exportResults_makeSummary(self, exportResultsObject):
-        pass
-
-
-    @hook_spec
-    def exportResults___writeSummaryCell(self, exportResultsObject, lineHeader, lineText, row=None, format=None):
-        pass
-
-
-    @hook_spec
-    def exportResults___getOutputFileName(self, exportResultsObject):
-        pass
-
-
-    @hook_spec
-    def exportResults___setHeaderDetailSheet(self, exportResultsObject):
-        pass
-
-
-    @hook_spec
-    def exportResults__exportData(self, exportResultsObject):
-        pass
-
-
-    @hook_spec
-    def exportResults___writeCell(self, exportResultsObject, line, cellNumber, testRecordDict, fieldName, strip=False):
-        pass
-
-
-    @hook_spec
-    def exportResults_closeExcel(self, exportResultsObject, line, cellNumber, testRecordDict, fieldName, strip=False):
-        pass
-
-    # -------------------------------------------------------------------
-
-    # correspond to baangt/base/ExportResults
-
-    # -------------------------------------------------------------------
-
-    # ExcelSheetHelperFunctions
-
-    # -------------------------------------------------------------------
-    @hook_spec
-    def excelSheetHelperFunctions_init(self):
-        pass
-
-
-    @hook_spec
-    def excelSheetHelperFunctions_set_column_autowidth(self, excelSheetHelperFunctionsObject, worksheet, column):
-        pass
-
-
-    @hook_spec
-    def excelSheetHelperFunctions_get_column_width(self, excelSheetHelperFunctionsObject, worksheet, column):
-        pass
-
-    # -------------------------------------------------------------------
-
-    # correspond to baangt/base/ExportResults
-
-    # -------------------------------------------------------------------
-
-    # ExportTiming
-
-    # -------------------------------------------------------------------
-
-
-    @hook_spec
-    def exportTiming_init(self, testdataRecords, sheet):
-        pass
-
-
-    @hook_spec
-    def exportTiming_writeHeader(self, exportTimingObject):
-        pass
-
-
-    @hook_spec
-    def exportTiming_writeLines(self, exportTimingObject):
-        pass
-
-
-    @hook_spec
-    def exportTiming_shortenTimingValue(self, exportTimingObject, timingValue):
-        pass
-
-
-    @hook_spec
-    def exportTiming_writeCell(self, exportTimingObject, row, col, content, format=None):
-        pass
-
-
-    @hook_spec
-    def exportTiming_findAllTimingSections(self, exportTimingObject):
-        pass
-
-
-    @hook_spec
-    def exportTiming_interpretTimeLog(self, exportTimingObject, lTimeLog):
-        pass
-
-
-    # -------------------------------------------------------------------
-
-    # correspond to baangt/base/BrowserDriver
-
-    # -------------------------------------------------------------------
-
-    @hook_spec
-    def browserDriver_init(self, timing=None, screenshotPath=None):
-        pass
-
-
-    @hook_spec
-    def browserDriver_createNewBrowser(self, browserDriverObject, browserName=GC.BROWSER_FIREFOX, desiredCapabilities=None, **kwargs):
-        pass
-
-
-    @hook_spec
-    def browserDriver___findBrowserDriverPaths(self, browserDriverObject, filename):
-        pass
-
-
-    @hook_spec
-    def browserDriver_slowExecutionToggle(self, browserDriverObject, newSlowExecutionWaitTimeInSeconds = None):
-        pass
-
-
-    @hook_spec
-    def browserDriver___createBrowserOptions(self, browserDriverObject, browserName, desiredCapabilities):
-        pass
-
-
-    @hook_spec
-    def browserDriver_closeBrowser(self, browserDriverObject):
-        pass
-
-
-    @hook_spec
-    def browserDriver__log(self, browserDriverObject, logType, logText, **kwargs):
-        pass
-
-
-    @hook_spec
-    def browserDriver_takeScreenshot(self, browserDriverObject, screenShotPath=None):
-        pass
-
-
-    @hook_spec
-    def browserDriver_handleIframe(self, browserDriverObject, iframe=None):
-        pass
-
-    @hook_spec
-    def browserDriver_handleWindow(self, browserDriverObject, windowNumber=None, function=None):
-        pass
-
-
-    @hook_spec
-    def browserDriver_findByAndWaitForValue(self, browserDriverObject, id=None, css=None, xpath=None, class_name=None, iframe=None, timeout=20,
-                                  optional=False):
-        pass
-
-
-    @hook_spec
-    def browserDriver_findByAndSetText(self, browserDriverObject, id=None, css=None, xpath=None, class_name=None, value=None, iframe=None,
-                             timeout=60, optional=False):
-        pass
-
-
-    @hook_spec
-    def browserDriver_findByAndSetTextIf(self, browserDriverObject, id=None, css=None, xpath=None, class_name=None, value=None, iframe=None,
-                               timeout=60):
-        pass
-
-
-    @hook_spec
-    def browserDriver_findByAndSetTextValidated(self, browserDriverObject,id = None,
-                           css = None,
-                           xpath = None,
-                           class_name = None,
-                           value = None,
-                           iframe = None,
-                           timeout = 60,
-                           retries = 5):
-        pass
-
-
-    @hook_spec
-    def browserDriver_submit(self, browserDriverObject):
-        pass
-
-
-    @hook_spec
-    def browserDriver_findByAndClick(self, browserDriverObject, id = None, css=None, xpath=None, class_name=None, iframe=None, timeout=20, optional=False):
-        pass
-
-
-    @hook_spec
-    def browserDriver_findByAndClickIf(self, browserDriverObject, id=None, css=None, xpath=None, class_name=None, iframe=None, timeout=60,
-                             value=None, optional=False):
-        pass
-
-
-    @hook_spec
-    def browserDriver_findByAndForceText(self, browserDriverObject, id=None, css=None, xpath=None, class_name=None, value=None,
-                               iframe=None, timeout=60, optional=False):
-        pass
-
-
-    @hook_spec
-    def browserDriver_findBy(self, browserDriverObject, id=None, css=None, xpath=None, class_name=None, iframe=None, timeout=60, loggingOn=True,
-                   optional=False):
-        pass
-
-
-    @hook_spec
-    def browserDriver_getURL(self, browserDriverObject):
-        pass
-
-
-    @hook_spec
-    def browserDriver___tryAndRetry(self, browserDriverObject, id=None, css=None, xpath=None, class_name=None, timeout=20):
-        pass
-
-
-    @hook_spec
-    def browserDriver_findWaitNotVisible(self, browserDriverObject, xpath=None, id=None, timeout = 90):
-        pass
-
-
-    @hook_spec
-    def browserDriver_sleep(self, browserDriverObject, sleepTimeinSeconds):
-        pass
-
-
-    @hook_spec
-    def browserDriver___doSomething(self, browserDriverObject, command, value=None, timeout=20, xpath=None, optional=False):
-        pass
-
-
-    @hook_spec
-    def browserDriver_goToUrl(self, browserDriverObject, url):
-        pass
-
-
-    @hook_spec
-    def browserDriver_goBack(self, browserDriverObject):
-        pass
-
-
-    @hook_spec
-    def browserDriver_javaScript(self, browserDriverObject, jsText):
-        pass
-
-

+ 0 - 0
baangt/katalonImporter/__init__.py


+ 0 - 493
baangt/katalonImporter/katalonImport.py

@@ -1,493 +0,0 @@
-import os
-import uuid
-import xlsxwriter
-import glob
-import logging
-import sys
-from bs4 import BeautifulSoup as bs4
-from pathlib import Path
-
-def setupLogger():
-    import logging
-    import os
-    from datetime import datetime
-
-    logPath = "/".join(os.path.dirname(os.path.realpath(__file__)).split('/')[0:-1]) + "/logs/"
-    Path(logPath).mkdir(parents=True, exist_ok=True)
-    logFilename = (logPath +
-                   datetime.now().strftime("%Y%m%d_%H%M%S") + '.log')
-    print(f"Logfile verwendet: {logFilename}")
-
-    # Bit more advanced logging:
-    logger = logging.getLogger('pyC')
-    logger.setLevel(logging.DEBUG)
-    # create file handler which logs even debug messages
-    fileHandler = logging.FileHandler(logFilename, encoding="UTF-8")
-    fileHandler.setLevel(level=logging.DEBUG)
-    # create console handler with a higher log level
-    channelHandler = logging.StreamHandler()
-    channelHandler.setLevel(logging.INFO)
-    # create formatter and add it to the handlers
-    formatter = logging.Formatter('%(asctime)s _ %(levelname)s _ %(module)s _ %(funcName)s : %(message)s')
-    channelHandler.setFormatter(formatter)
-    fileHandler.setFormatter(formatter)
-    # add the handlers to logger
-    logger.addHandler(channelHandler)
-    logger.addHandler(fileHandler)
-    return logger
-
-def readXMLFile(fileNameAndPath):
-    with open(fileNameAndPath, "r") as file:
-        content=file.readlines()
-        content = "".join(content)
-        fileAsDOM = bs4(content, "lxml")
-    return fileAsDOM
-
-def readFile(fileNameAndPath):
-    with open(fileNameAndPath, "r") as file:
-        content=file.readlines()
-    return content
-
-class LocatorObjects:
-    def __init__(self):
-        self.objects = {}
-
-    def add(self, objectDefinition, path):
-        self.objects[path] = objectDefinition
-
-    def exportXLS(self, wsheet: xlsxwriter.Workbook.worksheet_class):
-        wsheet.write(0,0, "Location")
-        wsheet.write(0,1, "SelectorType")
-        wsheet.write(0,2, "SelectorValue")
-        wsheet.write(0,3, "IFrame")
-        for line, (lObjectKey, lObjectValue) in enumerate(self.objects.items()):
-            wsheet.write(line+1, 0, lObjectValue.fileNameAndPathKatalonInternal)
-            wsheet.write(line+1, 1, lObjectValue.selector)
-            wsheet.write(line+1, 2, lObjectValue.selectorValue)
-            wsheet.write(line+1, 3, lObjectValue.iframe)
-
-class Groovy:
-    def __init__(self):
-        self.groovyScriptClasses = {}
-
-    def add(self, groovyScript, path):
-        self.groovyScriptClasses[path] = groovyScript
-
-    def doReplacementOfLiterals(self, objects: LocatorObjects):
-        for key, value in self.groovyScriptClasses.items():
-            value.replaceLiteralsWithLocators(objects)
-
-
-class TestCases:
-    def __init__(self):
-        self.testcases = {}
-
-    def add(self, testCase, path):
-        self.testcases[path] = testCase
-
-    @staticmethod
-    def decodeHex(string):
-        myUuid = uuid.UUID(string)
-        return myUuid.fields
-
-class fileHandling:
-    def __init__(self, fileNameAndPath, format='XML'):
-        self.fileNameAndPath = fileNameAndPath
-        if format == 'XML':
-            self.fileAsDOM: bs4 = readXMLFile(fileNameAndPath)
-        else:
-            self.fileAsDOM = readFile(fileNameAndPath)
-
-        self.name = None
-        self.fileNameAndPathKatalonInternal = fileHandling._makeShorterPath(self.fileNameAndPath)
-
-    def logFileContentsHeader(self):
-        logger.info(f"Processing file: {self.fileNameAndPath}")
-
-    @staticmethod
-    def _makeShorterPath(longPath):
-        shortPath = fileHandling.__cutBefore(longPath, "Object Repository/")
-        shortPath = fileHandling.__cutBefore(shortPath, "Test Cases/")
-        shortPath = fileHandling.__cutBefore(shortPath, "Scripts/")
-        return shortPath
-
-    @staticmethod
-    def __cutBefore(path, cutBeforeValue):
-        if cutBeforeValue in path:
-            return cutBeforeValue + "".join(path.split(cutBeforeValue)[1:])
-        return path
-
-class translateTestCase(fileHandling):
-    def __init__(self, fileNameAndPath):
-        super().__init__(fileNameAndPath)
-        self.scripts = {}
-        self.outputAnalysis()
-
-    def outputAnalysis(self):
-        lAttrib = self.fileAsDOM.find("testcaseguid").get_text(strip=True)
-        hexDecode = TestCases.decodeHex(lAttrib)
-        super().logFileContentsHeader()
-        logger.info(f"TestCaseGuid: {lAttrib}, UUID-Decoded: {hexDecode}")
-        self.findGroovyScript()
-
-    def findGroovyScript(self):
-        # There should be a groovy script with a random number in a directory
-        # /Scripts/<path_to_Testcase>/<testCase>/<random>.groovy
-        # Current fileNameAndPath: '/Users/bernhardbuhl/git/KatalonVIG/Test Cases/000 Login/Open_VN_Auskunft.tc'
-        splitList = self.fileNameAndPath.split("/Test Cases/")
-        basePath = splitList[0]
-        restPath = "".join(splitList[1:])
-        testCaseFileName = "".join(restPath.split("/")[-1:])
-        restPath = restPath.replace(testCaseFileName, "")
-        testCaseFileName = testCaseFileName.replace(".tc", "")
-        searchScriptPath = basePath + "/Scripts/" + restPath + testCaseFileName + "/"
-        os.chdir(searchScriptPath)
-        l_files = glob.glob("*.groovy")
-        for file in l_files:
-            self.scripts[file] = translateGoovy(searchScriptPath + file)
-            self.scripts[file].savePythonFile(self.fileNameAndPath)
-
-class translateGoovy(fileHandling):
-    def __init__(self, fileNameAndPath):
-        super().__init__(fileNameAndPath, format="groovy")
-        self.interpretGroovy()
-        # self.savePythonFile() will be executed from caller
-
-    def interpretGroovy(self):
-        lOutput = ""
-        for groovyLine in self.fileAsDOM:
-            groovyLine = translateGoovy.replaceGroovyLine(groovyLine)
-            if len(groovyLine) > 0:
-                lOutput += groovyLine
-        self.fileAsDOM = lOutput
-        self.replaceLiteralsWithLocators("Dummy")
-
-    def replaceLiteralsWithLocators(self, locatorObjectClasses: LocatorObjects):
-        """Replace the internal Katalon-Links with actual definitions"""
-        # The internal Locator is '<ObjectFolder>/<subfolder./.n>/<name>
-        # The filename is '/Object Repository/<ObjectFolder>/<subfolder./.n>/<name>.rs
-        basePath = self.fileNameAndPath.split("/Scripts/")[0]
-        lOutput = ""
-        lInput = self.fileAsDOM.split("\n")
-        for line in lInput:
-            # fixme: Here's an error - removes variable names
-            if "fragen" in self.fileAsDOM:
-                x = 2
-            if "(xpath='" in line:
-                lFileAndPathLocator = line.split("xpath='")[1]
-                lFileAndPathLocator = lFileAndPathLocator.split("'")[0]
-                lFileAndPathLocator = basePath + "/Object Repository/" + lFileAndPathLocator + ".rs"
-                # Sometimes Katalon Studio prefixes with Object Repository and sometimes it doesn't.
-                lFileAndPathLocator = lFileAndPathLocator.replace("/Object Repository/Object Repository/", "/Object Repository/")
-                lLocatorObject = translateObjectDefinition(lFileAndPathLocator)
-
-                lStart = line.split("xpath='")[0]
-                lEnd = line.split("xpath='")[1:]
-                lEnd = "'".join(lEnd[0].split("'")[1:])
-
-                if lLocatorObject.selectorValue:
-                    lLocatorObject.selectorValue = lLocatorObject.selectorValue.replace("'", '"')
-                    line = lStart + lLocatorObject.selector.lower() + "='" + lLocatorObject.selectorValue + "'" + lEnd
-                else:
-                    logger.warning(f"Couldn't find locator definition for Object {lLocatorObject.fileNameAndPathKatalonInternal}")
-                    line = "# FIXME: " + lStart + lLocatorObject.fileNameAndPathKatalonInternal + lEnd
-
-            lOutput = lOutput + "\n" + line
-        self.fileAsDOM = lOutput
-
-    def savePythonFile(self, saveAsClassName = None):
-        if saveAsClassName:
-            lFileExport = saveAsClassName
-            lFileExport = lFileExport.replace(".tc", ".py")
-            self._createHeaderForClass(saveAsClassName)
-        else:
-            lFileExport = self.fileNameAndPath.replace(".groovy", ".py")
-        lFileExport = self._replacePathForExport(lFileExport)
-        lPathExport = "/".join(lFileExport.split("/")[0:-1])
-        Path(lPathExport).mkdir(parents=True, exist_ok=True)
-        with open(lFileExport, "w") as file:
-            file.writelines(self.fileAsDOM)
-
-    @staticmethod
-    def _replacePathForExport(fullPathAndFileName):
-        lFileExport = fullPathAndFileName.replace("/Test Cases/", "/Python/Test Cases/")
-        lFileExport = lFileExport.replace("/Scripts/", "/Python/Scripts/")
-        if not "Python" in lFileExport:
-            logger.critical(f"Should replace path, but can't. input: {fullPathAndFileName}. \nI better stop now.")
-            sys.exit("Critical program error or so.")
-        return lFileExport
-
-    def _createHeaderForClass(self, classNameForHeader):
-        header = """
-import baangt.base.GlobalConstants as GC
-from baangt.TestSteps.TestStepMaster import TestStepMaster
-
-
-"""
-        header = header + f"class {classNameForHeader.split('/')[-1].replace('.tc', '')}(TestStepMaster):"
-
-        header = header + """
-\tdef __init__(self, **kwargs):
-\t\tsuper().__init__(**kwargs)
-\t\tself.execute()
-
-\tdef execute(self):"""
-
-        self.addTabs()
-        self.fileAsDOM = header + '\n' + self.fileAsDOM
-
-    def addTabs(self):
-        """
-        Add Tab-Stop to each from Groovy converted Code-Line as this code runs under Method execute()
-        """
-        self.fileAsDOM = self.fileAsDOM.replace("\n\n", "\n")
-        l_lines = self.fileAsDOM.split("\n")
-        result = ""
-        for line in l_lines:
-            line = '\t\t' + line
-            result = result + '\n' + line
-        self.fileAsDOM = result
-
-    @staticmethod
-    def replaceGroovyLine(l_string):
-        l_string = l_string.replace(") {", "")
-        l_string = l_string.replace("WebUI.click(findTestObject(", "self.browserSession.findByAndClick(xpath=")
-        l_string = l_string.replace("WebUI.verifyElementPresent(findTestObject(", "self.browserSession.findBy(xpath=")
-        l_string = l_string.replace("WebUI.setText(findTestObject(", "self.browserSession.findByAndSetText(xpath=")
-        l_string = l_string.replace("WebUI.waitForElementVisible(findTestObject(", "self.browserSession.findBy(xpath=")
-        l_string = l_string.replace("WebUI.verifyElementNotPresent(findTestObject(",
-                                    "self.browserSession.findWaitNotVisible(xpath=")
-        l_string = l_string.replace("WebUI.waitForElementNotPresent(findTestObject(",
-                                    "self.browserSession.findWaitNotVisible(xpath=")
-        l_string = l_string.replace("WebUI.doubleClick(findTestObject(",
-                                    "self.browserSession.findByAndClick(xpath=")
-        l_string = l_string.replace("WebUI.waitForElementPresent(findTestObject(",
-                                    "self.browserSession.findBy(xpath=")
-        l_string = l_string.replace("if (", "if ")
-        l_string = l_string.replace(" && ", " and ")
-        l_string = l_string.replacE(" || ", " or ")
-        l_string = l_string.replace("else", "else:")
-        l_string = l_string.replace("{", "")
-        l_string = l_string.replace("}", "")
-        l_string = l_string.replace("))", ")")
-        l_string = l_string.replace(".toString()","")
-        l_string = l_string.replace(".trim()", ".strip()")
-
-        if "WebUI.waitForElementClickable" in l_string:
-            l_string = ""
-
-        if "//" in l_string.strip()[0:2]:
-            l_string = l_string.replace("//", "# ")
-
-        if "'" in l_string.strip()[0:2]:
-            l_string = "# " + l_string
-
-        if "not_run" in l_string.strip()[0:7]:
-            l_string = l_string.replace("not_run:", "# ")
-
-        if "if " in l_string:
-            l_string = l_string.strip() + ":\n"
-
-        if "import " in l_string[0:10]:
-            l_string = ""
-
-        if "Thread.sleep" in l_string:
-            sleep_time = l_string.split("(")[1]
-            sleep_time = sleep_time.split(")")[0]
-            # Sleep time in Milliseonds.
-            sleep_time = float(sleep_time) / 1000
-            l_string = l_string.split("Thread")[0] + "self.browserSession.sleep(" + str(sleep_time) + ")\n"
-
-        if "WebUI.delay(" in l_string:
-            sleep_time = l_string.split("(")[1]
-            sleep_time = sleep_time.split(")")[0]
-            # sleep time in Seconds
-            l_string = l_string.split("WebUI")[0] + "self.browserSession.sleep(" + sleep_time + ")\n"
-
-        if "scrollTo" in l_string:
-            l_string = ""
-
-        if "CustomKeyword" in l_string:
-            l_string = ""
-
-        if "self.testcaseDataDict" in l_string and "self.browserSession" in l_string and l_string[-1:] == ")":
-            l_string = l_string[0:-1] + "'])"
-            # schließende Klammer zu früh...
-            l_line_split = l_string.split(")")
-            l_string = l_line_split[0] + l_line_split[1] + ")"
-            # value als named parameter übergeben
-            l_string = l_string.replace("self.testcaseDataDict", "value=self.testcaseDataDict")
-
-        l_string = translateGoovy._replaceGlobalVariable(l_string)
-        l_string = translateGoovy._replaceOtherStuff(l_string)
-        return l_string
-
-    @staticmethod
-    def _replaceOtherStuff(stringIn):
-        if ".contains(" in stringIn:
-            # Syntax: "<space><bla>.contains('blabla')<space>
-            # fixme: not done yet. Implement...
-            pass
-        if "self.browserSession.findByAndSetText(" in stringIn:
-            # in self.browserSession.findByAndSetText( after the xpath='value' there is a closing bracket before the value
-            # That must got and self.testcasedataDict must be preceeded with "value="
-            stringIn = stringIn.replace("self.testcaseDataDict", "value=self.testcaseDataDict")
-            stringIn = stringIn.replace("'), value=", "', value=")
-
-        if "self.browserSession.findWaitNotVisible" in stringIn:
-            # there's a timeout-value, that needs to be a parameter "timeout="
-            # formats:
-            #    <bla>,<blank><timeout>)
-            #    <bla>,<timeout>)
-
-            lFirst = stringIn.split(",")
-            lEnd = lFirst[-1]
-            lEnd = ", timeout = " + lEnd
-            stringIn = " ".join(lFirst[0:-1]) + lEnd
-            stringIn = stringIn.replace("), timeout =", ", timeout =")
-
-        stringIn = stringIn.replace("else: if", "elif")
-
-        # Fix space vs. tab in the file beginning
-        lengthDiffSpace = len(stringIn) - len(stringIn.lstrip(' '))
-        if lengthDiffSpace > 0:
-            if lengthDiffSpace >= 8:
-                stringIn = '\t\t' + stringIn.lstrip(' ')
-            elif lengthDiffSpace >= 4:
-                stringIn = '\t' + stringIn.lstrip(' ')
-        return stringIn
-
-
-    @staticmethod
-    def _replaceGlobalVariable(stringIn):
-        """
-        We have any of the following occurances:
-        covered: <blank>GlobalVariable.variablename<blank>
-        covered: (GlobalVariable.variablename)
-        covered: (GlobalVariable.variablename<blank>
-        covered: <blank>GlobalVariable.variablename<lineend>
-        covered: <blank>GlobalVariable.variablename)
-        covered: <blank>GlobalVariable.variablename.contains('<text>'):
-        <blank>GlobalVariable.variablename.toLowerCase()<blank>
-        covered: <linestart>GlobalVariable.variablename<blank>
-
-        That was the old code:
-        #l_string = l_string.replace("((GlobalVariable.", "self.testcaseDataDict['")
-        #l_string = l_string.replace("GlobalVariable.", "self.testcaseDataDict['")
-        #l_string = l_string.replace(" ==", "'] ==")
-        #l_string = l_string.replace(" !=", "'] !=")
-        """
-        if not "GlobalVariable" in stringIn:
-            return stringIn
-
-        while "GlobalVariable" in stringIn:
-            addCRLF = False
-            replaceVariable = stringIn.split("GlobalVariable")[1]
-            replaceVariable = replaceVariable.split(" ")[0]
-            replaceVariable = replaceVariable.split(")")[0]
-            replaceVariable = replaceVariable.split(".")[1]
-            if "\n" in replaceVariable:
-                replaceVariable = replaceVariable.split("\n")[0]
-            destination = "self.testcaseDataDict['" + replaceVariable + "']"
-            replaceVariable = "GlobalVariable." + replaceVariable
-            stringIn = stringIn.replace(replaceVariable, destination)
-            if addCRLF:
-                stringIn = stringIn + "\n"
-
-        if ".length()" in stringIn:
-            stringIn = translateGoovy._replaceLengthMethodGroovy(stringIn)
-        if ".toLowerCase()" in stringIn:
-            stringIn = stringIn.replace("toLowerCase()", "lower()")
-        return stringIn
-        pass
-
-    @staticmethod
-    def _replaceLengthMethodGroovy(stringIn):
-        """bla.length() in Groovy should become len(bla)"""
-        # Find the variable, that we want to know the length of:
-        stringIn = stringIn.strip()
-        partsBeforeAndAfter = stringIn.split(".length()")
-        variableParts = partsBeforeAndAfter[0].split(" ")
-        variableParts[-1] = "len(" + variableParts[-1]
-        partsBeforeAndAfter[0] = " ".join(variableParts) + ")"
-        return "".join(partsBeforeAndAfter)
-
-class translateObjectDefinition(fileHandling):
-    def __init__(self, fileNameAndPath):
-        super().__init__(fileNameAndPath)
-        self.selector = None
-        self.selectorValue = None
-        self.iframe = None
-        self.analyze()
-        self.outputAnalysis()
-
-    def outputAnalysis(self):
-        super().logFileContentsHeader()
-        logger.debug(f"Selektor: {self.selector}")
-        logger.debug(f"Value: {self.selectorValue}")
-        logger.debug(f"Iframe: {self.iframe}")
-
-    def analyze(self):
-        nameTag = self.fileAsDOM.find("name")
-        self.name = nameTag.text
-        self.selector = self.fileAsDOM.find("selectormethod").get_text(strip=True) # XPATH, CSS
-        selectorDrillDown = self.fileAsDOM.findAll("selectorcollection")
-        for node in selectorDrillDown:
-            lEntries = node.findAll("entry")
-            for entry in lEntries:
-                if entry.find("key").get_text(strip=True) == self.selector:
-                    self.selectorValue = entry.find("value").get_text(strip=True)
-
-        # Find potential Iframe:
-        iframeDrilldown = self.fileAsDOM.find("webelementproperties")
-        if iframeDrilldown:
-            if iframeDrilldown.find("name").get_text(strip=True) == "ref_element":
-                self.iframe = iframeDrilldown.find("value").get_text(strip=True)
-
-
-def doImport(importDir):
-    excludeDirs = ["Check Points", "Test Suites", ".git", "Reports", "/bin", "/settings", "/Drivers", "/Plugins",
-                   "/Data Files", "/0testdateninput", "/2shared", "/1testout", "/Keywords", "/report", "/Libs",
-                   "/Checkpoints", "/Include", "/3tools", "/Scripts", "/Test Listen"]
-    for (root, dirs, files) in os.walk(importDir, topdown=True):
-        lContinue = True
-        for excludeDir in excludeDirs:
-            if excludeDir in root:
-                lContinue = False
-                break
-        if not lContinue:
-            continue
-        logger.info(str(root) + str(dirs) + str(files))
-        for file in files:
-            fileAndPath = _getFileAndPath(file=file, path=root)
-            #if file[-3:] == '.rs':
-            #    lDefinition = translateObjectDefinition(fileAndPath)
-            #    locatorObjects.add(lDefinition, fileAndPath)
-            if file[-3:] == '.tc':
-                lTestCase = translateTestCase(fileAndPath)
-                testCases.add(lTestCase, fileAndPath)
-            #if file[-7:] == '.groovy':
-            #    lGroovy = translateGoovy(fileAndPath)
-            #    groovy.add(lGroovy, fileAndPath)
-
-def _getFileAndPath(file, path):
-    return path + "/" + file
-
-def exportResults():
-    lXLS = xlsxwriter.Workbook("export.xlsx")
-    lWSObjects = lXLS.add_worksheet("Locators")
-    locatorObjects.exportXLS(lWSObjects)
-    lXLS.close()
-
-
-if __name__ == '__main__':
-    logger = setupLogger()
-    IMPORTDIR = '/Users/bernhardbuhl/git/KatalonVIG'
-    locatorObjects = LocatorObjects()
-    testCases = TestCases()
-    groovy = Groovy()
-    doImport(IMPORTDIR)
-    groovy.doReplacementOfLiterals(objects=locatorObjects)
-    exportResults()
-    print("Python-Files created in Katalon-Basepath/Python")
-    print("Excel Export of Locators in export.xlsx")

BIN
baangt/ressources/baangtIcon.png


BIN
baangt/ressources/baangtLogo.png


BIN
baangt/ressources/baangtLogo.snagproj


BIN
baangt/ressources/baangtLogo2020.png


BIN
baangt/ressources/baangtLogo2020Small.png


BIN
baangt/ressources/favicon.ico


+ 0 - 297
baangt/ui/ImportKatalonRecorder.py

@@ -1,297 +0,0 @@
-import PySimpleGUI as sg
-import os
-import xlsxwriter
-from pathlib import Path
-import pyperclip
-import logging
-import re
-from baangt.base.ExportResults.ExportResults import ExcelSheetHelperFunctions
-
-# re for to extract 'text' from  //input[@type='text']
-extract_var = r'.*\'(.*)\''
-
-logger = logging.getLogger("pyC")
-
-
-class ImportKatalonRecorder:
-    def __init__(self, directory):
-        self.window = None
-        self.directory = directory
-        self.clipboardText = ""
-        self.outputText = ""
-        self.outputData = {}
-
-        self.clipboardText = """open | https://www.orf.at/ | 
-click | //main[@id='content']/div[2]/div/div[2]/a/div[2]/div | 
-click | //input[@type='text'] | 
-type | //input[@type='text'] | test151
-click | //input[@type='password'] | 
-type | //input[@type='password'] | test4711
-click | xpath=(//button[@type='button'])[5] | 
-click | //div[@id='main']/div[2]/div/div[2]/div/div[3]/div[2]/button/div[2]/i | 
-click | //input[@type='text'] | 
-type | //input[@type='text'] | franzi
-type | xpath=(//input[@type='text'])[2] | franzi@fritzi.com
-type | //input[@type='password'] | 1234567
-type | xpath=(//input[@type='password'])[2] | 1234567
-click | //div[@id='main']/div[2]/div/div[2]/div/div[5]/button/div[2] | 
-click | //input[@type='password'] | 
-type | //input[@type='password'] | 12345678
-type | xpath=(//input[@type='password'])[2] | 12345678
-click | //div[@id='main']/div[2]/div/div[2]/div/div[5]/button/div[2]/i | 
-click | xpath=(//input[@type='password'])[2] | 
-click | //input[@type='password'] | 
-sendKeys | //input[@type='password'] | ${KEY_DOWN}
-sendKeys | //input[@type='password'] | ${KEY_DOWN}
-type | //input[@type='password'] | 12345678!!
-click | xpath=(//input[@type='password'])[2] | 
-type | xpath=(//input[@type='password'])[2] | 12345678!!
-click | //div[@id='main']/div[2]/div/div[2]/div/div[5]/button/div[2] | 
-"""
-        self.outputFormatted = []
-        self.startWindow()
-        self.fileNameExport = None
-
-    def startWindow(self):
-        self.getLayout()
-        sg.theme("TanBlue")
-
-        self.window = sg.Window("Baangt interactive Starter", layout=self.getLayout())
-
-        lWindow = self.window
-        lWindow.finalize()
-
-        while True:
-            lEvent, lValues = lWindow.read()
-            if lEvent == "Exit":
-                break
-
-            if lEvent == 'Save':
-                self.fileNameExport = sg.popup_get_text("Name of new Testcase:")
-                if self.fileNameExport:
-                    self.saveTestCase()
-
-            if lEvent == "TextIn":  # Textinput in TextIn
-                self.__importTranslateAndUpdate(lWindow=lWindow)
-
-            if lEvent == 'Import from Clipboard':
-                self.clipboardText = pyperclip.paste()
-                self.window["TextIn"].update(value=self.clipboardText)
-                self.__importTranslateAndUpdate(lWindow=lWindow)
-
-        lWindow.close()
-
-    def __importTranslateAndUpdate(self, lWindow):
-        self.importClipboard()
-        lWindow['TextOut'].update(value=self.outputText)
-
-    def saveTestCase(self):
-        if "XLSX" not in self.fileNameExport.upper():
-            self.fileNameExport = self.fileNameExport + ".xlsx"
-        lExcel = xlsxwriter.Workbook(str(Path(self.directory).joinpath(self.fileNameExport)))
-        lWorksheetDefinition = lExcel.add_worksheet("TestStepExecution")
-        lWorksheetData = lExcel.add_worksheet("data")
-        self.saveTestCaseHeader(worksheet=lWorksheetDefinition)
-        self.saveTestCaseExecution(worksheet=lWorksheetDefinition)
-        self.saveTestData(lWorksheetData)
-        lExcel.close()
-
-    def saveTestData(self, worksheet):
-        # fixed: Translated the values in Testcase with VariableNames and then write those to data.
-        for index, data in enumerate(sorted(self.outputData.items())):
-            # remove $( and ) from header
-            header = re.search(r'.*\((.*)\)', data[0]).groups()[0]
-            self.writeCell(worksheet, 0, index, header)
-            self.writeCell(worksheet, 1, index, data[1])
-
-        for i in range(len(self.outputData)):
-            ExcelSheetHelperFunctions.set_column_autowidth(worksheet, i)
-
-    def saveTestCaseHeader(self, worksheet):
-        self.writeCell(worksheet, 0, 0, "Activity")
-        self.writeCell(worksheet, 0, 1, "LocatorType")
-        self.writeCell(worksheet, 0, 2, "Locator")
-        self.writeCell(worksheet, 0, 3, "Value")
-        self.writeCell(worksheet, 0, 4, "Comparison")
-        self.writeCell(worksheet, 0, 5, "Value2")
-        self.writeCell(worksheet, 0, 6, "Timeout")
-        self.writeCell(worksheet, 0, 7, "Optional")
-        self.writeCell(worksheet, 0, 8, "Release")
-        # fixme: Write comments in the header line
-
-    def saveTestCaseExecution(self, worksheet):
-        for row, line in enumerate(self.outputFormatted, start=1):
-            if not line:
-                continue
-            for col, (key, value) in enumerate(line.items()):
-                self.writeCell(worksheet, row, col, value)
-        for i in range(len(self.outputFormatted)):
-            ExcelSheetHelperFunctions.set_column_autowidth(worksheet, i)
-
-    def writeCell(self, sheet, cellRow, cellCol, value, format=None):
-        sheet.write(cellRow, cellCol, value)
-
-    def getLayout(self):
-        lLayout = []
-        lLayout.append([sg.Multiline(default_text=self.clipboardText, size=(50, 30), key='TextIn', change_submits=True),
-                        sg.Multiline(default_text=self.outputText, size=(50, 30), key='TextOut')])
-        lLayout.append([sg.Text("Select Directory, Testrun and Global settings to use:")])
-        lLayout.append([sg.Text("Directory", size=(15, 1)),
-                        sg.In(key="-directory-", size=(30, 1), enable_events=True, default_text=self.directory),
-                        sg.FolderBrowse(initial_folder=os.getcwd(), enable_events=True)])
-
-        lLayout.append([sg.Button('Save'), sg.Button('Exit'), sg.Button("Import from Clipboard")])
-
-        return lLayout
-
-    def importClipboard(self):
-        lineOut = []
-
-        for line in self.clipboardText.split("\n"):
-            if len(line.strip()) > 2:
-                if "|" not in line:
-                    continue
-
-                lineOut.append(self.doTranslate(line))
-        # Further process into $('variable') and value format
-        lineOut, self.outputData = ImportKatalonRecorder.splitVariable(lineOut)
-
-        self.outputText = ""
-        self.outputFormatted = lineOut
-        for line in lineOut:
-            if not line:
-                continue
-            lStr = ""
-            for key, value in line.items():
-                lStr = lStr + f"{key}: {value}, "
-            lStr = lStr + "\n"
-            self.outputText += lStr
-
-    def doTranslate(self, lineIn):
-        value = ""
-        if "|" not in lineIn:
-            return None
-        lParts = lineIn.split("|")
-        command = lParts[0].strip()
-        locator = lParts[1].strip()
-        if command == "type" or command == "sendKeys":
-            value = lParts[2].strip()
-
-        if command == 'click':
-            return ImportKatalonRecorder.doTranslateClick(locator)
-        elif command == 'type':
-            return ImportKatalonRecorder.doTranslateType(locator, value)
-        elif command == 'sendKeys':
-            return ImportKatalonRecorder.doTranslateType(locator, value)
-        elif command == 'open':
-            return ImportKatalonRecorder.__fillDict("GOTOURL", "", locator)
-        elif command == 'goBackAndWait' or command == 'goBack':
-            return ImportKatalonRecorder.doTranslategoBackAndWait()
-        elif command == 'select':
-            return ImportKatalonRecorder.doTranslateSelect(locator)
-        else:
-            logger.exception(f"Translation for command not implemented: {command}")
-            return None
-
-    @staticmethod
-    def doTranslateType(locator, value):
-        return ImportKatalonRecorder.__fillDict("SETTEXT", locator, value)
-
-    @staticmethod
-    def doTranslateClick(locator):
-        return ImportKatalonRecorder.__fillDict("click", locator)
-
-    @staticmethod
-    def doTranslategoBackAndWait():
-        return ImportKatalonRecorder.__fillDict("goBack", "")
-
-    @staticmethod
-    def doTranslateSelect(locator):
-        return ImportKatalonRecorder.__fillDict("select", locator)
-
-    @staticmethod
-    def __fillDict(activity, locator, value="", locatorType='xpath'):
-        if activity == 'select':
-            activity = 'click'
-            return {"Activity": activity,
-                    "LocatorType": locatorType,
-                    "Locator": ImportKatalonRecorder.doTranslateLocator(locator, "select"),
-                    "Value": str(value)}
-
-        return {"Activity": activity,
-                "LocatorType": locatorType,
-                "Locator": ImportKatalonRecorder.doTranslateLocator(locator),
-                "Value": str(value)
-                }
-
-    @staticmethod
-    def doTranslateLocator(locator, specialInstructions=None):
-
-        if "xpath=" in locator:
-            # he comes with xpath=(//button[@type='button'])[5]
-            locator = locator.replace("xpath=", "")
-        if locator[0:3].upper() == "ID=":
-            # This is an ID. Translate the ID to proper xpath-Syntax
-            if specialInstructions == "select":
-                preLocator = "//select[@id='"
-            else:
-                preLocator = "//*[@id='"
-            postLocator = "']"
-            locator = locator[3:]
-            locator = preLocator + locator + postLocator
-        if locator[0:5].upper() == 'LINK=':
-            # //a[text()='text_i_want_to_find']/@href --> Didn't work. Return Text and Selenium can't handle that
-            # //a[contains(., 'Button')]
-            preLocator = "//a[contains(., '"
-            postLocator = "')]"
-            locator = locator[5:]
-            locator = preLocator + locator + postLocator
-        return locator
-
-    @staticmethod
-    def splitVariable(lines):
-        # make outputData empty if already there
-        """ This function will process each line and format the
-            'Value' column in format $(variabe).
-
-        @output: list(lines), list(outputData)
-        """
-
-        outputData = {}
-        # loop through line and process if 'Value' column there
-        for line in lines:
-            # Check for GOTOURL activity
-            # skip if lines == NONE
-            if not line:
-                continue
-            if line['Activity'] == "GOTOURL":
-                # we should make variable for url
-                line['Value'] = ImportKatalonRecorder.prepareKeyValue(outputData, 'url', line['Value'])
-
-            elif line['Value'].strip():
-                # we will make it variable
-                result = re.search(extract_var, line['Locator'])
-                if result:
-                    # create variable format like $(value)
-                    result = result.groups()[0]
-
-                    line['Value'] = ImportKatalonRecorder.prepareKeyValue(outputData, result, line['Value'])
-        return lines, outputData
-
-    @staticmethod
-    def prepareKeyValue(outputData, key, value):
-        """ This function will append key and value in 
-            outputData
-            if variable key exist, it will rename by
-                    key1, key2, ...
-        """
-        for i in range(1, 100):
-            var = "$(" + key + str(i) + ")"
-            if not outputData.get(var, None):
-                outputData[var] = value
-                return var
-            else:
-                continue
-
-    def exportResult(self):
-        pass

+ 0 - 0
baangt/ui/__init__.py


+ 0 - 0
baangt/ui/logs/20200129_174558.log


Some files were not shown because too many files changed in this diff