diff --git a/doc/contribuing/testing.rst b/doc/contribuing/testing.rst index f582033c..5dc85ff1 100644 --- a/doc/contribuing/testing.rst +++ b/doc/contribuing/testing.rst @@ -61,14 +61,14 @@ In addition to pytest option, some flags can be set with the following arguments ``--dev-mode`` Shortcut for ``--keep-containers``, ``--keep-profiles`` and ``--keep-vnc``. This is useful, as you guess with its names, for development of tests. User can then log-in into - the ``sat`` container, launch a Python console, and work with the automated browser in + the ``backend`` container, launch a Python console, and work with the automated browser in real-time. Basic commands to launch a browser and log-in with test account are printed at the end of the tests. Note that if you want to have profiles created, or extra tools like the fake SMTP server, you'll have to launch at least one test which require them. - To log-in into the ``sat`` container, you can use the following command, from + To log-in into the ``backend`` container, you can use the following command, from ``/docker`` directory:: - $ docker-compose -f docker-compose_e2e.yml exec sat /bin/bash + $ docker-compose -f docker-compose_e2e.yml exec backend /bin/bash Then run a python console with given instructions @@ -100,7 +100,7 @@ Here are the fixture common to all e2e tests which are good to know: ``test_profiles`` Creates a bunch of test accounts which are available during the whole test session. Those account are destroyed once all the tests are finished (successful or not), except - if you set the ``SAT_TEST_E2E_KEEP_PROFILES`` environment variable (or use the + if you set the ``LIBERVIA_TEST_E2E_KEEP_PROFILES`` environment variable (or use the ``--keep-profiles`` flag in ``run_e2e.py``. The profiles created are in the form ``accountX`` for account on the ``server1.test``, @@ -127,7 +127,7 @@ Here are the fixture common to all e2e tests which are good to know: and ``fake_file.get_dest_hash(dest_file_path)`` will generate its hash once written. ``sent_emails`` - When used, a fake SMTP server (already configured in container's ``sat.conf``) will be + When used, a fake SMTP server (already configured in container's ``libervia.conf``) will be launched if it's not already, and all messages sent to it since the beginning of the test will be available in the given list. Message are subclasses of ``email.message.EmailMessage`` with the extra properties ``from_``, ``to``, ``subject`` @@ -135,21 +135,21 @@ Here are the fixture common to all e2e tests which are good to know: The SMTP server is terminated at the end of the test session. -jp e2e tests ------------- +libervia-cli e2e tests +---------------------- -End-to-end tests for ``jp`` are a good way to tests backend features without having to +End-to-end tests for ``libervia-cli`` are a good way to tests backend features without having to deal with frontends UI. Those tests use extensively the ``sh`` module, which helps -writing ``jp`` commands like if they where methods. +writing ``libervia-cli`` commands like if they where methods. Among the helping fixture (check the various ``conftest.py`` files for details), the following are specially good to know: -``jp_json`` - Set the ``json_raw`` output are parse it. When you use this instead of the normal ``jp``, +``li_json`` + Set the ``json_raw`` output are parse it. When you use this instead of the normal ``libervia-cli``, you'll get a Python object that you can manipulate easily. -``jp_elt`` +``li_elt`` Set the ``xml_raw`` output and parse it as a Twisted ``domish.Element``. When you use a command which can return XML, it is useful to get this object which is easy to manipulate in Python. @@ -161,7 +161,7 @@ following are specially good to know: method (this code is in a string which will be executed by Python interpreter, where the ``content`` variable is the received text). By default, the text is kept unmodified. - After ``editor`` has been used by the ``jp`` command, you can check its + After ``editor`` has been used by the ``libervia-cli`` command, you can check its ``original_content`` property to see the text that it received, and ``new_content`` property to see the text that has been written after updating the original content with the code set in ``set_filter``. @@ -198,14 +198,14 @@ examples Following examples have to be run from ``tests/e2e`` directory. -Run all tests for ``jp``:: +Run all tests for ``Libervia CLI``:: - $ ./run_e2e.py -k jp + $ ./run_e2e.py -k libervia-cli -Run all tests for ``Libervia`` with real-time visual feedback (note that you need to have +Run all tests for ``Libervia Web`` with real-time visual feedback (note that you need to have ``vncviewer`` installed and available in path, see above):: - $ ./run_e2e.py -k libervia --visual + $ ./run_e2e.py -k libervia-web --visual Run all tests with verbose mode (useful to know which test is currently running):: @@ -221,10 +221,10 @@ test to be sure to have test profiles created and fake SMTP server run…:: $ ./run_e2e.py -k user_can_create_account --dev-mode -…then to go into the ``sat`` container and work with the browser (to be run in ``docker`` +…then to go into the ``backend`` container and work with the browser (to be run in ``docker`` directory)…:: - $ docker-compose -f docker-compose_e2e.yml exec sat /bin/bash + $ docker-compose -f docker-compose_e2e.yml exec backend /bin/bash …and, inside the container, you can now run ``python3`` and enter instruction prints at the end of the test session. diff --git a/tests/e2e/conftest.py b/tests/e2e/conftest.py index 1ffe8a06..67985ce4 100644 --- a/tests/e2e/conftest.py +++ b/tests/e2e/conftest.py @@ -26,7 +26,7 @@ from aiosmtpd.controller import Controller from aiosmtpd.handlers import Message from email.message import EmailMessage import sh -from sh import jp +from sh import li import pytest @@ -35,7 +35,7 @@ class FakeFile: BUF_SIZE = 65535 def __init__(self): - self.tmp_dir_obj = tempfile.TemporaryDirectory(prefix="sat_e2e_test_files_") + self.tmp_dir_obj = tempfile.TemporaryDirectory(prefix="libervia_e2e_test_files_") self.tmp_dir_path = Path(self.tmp_dir_obj.name) self.source_files = self.tmp_dir_path / "source" self.source_files.mkdir() @@ -157,7 +157,7 @@ def test_profiles(): profile = f"account{account_idx}{profile_suff}" profiles.append(profile) try: - jp.account.create( + li.account.create( f"account{account_idx}@server{server_idx}.test", "test", profile=profile, @@ -167,14 +167,14 @@ def test_profiles(): # this is the conlict exit code, this can happen when tests are run # inside a container when --keep-profiles is used with run_e2e.py. pass - jp.profile.modify(profile="account1", default=True, connect=True) - jp.profile.connect(profile="account1_s2", connect=True) + li.profile.modify(profile="account1", default=True, connect=True) + li.profile.connect(profile="account1_s2", connect=True) yield tuple(profiles) # This environment may be used during tests development - if os.getenv("SAT_TEST_E2E_KEEP_PROFILES") == None: + if os.getenv("LIBERVIA_TEST_E2E_KEEP_PROFILES") == None: for profile in profiles: - jp.account.delete(profile=profile, connect=True, force=True) - jp.profile.delete(profile, force=True) + li.account.delete(profile=profile, connect=True, force=True) + li.profile.delete(profile, force=True) @pytest.fixture(scope="class") @@ -183,25 +183,25 @@ def pubsub_nodes(test_profiles): Both nodes will be created with "account1" profile, named "test" and have an "open" access model. - One node will account1's PEP, the other one on pubsub.server1.test. + One node will be on account1's PEP, the other one on pubsub.server1.test. """ - jp.pubsub.node.create( + li.pubsub.node.create( "-f", "access_model", "open", node="test", profile="account1", connect=True ) - jp.pubsub.node.create( + li.pubsub.node.create( "-f", "access_model", "open", service="pubsub.server1.test", node="test", profile="account1" ) yield - jp.pubsub.node.delete( + li.pubsub.node.delete( node="test", profile="account1", connect=True, force=True ) - jp.pubsub.node.delete( + li.pubsub.node.delete( service="pubsub.server1.test", node="test", profile="account1", force=True diff --git a/tests/e2e/jp/conftest.py b/tests/e2e/libervia-cli/conftest.py similarity index 88% rename from tests/e2e/jp/conftest.py rename to tests/e2e/libervia-cli/conftest.py index 94a41d8b..9f6f56a8 100644 --- a/tests/e2e/jp/conftest.py +++ b/tests/e2e/libervia-cli/conftest.py @@ -23,11 +23,11 @@ from pathlib import Path from textwrap import dedent import json import pytest -from sh import jp +from sh import li -class JpJson: - """jp like commands parsing result as JSON""" +class LiberviaCliJson: + """li like commands parsing result as JSON""" def __init__(self): self.subcommands = [] @@ -37,20 +37,20 @@ class JpJson: self.subcommands.clear() kwargs['output'] = 'json_raw' kwargs['_tty_out'] = False - cmd = jp(*args, **kwargs) + cmd = li(*args, **kwargs) return json.loads(cmd.stdout) def __getattr__(self, name): if name.startswith('_'): - # no jp subcommand starts with a "_", + # no li subcommand starts with a "_", # and pytest uses some attributes with this name scheme return super().__getattr__(name) self.subcommands.append(name) return self -class JpElt(JpJson): - """jp like commands parsing result as domishElement""" +class LiberviaCliElt(LiberviaCliJson): + """li like commands parsing result as domishElement""" def __init__(self): super().__init__() @@ -62,7 +62,7 @@ class JpElt(JpJson): self.subcommands.clear() kwargs['output'] = 'xml_raw' kwargs['_tty_out'] = False - cmd = jp(*args, **kwargs) + cmd = li(*args, **kwargs) return self.parser(cmd.stdout.decode().strip()) @@ -71,7 +71,7 @@ class Editor: def __init__(self): # temporary directory will be deleted Automatically when this object will be # destroyed - self.tmp_dir_obj = tempfile.TemporaryDirectory(prefix="sat_e2e_test_editor_") + self.tmp_dir_obj = tempfile.TemporaryDirectory(prefix="libervia_e2e_test_editor_") self.tmp_dir_path = Path(self.tmp_dir_obj.name) if not sys.executable: raise Exception("Can't find python executable") @@ -152,15 +152,15 @@ class Editor: @pytest.fixture(scope="session") -def jp_json(): - """Run jp with "json_raw" output, and returns the parsed value""" - return JpJson() +def li_json(): + """Run li with "json_raw" output, and returns the parsed value""" + return LiberviaCliJson() @pytest.fixture(scope="session") -def jp_elt(): - """Run jp with "xml_raw" output, and returns the parsed value""" - return JpElt() +def li_elt(): + """Run li with "xml_raw" output, and returns the parsed value""" + return LiberviaCliElt() @pytest.fixture() diff --git a/tests/e2e/jp/test_jp.py b/tests/e2e/libervia-cli/test_libervia-cli.py similarity index 71% rename from tests/e2e/jp/test_jp.py rename to tests/e2e/libervia-cli/test_libervia-cli.py index 9cab38aa..90fd756b 100644 --- a/tests/e2e/jp/test_jp.py +++ b/tests/e2e/libervia-cli/test_libervia-cli.py @@ -20,11 +20,11 @@ import os import shutil import pytest import sh -from sh import jp +from sh import li from sat.tools.common import uri -if os.getenv("SAT_TEST_ENV_E2E") is None: +if os.getenv("LIBERVIA_TEST_ENV_E2E") is None: pytest.skip( "skipping end-to-end tests, we are not in a test environment", allow_module_level=True @@ -36,118 +36,118 @@ pytestmark = pytest.mark.usefixtures("test_profiles") class TestInstall: - def test_jp_can_run(self): - jp("--version") + def test_li_can_run(self): + li("--version") -class TestJpAccount: +class TestLiberviaCliAccount: - def test_create_and_delete(self, jp_json): + def test_create_and_delete(self, li_json): """Create an account in-band, connect it, then delete it and its profile""" - jp.account.create( + li.account.create( "test_create@server1.test", "test", profile="test_create", host="server1.test" ) - profiles = jp_json.profile.list() + profiles = li_json.profile.list() assert "test_create" in profiles - jp.profile.connect(connect=True, profile="test_create") - jp.account.delete(profile="test_create", force=True) - jp.profile.delete("test_create", force=True) - profiles = jp_json.profile.list() + li.profile.connect(connect=True, profile="test_create") + li.account.delete(profile="test_create", force=True) + li.profile.delete("test_create", force=True) + profiles = li_json.profile.list() assert "test_create" not in profiles @pytest.mark.usefixtures("pubsub_nodes") -class TestJpPubsub: +class TestLiberviaCliPubsub: def test_node_create_info_delete(self): node_name = "tmp_node" with pytest.raises(sh.ErrorReturnCode_16): # the node should not exist - jp.pubsub.node.info(node=node_name) + li.pubsub.node.info(node=node_name) try: - jp.pubsub.node.create(node=node_name) - # if node exist has expected, following command won't raise an exception - metadata = jp.pubsub.node.info(node=node_name) + li.pubsub.node.create(node=node_name) + # if node exist as expected, following command won't raise an exception + metadata = li.pubsub.node.info(node=node_name) assert len(metadata.strip()) finally: - jp.pubsub.node.delete(node=node_name, force=True) + li.pubsub.node.delete(node=node_name, force=True) with pytest.raises(sh.ErrorReturnCode_16): # the node should not exist anymore - jp.pubsub.node.info(node=node_name) + li.pubsub.node.info(node=node_name) - def test_set_get_delete_purge(self, jp_elt): + def test_set_get_delete_purge(self, li_elt): content = "test item" payload = f"{content}" # we create 3 items and check them - item1_id = jp.pubsub.set(node="test", quiet=True, _in=payload) - item2_id = jp.pubsub.set(node="test", quiet=True, _in=payload) - item3_id = jp.pubsub.set(node="test", quiet=True, _in=payload) - parsed_elt = jp_elt.pubsub.get(node="test", item=item1_id) + item1_id = li.pubsub.set(node="test", quiet=True, _in=payload) + item2_id = li.pubsub.set(node="test", quiet=True, _in=payload) + item3_id = li.pubsub.set(node="test", quiet=True, _in=payload) + parsed_elt = li_elt.pubsub.get(node="test", item=item1_id) payload = parsed_elt.firstChildElement() assert payload.name == 'test' assert str(payload) == content - parsed_elt = jp_elt.pubsub.get(node="test", item=item2_id) + parsed_elt = li_elt.pubsub.get(node="test", item=item2_id) payload = parsed_elt.firstChildElement() assert payload.name == 'test' assert str(payload) == content - parsed_elt = jp_elt.pubsub.get(node="test", item=item3_id) + parsed_elt = li_elt.pubsub.get(node="test", item=item3_id) payload = parsed_elt.firstChildElement() assert payload.name == 'test' assert str(payload) == content # deleting first item should work - jp.pubsub.delete(node="test", item=item1_id, force=True) + li.pubsub.delete(node="test", item=item1_id, force=True) with pytest.raises(sh.ErrorReturnCode_16): - jp.pubsub.get(node="test", item=item1_id) + li.pubsub.get(node="test", item=item1_id) # there must be a least item2 and item3 in the node - node_items = jp_elt.pubsub.get(node="test") + node_items = li_elt.pubsub.get(node="test") assert len(list(node_items.elements())) >= 2 # after purge, node must be empty - jp.pubsub.node.purge(node="test", force=True) - node_items = jp_elt.pubsub.get(node="test") + li.pubsub.node.purge(node="test", force=True) + node_items = li_elt.pubsub.get(node="test") assert len(list(node_items.elements())) == 0 - def test_edit(self, editor, jp_elt): + def test_edit(self, editor, li_elt): content = "original item" payload = f"{content}" - item_id = jp.pubsub.set(node="test", quiet=True, _in=payload) + item_id = li.pubsub.set(node="test", quiet=True, _in=payload) editor.set_filter('content.replace("original", "edited")') - jp.pubsub.edit(node="test", item=item_id, _env=editor.env) + li.pubsub.edit(node="test", item=item_id, _env=editor.env) assert "original item" in editor.original_content - parsed_elt = jp_elt.pubsub.get(node="test", item=item_id) + parsed_elt = li_elt.pubsub.get(node="test", item=item_id) edited_payload = parsed_elt.firstChildElement() expected_edited_content = content.replace("original", "edited") assert edited_payload.name == 'test' assert str(edited_payload) == expected_edited_content - def test_affiliations(self, jp_json): - affiliations = jp_json.pubsub.affiliations() + def test_affiliations(self, li_json): + affiliations = li_json.pubsub.affiliations() assert affiliations["test"] == "owner" def test_uri(self): - built_uri = jp.pubsub.uri( + built_uri = li.pubsub.uri( service="pubsub.example.net", node="some_node" ).strip() assert built_uri == "xmpp:pubsub.example.net?;node=some_node" - built_uri = jp.pubsub.uri( + built_uri = li.pubsub.uri( service="pubsub.example.net", node="some_node", item="some_item" ).strip() assert built_uri == "xmpp:pubsub.example.net?;node=some_node;item=some_item" -class TestJpBlog: +class TestLiberviaCliBlog: MICROBLOG_NS = "urn:xmpp:microblog:0" - def test_set_get(self, jp_json): - jp.blog.set(_in="markdown **bold** [link](https://example.net)") - item_data = jp_json.blog.get(max=1) + def test_set_get(self, li_json): + li.blog.set(_in="markdown **bold** [link](https://example.net)") + item_data = li_json.blog.get(max=1) item = item_data[0][0] metadata = item_data[1] assert metadata['service'] == "account1@server1.test" @@ -170,13 +170,13 @@ class TestJpBlog: assert item['author'] == 'account1' assert item['author_jid'] == 'account1@server1.test' - def test_edit(self, editor, jp_json): + def test_edit(self, editor, li_json): payload_md = "content in **markdown**" editor.set_filter(repr(payload_md)) - jp.blog.edit(_env=editor.env) + li.blog.edit(_env=editor.env) assert len(editor.original_content) == 0 assert editor.new_content == payload_md - items_data = jp_json.blog.get(max=1) + items_data = li_json.blog.get(max=1) last_item = items_data[0][0] last_item_id = last_item['id'] assert last_item['content'] == "content in markdown" @@ -184,10 +184,10 @@ class TestJpBlog: "

content in markdown

" ) editor.set_filter('f"{content} extended"') - jp.blog.edit("--last-item", _env=editor.env) + li.blog.edit("--last-item", _env=editor.env) assert editor.original_content == payload_md assert editor.new_content == f"{payload_md} extended" - items_data = jp_json.blog.get(max=1) + items_data = li_json.blog.get(max=1) last_item = items_data[0][0] # we check that the id hasn't been modified assert last_item['id'] == last_item_id @@ -197,16 +197,16 @@ class TestJpBlog: ) -class TestJpFile: +class TestLiberviaCliFile: def test_upload_get(self, fake_file): source_file = fake_file.size(10240) source_file_hash = fake_file.get_source_hash(source_file) - upload_url = jp.file.upload(source_file).strip() + upload_url = li.file.upload(source_file).strip() dest_file = fake_file.new_dest_file() try: - jp.file.get(upload_url, dest_file=dest_file) + li.file.get(upload_url, dest_file=dest_file) dest_file_hash = fake_file.get_dest_hash(dest_file) finally: dest_file.unlink() @@ -216,11 +216,11 @@ class TestJpFile: def test_send_receive(self, fake_file): source_file = fake_file.size(10240) source_file_hash = fake_file.get_source_hash(source_file) - send_cmd = jp.file.send(source_file, "account1@server2.test", _bg=True) + send_cmd = li.file.send(source_file, "account1@server2.test", _bg=True) dest_path = fake_file.dest_files / "test_send_receive" dest_path.mkdir() try: - jp.file.receive( + li.file.receive( "account1@server1.test", profile="account1_s2", path=dest_path) dest_file = dest_path / source_file.name dest_file_hash = fake_file.get_dest_hash(dest_file) diff --git a/tests/e2e/libervia/conftest.py b/tests/e2e/libervia-web/conftest.py similarity index 88% rename from tests/e2e/libervia/conftest.py rename to tests/e2e/libervia-web/conftest.py index 0698a312..f2ed0d02 100644 --- a/tests/e2e/libervia/conftest.py +++ b/tests/e2e/libervia-web/conftest.py @@ -26,9 +26,9 @@ from pathlib import Path import helium -LIBERVIA_HOST = "libervia.test" -LIBERVIA_PORT_HTTPS = 8443 -BASE_URL = f"https://{LIBERVIA_HOST}:{LIBERVIA_PORT_HTTPS}" +WEB_HOST = "libervia-web.test" +WEB_PORT_HTTPS = 8443 +BASE_URL = f"https://{WEB_HOST}:{WEB_PORT_HTTPS}" SIZE_DESKTOP = (1024, 728) SIZE_MOBILE = (380, 640) accounts_cookies = {} @@ -48,7 +48,7 @@ def screenshot_on_failure(request): yield if request.node.rep_setup.passed: if request.node.rep_call.failed: - report_dir = Path(os.getenv("SAT_TEST_REPORT_DIR", "/tmp/tests_report")) + report_dir = Path(os.getenv("LIBERVIA_TEST_REPORT_DIR", "/tmp/tests_report")) dest_dir = report_dir/"screenshots" dest_dir.mkdir(parents=True, exist_ok=True) filename = f"{datetime.now().isoformat()}_{request.node.name}.png" @@ -76,15 +76,15 @@ def wait_for_socket(host, port, retries=30): @pytest.fixture(scope="session") def browser(): - if os.getenv("SAT_TEST_E2E_LIBERVIA_NO_HEADLESS") is not None: + if os.getenv("LIBERVIA_TEST_E2E_WEB_NO_HEADLESS") is not None: kwargs = {} else: kwargs = {"headless": True} driver = helium.start_firefox(**kwargs) driver.set_window_size(*SIZE_DESKTOP) - wait_for_socket(LIBERVIA_HOST, LIBERVIA_PORT_HTTPS) + wait_for_socket(WEB_HOST, WEB_PORT_HTTPS) yield helium - if os.getenv("SAT_TEST_E2E_LIBERVIA_KEEP_BROWSER") is None: + if os.getenv("LIBERVIA_TEST_E2E_WEB_KEEP_BROWSER") is None: helium.kill_browser() @@ -97,7 +97,7 @@ def log_in(browser, account): account_cookies = accounts_cookies[account] except KeyError: browser.get_driver().delete_all_cookies() - browser.go_to("https://libervia.test:8443/login") + browser.go_to("https://libervia-web.test:8443/login") browser.write(account, into="login") browser.write("test", into="password") browser.click("log in") diff --git a/tests/e2e/libervia/test_libervia.py b/tests/e2e/libervia-web/test_libervia-web.py similarity index 93% rename from tests/e2e/libervia/test_libervia.py rename to tests/e2e/libervia-web/test_libervia-web.py index 54714cc2..3fae8a85 100644 --- a/tests/e2e/libervia/test_libervia.py +++ b/tests/e2e/libervia-web/test_libervia-web.py @@ -25,7 +25,7 @@ from helium import ( ) -if os.getenv("SAT_TEST_ENV_E2E_LIBERVIA") is None: +if os.getenv("LIBERVIA_TEST_ENV_E2E_WEB") is None: pytest.skip( "skipping end-to-end tests, we are not in a test environment for Libervia", allow_module_level=True @@ -37,7 +37,7 @@ pytestmark = pytest.mark.usefixtures("test_profiles", "screenshot_on_failure") class TestLogin: def test_user_can_create_account(self, nobody_logged_in, sent_emails): - go_to("https://libervia.test:8443/login") + go_to("https://libervia-web.test:8443/login") click("no account yet") write("new_account", into="login") write("some_email@example.net", into="email") @@ -61,7 +61,7 @@ class TestLogin: # use can now log-in def test_user_can_log_in(self, nobody_logged_in): - go_to("https://libervia.test:8443/login") + go_to("https://libervia-web.test:8443/login") write("account1_s3", into="login") write("test", into="password") click("log in") @@ -69,7 +69,7 @@ class TestLogin: assert Button("Disconnect").exists() def test_wrong_password_fails(self, nobody_logged_in): - go_to("https://libervia.test:8443/login") + go_to("https://libervia-web.test:8443/login") write("account1_s2", into="login") write("wrong_password", into="password") click("log in") @@ -78,13 +78,13 @@ class TestLogin: class TestPhotos: ACCOUNT1_ALBUMS_URL = ( - "https://libervia.test:8443/photos/album/account1@files.server1.test/albums" + "https://libervia-web.test:8443/photos/album/account1@files.server1.test/albums" ) TEST_ALBUM_URL = f"{ACCOUNT1_ALBUMS_URL}/test%20album" @pytest.mark.dependency(name="create_album") def test_user_can_create_album(self, log_in_account1): - go_to("https://libervia.test:8443/photos") + go_to("https://libervia-web.test:8443/photos") wait_until(Link("create").exists) click("create") write("test album", into="album name") @@ -92,6 +92,7 @@ class TestPhotos: album_link = Link("test album") wait_until(album_link.exists) click(album_link) + wait_until(lambda: S("#file_drop").exists()) wait_until(lambda: not S("#loading_screen").exists()) drag_file("/src/sat/tests/_files/test_1.jpg", "drop photos here") wait_until(lambda: len(find_all(S("div.progress_finished")))==1) @@ -138,7 +139,7 @@ class TestPhotos: go_to(self.TEST_ALBUM_URL) wait_until(lambda: not S("#loading_screen").exists()) click("manage invitations") - assert Text("people who can access this album").exists() + assert Text("people who can access this page").exists() contact_input = S("input[name='contact']") write("account1@server2.test", into=contact_input) press(ENTER) @@ -158,7 +159,7 @@ class TestPhotos: go_to(self.TEST_ALBUM_URL) wait_until(lambda: not S("#loading_screen").exists()) click("manage invitations") - assert Text("people who can access this album").exists() + assert Text("people who can access this page").exists() click("invite by email") wait_until(Text("Invite somebody by email").exists) write("somebody@example.net", "email") diff --git a/tests/e2e/run_e2e.py b/tests/e2e/run_e2e.py index b5297e5e..8557529f 100755 --- a/tests/e2e/run_e2e.py +++ b/tests/e2e/run_e2e.py @@ -25,10 +25,10 @@ from textwrap import dedent from datetime import datetime import sh import io -import re import sat_templates import libervia from sat.core import exceptions +from sat.tools.common import regex import yaml try: from yaml import CLoader as Loader, CDumper as Dumper @@ -48,22 +48,48 @@ dev_mode_inst = dedent("""\ from helium import * start_firefox() - go_to("https://libervia.test:8443/login") + go_to("https://libervia-web.test:8443/login") write("account1", "login") write("test", "password") click("log in") """) report_buffer = io.StringIO() +live_out_buf = [] +live_err_buf = [] def live_out(data): - sys.stdout.write(data) + if live_out_buf: + # we may get bytes when buffer is reached and we are in the middle of an unicode + # sequence. In this case we buffer it, and print it when it's complete + if isinstance(data, str): + data = b''.join(live_out_buf).decode() + data + live_out_buf.clear() + else: + live_out_buf.append(data) + return + try: + sys.stdout.write(data) + except TypeError: + live_out_buf.append(data) + return sys.stdout.flush() report_buffer.write(data) def live_err(data): - sys.stderr.write(data) + if live_err_buf: + if isinstance(data, str): + data = b''.join(live_err_buf).decode() + data + live_err_buf.clear() + else: + live_err_buf.append(data) + return + try: + sys.stderr.write(data) + except TypeError: + live_err_buf.append(data) + return sys.stderr.flush() report_buffer.write(data) @@ -82,9 +108,24 @@ def get_opt(opt_name): def set_env(override, name, value="1"): """Set environement variable""" - environment = override["services"]["sat"].setdefault("environment", {}) + environment = override["services"]["backend"].setdefault("environment", {}) environment[name] = value +def write_report_log(path, log_raw, with_ansi=False): + log_raw = str(log_raw) + if with_ansi: + # we save 2 versions: one with ANSI escape codes + report_ansi = path.with_suffix(".ansi") + with report_ansi.open('w') as f: + f.write(log_raw) + # and one cleaner, without them + report_log = path.with_suffix(".log") + with report_log.open('w') as f: + f.write(regex.RE_ANSI_REMOVE.sub('', log_raw)) + else: + report_log = path.with_suffix(".log") + with report_log.open('w') as f: + f.write(log_raw) def use_e2e_env(): visual = get_opt(OPT_VISUAL) @@ -105,7 +146,7 @@ def use_e2e_env(): package_path = p / "sat" docker_path = p / "docker" if package_path.is_dir() and docker_path.is_dir(): - sat_root_path = p + backend_root_path = p break else: raise exceptions.NotFound( @@ -113,14 +154,14 @@ def use_e2e_env(): "from the backend repository?" ) - libervia_path = Path(libervia.__file__).parent.resolve() - libervia_root_path = libervia_path.parent - if (libervia_root_path / ".hg").is_dir(): - libervia_source = libervia_root_path - libervia_target = "/src/libervia" + libervia_web_path = Path(libervia.__file__).parent.resolve() + libervia_web_root_path = libervia_web_path.parent + if (libervia_web_root_path / ".hg").is_dir(): + libervia_web_source = libervia_web_root_path + libervia_web_target = "/src/libervia" else: - libervia_source = libervia_path - libervia_target = "/src/libervia/libervia" + libervia_web_source = libervia_web_path + libervia_web_target = "/src/libervia/libervia" sat_templates_path = Path(sat_templates.__file__).parent.resolve() sat_templates_root_path = sat_templates_path.parent @@ -131,9 +172,9 @@ def use_e2e_env(): sat_templates_source = sat_templates_path sat_templates_target = "/src/sat_templates/sat_templates" - compose_e2e_path = docker_path / "docker-compose_e2e.yml" + compose_e2e_path = docker_path / "docker-compose-e2e.yml" if not compose_e2e_path.is_file(): - raise exceptions.NotFound('"docker-compose_e2e.yml" file can\'t be found') + raise exceptions.NotFound('"docker-compose-e2e.yml" file can\'t be found') with tempfile.TemporaryDirectory(prefix="sat_test_e2e_") as temp_dir: override_path = Path(temp_dir) / "test_override.yml" @@ -141,21 +182,21 @@ def use_e2e_env(): dedent(f"""\ version: "3.6" services: - sat: + backend: volumes: - type: bind - source: {sat_root_path} + source: {backend_root_path} target: /src/sat read_only: true - libervia: + web: volumes: - type: bind - source: {sat_root_path} + source: {backend_root_path} target: /src/sat read_only: true - type: bind - source: {libervia_source} - target: {libervia_target} + source: {libervia_web_source} + target: {libervia_web_target} read_only: true - type: bind source: {sat_templates_source} @@ -167,13 +208,13 @@ def use_e2e_env(): ) if keep_profiles: - set_env(override, "SAT_TEST_E2E_KEEP_PROFILES") + set_env(override, "LIBERVIA_TEST_E2E_KEEP_PROFILES") if visual: - set_env(override, "SAT_TEST_E2E_LIBERVIA_NO_HEADLESS") + set_env(override, "LIBERVIA_TEST_E2E_WEB_NO_HEADLESS") if keep_browser: - set_env(override, "SAT_TEST_E2E_LIBERVIA_KEEP_BROWSER") + set_env(override, "LIBERVIA_TEST_E2E_WEB_KEEP_BROWSER") with override_path.open("w") as f: yaml.dump(override, f, Dumper=Dumper) @@ -183,13 +224,13 @@ def use_e2e_env(): docker_compose.up("-d") p = docker_compose.exec( - "-T", "--workdir", "/src/sat/tests", "sat", + "-T", "--workdir", "/src/sat/tests", "backend", "pytest", "-o", "cache_dir=/tmp", *sys.argv[1:], color="yes", _in=sys.stdin, _out=live_out, _out_bufsize=0, _err=live_err, _err_bufsize=0, _bg=True ) if visual: - vnc_port = docker_compose.port("sat", "5900").split(':', 1)[1].strip() + vnc_port = docker_compose.port("backend", "5900").split(':', 1)[1].strip() p_vnc = sh.vncviewer( f"localhost:{vnc_port}", _bg=True, @@ -204,22 +245,25 @@ def use_e2e_env(): try: p.wait() except sh.ErrorReturnCode as e: - sat_cont_id = docker_compose.ps("-q", "sat").strip() - report_dest = Path(f"reports_{datetime.now().isoformat()}/") + libervia_cont_id = docker_compose.ps("-q", "backend").strip() + report_dest = Path(f"report_{datetime.now().isoformat()}/") # we need to make `report_dest` explicitely local with "./", otherwise # docker parse takes it as a container path due to the presence of ":" # with `isoformat()`. - sh.docker.cp(f"{sat_cont_id}:/reports", f"./{report_dest}") - # we save 2 versions: one with ANSI escape codes - report_ansi = report_dest / "report.ansi" - with report_ansi.open('w') as f: - f.write(report_buffer.getvalue()) - # and one without (cf. https://stackoverflow.com/a/14693789) - ansi_escape = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])') - report_log = report_dest / "report.log" - with report_log.open('w') as f: - f.write(ansi_escape.sub('', report_buffer.getvalue())) - + sh.docker.cp(f"{libervia_cont_id}:/reports", f"./{report_dest}") + write_report_log( + report_dest/"report", + report_buffer.getvalue(), + with_ansi=True + ) + write_report_log( + report_dest/"backend", + docker_compose.logs("--no-log-prefix", "backend") + ) + write_report_log( + report_dest/"web", + docker_compose.logs("--no-log-prefix", "web") + ) print(f"report saved to {report_dest}") sys.exit(e.exit_code) finally: