"""Real Chromium integration tests; goals/actions are test fixtures, not a policy.""" import json from pathlib import Path import tempfile import unittest from playwright.sync_api import sync_playwright from baim.actions import Action, Kind, Decision from baim.authority import Authority from baim.browser import Browser from baim.memory import Recorder from baim.runtime import Runtime class BrowserTests(unittest.TestCase): @classmethod def setUpClass(cls): cls.pw = sync_playwright().start() cls.chromium = cls.pw.chromium.launch(headless=True) @classmethod def tearDownClass(cls): cls.chromium.close() cls.pw.stop() def setUp(self): self.context = self.chromium.new_context() self.browser = Browser(self.context) self.authority = Authority() self.authority.replace('Fill the labelled field and activate the button') self.browser.page.set_content(''' Pending
''') self.browser.observe() self.runtime = Runtime(self.browser, self.authority, lambda *_: True, completion=lambda b: 'acknowledgment_visible' if b.page.locator('output').inner_text() == 'Accepted' else None) def tearDown(self): self.context.close() def ref(self, name): return next(e.ref for e in self.browser.state.elements if e.name == name) def execute(self, kind, *args): return self.runtime.execute(Decision(self.authority.ticket(self.browser.state), Action(kind, args))) def test_multistep_execution_and_completion(self): self.assertEqual(self.execute(Kind.FINISH).code, 'COMPLETION_NOT_PROVEN') self.assertEqual(self.execute(Kind.TYPE, self.ref('Destination'), 'Tokyo').status, 'ok') self.browser.observe() self.assertEqual(self.execute(Kind.CLICK, self.ref('Proceed')).status, 'ok') self.browser.observe() self.assertEqual(self.execute(Kind.FINISH).evidence, 'acknowledgment_visible') self.assertEqual(self.browser.page.locator('label input').input_value(), 'Tokyo') def test_frame_and_shadow_nodes(self): self.assertEqual(self.execute(Kind.CLICK, self.ref('Inside')).status, 'ok') self.browser.observe() self.assertEqual(self.execute(Kind.CLICK, self.ref('Shadow')).status, 'ok') def test_path_optimization_preserves_semantics(self): optimized = self.browser.state self.browser.path_mode = 'naive' naive = self.browser.observe() self.assertEqual(optimized.elements, naive.elements) self.assertEqual(optimized.state_hash, naive.state_hash) def test_changed_and_detached_targets(self): ref = self.ref('Proceed') self.browser.page.locator('button').first.evaluate("el => el.textContent = 'Changed'") self.assertEqual(self.execute(Kind.CLICK, ref).code, 'CHANGED_TARGET') self.browser.page.locator('button').first.evaluate('el => el.remove()') self.assertEqual(self.execute(Kind.CLICK, ref).code, 'DETACHED_TARGET') def test_old_task_cannot_execute_or_finish(self): decision = Decision(self.authority.ticket(self.browser.state), Action(Kind.CLICK, (self.ref('Proceed'),))) self.authority.replace('New task') self.assertEqual(self.runtime.execute(decision).code, 'STALE_AUTHORITY_OR_OBSERVATION') self.assertEqual(self.execute(Kind.FINISH).code, 'NO_COMPLETION_VERIFIER') self.assertEqual(self.browser.page.locator('output').inner_text(), 'Pending') def test_loops_sensitive_targets_and_url_schemes(self): secret = next(e.ref for e in self.browser.state.elements if e.sensitive) self.assertEqual(self.execute(Kind.TYPE, secret, 'dont-log-me').code, 'SENSITIVE_TARGET_REQUIRES_HOST_FLOW') self.assertEqual(self.execute(Kind.NAVIGATE, 'javascript:alert(1)').code, 'UNSAFE_URL') for _ in range(3): self.assertEqual(self.execute(Kind.WAIT, 0).status, 'ok') self.assertEqual(self.execute(Kind.WAIT, 0).code, 'REPEATED_ACTION') def test_recording_contains_no_raw_typed_values(self): with tempfile.TemporaryDirectory() as temp: path = Path(temp) / 'events.sqlite' recorder = Recorder(path) self.runtime.recorder = recorder try: self.assertEqual(self.execute(Kind.TYPE, self.ref('Destination'), 'sensitive-value-123').status, 'ok') self.assertEqual(recorder.db.execute('SELECT count(*) FROM events').fetchone()[0], 1) finally: recorder.close() self.assertNotIn(b'sensitive-value-123', path.read_bytes()) def test_document_navigation_invalidates_all_actions(self): self.browser.page.goto('data:text/html,

New document

') self.assertEqual(self.execute(Kind.WAIT, 0).code, 'CHANGED_DOCUMENT') def test_host_policy_denies_page_instruction(self): self.runtime.permission = lambda *_: False self.assertEqual(self.execute(Kind.CLICK, self.ref('Proceed')).code, 'HOST_POLICY_DENIED') self.assertEqual(self.browser.page.locator('output').inner_text(), 'Pending')