Skip to content

Commit 6fed1f0

Browse files
authored
✅ e2e: new workflow to test tiplite (#6388)
1 parent 24b7dd5 commit 6fed1f0

File tree

9 files changed

+205
-75
lines changed

9 files changed

+205
-75
lines changed
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
from pydantic import SecretStr
2+
3+
4+
def _mask(value):
5+
"""
6+
Mask the password, showing only the first and last characters
7+
or *** if very short passwords
8+
"""
9+
if len(value) > 2:
10+
masked_value = value[0] + "*" * (len(value) - 2) + value[-1]
11+
else:
12+
# In case of very short passwords
13+
masked_value = "*" * len(value)
14+
return masked_value
15+
16+
17+
def _hash(value):
18+
"""Uses hash number to mask the password"""
19+
return f"hash:{hash(value)}"
20+
21+
22+
class Secret4TestsStr(SecretStr):
23+
"""Prints a hint of the secret
24+
TIP: Can be handy for testing
25+
"""
26+
27+
def _display(self) -> str | bytes:
28+
# SEE overrides _SecretBase._display
29+
value = self.get_secret_value()
30+
return _mask(value) if value else ""
31+
32+
33+
assert str(Secret4TestsStr("123456890")) == "1*******0"
34+
assert "1*******0" in repr(Secret4TestsStr("123456890"))

services/static-webserver/client/source/class/osparc/product/TIPTeaser.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@ qx.Class.define("osparc.product.TIPTeaser", {
3535
});
3636

3737
this.getChildControl("teaser-text");
38+
39+
osparc.utils.Utils.setIdToWidget(this, "tipTeaserWindow");
40+
41+
const closeBtn = this.getChildControl("close-button");
42+
osparc.utils.Utils.setIdToWidget(closeBtn, "tipTeaserWindowCloseBtn");
3843
},
3944

4045
statics: {

tests/e2e-playwright/.gitignore

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
test-results
1+
.e2e-playwright-*.txt
22
assets
33
report.html
4-
.e2e-playwright-*.txt
54
report.xml
5+
test-results

tests/e2e-playwright/Makefile

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,13 +117,17 @@ CLASSIC_TIP_INPUT_FILE := .e2e-playwright-classictip-env.txt
117117
$(SLEEPERS_INPUT_FILE) $(JUPYTER_LAB_INPUT_FILE) $(CLASSIC_TIP_INPUT_FILE) $(S4L_INPUT_FILE):
118118
@read -p "Enter your product URL: " PRODUCT_URL; \
119119
read -p "Is the product billable [y/n]: " BILLABLE; \
120+
read -p "Is the product lite [y/n]: " IS_LITE; \
120121
read -p "Is the test running in autoscaled deployment [y/n]: " AUTOSCALED; \
121122
read -p "Enter your username: " USER_NAME; \
122123
read -s -p "Enter your password: " PASSWORD; echo ""; \
123124
echo "--product-url=$$PRODUCT_URL --user-name=$$USER_NAME --password=$$PASSWORD" > $@; \
124125
if [ "$$BILLABLE" = "y" ]; then \
125126
echo "--product-billable" >> $@; \
126127
fi; \
128+
if [ "$$IS_LITE" = "y" ]; then \
129+
echo "--product-lite" >> $@; \
130+
fi; \
127131
if [ "$$AUTOSCALED" = "y" ]; then \
128132
echo "--autoscaled" >> $@; \
129133
fi; \
@@ -183,4 +187,4 @@ define run_test_on_chrome
183187
endef
184188

185189
clean:
186-
@rm -rf $(SLEEPERS_INPUT_FILE) $(JUPYTER_LAB_INPUT_FILE) $(CLASSIC_TIP_INPUT_FILE)
190+
-@rm -rf $(SLEEPERS_INPUT_FILE) $(JUPYTER_LAB_INPUT_FILE) $(CLASSIC_TIP_INPUT_FILE)

tests/e2e-playwright/README.md

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,36 @@
1+
2+
3+
## Usage
4+
15
### Auto generate new test
2-
`playwright codegen sim4life.io`
6+
```
7+
playwright codegen sim4life.io
8+
```
39

410
### Run test locally with headed mode
511
```
612
pytest -s tests/sim4life.py --headed --browser chromium --product-billable --product-url https://sim4life.io/ --user-name YOUR_USERNAME --password YOUR_PASSWORD --service-key sim4life-8-0-0-dy
713
```
814

915
### Check test results output
10-
`playwright show-trace test-results/tests-sim4life-py-test-billable-sim4life-chromium/trace.zip`
16+
```
17+
playwright show-trace test-results/tests-sim4life-py-test-billable-sim4life-chromium/trace.zip
18+
```
1119

1220
### Run debug mode
13-
`PWDEBUG=1 pytest -s tests/sim4life.py`
21+
```
22+
PWDEBUG=1 pytest -s tests/sim4life.py
23+
```
1424

1525
### Run test in different browsers
16-
`pytest -s tests/sim4life.py --tracing on --html=report.html --browser chromium --browser firefox`
26+
```
27+
pytest -s tests/sim4life.py --tracing on --html=report.html --browser chromium --browser firefox
28+
```
1729

18-
### or in chrome/msedge
19-
`pytest -s tests/sim4life.py --tracing on --html=report.html --browser-channel chrome`
30+
### or in chrome/ms-edge
31+
```
32+
pytest -s tests/sim4life.py --tracing on --html=report.html --browser-channel chrome
33+
```
2034

21-
### Runs in CI
35+
## e2e CI
2236
- https://git.speag.com/oSparc/e2e-backend

tests/e2e-playwright/tests/conftest.py

Lines changed: 33 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
from playwright.sync_api import APIRequestContext, BrowserContext, Page, WebSocket
2323
from playwright.sync_api._generated import Playwright
2424
from pydantic import AnyUrl, TypeAdapter
25-
from pytest import Item
25+
from pytest_simcore.helpers.faker_factories import DEFAULT_TEST_PASSWORD
2626
from pytest_simcore.helpers.logging_tools import log_context
2727
from pytest_simcore.helpers.playwright import (
2828
MINUTE,
@@ -36,6 +36,7 @@
3636
decode_socketio_42_message,
3737
web_socket_default_log_handler,
3838
)
39+
from pytest_simcore.helpers.pydantic_extension import Secret4TestsStr
3940

4041
_PROJECT_CLOSING_TIMEOUT: Final[int] = 10 * MINUTE
4142
_OPENING_NEW_EMPTY_PROJECT_MAX_WAIT_TIME: Final[int] = 30 * SECOND
@@ -79,6 +80,12 @@ def pytest_addoption(parser: pytest.Parser) -> None:
7980
default=False,
8081
help="Whether product is billable or not",
8182
)
83+
group.addoption(
84+
"--product-lite",
85+
action="store_true",
86+
default=False,
87+
help="Whether product is lite version or not",
88+
)
8289
group.addoption(
8390
"--autoscaled",
8491
action="store_true",
@@ -116,7 +123,7 @@ def pytest_addoption(parser: pytest.Parser) -> None:
116123

117124

118125
# Dictionary to store start times of tests
119-
_test_start_times = {}
126+
_test_start_times: dict[str, datetime.datetime] = {}
120127

121128

122129
def pytest_runtest_setup(item):
@@ -144,7 +151,7 @@ def _construct_graylog_url(
144151
return f"{monitoring_url}/graylog/search?{query}"
145152

146153

147-
def pytest_runtest_makereport(item: Item, call):
154+
def pytest_runtest_makereport(item: pytest.Item, call):
148155
"""
149156
Hook to add extra information when a test fails.
150157
"""
@@ -171,7 +178,6 @@ def pytest_runtest_makereport(item: Item, call):
171178
)
172179
diagnostics["duration"] = str(end_time - start_time)
173180

174-
# Print the diagnostics report
175181
with log_context(
176182
logging.WARNING,
177183
f"ℹ️ Diagnostics report for {test_name} ---", # noqa: RUF001
@@ -217,23 +223,29 @@ def user_name(request: pytest.FixtureRequest, auto_register: bool, faker: Faker)
217223
@pytest.fixture
218224
def user_password(
219225
request: pytest.FixtureRequest, auto_register: bool, faker: Faker
220-
) -> str:
226+
) -> Secret4TestsStr:
221227
if auto_register:
222-
return faker.password(length=12)
228+
return Secret4TestsStr(DEFAULT_TEST_PASSWORD)
223229
if osparc_password := request.config.getoption("--password"):
224230
assert isinstance(osparc_password, str)
225-
return osparc_password
226-
return os.environ["USER_PASSWORD"]
231+
return Secret4TestsStr(osparc_password)
232+
return Secret4TestsStr(os.environ["USER_PASSWORD"])
227233

228234

229235
@pytest.fixture(scope="session")
230-
def product_billable(request: pytest.FixtureRequest) -> bool:
236+
def is_product_billable(request: pytest.FixtureRequest) -> bool:
231237
billable = request.config.getoption("--product-billable")
232238
return TypeAdapter(bool).validate_python(billable)
233239

234240

235241
@pytest.fixture(scope="session")
236-
def autoscaled(request: pytest.FixtureRequest) -> bool:
242+
def is_product_lite(request: pytest.FixtureRequest) -> bool:
243+
enabled = request.config.getoption("--product-lite")
244+
return TypeAdapter(bool).validate_python(enabled)
245+
246+
247+
@pytest.fixture(scope="session")
248+
def is_autoscaled(request: pytest.FixtureRequest) -> bool:
237249
autoscaled = request.config.getoption("--autoscaled")
238250
return TypeAdapter(bool).validate_python(autoscaled)
239251

@@ -280,7 +292,7 @@ def register(
280292
page: Page,
281293
product_url: AnyUrl,
282294
user_name: str,
283-
user_password: str,
295+
user_password: Secret4TestsStr,
284296
) -> Callable[[], AutoRegisteredUser]:
285297
def _do() -> AutoRegisteredUser:
286298
with log_context(
@@ -297,11 +309,13 @@ def _do() -> AutoRegisteredUser:
297309
for pass_id in ["registrationPass1Fld", "registrationPass2Fld"]:
298310
user_password_box = page.get_by_test_id(pass_id)
299311
user_password_box.click()
300-
user_password_box.fill(user_password)
312+
user_password_box.fill(user_password.get_secret_value())
301313
with page.expect_response(re.compile(r"/auth/register")) as response_info:
302314
page.get_by_test_id("registrationSubmitBtn").click()
303315
assert response_info.value.ok, response_info.value.json()
304-
return AutoRegisteredUser(user_email=user_name, password=user_password)
316+
return AutoRegisteredUser(
317+
user_email=user_name, password=user_password.get_secret_value()
318+
)
305319

306320
return _do
307321

@@ -311,7 +325,7 @@ def log_in_and_out(
311325
page: Page,
312326
product_url: AnyUrl,
313327
user_name: str,
314-
user_password: str,
328+
user_password: Secret4TestsStr,
315329
auto_register: bool,
316330
register: Callable[[], AutoRegisteredUser],
317331
) -> Iterator[WebSocket]:
@@ -352,7 +366,7 @@ def log_in_and_out(
352366
_user_email_box.fill(user_name)
353367
_user_password_box = page.get_by_test_id("loginPasswordFld")
354368
_user_password_box.click()
355-
_user_password_box.fill(user_password)
369+
_user_password_box.fill(user_password.get_secret_value())
356370
with page.expect_response(re.compile(r"/login")) as response_info:
357371
page.get_by_test_id("loginSubmitBtn").click()
358372
assert response_info.value.ok, f"{response_info.value.json()}"
@@ -392,7 +406,7 @@ def log_in_and_out(
392406
def create_new_project_and_delete(
393407
page: Page,
394408
log_in_and_out: WebSocket,
395-
product_billable: bool,
409+
is_product_billable: bool,
396410
api_request_context: APIRequestContext,
397411
product_url: AnyUrl,
398412
) -> Iterator[Callable[[tuple[RunningState], bool], dict[str, Any]]]:
@@ -411,7 +425,7 @@ def _(
411425
), "misuse of this fixture! only 1 study can be opened at a time. Otherwise please modify the fixture"
412426
with log_context(
413427
logging.INFO,
414-
f"Open project in {product_url=} as {product_billable=}",
428+
f"Open project in {product_url=} as {is_product_billable=}",
415429
) as ctx:
416430
waiter = SocketIOProjectStateUpdatedWaiter(expected_states=expected_states)
417431
timeout = (
@@ -473,7 +487,7 @@ def wait_for_done(response):
473487
...
474488
else:
475489
open_button.click()
476-
if product_billable:
490+
if is_product_billable:
477491
# Open project with default resources
478492
page.get_by_test_id("openWithResources").click()
479493
project_data = response_info.value.json()
@@ -512,7 +526,7 @@ def wait_for_done(response):
512526
for project_uuid in created_project_uuids:
513527
with log_context(
514528
logging.INFO,
515-
f"Delete project with {project_uuid=} in {product_url=} as {product_billable=}",
529+
f"Delete project with {project_uuid=} in {product_url=} as {is_product_billable=}",
516530
):
517531
response = api_request_context.delete(
518532
f"{product_url}v0/projects/{project_uuid}"

tests/e2e-playwright/tests/sim4life/test_sim4life.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ def test_sim4life(
3131
log_in_and_out: WebSocket,
3232
service_key: str,
3333
use_plus_button: bool,
34-
autoscaled: bool,
34+
is_autoscaled: bool,
3535
check_videostreaming: bool,
3636
):
3737
if use_plus_button:
@@ -49,7 +49,11 @@ def test_sim4life(
4949
assert len(node_ids) == 1, "Expected 1 node in the workbench!"
5050

5151
resp = wait_for_launched_s4l(
52-
page, node_ids[0], log_in_and_out, autoscaled=autoscaled, copy_workspace=False
52+
page,
53+
node_ids[0],
54+
log_in_and_out,
55+
autoscaled=is_autoscaled,
56+
copy_workspace=False,
5357
)
5458
s4l_websocket = resp["websocket"]
5559
with web_socket_default_log_handler(s4l_websocket):

tests/e2e-playwright/tests/sim4life/test_template.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ def test_template(
2424
create_project_from_template_dashboard: Callable[[str], dict[str, Any]],
2525
log_in_and_out: WebSocket,
2626
template_id: str,
27-
autoscaled: bool,
27+
is_autoscaled: bool,
2828
check_videostreaming: bool,
2929
):
3030
project_data = create_project_from_template_dashboard(template_id)
@@ -37,7 +37,7 @@ def test_template(
3737
assert len(node_ids) == 1, "Expected 1 node in the workbench!"
3838

3939
resp = wait_for_launched_s4l(
40-
page, node_ids[0], log_in_and_out, autoscaled=autoscaled, copy_workspace=True
40+
page, node_ids[0], log_in_and_out, autoscaled=is_autoscaled, copy_workspace=True
4141
)
4242
s4l_websocket = resp["websocket"]
4343
with web_socket_default_log_handler(s4l_websocket):

0 commit comments

Comments
 (0)