Replace import_orders (insert-per-run) with orders table (one row per order, upsert on conflict). Eliminates dedup CTE on every dashboard query and prevents unbounded row growth at 4-500 orders/sync. Key changes: - orders table: PK order_number, upsert via ON CONFLICT DO UPDATE; COALESCE preserves id_comanda once set; times_skipped auto-increments - sync_run_orders: lightweight junction (sync_run_id, order_number) replaces sync_run_id column on orders - order_items: PK changed to (order_number, sku), INSERT OR IGNORE - Auto-migration in init_sqlite(): import_orders → orders on first boot, old table renamed to import_orders_bak - /api/dashboard/orders: period_days param (3/7/30/0=all, default 7) - Dashboard: period selector buttons in orders card header - start.sh: stop existing process on port 5003 before restart; remove --reload (broken on WSL2 /mnt/e/) - Add invoice_service, E2E Playwright tests, Oracle package updates Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
82 lines
3.1 KiB
Python
82 lines
3.1 KiB
Python
"""E2E: Mappings page with sortable headers, grouping, multi-CODMAT modal."""
|
|
import pytest
|
|
from playwright.sync_api import Page, expect
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def navigate_to_mappings(page: Page, app_url: str):
|
|
page.goto(f"{app_url}/mappings")
|
|
page.wait_for_load_state("networkidle")
|
|
|
|
|
|
def test_mappings_page_loads(page: Page):
|
|
"""Verify mappings page renders."""
|
|
expect(page.locator("h4")).to_contain_text("Mapari SKU")
|
|
|
|
|
|
def test_sortable_headers_present(page: Page):
|
|
"""R7: Verify sortable column headers with sort icons."""
|
|
sortable_ths = page.locator("th.sortable")
|
|
count = sortable_ths.count()
|
|
assert count >= 5, f"Expected at least 5 sortable columns, got {count}"
|
|
|
|
sort_icons = page.locator(".sort-icon")
|
|
assert sort_icons.count() >= 5, f"Expected at least 5 sort-icon spans, got {sort_icons.count()}"
|
|
|
|
|
|
def test_product_name_column_exists(page: Page):
|
|
"""R4: Verify 'Produs Web' column exists in header."""
|
|
headers = page.locator("thead th")
|
|
texts = headers.all_text_contents()
|
|
assert any("Produs Web" in t for t in texts), f"'Produs Web' column not found in headers: {texts}"
|
|
|
|
|
|
def test_um_column_exists(page: Page):
|
|
"""R12: Verify 'UM' column exists in header."""
|
|
headers = page.locator("thead th")
|
|
texts = headers.all_text_contents()
|
|
assert any("UM" in t for t in texts), f"'UM' column not found in headers: {texts}"
|
|
|
|
|
|
def test_show_inactive_toggle_exists(page: Page):
|
|
"""R5: Verify 'Arata inactive' toggle is present."""
|
|
toggle = page.locator("#showInactive")
|
|
expect(toggle).to_be_visible()
|
|
label = page.locator("label[for='showInactive']")
|
|
expect(label).to_contain_text("Arata inactive")
|
|
|
|
|
|
def test_sort_click_changes_icon(page: Page):
|
|
"""R7: Clicking a sortable header should display a sort direction arrow."""
|
|
sku_header = page.locator("th.sortable", has_text="SKU")
|
|
sku_header.click()
|
|
page.wait_for_timeout(500)
|
|
|
|
icon = page.locator(".sort-icon[data-col='sku']")
|
|
text = icon.text_content()
|
|
assert text in ("↑", "↓"), f"Expected sort arrow (↑ or ↓), got '{text}'"
|
|
|
|
|
|
def test_add_modal_multi_codmat(page: Page):
|
|
"""R11: Verify the add mapping modal supports multiple CODMAT lines."""
|
|
page.locator("button", has_text="Adauga Mapare").click()
|
|
page.wait_for_timeout(500)
|
|
|
|
codmat_lines = page.locator(".codmat-line")
|
|
assert codmat_lines.count() >= 1, "Expected at least one CODMAT line in modal"
|
|
|
|
page.locator("button", has_text="Adauga CODMAT").click()
|
|
page.wait_for_timeout(300)
|
|
assert codmat_lines.count() >= 2, "Expected a second CODMAT line after clicking Adauga CODMAT"
|
|
|
|
# Second line must have a remove button
|
|
remove_btns = page.locator(".codmat-line:nth-child(2) button.btn-outline-danger")
|
|
assert remove_btns.count() >= 1, "Second CODMAT line is missing remove button"
|
|
|
|
|
|
def test_search_input_exists(page: Page):
|
|
"""Verify search input is present with the correct placeholder."""
|
|
search = page.locator("#searchInput")
|
|
expect(search).to_be_visible()
|
|
expect(search).to_have_attribute("placeholder", "Cauta SKU, CODMAT sau denumire...")
|