import random import string from playwright.sync_api import Page, expect def _unique_currency_code(existing: set[str]) -> str: """Generate a unique three-letter code not present in *existing*.""" alphabet = string.ascii_uppercase for _ in range(100): candidate = "".join(random.choices(alphabet, k=3)) if candidate not in existing and candidate != "USD": return candidate raise AssertionError( "Unable to generate a unique currency code for the test run." ) def _metric_value(page: Page, element_id: str) -> int: locator = page.locator(f"#{element_id}") expect(locator).to_be_visible() return int(locator.inner_text().strip()) def _expect_feedback(page: Page, expected_text: str) -> None: page.wait_for_function( "expected => {" " const el = document.getElementById('currency-form-feedback');" " if (!el) return false;" " const text = (el.textContent || '').trim();" " return !el.classList.contains('hidden') && text === expected;" "}", arg=expected_text, ) feedback = page.locator("#currency-form-feedback") expect(feedback).to_have_text(expected_text) def test_currency_workflow_create_update_toggle(page: Page) -> None: """Exercise create, update, and toggle flows on the currency settings page.""" page.goto("/ui/currencies") expect(page).to_have_title("Currencies ยท CalMiner") expect(page.locator("h2:has-text('Currency Overview')")).to_be_visible() code_cells = page.locator("#currencies-table-body tr td:nth-child(1)") existing_codes = { text.strip().upper() for text in code_cells.all_inner_texts() } total_before = _metric_value(page, "currency-metric-total") active_before = _metric_value(page, "currency-metric-active") inactive_before = _metric_value(page, "currency-metric-inactive") new_code = _unique_currency_code(existing_codes) new_name = f"Test Currency {new_code}" new_symbol = new_code[0] page.fill("#currency-form-code", new_code) page.fill("#currency-form-name", new_name) page.fill("#currency-form-symbol", new_symbol) page.select_option("#currency-form-status", "true") with page.expect_response("**/api/currencies/") as create_info: page.click("button[type='submit']") create_response = create_info.value assert create_response.status == 201 _expect_feedback(page, "Currency created successfully.") page.wait_for_function( "expected => Number(document.getElementById('currency-metric-total').textContent.trim()) === expected", arg=total_before + 1, ) page.wait_for_function( "expected => Number(document.getElementById('currency-metric-active').textContent.trim()) === expected", arg=active_before + 1, ) row = page.locator("#currencies-table-body tr").filter(has_text=new_code) expect(row).to_be_visible() expect(row.locator("td").nth(3)).to_have_text("Active") # Switch to update mode using the existing currency option. page.select_option("#currency-form-existing", new_code) updated_name = f"{new_name} Updated" updated_symbol = f"{new_symbol}$" page.fill("#currency-form-name", updated_name) page.fill("#currency-form-symbol", updated_symbol) page.select_option("#currency-form-status", "false") with page.expect_response(f"**/api/currencies/{new_code}") as update_info: page.click("button[type='submit']") update_response = update_info.value assert update_response.status == 200 _expect_feedback(page, "Currency updated successfully.") page.wait_for_function( "expected => Number(document.getElementById('currency-metric-active').textContent.trim()) === expected", arg=active_before, ) page.wait_for_function( "expected => Number(document.getElementById('currency-metric-inactive').textContent.trim()) === expected", arg=inactive_before + 1, ) expect(row.locator("td").nth(1)).to_have_text(updated_name) expect(row.locator("td").nth(2)).to_have_text(updated_symbol) expect(row.locator("td").nth(3)).to_contain_text("Inactive") toggle_button = row.locator("button[data-action='toggle']") expect(toggle_button).to_have_text("Activate") with page.expect_response( f"**/api/currencies/{new_code}/activation" ) as toggle_info: toggle_button.click() toggle_response = toggle_info.value assert toggle_response.status == 200 page.wait_for_function( "expected => Number(document.getElementById('currency-metric-active').textContent.trim()) === expected", arg=active_before + 1, ) page.wait_for_function( "expected => Number(document.getElementById('currency-metric-inactive').textContent.trim()) === expected", arg=inactive_before, ) _expect_feedback(page, f"Currency {new_code} activated.") expect(row.locator("td").nth(3)).to_contain_text("Active") expect(row.locator("button[data-action='toggle']")).to_have_text( "Deactivate" )