toolium 3.4.0.dev0__tar.gz → 3.6.0.dev0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/CHANGELOG.rst +30 -2
  2. {toolium-3.4.0.dev0/toolium.egg-info → toolium-3.6.0.dev0}/PKG-INFO +13 -5
  3. toolium-3.6.0.dev0/VERSION +1 -0
  4. {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/requirements.txt +0 -1
  5. toolium-3.6.0.dev0/requirements_dev.txt +16 -0
  6. {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/setup.py +23 -2
  7. {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/driver_wrapper.py +8 -1
  8. toolium-3.6.0.dev0/toolium/utils/ai_utils.py +188 -0
  9. {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/utils/dataset.py +48 -28
  10. {toolium-3.4.0.dev0 → toolium-3.6.0.dev0/toolium.egg-info}/PKG-INFO +13 -5
  11. {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium.egg-info/SOURCES.txt +1 -0
  12. {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium.egg-info/requires.txt +11 -1
  13. toolium-3.4.0.dev0/VERSION +0 -1
  14. toolium-3.4.0.dev0/requirements_dev.txt +0 -13
  15. {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/LICENSE +0 -0
  16. {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/MANIFEST.in +0 -0
  17. {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/README.rst +0 -0
  18. {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/setup.cfg +0 -0
  19. {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/__init__.py +0 -0
  20. {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/behave/__init__.py +0 -0
  21. {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/behave/env_utils.py +0 -0
  22. {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/behave/environment.py +0 -0
  23. {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/config_driver.py +0 -0
  24. {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/config_driver_playwright.py +0 -0
  25. {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/config_files.py +0 -0
  26. {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/config_parser.py +0 -0
  27. {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/driver_wrappers_pool.py +0 -0
  28. {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/jira.py +0 -0
  29. {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/pageelements/__init__.py +0 -0
  30. {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/pageelements/button_page_element.py +0 -0
  31. {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/pageelements/checkbox_page_element.py +0 -0
  32. {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/pageelements/group_page_element.py +0 -0
  33. {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/pageelements/input_radio_page_element.py +0 -0
  34. {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/pageelements/input_text_page_element.py +0 -0
  35. {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/pageelements/link_page_element.py +0 -0
  36. {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/pageelements/page_element.py +0 -0
  37. {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/pageelements/page_elements.py +0 -0
  38. {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/pageelements/playwright/__init__.py +0 -0
  39. {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/pageelements/playwright/button_page_element.py +0 -0
  40. {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/pageelements/playwright/input_text_page_element.py +0 -0
  41. {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/pageelements/playwright/page_element.py +0 -0
  42. {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/pageelements/playwright/text_page_element.py +0 -0
  43. {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/pageelements/select_page_element.py +0 -0
  44. {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/pageelements/text_page_element.py +0 -0
  45. {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/pageobjects/__init__.py +0 -0
  46. {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/pageobjects/common_object.py +0 -0
  47. {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/pageobjects/mobile_page_object.py +0 -0
  48. {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/pageobjects/page_object.py +0 -0
  49. {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/pytest_fixtures.py +0 -0
  50. {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/resources/VisualTests.css +0 -0
  51. {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/resources/VisualTests.js +0 -0
  52. {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/resources/VisualTestsTemplate.html +0 -0
  53. {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/selenoid.py +0 -0
  54. {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/test_cases.py +0 -0
  55. {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/utils/__init__.py +0 -0
  56. {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/utils/data_generator.py +0 -0
  57. {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/utils/download_files.py +0 -0
  58. {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/utils/driver_utils.py +0 -0
  59. {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/utils/driver_wait_utils.py +0 -0
  60. {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/utils/path_utils.py +0 -0
  61. {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/utils/poeditor.py +0 -0
  62. {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/visual_test.py +0 -0
  63. {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium.egg-info/dependency_links.txt +0 -0
  64. {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium.egg-info/top_level.txt +0 -0
@@ -1,12 +1,40 @@
1
1
  Toolium Changelog
2
2
  =================
3
3
 
4
- v3.4.0
4
+ v3.6.0
5
5
  ------
6
6
 
7
7
  *Release date: In development*
8
8
 
9
- - Now Playwright is available to run web tests instead of Selenium
9
+ - Add text comparison methods based on AI libraries. To use them, install the `ai` extra dependency:
10
+
11
+ .. code:: console
12
+
13
+ $ pip install toolium[ai]
14
+
15
+ v3.5.0
16
+ ------
17
+
18
+ *Release date: 2025-07-29*
19
+
20
+ - Add support for Python 3.13
21
+ - Remove Python 3.8 support (Python 3.8 reached the end of its life on October 14th, 2024)
22
+ - Add `LANG` replacement, to use different language translations. Example: [LANG:some_text::new_language]
23
+ - Playwright installation is now optional, it can be installed with the `playwright` extra dependency. To install
24
+ Toolium with Playwright support, use:
25
+
26
+ .. code:: console
27
+
28
+ $ pip install toolium[playwright]
29
+
30
+ v3.4.0
31
+ ------
32
+
33
+ *Release date: 2025-03-06*
34
+
35
+ - Now Playwright is available to run web tests instead of Selenium by configuring `web_library: playwright` in [Driver]
36
+ section
37
+ - Allow setting the number of decimal places for seconds when using the `[NOW]` replacement with a specific format
10
38
 
11
39
  v3.3.1
12
40
  ------
@@ -1,23 +1,23 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: toolium
3
- Version: 3.4.0.dev0
3
+ Version: 3.6.0.dev0
4
4
  Summary: Wrapper tool of Selenium and Appium libraries to test web and mobile applications in a single project
5
5
  Home-page: https://github.com/telefonica/toolium
6
6
  Author: Rubén González Alonso, Telefónica I+D
7
7
  Author-email: ruben.gonzalezalonso@telefonica.com
8
8
  License: Apache 2.0
9
- Keywords: selenium appium webdriver web_automation mobile_automation page_object visual_testing bdd behave pytest
9
+ Keywords: selenium,appium,webdriver,web_automation,mobile_automation,page_object,visual_testing,ai,bdd,behave,pytest
10
10
  Classifier: Development Status :: 5 - Production/Stable
11
11
  Classifier: Intended Audience :: Developers
12
12
  Classifier: Intended Audience :: Other Audience
13
13
  Classifier: License :: OSI Approved :: Apache Software License
14
14
  Classifier: Natural Language :: English
15
15
  Classifier: Operating System :: OS Independent
16
- Classifier: Programming Language :: Python :: 3.8
17
16
  Classifier: Programming Language :: Python :: 3.9
18
17
  Classifier: Programming Language :: Python :: 3.10
19
18
  Classifier: Programming Language :: Python :: 3.11
20
19
  Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
21
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
22
  Classifier: Topic :: Software Development :: Quality Assurance
23
23
  Classifier: Topic :: Software Development :: Testing
@@ -25,13 +25,19 @@ Description-Content-Type: text/x-rst
25
25
  License-File: LICENSE
26
26
  Requires-Dist: requests~=2.27
27
27
  Requires-Dist: selenium~=4.0
28
- Requires-Dist: playwright~=1.43
29
28
  Requires-Dist: Appium-Python-Client<5.0,>=2.3
30
29
  Requires-Dist: Pillow~=10.1
31
30
  Requires-Dist: screeninfo~=0.8
32
31
  Requires-Dist: lxml~=5.1
33
32
  Requires-Dist: Faker~=25.9
34
33
  Requires-Dist: phonenumbers~=8.13
34
+ Provides-Extra: playwright
35
+ Requires-Dist: playwright~=1.43; extra == "playwright"
36
+ Provides-Extra: ai
37
+ Requires-Dist: spacy~=3.8.7; extra == "ai"
38
+ Requires-Dist: sentence-transformers~=5.1; extra == "ai"
39
+ Requires-Dist: transformers==4.56.2; python_version < "3.10" and extra == "ai"
40
+ Requires-Dist: openai~=1.108; extra == "ai"
35
41
  Dynamic: author
36
42
  Dynamic: author-email
37
43
  Dynamic: classifier
@@ -40,6 +46,8 @@ Dynamic: description-content-type
40
46
  Dynamic: home-page
41
47
  Dynamic: keywords
42
48
  Dynamic: license
49
+ Dynamic: license-file
50
+ Dynamic: provides-extra
43
51
  Dynamic: requires-dist
44
52
  Dynamic: summary
45
53
 
@@ -0,0 +1 @@
1
+ 3.6.0.dev0
@@ -1,6 +1,5 @@
1
1
  requests~=2.27 # api tests
2
2
  selenium~=4.0 # web tests
3
- playwright~=1.43 # web tests
4
3
  Appium-Python-Client>=2.3,<5.0 # mobile tests
5
4
  Pillow~=10.1 # visual testing
6
5
  screeninfo~=0.8
@@ -0,0 +1,16 @@
1
+ pytest~=7.2
2
+ coverage~=6.5
3
+ coveralls~=3.3
4
+ mock~=5.0
5
+ requests-mock~=1.10
6
+ Pygments~=2.14
7
+ flake8~=6.0
8
+ build~=0.10
9
+ wheel~=0.40
10
+ twine~=4.0
11
+ behave==1.2.6 # behave tests
12
+ importlib_metadata==7.2.1
13
+ spacy~=3.8.7
14
+ sentence-transformers~=5.1
15
+ transformers==4.56.2; python_version < '3.10' # Issue https://github.com/huggingface/transformers/issues/41339
16
+ openai~=1.108
@@ -61,6 +61,15 @@ setup(
61
61
  install_requires=read_file('requirements.txt').splitlines(),
62
62
  setup_requires=['pytest-runner'],
63
63
  tests_require=read_file('requirements_dev.txt').splitlines(),
64
+ extras_require={
65
+ "playwright": ["playwright~=1.43"],
66
+ "ai": [
67
+ "spacy~=3.8.7",
68
+ "sentence-transformers~=5.1",
69
+ "transformers==4.56.2; python_version < '3.10'",
70
+ "openai~=1.108"
71
+ ]
72
+ },
64
73
  test_suite='toolium.test',
65
74
  author='Rubén González Alonso, Telefónica I+D',
66
75
  author_email='ruben.gonzalezalonso@telefonica.com',
@@ -68,7 +77,19 @@ setup(
68
77
  description='Wrapper tool of Selenium and Appium libraries to test web and mobile applications in a single project',
69
78
  long_description_content_type='text/x-rst',
70
79
  long_description=get_long_description(),
71
- keywords='selenium appium webdriver web_automation mobile_automation page_object visual_testing bdd behave pytest',
80
+ keywords=[
81
+ 'selenium',
82
+ 'appium',
83
+ 'webdriver',
84
+ 'web_automation',
85
+ 'mobile_automation',
86
+ 'page_object',
87
+ 'visual_testing',
88
+ 'ai',
89
+ 'bdd',
90
+ 'behave',
91
+ 'pytest'
92
+ ],
72
93
  classifiers=[
73
94
  'Development Status :: 5 - Production/Stable',
74
95
  'Intended Audience :: Developers',
@@ -76,11 +97,11 @@ setup(
76
97
  'License :: OSI Approved :: Apache Software License',
77
98
  'Natural Language :: English',
78
99
  'Operating System :: OS Independent',
79
- 'Programming Language :: Python :: 3.8',
80
100
  'Programming Language :: Python :: 3.9',
81
101
  'Programming Language :: Python :: 3.10',
82
102
  'Programming Language :: Python :: 3.11',
83
103
  'Programming Language :: Python :: 3.12',
104
+ 'Programming Language :: Python :: 3.13',
84
105
  'Topic :: Software Development :: Libraries :: Python Modules',
85
106
  'Topic :: Software Development :: Quality Assurance',
86
107
  'Topic :: Software Development :: Testing',
@@ -20,7 +20,10 @@ import logging.config
20
20
  import os
21
21
 
22
22
  import screeninfo
23
- from playwright.async_api import async_playwright
23
+ try:
24
+ from playwright.async_api import async_playwright
25
+ except ImportError:
26
+ async_playwright = None
24
27
 
25
28
  from toolium.config_driver import ConfigDriver
26
29
  from toolium.config_driver_playwright import ConfigDriverPlayWright
@@ -265,6 +268,10 @@ class DriverWrapper(object):
265
268
 
266
269
  :returns: playwright page
267
270
  """
271
+ if async_playwright is None:
272
+ raise ImportError("Playwright is not installed. Please run 'pip install toolium[playwright]' to use"
273
+ " Playwright features")
274
+
268
275
  async_loop = self.async_loop
269
276
  self.playwright = async_loop.run_until_complete(async_playwright().start())
270
277
 
@@ -0,0 +1,188 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Copyright 2025 Telefónica Innovación Digital, S.L.
4
+ This file is part of Toolium.
5
+
6
+ Licensed under the Apache License, Version 2.0 (the "License");
7
+ you may not use this file except in compliance with the License.
8
+ You may obtain a copy of the License at
9
+
10
+ http://www.apache.org/licenses/LICENSE-2.0
11
+
12
+ Unless required by applicable law or agreed to in writing, software
13
+ distributed under the License is distributed on an "AS IS" BASIS,
14
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ See the License for the specific language governing permissions and
16
+ limitations under the License.
17
+ """
18
+
19
+ import json
20
+ import logging
21
+
22
+ # Library imports must be optional to allow installing Toolium without `ai` extra dependency
23
+ try:
24
+ import spacy
25
+ except ImportError:
26
+ spacy = None
27
+ try:
28
+ from sentence_transformers import SentenceTransformer
29
+ except ImportError:
30
+ SentenceTransformer = None
31
+ try:
32
+ from openai import OpenAI, AzureOpenAI
33
+ except ImportError:
34
+ OpenAI = None
35
+ AzureOpenAI = None
36
+
37
+ from toolium.driver_wrappers_pool import DriverWrappersPool
38
+
39
+
40
+ # Configure logger
41
+ logger = logging.getLogger(__name__)
42
+
43
+
44
+ def get_text_similarity_with_spacy(text, expected_text, model_name=None):
45
+ """
46
+ Return similarity between two texts using spaCy
47
+
48
+ :param text: string to compare
49
+ :param expected_text: string with the expected text
50
+ :param model_name: name of the spaCy model to use
51
+ :returns: similarity score between the two texts
52
+ """
53
+ if spacy is None:
54
+ raise ImportError("spaCy is not installed. Please run 'pip install toolium[ai]' to use spaCy features")
55
+ config = DriverWrappersPool.get_default_wrapper().config
56
+ model_name = model_name or config.get_optional('AI', 'spacy_model', 'en_core_web_sm')
57
+ model = spacy.load(model_name)
58
+ similarity = model(text).similarity(model(expected_text))
59
+ logger.info(f"spaCy similarity: {similarity} between '{text}' and '{expected_text}'")
60
+ return similarity
61
+
62
+
63
+ def get_text_similarity_with_sentence_transformers(text, expected_text, model_name=None):
64
+ """
65
+ Return similarity between two texts using Sentence Transformers
66
+
67
+ :param text: string to compare
68
+ :param expected_text: string with the expected text
69
+ :param model_name: name of the Sentence Transformers model to use
70
+ :returns: similarity score between the two texts
71
+ """
72
+ if SentenceTransformer is None:
73
+ raise ImportError("Sentence Transformers is not installed. Please run 'pip install toolium[ai]'"
74
+ " to use Sentence Transformers features")
75
+ config = DriverWrappersPool.get_default_wrapper().config
76
+ model_name = model_name or config.get_optional('AI', 'sentence_transformers_model', 'all-mpnet-base-v2')
77
+ model = SentenceTransformer(model_name)
78
+ similarity = float(model.similarity(model.encode(expected_text), model.encode(text)))
79
+ similarity = 1 if similarity > 1 else similarity # similarity can be slightly > 1 due to float precision
80
+ logger.info(f"Sentence Transformers similarity: {similarity} between '{text}' and '{expected_text}'")
81
+ return similarity
82
+
83
+
84
+ def openai_request(system_message, user_message, model_name=None, azure=False):
85
+ """
86
+ Make a request to OpenAI API (Azure or standard)
87
+
88
+ :param system_message: system message to set the behavior of the assistant
89
+ :param user_message: user message with the request
90
+ :param model: model to use
91
+ :param azure: whether to use Azure OpenAI or standard OpenAI
92
+ :returns: response from OpenAI
93
+ """
94
+ if OpenAI is None:
95
+ raise ImportError("OpenAI is not installed. Please run 'pip install toolium[ai]' to use OpenAI features")
96
+ config = DriverWrappersPool.get_default_wrapper().config
97
+ model_name = model_name or config.get_optional('AI', 'openai_model', 'gpt-4o-mini')
98
+ logger.info(f"Calling to OpenAI API with model {model_name}")
99
+ client = AzureOpenAI() if azure else OpenAI()
100
+ completion = client.chat.completions.create(
101
+ model=model_name,
102
+ messages=[
103
+ {"role": "system", "content": system_message},
104
+ {"role": "user", "content": user_message},
105
+ ],
106
+ )
107
+ response = completion.choices[0].message.content
108
+ logger.debug(f"OpenAI response: {response}")
109
+ return response
110
+
111
+
112
+ def get_text_similarity_with_openai(text, expected_text, azure=False):
113
+ """
114
+ Return semantic similarity between two texts using OpenAI LLM
115
+
116
+ :param text: string to compare
117
+ :param expected_text: string with the expected text
118
+ :param azure: whether to use Azure OpenAI or standard OpenAI
119
+ :returns: tuple with similarity score between the two texts and explanation
120
+ """
121
+ system_message = (
122
+ "You have to decide if the LLM answer is correct or not, comparing it with the expected answer."
123
+ " Respond with a percentage between 0 and 1 depending on how correct you think the answer is and with an"
124
+ " explanation of why, returning a json object: {{\"similarity\": {PERCENTAGE}, \"explanation\": {EXPLANATION}}}"
125
+ " The answer is correct if it is semantically similar to the expected answer, it does not have to be identical,"
126
+ " but its meaning should be similar."
127
+ )
128
+ user_message = (
129
+ f"The expected answer is: {expected_text}."
130
+ f" The LLM answer is: {text}."
131
+ )
132
+ response = openai_request(system_message, user_message, azure=azure)
133
+ try:
134
+ response = json.loads(response)
135
+ similarity = float(response['similarity'])
136
+ explanation = response['explanation']
137
+ except (KeyError, ValueError, TypeError) as e:
138
+ raise ValueError(f"Unexpected response format from OpenAI: {response}") from e
139
+ logger.info(f"OpenAI LLM similarity: {similarity} between '{text}' and '{expected_text}'."
140
+ f" LLM explanation: {explanation}")
141
+ return similarity
142
+
143
+
144
+ def get_text_similarity_with_azure_openai(text, expected_text):
145
+ """
146
+ Return semantic similarity between two texts using Azure OpenAI LLM
147
+
148
+ :param text: string to compare
149
+ :param expected_text: string with the expected text
150
+ :returns: tuple with similarity score between the two texts and explanation
151
+ """
152
+ return get_text_similarity_with_openai(text, expected_text, azure=True)
153
+
154
+
155
+ def assert_text_similarity(text, expected_texts, threshold, similarity_method=None):
156
+ """
157
+ Get similarity between one text and a list of expected texts and assert if any of the expected texts is similar.
158
+
159
+ :param text: string to compare
160
+ :param expected_texts: string or list of strings with the expected texts
161
+ :param threshold: minimum similarity score to consider texts similar
162
+ :param similarity_method: method to use for text comparison ('spacy', 'sentence_transformers', 'openai'
163
+ or 'azure_openai')
164
+ """
165
+ config = DriverWrappersPool.get_default_wrapper().config
166
+ similarity_method = similarity_method or config.get_optional('AI', 'text_similarity_method', 'spacy')
167
+ expected_texts = [expected_texts] if isinstance(expected_texts, str) else expected_texts
168
+ error_message = ""
169
+ for expected_text in expected_texts:
170
+ try:
171
+ similarity = globals()[f'get_text_similarity_with_{similarity_method}'](text, expected_text)
172
+ except KeyError:
173
+ raise ValueError(f"Unknown similarity_method: '{similarity_method}', please use 'spacy',"
174
+ f" 'sentence_transformers', 'openai' or 'azure_openai'")
175
+
176
+ texts_message = f"Received text: {text}\nExpected text: {expected_text}"
177
+ if similarity < threshold:
178
+ error_message = f'{error_message}\n' if error_message else ""
179
+ error_message = (f"{error_message}Similarity between received and expected texts"
180
+ f" is below threshold: {similarity} < {threshold}\n{texts_message}")
181
+ else:
182
+ logger.info(f"Similarity between received and expected texts"
183
+ f" is above threshold: {similarity} >= {threshold}\n{texts_message}")
184
+ return
185
+
186
+ # Any expected text did not meet the threshold
187
+ logger.error(error_message)
188
+ assert False, error_message
@@ -73,10 +73,11 @@ def replace_param(param, language='es', infer_param_type=True):
73
73
  - [RANDOM_PHONE_NUMBER] Generates a random phone number for language and country configured
74
74
  in dataset.language and dataset.country
75
75
  - [TIMESTAMP] Generates a timestamp from the current time
76
- - [DATETIME] Generates a datetime from the current time
77
- - [NOW] Similar to DATETIME without milliseconds; the format depends on the language
76
+ - [DATETIME] Generates a datetime from the current time (UTC)
77
+ - [NOW] Similar to DATETIME without microseconds; the format depends on the language
78
78
  - [NOW(%Y-%m-%dT%H:%M:%SZ)] Same as NOW but using an specific format by the python strftime function of
79
- the datetime module
79
+ the datetime module. In the case of the %f placeholder, the syntax has been extended to allow setting
80
+ an arbitrary number of digits (e.g. %3f would leave just the 3 most significant digits and truncate the rest)
80
81
  - [NOW + 2 DAYS] Similar to NOW but two days later
81
82
  - [NOW - 1 MINUTES] Similar to NOW but one minute earlier
82
83
  - [NOW(%Y-%m-%dT%H:%M:%SZ) - 7 DAYS] Similar to NOW but seven days before and with the indicated format
@@ -264,8 +265,12 @@ def _replace_param_transform_string(param):
264
265
  except json.decoder.JSONDecodeError:
265
266
  new_param = eval(type_mapping_match_group.group(2))
266
267
  elif type_mapping_match_group.group(1) in ['INT', 'FLOAT']:
267
- exec(f'exec_param = {type_mapping_match_group.group(1).lower()}({type_mapping_match_group.group(2)})')
268
- new_param = locals()['exec_param']
268
+ exec_env = {}
269
+ type_str = type_mapping_match_group.group(1).lower()
270
+ value_str = type_mapping_match_group.group(2)
271
+ exec_code = f"exec_param = {type_str}({value_str})"
272
+ exec(exec_code, {}, exec_env)
273
+ new_param = exec_env['exec_param']
269
274
  else:
270
275
  replace_param = _get_substring_replacement(type_mapping_match_group)
271
276
  new_param = new_param.replace(type_mapping_match_group.group(), replace_param)
@@ -298,13 +303,38 @@ def _get_substring_replacement(type_mapping_match_group):
298
303
  return replace_param
299
304
 
300
305
 
306
+ def _get_format_with_number_of_decimals(base, language):
307
+ """
308
+ Get the format and the number of decimals from the base string.
309
+ """
310
+ def _is_only_date(base):
311
+ return 'TODAY' in base
312
+
313
+ def _default_format(base):
314
+ date_format = '%d/%m/%Y' if language == 'es' else '%Y/%m/%d'
315
+ if _is_only_date(base):
316
+ return date_format
317
+ return f'{date_format} %H:%M:%S'
318
+
319
+ format_matcher = re.search(r'\((.*)\)', base)
320
+ if format_matcher and len(format_matcher.groups()) == 1:
321
+ time_format = format_matcher.group(1)
322
+ decimal_matcher = re.search(r'%(\d+)f', time_format)
323
+ if decimal_matcher and len(decimal_matcher.groups()) == 1:
324
+ return time_format.replace(decimal_matcher.group(0), '%f'), int(decimal_matcher.group(1))
325
+ return time_format, None
326
+ return _default_format(base), None
327
+
328
+
301
329
  def _replace_param_date(param, language):
302
330
  """
303
331
  Transform param value in a date after applying the specified delta.
304
332
  E.g. [TODAY - 2 DAYS], [NOW - 10 MINUTES]
305
333
  An specific format could be defined in the case of NOW this way: NOW('THEFORMAT')
306
334
  where THEFORMAT is any valid format accepted by the python
307
- [datetime.strftime](https://docs.python.org/3/library/datetime.html#datetime.date.strftime) function
335
+ [datetime.strftime](https://docs.python.org/3/library/datetime.html#datetime.date.strftime) function.
336
+ In the case of the %f placeholder, the syntax has been extended to allow setting an arbitrary number of digits
337
+ (e.g. %3f would leave just the 3 most significant digits and truncate the rest).
308
338
 
309
339
  :param param: parameter value
310
340
  :param language: language to configure date format for NOW and TODAY
@@ -321,28 +351,16 @@ def _replace_param_date(param, language):
321
351
  the_units = units.lower()
322
352
  return now + datetime.timedelta(**dict([(the_units, the_amount)]))
323
353
 
324
- def _is_only_date(base):
325
- return 'TODAY' in base
326
-
327
- def _default_format(base):
328
- date_format = '%d/%m/%Y' if language == 'es' else '%Y/%m/%d'
329
- if _is_only_date(base):
330
- return date_format
331
- return f'{date_format} %H:%M:%S'
332
-
333
- def _get_format(base):
334
- format_matcher = re.match(r'.*\((.*)\).*', base)
335
- if format_matcher and len(format_matcher.groups()) == 1:
336
- return format_matcher.group(1)
337
- return _default_format(base)
338
-
339
354
  matcher = _date_matcher()
340
355
  if not matcher:
341
356
  return param, False
342
357
 
343
358
  base, amount, units = list(matcher.groups())
344
- format_str = _get_format(base)
359
+ format_str, number_of_decimals = _get_format_with_number_of_decimals(base, language)
345
360
  date = _offset_datetime(amount, units)
361
+ if number_of_decimals:
362
+ decimals = f"{date.microsecond / 1_000_000:.{number_of_decimals}f}"[2:]
363
+ format_str = format_str.replace("%f", decimals)
346
364
  return date.strftime(format_str), True
347
365
 
348
366
 
@@ -765,18 +783,20 @@ def get_message_property(param, language_terms, language_key):
765
783
  :param language_key: language key
766
784
  :return: the message mapped to the given key in the given language
767
785
  """
768
- key_list = param.split(".")
769
- language_terms_aux = deepcopy(language_terms)
786
+ lang_param, expected_lang = param.split('::', 1) if '::' in param else (param, language_key)
787
+
788
+ key_list = lang_param.split(".")
789
+ lang_terms_aux = deepcopy(language_terms)
770
790
  try:
771
791
  for key in key_list:
772
- language_terms_aux = language_terms_aux[key]
773
- logger.debug(f"Mapping language param '{param}' to its configured value '{language_terms_aux[language_key]}'")
792
+ lang_terms_aux = lang_terms_aux[key]
793
+ logger.debug(f"Mapping language param '{lang_param}' to its configured value '{lang_terms_aux[language_key]}'")
774
794
  except KeyError:
775
- msg = f"Mapping chain '{param}' not found in the language properties file"
795
+ msg = f"Mapping chain '{lang_param}' not found in the language properties file"
776
796
  logger.error(msg)
777
797
  raise KeyError(msg)
778
798
 
779
- return language_terms_aux[language_key]
799
+ return lang_terms_aux[expected_lang]
780
800
 
781
801
 
782
802
  def get_translation_by_poeditor_reference(reference, poeditor_terms):
@@ -1,23 +1,23 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: toolium
3
- Version: 3.4.0.dev0
3
+ Version: 3.6.0.dev0
4
4
  Summary: Wrapper tool of Selenium and Appium libraries to test web and mobile applications in a single project
5
5
  Home-page: https://github.com/telefonica/toolium
6
6
  Author: Rubén González Alonso, Telefónica I+D
7
7
  Author-email: ruben.gonzalezalonso@telefonica.com
8
8
  License: Apache 2.0
9
- Keywords: selenium appium webdriver web_automation mobile_automation page_object visual_testing bdd behave pytest
9
+ Keywords: selenium,appium,webdriver,web_automation,mobile_automation,page_object,visual_testing,ai,bdd,behave,pytest
10
10
  Classifier: Development Status :: 5 - Production/Stable
11
11
  Classifier: Intended Audience :: Developers
12
12
  Classifier: Intended Audience :: Other Audience
13
13
  Classifier: License :: OSI Approved :: Apache Software License
14
14
  Classifier: Natural Language :: English
15
15
  Classifier: Operating System :: OS Independent
16
- Classifier: Programming Language :: Python :: 3.8
17
16
  Classifier: Programming Language :: Python :: 3.9
18
17
  Classifier: Programming Language :: Python :: 3.10
19
18
  Classifier: Programming Language :: Python :: 3.11
20
19
  Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
21
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
22
  Classifier: Topic :: Software Development :: Quality Assurance
23
23
  Classifier: Topic :: Software Development :: Testing
@@ -25,13 +25,19 @@ Description-Content-Type: text/x-rst
25
25
  License-File: LICENSE
26
26
  Requires-Dist: requests~=2.27
27
27
  Requires-Dist: selenium~=4.0
28
- Requires-Dist: playwright~=1.43
29
28
  Requires-Dist: Appium-Python-Client<5.0,>=2.3
30
29
  Requires-Dist: Pillow~=10.1
31
30
  Requires-Dist: screeninfo~=0.8
32
31
  Requires-Dist: lxml~=5.1
33
32
  Requires-Dist: Faker~=25.9
34
33
  Requires-Dist: phonenumbers~=8.13
34
+ Provides-Extra: playwright
35
+ Requires-Dist: playwright~=1.43; extra == "playwright"
36
+ Provides-Extra: ai
37
+ Requires-Dist: spacy~=3.8.7; extra == "ai"
38
+ Requires-Dist: sentence-transformers~=5.1; extra == "ai"
39
+ Requires-Dist: transformers==4.56.2; python_version < "3.10" and extra == "ai"
40
+ Requires-Dist: openai~=1.108; extra == "ai"
35
41
  Dynamic: author
36
42
  Dynamic: author-email
37
43
  Dynamic: classifier
@@ -40,6 +46,8 @@ Dynamic: description-content-type
40
46
  Dynamic: home-page
41
47
  Dynamic: keywords
42
48
  Dynamic: license
49
+ Dynamic: license-file
50
+ Dynamic: provides-extra
43
51
  Dynamic: requires-dist
44
52
  Dynamic: summary
45
53
 
@@ -51,6 +51,7 @@ toolium/resources/VisualTests.css
51
51
  toolium/resources/VisualTests.js
52
52
  toolium/resources/VisualTestsTemplate.html
53
53
  toolium/utils/__init__.py
54
+ toolium/utils/ai_utils.py
54
55
  toolium/utils/data_generator.py
55
56
  toolium/utils/dataset.py
56
57
  toolium/utils/download_files.py
@@ -1,9 +1,19 @@
1
1
  requests~=2.27
2
2
  selenium~=4.0
3
- playwright~=1.43
4
3
  Appium-Python-Client<5.0,>=2.3
5
4
  Pillow~=10.1
6
5
  screeninfo~=0.8
7
6
  lxml~=5.1
8
7
  Faker~=25.9
9
8
  phonenumbers~=8.13
9
+
10
+ [ai]
11
+ spacy~=3.8.7
12
+ sentence-transformers~=5.1
13
+ openai~=1.108
14
+
15
+ [ai:python_version < "3.10"]
16
+ transformers==4.56.2
17
+
18
+ [playwright]
19
+ playwright~=1.43
@@ -1 +0,0 @@
1
- 3.4.0.dev0
@@ -1,13 +0,0 @@
1
- pytest~=7.2
2
- coverage~=6.5
3
- coveralls~=3.3
4
- mock~=5.0
5
- requests-mock~=1.10
6
- Pygments~=2.14
7
- flake8~=5.0; python_version < '3.8'
8
- flake8~=6.0; python_version >= '3.8'
9
- build~=0.10
10
- wheel~=0.40
11
- twine~=4.0
12
- behave==1.2.6 # behave tests
13
- importlib_metadata==7.2.1
File without changes
File without changes
File without changes
File without changes