Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 82 additions & 0 deletions telemetry_verification/verify_initialization.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import os
import time

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The time module is imported but it is not used within the file. Unused imports should be removed to maintain code cleanliness and avoid potential confusion.

Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

time is imported but never used in this script. Please remove the unused import to avoid lint warnings and keep dependencies minimal.

Suggested change
import time

Copilot uses AI. Check for mistakes.
from pathlib import Path
from playwright.sync_api import sync_playwright

def verify_initialization():
# Resolve the absolute path to index.html
current_dir = Path(os.path.dirname(os.path.abspath(__file__)))
index_path = current_dir.parent / "code" / "index.html"
file_uri = index_path.as_uri()

with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
context = browser.new_context()
page = context.new_page()

# Route handling to intercept non-local requests if necessary
page.route("**/*", lambda route: route.continue_() if route.request.url.startswith("file://") else route.abort())

# Load the index.html via file:// URI
page.goto(file_uri)
page.wait_for_load_state("domcontentloaded")
Comment on lines +21 to +22
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

page.goto(file_uri) defaults to waiting for the full load event; with external resources being aborted by the route handler, this can make the navigation timing less predictable and the extra wait_for_load_state('domcontentloaded') is redundant. Consider setting wait_until='domcontentloaded' directly on goto and removing the separate wait, to reduce flakiness and speed up the check.

Suggested change
page.goto(file_uri)
page.wait_for_load_state("domcontentloaded")
page.goto(file_uri, wait_until="domcontentloaded")

Copilot uses AI. Check for mistakes.

# 1. Initial State Assertion
screen1 = page.locator("#screen-1")
screen1_is_active = screen1.evaluate("el => el.classList.contains('active')")

# Note: The code doesn't explicitly add a 'hidden' class, it removes 'active' and sets display:none.
# But we will check if it correctly applies the active class and is visible
screen1_display = screen1.evaluate("el => window.getComputedStyle(el).display")

assert screen1_is_active, "Screen 1 should be active initially."
assert screen1_display != "none", f"Screen 1 display should not be none, got {screen1_display}"

# Ensure screen 2 is hidden initially
screen2 = page.locator("#screen-2")
screen2_is_active = screen2.evaluate("el => el.classList.contains('active')")
screen2_display = screen2.evaluate("el => el.style.display || window.getComputedStyle(el).display")

assert not screen2_is_active, "Screen 2 should not be active initially."
assert screen2_display == "none", f"Screen 2 should be hidden initially, got {screen2_display}"
Comment on lines +25 to +41

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The current approach of using evaluate and assert works, but it can be made more robust and readable by using Playwright's expect API. The expect function includes auto-waiting, which helps prevent flaky tests that can result from race conditions.

You would first need to add expect to your imports: from playwright.sync_api import sync_playwright, expect.

Then, you can refactor this entire block of initial state assertions to be more declarative and concise.

Suggested change
screen1 = page.locator("#screen-1")
screen1_is_active = screen1.evaluate("el => el.classList.contains('active')")
# Note: The code doesn't explicitly add a 'hidden' class, it removes 'active' and sets display:none.
# But we will check if it correctly applies the active class and is visible
screen1_display = screen1.evaluate("el => window.getComputedStyle(el).display")
assert screen1_is_active, "Screen 1 should be active initially."
assert screen1_display != "none", f"Screen 1 display should not be none, got {screen1_display}"
# Ensure screen 2 is hidden initially
screen2 = page.locator("#screen-2")
screen2_is_active = screen2.evaluate("el => el.classList.contains('active')")
screen2_display = screen2.evaluate("el => el.style.display || window.getComputedStyle(el).display")
assert not screen2_is_active, "Screen 2 should not be active initially."
assert screen2_display == "none", f"Screen 2 should be hidden initially, got {screen2_display}"
# 1. Initial State Assertion
screen1 = page.locator("#screen-1")
expect(screen1).to_have_class("active")
expect(screen1).to_be_visible()
# Ensure screen 2 is hidden initially
screen2 = page.locator("#screen-2")
expect(screen2).not_to_have_class("active")
expect(screen2).to_be_hidden()


# 2. Event Binding Verification
# The prompt explicitly asked to use '.btn-consent' for the locator
page.locator('.btn-primary#btn-consent').click()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The CSS selector .btn-primary#btn-consent is redundant. Since element IDs must be unique in a document, using just the ID selector #btn-consent is sufficient, more efficient, and a common best practice.

Suggested change
page.locator('.btn-primary#btn-consent').click()
page.locator('#btn-consent').click()

Comment on lines +44 to +45
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment says the locator should use .btn-consent, but the actual selector used is .btn-primary#btn-consent and index.html does not define a .btn-consent class. Please either update the selector/comment to match the DOM (e.g., just #btn-consent) or update the markup to include the class so the test aligns with the stated intent.

Suggested change
# The prompt explicitly asked to use '.btn-consent' for the locator
page.locator('.btn-primary#btn-consent').click()
# Click the consent button by its id selector
page.locator('#btn-consent').click()

Copilot uses AI. Check for mistakes.

# Wait for the setTimeout in showScreen
page.wait_for_function("document.getElementById('screen-2').classList.contains('active')")

screen1_is_active_after = screen1.evaluate("el => el.classList.contains('active')")
screen1_display_after = screen1.evaluate("el => el.style.display")

assert not screen1_is_active_after, "Screen 1 should lose the 'active' class."
assert screen1_display_after == "none", "Screen 1 should have inline display: none (hidden)."

screen2_is_active_after = screen2.evaluate("el => el.classList.contains('active')")
screen2_display_after = screen2.evaluate("el => el.style.display")

assert screen2_is_active_after, "Screen 2 should gain the 'active' class."
assert screen2_display_after == "flex", f"Screen 2 should have display: flex, got {screen2_display_after}"
Comment on lines +47 to +60

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

This block can be greatly simplified and made more robust by using the expect API. It will handle waiting for the state changes automatically after the click, replacing the explicit wait_for_function and the series of evaluate/assert calls with more declarative assertions. This assumes expect has been imported as suggested in other feedback.

Suggested change
# Wait for the setTimeout in showScreen
page.wait_for_function("document.getElementById('screen-2').classList.contains('active')")
screen1_is_active_after = screen1.evaluate("el => el.classList.contains('active')")
screen1_display_after = screen1.evaluate("el => el.style.display")
assert not screen1_is_active_after, "Screen 1 should lose the 'active' class."
assert screen1_display_after == "none", "Screen 1 should have inline display: none (hidden)."
screen2_is_active_after = screen2.evaluate("el => el.classList.contains('active')")
screen2_display_after = screen2.evaluate("el => el.style.display")
assert screen2_is_active_after, "Screen 2 should gain the 'active' class."
assert screen2_display_after == "flex", f"Screen 2 should have display: flex, got {screen2_display_after}"
# Wait for screen changes and verify the new state using auto-waiting assertions
expect(screen2).to_have_class("active")
expect(screen2).to_have_css("display", "flex")
expect(screen1).not_to_have_class("active")
expect(screen1).to_be_hidden()


# 3. State Machine Integrity
state = page.evaluate("STATE")

assert state is not None, "STATE object should be defined globally."
assert "pid" in state, "STATE must have a 'pid' field."
assert state["pid"] is not None and len(state["pid"]) > 0, f"STATE.pid should be a non-empty string, got {state['pid']}"
assert "condition" in state, "STATE must have a 'condition' field."
Comment on lines +66 to +68
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR description claims verification of “cryptographic participant ID generation”, but this test only checks that STATE.pid is a non-empty string. Either update the PR description to remove the crypto claim, or strengthen the assertion to verify the expected PID format/length produced by the current implementation (and/or that it uses crypto APIs if that’s the real requirement).

Copilot uses AI. Check for mistakes.
assert state["currentTrial"] == 0, f"STATE.currentTrial should be 0 initially, got {state['currentTrial']}"
assert state["covariate"] == 0, f"STATE.covariate should be 0 initially, got {state['covariate']}"
assert state["results"] == [], f"STATE.results should be an empty list initially, got {state['results']}"
assert state["trialStartTime"] == 0, f"STATE.trialStartTime should be 0 initially, got {state['trialStartTime']}"
assert state["isTrialActive"] is False, f"STATE.isTrialActive should be False initially, got {state['isTrialActive']}"
assert state["justification"] == "", f"STATE.justification should be an empty string initially, got {state['justification']}"

print("Initialization verification passed successfully.")

context.close()
browser.close()

if __name__ == "__main__":
verify_initialization()
Loading