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.
- {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/CHANGELOG.rst +30 -2
- {toolium-3.4.0.dev0/toolium.egg-info → toolium-3.6.0.dev0}/PKG-INFO +13 -5
- toolium-3.6.0.dev0/VERSION +1 -0
- {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/requirements.txt +0 -1
- toolium-3.6.0.dev0/requirements_dev.txt +16 -0
- {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/setup.py +23 -2
- {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/driver_wrapper.py +8 -1
- toolium-3.6.0.dev0/toolium/utils/ai_utils.py +188 -0
- {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/utils/dataset.py +48 -28
- {toolium-3.4.0.dev0 → toolium-3.6.0.dev0/toolium.egg-info}/PKG-INFO +13 -5
- {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium.egg-info/SOURCES.txt +1 -0
- {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium.egg-info/requires.txt +11 -1
- toolium-3.4.0.dev0/VERSION +0 -1
- toolium-3.4.0.dev0/requirements_dev.txt +0 -13
- {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/LICENSE +0 -0
- {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/MANIFEST.in +0 -0
- {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/README.rst +0 -0
- {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/setup.cfg +0 -0
- {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/__init__.py +0 -0
- {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/behave/__init__.py +0 -0
- {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/behave/env_utils.py +0 -0
- {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/behave/environment.py +0 -0
- {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/config_driver.py +0 -0
- {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/config_driver_playwright.py +0 -0
- {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/config_files.py +0 -0
- {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/config_parser.py +0 -0
- {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/driver_wrappers_pool.py +0 -0
- {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/jira.py +0 -0
- {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/pageelements/__init__.py +0 -0
- {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/pageelements/button_page_element.py +0 -0
- {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/pageelements/checkbox_page_element.py +0 -0
- {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/pageelements/group_page_element.py +0 -0
- {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/pageelements/input_radio_page_element.py +0 -0
- {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/pageelements/input_text_page_element.py +0 -0
- {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/pageelements/link_page_element.py +0 -0
- {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/pageelements/page_element.py +0 -0
- {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/pageelements/page_elements.py +0 -0
- {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/pageelements/playwright/__init__.py +0 -0
- {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/pageelements/playwright/button_page_element.py +0 -0
- {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/pageelements/playwright/input_text_page_element.py +0 -0
- {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/pageelements/playwright/page_element.py +0 -0
- {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/pageelements/playwright/text_page_element.py +0 -0
- {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/pageelements/select_page_element.py +0 -0
- {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/pageelements/text_page_element.py +0 -0
- {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/pageobjects/__init__.py +0 -0
- {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/pageobjects/common_object.py +0 -0
- {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/pageobjects/mobile_page_object.py +0 -0
- {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/pageobjects/page_object.py +0 -0
- {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/pytest_fixtures.py +0 -0
- {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/resources/VisualTests.css +0 -0
- {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/resources/VisualTests.js +0 -0
- {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/resources/VisualTestsTemplate.html +0 -0
- {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/selenoid.py +0 -0
- {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/test_cases.py +0 -0
- {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/utils/__init__.py +0 -0
- {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/utils/data_generator.py +0 -0
- {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/utils/download_files.py +0 -0
- {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/utils/driver_utils.py +0 -0
- {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/utils/driver_wait_utils.py +0 -0
- {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/utils/path_utils.py +0 -0
- {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/utils/poeditor.py +0 -0
- {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/visual_test.py +0 -0
- {toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium.egg-info/dependency_links.txt +0 -0
- {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
|
+
v3.6.0
|
|
5
5
|
------
|
|
6
6
|
|
|
7
7
|
*Release date: In development*
|
|
8
8
|
|
|
9
|
-
-
|
|
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.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: toolium
|
|
3
|
-
Version: 3.
|
|
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
|
|
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
|
|
@@ -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=
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
268
|
-
|
|
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 =
|
|
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
|
-
|
|
769
|
-
|
|
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
|
-
|
|
773
|
-
logger.debug(f"Mapping language param '{
|
|
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 '{
|
|
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
|
|
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.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: toolium
|
|
3
|
-
Version: 3.
|
|
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
|
|
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
|
toolium-3.4.0.dev0/VERSION
DELETED
|
@@ -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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/pageelements/playwright/button_page_element.py
RENAMED
|
File without changes
|
{toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/pageelements/playwright/input_text_page_element.py
RENAMED
|
File without changes
|
|
File without changes
|
{toolium-3.4.0.dev0 → toolium-3.6.0.dev0}/toolium/pageelements/playwright/text_page_element.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|