From 8ae8a9183ea0e0d0d34ef5cbb05df33715498185 Mon Sep 17 00:00:00 2001 From: lauty95 Date: Fri, 13 Feb 2026 14:06:21 +0000 Subject: [PATCH 01/10] fix: expect added to be visible --- beam/tests/mobile/test_manufacture.py | 3 +++ beam/tests/mobile/test_mobile.py | 4 ++++ beam/tests/mobile/test_receive.py | 15 +++++++++++++++ 3 files changed, 22 insertions(+) diff --git a/beam/tests/mobile/test_manufacture.py b/beam/tests/mobile/test_manufacture.py index 12b1d171..76e8de8d 100644 --- a/beam/tests/mobile/test_manufacture.py +++ b/beam/tests/mobile/test_manufacture.py @@ -50,12 +50,15 @@ def test_complete_partial_stock_entry(page): # navigate in the following order: Home -> Manufacture -> Work Order page.get_by_text("Manufacture").click() + + expect(page.locator("css=.beam_list-item").first).to_be_visible() page.locator("css=.beam_list-item").first.click() # get the selected Work Order order_id = page.url.split("/")[-1] assert order_id + expect(page.locator("css=.box .beam_list-item").first).to_be_visible() # ensure there are no existing Stock Entries against this Work Order entry = frappe.db.exists( "Stock Entry", diff --git a/beam/tests/mobile/test_mobile.py b/beam/tests/mobile/test_mobile.py index de57efa8..26d46cfe 100644 --- a/beam/tests/mobile/test_mobile.py +++ b/beam/tests/mobile/test_mobile.py @@ -21,8 +21,12 @@ def test_scan_item_barcode(page, route): # navigate in the following order: Home -> List -> Form page.get_by_text(route).click() + # wait for list to load + expect(page.locator("css=.beam_list-item").first).to_be_visible() page.locator("css=.beam_list-item").first.click() + # wait for items to load after navigation + expect(page.locator("css=.box .beam_list-item").first).to_be_visible() # find the first item in the list item = page.locator("css=.box .beam_list-item").first item_name, *others = item.inner_text().split("\n") diff --git a/beam/tests/mobile/test_receive.py b/beam/tests/mobile/test_receive.py index fac92732..33060241 100644 --- a/beam/tests/mobile/test_receive.py +++ b/beam/tests/mobile/test_receive.py @@ -22,6 +22,8 @@ @pytest.mark.order(2) def test_scan_invalid_barcode(page): page.get_by_text("Receive").click() + # wait for list to load + expect(page.locator("css=.beam_list-item").first).to_be_visible() page.locator("css=.beam_list-item").first.click() # get the selected Purchase Order @@ -30,6 +32,8 @@ def test_scan_invalid_barcode(page): order_id = path_parts[-1] if path_parts else None assert order_id + # wait for items to load after navigation + expect(page.locator("css=.box .beam_item-count").first).to_be_visible() # find all items in the list all_item_counts = page.locator("css=.box .beam_item-count") @@ -83,6 +87,8 @@ def test_receive_without_scanning(page): """Test trying to receive without scanning any items""" # navigate to a Purchase Order page.get_by_text("Receive").click() + # wait for list to load + expect(page.locator("css=.beam_list-item").first).to_be_visible() page.locator("css=.beam_list-item").first.click() # get the selected Purchase Order @@ -91,6 +97,8 @@ def test_receive_without_scanning(page): order_id = path_parts[-1] if path_parts else None assert order_id + # wait for items to load after navigation + expect(page.locator("css=.box .beam_list-item").first).to_be_visible() item = page.locator("css=.box .beam_list-item").first item_code, *others = item.inner_text().split("\n") @@ -136,6 +144,8 @@ def test_receive_without_scanning(page): def test_complete_partial_receipt(page): # navigate in the following order: Home -> Receive -> Purchase Order page.get_by_text("Receive").click() + + expect(page.locator("css=.beam_list-item").first).to_be_visible() page.locator("css=.beam_list-item").first.click() # get the selected Purchase Order @@ -148,6 +158,7 @@ def test_complete_partial_receipt(page): assert order_id + expect(page.locator("css=.box .beam_list-item").first).to_be_visible() # find the first item in the list item = page.locator("css=.box .beam_list-item").first item_code, *others = item.inner_text().split("\n") @@ -218,6 +229,8 @@ def test_rapid_barcode_scanning(page): """Test scanning multiple barcodes quickly""" # navigate to a Purchase Order page.get_by_text("Receive").click() + # wait for list to load + expect(page.locator("css=.beam_list-item").first).to_be_visible() page.locator("css=.beam_list-item").first.click() # get the selected Purchase Order @@ -226,6 +239,8 @@ def test_rapid_barcode_scanning(page): order_id = path_parts[-1] if path_parts else None assert order_id + # wait for items to load after navigation + expect(page.locator("css=.box .beam_list-item").first).to_be_visible() # find the first item in the list item = page.locator("css=.box .beam_list-item").first item_code, *others = item.inner_text().split("\n") From 739e0abf6b3e02b431cf30bda28afcf3d3e830b0 Mon Sep 17 00:00:00 2001 From: lauty95 Date: Fri, 13 Feb 2026 17:57:54 +0000 Subject: [PATCH 02/10] fix: linters --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index de282cf5..d8b30f14 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -55,7 +55,7 @@ repos: - tomli - repo: https://github.com/agritheory/test_utils - rev: v1.0.0 + rev: v1.14.0 hooks: - id: update_pre_commit_config - id: validate_copyright From 2b1e3351e61279f11ddd9df02d6be62158f3bbe9 Mon Sep 17 00:00:00 2001 From: lauty95 Date: Fri, 13 Feb 2026 17:58:32 +0000 Subject: [PATCH 03/10] feat: beam mobile user with no BEAM Mobile User role --- beam/tests/fixtures.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/beam/tests/fixtures.py b/beam/tests/fixtures.py index bf60c591..647afda9 100644 --- a/beam/tests/fixtures.py +++ b/beam/tests/fixtures.py @@ -854,4 +854,21 @@ "department": "Operations", "designation": "Baker", }, + { + "name": "Marcus Reynolds", + "gender": "Male", + "date_of_birth": "1990-05-15", + "date_of_joining": "2023-03-01", + "address": { + "address_line1": "456 Testing Lane", + "city": "Boston", + "state": "MA", + "postal_code": "02101", + }, + "phone": "(555) 123-4567", + "roles": ["Stock User"], # NO BEAM Mobile User role - for restriction tests + "reports_to": "Tristan Hawkins", + "department": "Operations", + "designation": "Warehouse Associate", + }, ] From 44742a24629e6003180635b166f30aeb4e0552d1 Mon Sep 17 00:00:00 2001 From: lauty95 Date: Fri, 13 Feb 2026 17:59:25 +0000 Subject: [PATCH 04/10] feat: is login by scan --- beam/tests/mobile/conftest.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/beam/tests/mobile/conftest.py b/beam/tests/mobile/conftest.py index 20ef8000..367b98d2 100644 --- a/beam/tests/mobile/conftest.py +++ b/beam/tests/mobile/conftest.py @@ -18,19 +18,24 @@ def browser_context_args(browser_context_args): @pytest.fixture(autouse=True) -def setup(page): +def setup(page, request): # delete all existing draft Purchase Receipts delete_draft_records(["Purchase Receipt", "Stock Entry"]) page.set_default_timeout(5000) base_url = frappe.utils.get_url() - page.goto(base_url) - # visiting the home page redirects to login page - page.get_by_role("textbox", name="Email").fill("support@agritheory.dev") - page.get_by_role("textbox", name="Password").fill("admin") - page.get_by_role("button", name="Login").click() # this will redirect to `/beam` + # Skip auto-login for scan-to-login tests (they need to start from login page) + is_login_test = "scan_to_login" in request.node.name + + if not is_login_test: + page.goto(base_url) + # visiting the home page redirects to login page + page.get_by_role("textbox", name="Email").fill("support@agritheory.dev") + page.get_by_role("textbox", name="Password").fill("admin") + page.get_by_role("button", name="Login").click() # this will redirect to `/beam` + yield # delete all Purchase Receipts created during the test From b119de5a61ef87f5a5de455d7e1dbd95effd3338 Mon Sep 17 00:00:00 2001 From: lauty95 Date: Fri, 13 Feb 2026 17:59:47 +0000 Subject: [PATCH 05/10] test: scan to login --- beam/tests/mobile/test_scan_to_login.py | 90 +++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 beam/tests/mobile/test_scan_to_login.py diff --git a/beam/tests/mobile/test_scan_to_login.py b/beam/tests/mobile/test_scan_to_login.py new file mode 100644 index 00000000..5154b05e --- /dev/null +++ b/beam/tests/mobile/test_scan_to_login.py @@ -0,0 +1,90 @@ +# Copyright (c) 2024, AgriTheory and contributors +# For license information, please see license.txt + +import frappe +import pytest +from playwright.sync_api import expect + +from beam.tests.test_utils import use_current_db_transaction + +MOBILE_USER_EMAIL = "dsolomon@cfc.co" # Has "BEAM Mobile User" role +NON_MOBILE_USER_EMAIL = "mreynolds@cfc.co" # Does NOT have "BEAM Mobile User" role + + +def get_user_barcode(user_email): + """Get the barcode for a user from the database""" + barcode = frappe.db.get_value( + "Item Barcode", + {"parent": user_email, "parenttype": "User"}, + "barcode" + ) + return barcode + + +def set_beam_setting(field, value): + """Helper to update BEAM Settings for tests""" + company = frappe.defaults.get_defaults().get("company") + beam_settings = frappe.get_doc("BEAM Settings", {"company": company}) + beam_settings.set(field, value) + beam_settings.save() + frappe.db.commit() + + +def logout(page): + page.goto(f"{frappe.utils.get_url()}/api/method/logout") + page.wait_for_timeout(1000) + + +@pytest.mark.order(7) +def test_scan_to_login_success_all_users(page): + """Test successful login scanning - All Users mode""" + with use_current_db_transaction(): + set_beam_setting("enable_scan_to_login", "All Users") + mobile_user_barcode = get_user_barcode(MOBILE_USER_EMAIL) + assert mobile_user_barcode, f"Barcode not found for user {MOBILE_USER_EMAIL}" + + # Navigate to login page + page.goto(f"{frappe.utils.get_url()}/login") + page.wait_for_load_state("networkidle") + + expect(page).to_have_url(frappe.utils.get_url() + "/login#login") + + with page.expect_request( + lambda request: request.headers.get("x-frappe-cmd") == "beam.beam.scan.user_login.scan_login" + ): + page.evaluate("barcode => window.scanHandler.scanner.simulate(document, barcode)", mobile_user_barcode) + + page.wait_for_timeout(2000) + expect(page).to_have_url(frappe.utils.get_url() + "/beam#/") + + # Verify user is authenticated by checking session in browser + logged_in_user = page.evaluate("() => frappe.session.user") + assert logged_in_user == MOBILE_USER_EMAIL, f"Expected {MOBILE_USER_EMAIL}, got {logged_in_user}" + + logout(page) + + +@pytest.mark.order(8) +def test_scan_to_login_invalid_barcode(page): + """Test rejection of non-user barcode""" + with use_current_db_transaction(): + set_beam_setting("enable_scan_to_login", "All Users") + item_barcode = frappe.get_value("Item Barcode", {"parent": "Butter"}, "barcode") + assert item_barcode, "Item barcode not found for test" + + # Navigate to login page + page.goto(f"{frappe.utils.get_url()}/login#login") + page.wait_for_load_state("networkidle") + + page.evaluate("barcode => window.scanHandler.scanner.simulate(document, barcode)", item_barcode) + page.wait_for_timeout(1000) + + # Verify error message appears + error_message = page.locator(".msgprint-dialog .modal-body") + expect(error_message).to_be_visible(timeout=5000) + expect(error_message).to_contain_text("Wrong barcode") + + # Verify we're still on login page + expect(page).to_have_url(frappe.utils.get_url() + "/login#login") + + From f364b3eff68a2bc2f1686eb1e95826cba2d54245 Mon Sep 17 00:00:00 2001 From: lauty95 Date: Fri, 20 Feb 2026 12:30:12 +0000 Subject: [PATCH 06/10] test: login behaviour --- beam/tests/mobile/test_scan_to_login.py | 201 ++++++++++++++++++++++-- 1 file changed, 187 insertions(+), 14 deletions(-) diff --git a/beam/tests/mobile/test_scan_to_login.py b/beam/tests/mobile/test_scan_to_login.py index 5154b05e..41998d61 100644 --- a/beam/tests/mobile/test_scan_to_login.py +++ b/beam/tests/mobile/test_scan_to_login.py @@ -14,9 +14,7 @@ def get_user_barcode(user_email): """Get the barcode for a user from the database""" barcode = frappe.db.get_value( - "Item Barcode", - {"parent": user_email, "parenttype": "User"}, - "barcode" + "Item Barcode", {"parent": user_email, "parenttype": "User"}, "barcode" ) return barcode @@ -42,25 +40,27 @@ def test_scan_to_login_success_all_users(page): set_beam_setting("enable_scan_to_login", "All Users") mobile_user_barcode = get_user_barcode(MOBILE_USER_EMAIL) assert mobile_user_barcode, f"Barcode not found for user {MOBILE_USER_EMAIL}" - + # Navigate to login page page.goto(f"{frappe.utils.get_url()}/login") page.wait_for_load_state("networkidle") - + expect(page).to_have_url(frappe.utils.get_url() + "/login#login") - + with page.expect_request( lambda request: request.headers.get("x-frappe-cmd") == "beam.beam.scan.user_login.scan_login" ): - page.evaluate("barcode => window.scanHandler.scanner.simulate(document, barcode)", mobile_user_barcode) - + page.evaluate( + "barcode => window.scanHandler.scanner.simulate(document, barcode)", mobile_user_barcode + ) + page.wait_for_timeout(2000) expect(page).to_have_url(frappe.utils.get_url() + "/beam#/") - + # Verify user is authenticated by checking session in browser logged_in_user = page.evaluate("() => frappe.session.user") assert logged_in_user == MOBILE_USER_EMAIL, f"Expected {MOBILE_USER_EMAIL}, got {logged_in_user}" - + logout(page) @@ -71,20 +71,193 @@ def test_scan_to_login_invalid_barcode(page): set_beam_setting("enable_scan_to_login", "All Users") item_barcode = frappe.get_value("Item Barcode", {"parent": "Butter"}, "barcode") assert item_barcode, "Item barcode not found for test" - + # Navigate to login page page.goto(f"{frappe.utils.get_url()}/login#login") page.wait_for_load_state("networkidle") - + page.evaluate("barcode => window.scanHandler.scanner.simulate(document, barcode)", item_barcode) page.wait_for_timeout(1000) - + # Verify error message appears error_message = page.locator(".msgprint-dialog .modal-body") expect(error_message).to_be_visible(timeout=5000) expect(error_message).to_contain_text("Wrong barcode") - + + # Verify we're still on login page + expect(page).to_have_url(frappe.utils.get_url() + "/login#login") + + +@pytest.mark.order(9) +def test_scan_to_login_mobile_users_only_success(page): + """Test login with mobile user role restriction - success case""" + with use_current_db_transaction(): + set_beam_setting("enable_scan_to_login", "Mobile Users Only") + + mobile_user_barcode = get_user_barcode(MOBILE_USER_EMAIL) + assert mobile_user_barcode, f"Barcode not found for user {MOBILE_USER_EMAIL}" + + page.goto(f"{frappe.utils.get_url()}/login") + page.wait_for_load_state("networkidle") + + with page.expect_request( + lambda request: request.headers.get("x-frappe-cmd") == "beam.beam.scan.user_login.scan_login" + ): + page.evaluate( + "barcode => window.scanHandler.scanner.simulate(document, barcode)", mobile_user_barcode + ) + + page.wait_for_timeout(2000) + expect(page).to_have_url(frappe.utils.get_url() + "/beam#/") + + logged_in_user = page.evaluate("() => frappe.session.user") + assert logged_in_user == MOBILE_USER_EMAIL + + logout(page) + + +@pytest.mark.order(10) +def test_scan_to_login_mobile_users_only_reject(page): + """Test rejection when user lacks BEAM Mobile User role""" + with use_current_db_transaction(): + set_beam_setting("enable_scan_to_login", "Mobile Users Only") + non_mobile_user_barcode = get_user_barcode(NON_MOBILE_USER_EMAIL) + assert non_mobile_user_barcode, f"Barcode not found for user {NON_MOBILE_USER_EMAIL}" + + page.goto(f"{frappe.utils.get_url()}/login") + page.wait_for_load_state("networkidle") + + page.evaluate( + "barcode => window.scanHandler.scanner.simulate(document, barcode)", non_mobile_user_barcode + ) + page.wait_for_timeout(2000) + + error_message = page.locator(".msgprint-dialog .modal-body") + expect(error_message).to_be_visible(timeout=2000) + expect(error_message).to_contain_text("Not Beam mobile user") + + expect(page).to_have_url(frappe.utils.get_url() + "/login#login") + + +@pytest.mark.order(11) +def test_scan_to_login_disabled(page): + """Test login when scanning is disabled""" + mobile_user_barcode = get_user_barcode(MOBILE_USER_EMAIL) + assert mobile_user_barcode, f"Barcode not found for user {MOBILE_USER_EMAIL}" + with use_current_db_transaction(): + set_beam_setting("enable_scan_to_login", "Not Allowed") + + page.goto(f"{frappe.utils.get_url()}/login") + page.wait_for_load_state("networkidle") + + page.evaluate( + "barcode => window.scanHandler.scanner.simulate(document, barcode)", mobile_user_barcode + ) + page.wait_for_timeout(1000) + + error_message = page.locator(".msgprint-dialog .modal-body") + expect(error_message).to_be_visible(timeout=2000) + expect(error_message).to_contain_text("Login scanning is not allowed") + + expect(page).to_have_url(frappe.utils.get_url() + "/login#login") + + +@pytest.mark.order(12) +def test_scan_to_login_ip_restriction_allowed(page): + """Test IP restriction - allowed IP""" + with use_current_db_transaction(): + set_beam_setting("enable_scan_to_login", "All Users") + mobile_user_barcode = get_user_barcode(MOBILE_USER_EMAIL) + assert mobile_user_barcode, f"Barcode not found for user {MOBILE_USER_EMAIL}" + set_beam_setting("restrict_ip", "127.0.0.1") + + page.goto(f"{frappe.utils.get_url()}/login") + page.wait_for_load_state("networkidle") + + # Scan user barcode (should work since we're on localhost) + with page.expect_request( + lambda request: request.headers.get("x-frappe-cmd") == "beam.beam.scan.user_login.scan_login" + ): + page.evaluate( + "barcode => window.scanHandler.scanner.simulate(document, barcode)", mobile_user_barcode + ) + + page.wait_for_timeout(2000) + expect(page).to_have_url(frappe.utils.get_url() + "/beam#/") + + logged_in_user = page.evaluate("() => frappe.session.user") + assert logged_in_user == MOBILE_USER_EMAIL + + logout(page) + with use_current_db_transaction(): + set_beam_setting("restrict_ip", "") # Clear IP restriction + + +@pytest.mark.order(13) +def test_scan_to_login_ip_restriction_blocked(page): + """Test IP restriction - blocked IP""" + with use_current_db_transaction(): + set_beam_setting("enable_scan_to_login", "All Users") + mobile_user_barcode = get_user_barcode(MOBILE_USER_EMAIL) + assert mobile_user_barcode, f"Barcode not found for user {MOBILE_USER_EMAIL}" + # Configure IP that won't match localhost (192.168.1.x subnet only) + set_beam_setting("restrict_ip", "192.168.1.") + + page.goto(f"{frappe.utils.get_url()}/login") + page.wait_for_load_state("networkidle") + + # Scan user barcode - should be rejected due to IP restriction + page.evaluate( + "barcode => window.scanHandler.scanner.simulate(document, barcode)", mobile_user_barcode + ) + page.wait_for_timeout(1000) + + # Verify error message appears + error_message = page.locator(".msgprint-dialog .modal-body") + expect(error_message).to_be_visible(timeout=5000) + expect(error_message).to_contain_text("Network not available") + # Verify we're still on login page expect(page).to_have_url(frappe.utils.get_url() + "/login#login") + with use_current_db_transaction(): + set_beam_setting("restrict_ip", "") + + +@pytest.mark.order(14) +def test_scan_to_login_disabled_user(page): + """Test rejection when user account is disabled""" + with use_current_db_transaction(): + set_beam_setting("enable_scan_to_login", "All Users") + + user = frappe.get_doc("User", NON_MOBILE_USER_EMAIL) + original_enabled_status = user.enabled + # Temporarily disable the user + user.enabled = 0 + user.save(ignore_permissions=True) + frappe.db.commit() + + disabled_user_barcode = get_user_barcode(NON_MOBILE_USER_EMAIL) + assert disabled_user_barcode, f"Barcode not found for user {NON_MOBILE_USER_EMAIL}" + + page.goto(f"{frappe.utils.get_url()}/login") + page.wait_for_load_state("networkidle") + + # Scan disabled user barcode + page.evaluate( + "barcode => window.scanHandler.scanner.simulate(document, barcode)", disabled_user_barcode + ) + page.wait_for_timeout(1000) + + error_message = page.locator(".msgprint-dialog .modal-body") + expect(error_message).to_be_visible(timeout=5000) + expect(error_message).to_contain_text("is disabled") + + expect(page).to_have_url(frappe.utils.get_url() + "/login#login") + + with use_current_db_transaction(): + user = frappe.get_doc("User", NON_MOBILE_USER_EMAIL) + user.enabled = original_enabled_status + user.save(ignore_permissions=True) + frappe.db.commit() From b69cb6bb3cf4f480adf956221375acfc96532777 Mon Sep 17 00:00:00 2001 From: lauty95 Date: Fri, 27 Mar 2026 18:00:52 +0000 Subject: [PATCH 07/10] fix: department updated --- beam/tests/fixtures.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/beam/tests/fixtures.py b/beam/tests/fixtures.py index 9cc53597..8bc59a80 100644 --- a/beam/tests/fixtures.py +++ b/beam/tests/fixtures.py @@ -728,7 +728,7 @@ }, "phone": "(704) 885-0542", "roles": ["Stock Manager", "Item Manager"], - # "department": "Operations", + "department": "Management - APC", "designation": "Bakery Manager", }, { @@ -745,7 +745,7 @@ "phone": "(658) 583-5499", "roles": ["Stock User", "BEAM Mobile User"], "reports_to": "Tristan Hawkins", - # "department": "Operations", + "department": "Management - APC", "designation": "Baker", }, { @@ -762,7 +762,7 @@ "phone": "(962) 762-5895", "roles": ["Stock User", "BEAM Mobile User"], "reports_to": "Tristan Hawkins", - # "department": "Operations", + "department": "Management - APC", "designation": "Baker", }, { @@ -779,7 +779,7 @@ "phone": "(366) 357-8223", "roles": ["Stock User", "BEAM Mobile User"], "reports_to": "Tristan Hawkins", - # "department": "Operations", + "department": "Management - APC", "designation": "Bakery Manager", }, { @@ -796,7 +796,7 @@ "phone": "(930) 920-4520", "roles": ["Stock User", "BEAM Mobile User"], "reports_to": "Tristan Hawkins", - # "department": "Operations", + "department": "Management - APC", "designation": "Baker", }, { @@ -813,7 +813,7 @@ "phone": "(054) 893-8970", "roles": ["Stock User", "BEAM Mobile User"], "reports_to": "Tristan Hawkins", - # "department": "Operations", + "department": "Management - APC", "designation": "Baker", }, { @@ -830,7 +830,7 @@ "phone": "(814) 677-9322", "roles": ["Stock User", "BEAM Mobile User"], "reports_to": "Tristan Hawkins", - # "department": "Operations", + "department": "Management - APC", "designation": "Baker", }, { @@ -847,7 +847,7 @@ "phone": "(133) 195-7828", "roles": ["Stock User", "BEAM Mobile User"], "reports_to": "Tristan Hawkins", - # "department": "Operations", + "department": "Management - APC", "designation": "Baker", }, { @@ -864,7 +864,7 @@ "phone": "(041) 000-2569", "roles": ["Stock User", "BEAM Mobile User"], "reports_to": "Tristan Hawkins", - # "department": "Operations", + "department": "Management - APC", "designation": "Baker", }, { @@ -881,7 +881,7 @@ "phone": "(555) 123-4567", "roles": ["Stock User"], # NO BEAM Mobile User role - for restriction tests "reports_to": "Tristan Hawkins", - "department": "Operations", + "department": "Operations - APC", "designation": "Warehouse Associate", }, ] From 900a75bf1c23a88747228924fc1525429c66c11d Mon Sep 17 00:00:00 2001 From: lauty95 Date: Mon, 30 Mar 2026 11:49:40 +0000 Subject: [PATCH 08/10] feat: pytest order by file --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 9b3c7c8a..a63c5f5c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -38,7 +38,7 @@ frappe = ">=15.0.0,<16.0.0" erpnext = ">=15.0.0,<16.0.0" [tool.pytest.ini_options] -addopts = "--cov=beam --cov-report term-missing" +addopts = "--cov=beam --cov-report term-missing --order-scope=module" [tool.black] line-length = 99 From fb4a8749acad73093e50f7466e80600cb7126ec9 Mon Sep 17 00:00:00 2001 From: lauty95 Date: Mon, 30 Mar 2026 11:59:27 +0000 Subject: [PATCH 09/10] fix: pytest order normalized --- beam/tests/mobile/test_mobile.py | 2 +- beam/tests/mobile/test_receive.py | 8 ++++---- beam/tests/mobile/test_repack.py | 10 +++++----- beam/tests/mobile/test_scan_to_login.py | 16 ++++++++-------- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/beam/tests/mobile/test_mobile.py b/beam/tests/mobile/test_mobile.py index 26d46cfe..e902d54f 100644 --- a/beam/tests/mobile/test_mobile.py +++ b/beam/tests/mobile/test_mobile.py @@ -16,7 +16,7 @@ # `page.expect_navigation()` since the latter won't work with Beam's hash-based routes -@pytest.mark.order(6) +@pytest.mark.order(1) @pytest.mark.parametrize("route", ["Ship"]) def test_scan_item_barcode(page, route): # navigate in the following order: Home -> List -> Form diff --git a/beam/tests/mobile/test_receive.py b/beam/tests/mobile/test_receive.py index 33060241..ceb9f4e0 100644 --- a/beam/tests/mobile/test_receive.py +++ b/beam/tests/mobile/test_receive.py @@ -19,7 +19,7 @@ # `page.expect_navigation()` won't work with Beam's hash-based routes -@pytest.mark.order(2) +@pytest.mark.order(1) def test_scan_invalid_barcode(page): page.get_by_text("Receive").click() # wait for list to load @@ -82,7 +82,7 @@ def test_scan_invalid_barcode(page): ), f"Invalid barcode scan should not create any Purchase Receipts, but found: {new_receipts}" -@pytest.mark.order(3) +@pytest.mark.order(2) def test_receive_without_scanning(page): """Test trying to receive without scanning any items""" # navigate to a Purchase Order @@ -140,7 +140,7 @@ def test_receive_without_scanning(page): ), f"Expected no new receipts, but count changed from {initial_count} to {final_count}" -@pytest.mark.order(4) +@pytest.mark.order(3) def test_complete_partial_receipt(page): # navigate in the following order: Home -> Receive -> Purchase Order page.get_by_text("Receive").click() @@ -224,7 +224,7 @@ def test_complete_partial_receipt(page): assert receipts[0]["received_qty"] == 1 -@pytest.mark.order(5) +@pytest.mark.order(4) def test_rapid_barcode_scanning(page): """Test scanning multiple barcodes quickly""" # navigate to a Purchase Order diff --git a/beam/tests/mobile/test_repack.py b/beam/tests/mobile/test_repack.py index 25f66033..524bb74b 100644 --- a/beam/tests/mobile/test_repack.py +++ b/beam/tests/mobile/test_repack.py @@ -34,7 +34,7 @@ def disable_handling_unit_for_tests(): frappe.db.commit() -@pytest.mark.order(8) +@pytest.mark.order(1) def test_repack_items_manually(page): page.get_by_text("Repack").click() expect(page).to_have_url(re.compile(r"#/repack"), timeout=15000) @@ -124,7 +124,7 @@ def test_repack_items_manually(page): assert submitted, f"Expected Stock Entry {stock_entry_name} to be submitted" -@pytest.mark.order(9) +@pytest.mark.order(2) def test_repack_using_bom(page): page.get_by_text("Repack").click() page.wait_for_load_state("networkidle") @@ -174,7 +174,7 @@ def test_repack_using_bom(page): assert entries, "Expected a draft Stock Entry to be created from BOM repack" -@pytest.mark.order(10) +@pytest.mark.order(3) def test_scan_item_for_repack(page): page.get_by_text("Repack").click() page.wait_for_load_state("networkidle") @@ -214,7 +214,7 @@ def test_scan_item_for_repack(page): expect(qty_input).to_have_value("2") -@pytest.mark.order(11) +@pytest.mark.order(4) def test_clear_repack_form(page): page.get_by_text("Repack").click() page.wait_for_load_state("networkidle") @@ -260,7 +260,7 @@ def test_clear_repack_form(page): expect(page.get_by_role("button", name="CLEAN", exact=True)).to_be_hidden() -@pytest.mark.order(12) +@pytest.mark.order(5) def test_repack_validation_single_warehouse_direction(page): page.get_by_text("Repack").click() page.wait_for_load_state("networkidle") diff --git a/beam/tests/mobile/test_scan_to_login.py b/beam/tests/mobile/test_scan_to_login.py index 41998d61..2d308697 100644 --- a/beam/tests/mobile/test_scan_to_login.py +++ b/beam/tests/mobile/test_scan_to_login.py @@ -33,7 +33,7 @@ def logout(page): page.wait_for_timeout(1000) -@pytest.mark.order(7) +@pytest.mark.order(1) def test_scan_to_login_success_all_users(page): """Test successful login scanning - All Users mode""" with use_current_db_transaction(): @@ -64,7 +64,7 @@ def test_scan_to_login_success_all_users(page): logout(page) -@pytest.mark.order(8) +@pytest.mark.order(2) def test_scan_to_login_invalid_barcode(page): """Test rejection of non-user barcode""" with use_current_db_transaction(): @@ -88,7 +88,7 @@ def test_scan_to_login_invalid_barcode(page): expect(page).to_have_url(frappe.utils.get_url() + "/login#login") -@pytest.mark.order(9) +@pytest.mark.order(3) def test_scan_to_login_mobile_users_only_success(page): """Test login with mobile user role restriction - success case""" with use_current_db_transaction(): @@ -116,7 +116,7 @@ def test_scan_to_login_mobile_users_only_success(page): logout(page) -@pytest.mark.order(10) +@pytest.mark.order(4) def test_scan_to_login_mobile_users_only_reject(page): """Test rejection when user lacks BEAM Mobile User role""" with use_current_db_transaction(): @@ -139,7 +139,7 @@ def test_scan_to_login_mobile_users_only_reject(page): expect(page).to_have_url(frappe.utils.get_url() + "/login#login") -@pytest.mark.order(11) +@pytest.mark.order(5) def test_scan_to_login_disabled(page): """Test login when scanning is disabled""" mobile_user_barcode = get_user_barcode(MOBILE_USER_EMAIL) @@ -162,7 +162,7 @@ def test_scan_to_login_disabled(page): expect(page).to_have_url(frappe.utils.get_url() + "/login#login") -@pytest.mark.order(12) +@pytest.mark.order(6) def test_scan_to_login_ip_restriction_allowed(page): """Test IP restriction - allowed IP""" with use_current_db_transaction(): @@ -193,7 +193,7 @@ def test_scan_to_login_ip_restriction_allowed(page): set_beam_setting("restrict_ip", "") # Clear IP restriction -@pytest.mark.order(13) +@pytest.mark.order(7) def test_scan_to_login_ip_restriction_blocked(page): """Test IP restriction - blocked IP""" with use_current_db_transaction(): @@ -224,7 +224,7 @@ def test_scan_to_login_ip_restriction_blocked(page): set_beam_setting("restrict_ip", "") -@pytest.mark.order(14) +@pytest.mark.order(8) def test_scan_to_login_disabled_user(page): """Test rejection when user account is disabled""" with use_current_db_transaction(): From 35a2845c33763ccd7aa3caa6bc6e2c3dd17357fd Mon Sep 17 00:00:00 2001 From: lauty95 Date: Mon, 30 Mar 2026 12:19:52 +0000 Subject: [PATCH 10/10] fix: auto barcode for all dt --- beam/tests/setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/beam/tests/setup.py b/beam/tests/setup.py index c17f8369..df2f05f9 100644 --- a/beam/tests/setup.py +++ b/beam/tests/setup.py @@ -201,6 +201,7 @@ def setup_beam_settings(settings): beams.enable_handling_units = True beams.receiving_workstation = "Receiving" beams.shipping_workstation = "Shipping" + beams.auto_barcode_doctypes = '["Item", "User", "Warehouse"]' beams.set("warehouse_types", [{"warehouse_type": "Quarantine"}]) beams.set( "routes",