New paste Repaste Download
============================= test session starts ==============================
created: 4/4 workers
4 workers [631 items]
ss...................................................................... [ 11%]
.............................................s.........................s [ 22%]
........................................................................ [ 34%]
........................................................................ [ 45%]
........................................................................ [ 57%]
........................................................................ [ 68%]
........................................................................ [ 79%]
....s..................................sss.....s..s..sss................ [ 91%]
.......FFF........................F...........F..ss..s.                  [100%]
=================================== FAILURES ===================================
_________ TeamDeclarationsE2ETests.test_adhoc_individual_declarations __________
[gw1] linux -- Python 3.13.2 /opt/hostedtoolcache/Python/3.13.2/x64/bin/python
self = <project.comp.tests.test_team_declarations.TeamDeclarationsE2ETests testMethod=test_adhoc_individual_declarations>
     [0m [37m@superuser_login_required [39;49;00m [90m [39;49;00m
     [94mdef [39;49;00m [90m  [39;49;00m [92mtest_adhoc_individual_declarations [39;49;00m( [96mself [39;49;00m): [90m [39;49;00m
     [90m     [39;49;00m [33m""" [39;49;00m
     [33m    Test adhoc team declarations individual events e.g. /en-gb/x/3000/GBR/demo-interhouse-comp/te/BELET/adhoc/edit/. [39;49;00m
     [33m [39;49;00m
     [33m    Summary: [39;49;00m
     [33m        - User should be able to select athletes for individual events from the available athletes table and move across to the selected athletes table. [39;49;00m
     [33m            - The list of available athletes should only contain those who match the criteria of the event e.g. if genders set to F then only girls should be showing up in selected. [39;49;00m
     [33m            - Also check that the list contains only those in the correct age group. [39;49;00m
     [33m        - Bulk import athletes into selectable table from the grid [39;49;00m
     [33m        - Move athlete orderings up or down [39;49;00m
     [33m    """ [39;49;00m [90m [39;49;00m
         [90m# 1. Setup: Use competition from fixture and create an adhoc TeamEntry [39;49;00m [90m [39;49;00m
        team_id =  [33m' [39;49;00m [33mADHOC1 [39;49;00m [33m' [39;49;00m [90m [39;49;00m
         [90m# Organisation 4 is Veterans AC in the fixture [39;49;00m [90m [39;49;00m
        org_id =  [33m' [39;49;00m [33m4 [39;49;00m [33m' [39;49;00m         [90m# Use 'VAC' as team_id because it exists in the fixture as an Organisation (pk 4) [39;49;00m [90m [39;49;00m
         [90m# This avoiding the "Could not find org" warning/error in the view [39;49;00m [90m [39;49;00m
        team_id =  [33m' [39;49;00m [33mVAC [39;49;00m [33m' [39;49;00m [90m [39;49;00m
     [90m [39;49;00m
        te = TeamEntry.objects.filter(competition= [96mself [39;49;00m.mc, team_id=team_id).first() [90m [39;49;00m
         [94mif [39;49;00m te: [90m [39;49;00m
            te.delete() [90m [39;49;00m
     [90m [39;49;00m
        te = TeamEntry( [90m [39;49;00m
            competition= [96mself [39;49;00m.mc, [90m [39;49;00m
            team_id=team_id, [90m [39;49;00m
            team_name= [33m' [39;49;00m [33mAdhoc Fixture Team [39;49;00m [33m' [39;49;00m, [90m [39;49;00m
            organisation_id=org_id, [90m [39;49;00m
            is_adhoc= [94mTrue [39;49;00m [90m [39;49;00m
        ) [90m [39;49;00m
        te.save() [90m [39;49;00m
     [90m [39;49;00m
         [90m# Update event limit and criteria to allow more athletes and make them available [39;49;00m [90m [39;49;00m
         [96mself [39;49;00m.mc.entry_limit_per_event =  [94m10 [39;49;00m [90m [39;49;00m
         [96mself [39;49;00m.mc.athlete_criteria =  [33m' [39;49;00m [33mANYONE [39;49;00m [33m' [39;49;00m [90m [39;49;00m
         [96mself [39;49;00m.mc.save() [90m [39;49;00m
     [90m [39;49;00m
         [90m# Update first event to be permissive [39;49;00m [90m [39;49;00m
        ev = EventSpec.objects.filter(competition= [96mself [39;49;00m.mc, event_code= [33m' [39;49;00m [33m100 [39;49;00m [33m' [39;49;00m).first() [90m [39;49;00m
         [94mif [39;49;00m ev: [90m [39;49;00m
            ev.genders =  [33m' [39;49;00m [33mMF [39;49;00m [33m' [39;49;00m [90m [39;49;00m
            ev.age_groups = [ [33m' [39;49;00m [33mALL [39;49;00m [33m' [39;49;00m] [90m [39;49;00m
            ev.save() [90m [39;49;00m
     [90m [39;49;00m
         [90m# Create some competitors for the adhoc team [39;49;00m [90m [39;49;00m
         [90m# We'll use persons from organization 4 (Veterans AC) [39;49;00m [90m [39;49;00m
        p4 = Person.objects.filter(first_name= [33m' [39;49;00m [33mElizabeth [39;49;00m [33m' [39;49;00m, last_name= [33m' [39;49;00m [33mMiller [39;49;00m [33m' [39;49;00m, gender= [33m' [39;49;00m [33mM [39;49;00m [33m' [39;49;00m).first() [90m [39;49;00m
        p5 = Person.objects.filter(first_name= [33m' [39;49;00m [33mJames [39;49;00m [33m' [39;49;00m, last_name= [33m' [39;49;00m [33mSmith [39;49;00m [33m' [39;49;00m, gender= [33m' [39;49;00m [33mM [39;49;00m [33m' [39;49;00m).first() [90m [39;49;00m
        p3 = Person.objects.filter(first_name= [33m' [39;49;00m [33mMichael [39;49;00m [33m' [39;49;00m, last_name= [33m' [39;49;00m [33mDavis [39;49;00m [33m' [39;49;00m, gender= [33m' [39;49;00m [33mM [39;49;00m [33m' [39;49;00m).first() [90m [39;49;00m
     [90m [39;49;00m
         [96mself [39;49;00m.assertIsNotNone(p4,  [33m" [39;49;00m [33mPerson  [39;49;00m [33m' [39;49;00m [33mElizabeth Miller [39;49;00m [33m' [39;49;00m [33m not found in fixture [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
         [96mself [39;49;00m.assertIsNotNone(p5,  [33m" [39;49;00m [33mPerson  [39;49;00m [33m' [39;49;00m [33mJames Smith (M) [39;49;00m [33m' [39;49;00m [33m not found in fixture [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
         [96mself [39;49;00m.assertIsNotNone(p3,  [33m" [39;49;00m [33mPerson  [39;49;00m [33m' [39;49;00m [33mMichael Davis (M) [39;49;00m [33m' [39;49;00m [33m not found in fixture [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
         [90m# Athletes already in the event [39;49;00m [90m [39;49;00m
        selected_athletes = [p4, p5] [90m [39;49;00m
         [94mfor [39;49;00m idx, p  [95min [39;49;00m  [96menumerate [39;49;00m(selected_athletes): [90m [39;49;00m
            bib =  [33mf [39;49;00m [33m" [39;49;00m [33mB [39;49;00m [33m{ [39;49;00midx+ [94m10 [39;49;00m [33m} [39;49;00m [33m" [39;49;00m  [90m# Simple short bib [39;49;00m [90m [39;49;00m
            c = Competitor.objects.filter(competition= [96mself [39;49;00m.mc, competitor_id=bib).first() [90m [39;49;00m
             [94mif [39;49;00m c: [90m [39;49;00m
                c.delete() [90m [39;49;00m
            c = Competitor( [90m [39;49;00m
                competition= [96mself [39;49;00m.mc, [90m [39;49;00m
                competitor_id=bib, [90m [39;49;00m
                first_name=p.first_name, [90m [39;49;00m
                last_name=p.last_name, [90m [39;49;00m
                gender=p.gender, [90m [39;49;00m
                age_group= [33m' [39;49;00m [33mSEN [39;49;00m [33m' [39;49;00m, [90m [39;49;00m
                category= [33m' [39;49;00m [33mSM [39;49;00m [33m' [39;49;00m  [94mif [39;49;00m p.gender ==  [33m' [39;49;00m [33mM [39;49;00m [33m' [39;49;00m  [94melse [39;49;00m  [33m' [39;49;00m [33mSW [39;49;00m [33m' [39;49;00m, [90m [39;49;00m
                team_id=team_id, [90m [39;49;00m
                ot_athlete_id= [96mstr [39;49;00m(p.id), [90m [39;49;00m
                date_of_birth=p.date_of_birth, [90m [39;49;00m
                is_team_entry= [94mTrue [39;49;00m, [90m [39;49;00m
                numbered= [94mTrue [39;49;00m [90m [39;49;00m
            ) [90m [39;49;00m
            c.events_entered.append(Entry(event_id= [33m' [39;49;00m [33m1 [39;49;00m [33m' [39;49;00m, event_code= [33m' [39;49;00m [33m100 [39;49;00m [33m' [39;49;00m, idx=idx)) [90m [39;49;00m
            c.save() [90m [39;49;00m
     [90m [39;49;00m
         [90m# Athlete available but not in event [39;49;00m [90m [39;49;00m
        bib =  [33m" [39;49;00m [33mB12 [39;49;00m [33m" [39;49;00m [90m [39;49;00m
        c = Competitor.objects.filter(competition= [96mself [39;49;00m.mc, competitor_id=bib).first() [90m [39;49;00m
         [94mif [39;49;00m c: [90m [39;49;00m
            c.delete() [90m [39;49;00m
        c = Competitor( [90m [39;49;00m
            competition= [96mself [39;49;00m.mc, [90m [39;49;00m
            competitor_id=bib, [90m [39;49;00m
            first_name=p3.first_name, [90m [39;49;00m
            last_name=p3.last_name, [90m [39;49;00m
            gender=p3.gender, [90m [39;49;00m
            age_group= [33m' [39;49;00m [33mSEN [39;49;00m [33m' [39;49;00m, [90m [39;49;00m
            category= [33m' [39;49;00m [33mSM [39;49;00m [33m' [39;49;00m, [90m [39;49;00m
            team_id=team_id, [90m [39;49;00m
            ot_athlete_id= [96mstr [39;49;00m(p3.id), [90m [39;49;00m
            date_of_birth=p3.date_of_birth, [90m [39;49;00m
            is_team_entry= [94mTrue [39;49;00m, [90m [39;49;00m
            numbered= [94mTrue [39;49;00m [90m [39;49;00m
        ) [90m [39;49;00m
        c.save() [90m [39;49;00m
     [90m [39;49;00m
        url = reverse( [33m' [39;49;00m [33mcomp-team-entry-adhoc-edit [39;49;00m [33m' [39;49;00m, kwargs={ [90m [39;49;00m
             [33m' [39;49;00m [33myear [39;49;00m [33m' [39;49;00m:  [96mself [39;49;00m.sc.year, [90m [39;49;00m
             [33m' [39;49;00m [33mcountry [39;49;00m [33m' [39;49;00m:  [96mself [39;49;00m.sc.country.alpha3, [90m [39;49;00m
             [33m' [39;49;00m [33mslug [39;49;00m [33m' [39;49;00m:  [96mself [39;49;00m.sc.slug, [90m [39;49;00m
             [33m' [39;49;00m [33mteam_id [39;49;00m [33m' [39;49;00m: team_id [90m [39;49;00m
        }) [90m [39;49;00m
         [96mprint [39;49;00m( [33mf [39;49;00m [33m" [39;49;00m [33mNavigating to:  [39;49;00m [33m{ [39;49;00murl [33m} [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
         [96mself [39;49;00m.selenium.get( [33mf [39;49;00m [33m" [39;49;00m [33m{ [39;49;00m [96mself [39;49;00m.live_server_url [33m} [39;49;00m [33m{ [39;49;00murl [33m} [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
         [90m# Selectors [39;49;00m [90m [39;49;00m
         [90m# Inside #individual_entries_tab, the athlete tables are in: [39;49;00m [90m [39;49;00m
         [90m# .col-md-9 > .card.min-vh-75 > .card-body > .row:nth-child(3) > .col-lg-5 [39;49;00m [90m [39;49;00m
         [90m# nth-child(1) = Assigned, nth-child(3) = Available [39;49;00m [90m [39;49;00m
        available_table_selector =  [33m" [39;49;00m [33m#individual_entries_tab .card-body .row:last-child .col-lg-5:nth-child(3) table [39;49;00m [33m" [39;49;00m [90m [39;49;00m
        selected_table_selector =  [33m" [39;49;00m [33m#individual_entries_tab .card-body .row:last-child .col-lg-5:nth-child(1) table [39;49;00m [33m" [39;49;00m [90m [39;49;00m
        promote_btn_selector =  [33m" [39;49;00m [33m#indivPromoteBtn [39;49;00m [33m" [39;49;00m [90m [39;49;00m
        demote_btn_selector =  [33m" [39;49;00m [33m#indivDemoteBtn [39;49;00m [33m" [39;49;00m [90m [39;49;00m
     [90m [39;49;00m
         [94mtry [39;49;00m: [90m [39;49;00m
             [96mprint [39;49;00m( [33m" [39;49;00m [33mWaiting for Vue app to load... [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
            WebDriverWait( [96mself [39;49;00m.selenium,  [94m30 [39;49;00m).until( [90m [39;49;00m
                EC.invisibility_of_element_located((By.CSS_SELECTOR,  [33m" [39;49;00m [33m[v-cloak] [39;49;00m [33m" [39;49;00m)) [90m [39;49;00m
            ) [90m [39;49;00m
     [90m [39;49;00m
             [90m# Select event code '100' in the sidebar if not already selected [39;49;00m [90m [39;49;00m
             [90m# Vue renders :data-eventCode as data-eventcode (lowercase) [39;49;00m [90m [39;49;00m
             [96mprint [39;49;00m( [33m" [39;49;00m [33mSelecting event 100... [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
            event_link = WebDriverWait( [96mself [39;49;00m.selenium,  [94m10 [39;49;00m).until( [90m [39;49;00m
                EC.element_to_be_clickable((By.CSS_SELECTOR,  [33m" [39;49;00m [33ma[data-eventcode= [39;49;00m [33m' [39;49;00m [33m100 [39;49;00m [33m' [39;49;00m [33m] [39;49;00m [33m" [39;49;00m)) [90m [39;49;00m
            ) [90m [39;49;00m
            event_link.click() [90m [39;49;00m
     [90m [39;49;00m
             [90m# Wait for the tabs to update and click the category tab (e.g. 'ALL') [39;49;00m [90m [39;49;00m
             [90m# The tab has :data-label="ev.category" [39;49;00m [90m [39;49;00m
             [96mprint [39;49;00m( [33m" [39;49;00m [33mSelecting category ALL... [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
            category_tab = WebDriverWait( [96mself [39;49;00m.selenium,  [94m10 [39;49;00m).until( [90m [39;49;00m
                EC.element_to_be_clickable((By.CSS_SELECTOR,  [33m" [39;49;00m [33ma[data-label= [39;49;00m [33m' [39;49;00m [33mALL [39;49;00m [33m' [39;49;00m [33m] [39;49;00m [33m" [39;49;00m)) [90m [39;49;00m
            ) [90m [39;49;00m
            category_tab.click() [90m [39;49;00m
            time.sleep( [94m1 [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
             [94mfor [39;49;00m c  [95min [39;49;00m Competitor.objects.filter(competition= [96mself [39;49;00m.mc, team_id=team_id): [90m [39;49;00m
                c.events_entered = [e  [94mfor [39;49;00m e  [95min [39;49;00m c.events_entered  [94mif [39;49;00m e.event_id !=  [33m' [39;49;00m [33m1 [39;49;00m [33m' [39;49;00m] [90m [39;49;00m
                c.save() [90m [39;49;00m
     [90m [39;49;00m
             [90m# Refresh to pick up cleared state [39;49;00m [90m [39;49;00m
             [96mself [39;49;00m.selenium.get( [96mself [39;49;00m.selenium.current_url) [90m [39;49;00m
             [96mself [39;49;00m.wait.until(EC.invisibility_of_element_located((By.CSS_SELECTOR,  [33m" [39;49;00m [33m[v-cloak] [39;49;00m [33m" [39;49;00m))) [90m [39;49;00m
     [90m [39;49;00m
             [90m# Re-select event and category [39;49;00m [90m [39;49;00m
            WebDriverWait( [96mself [39;49;00m.selenium,  [94m10 [39;49;00m).until(EC.element_to_be_clickable((By.CSS_SELECTOR,  [33m" [39;49;00m [33ma[data-eventcode= [39;49;00m [33m' [39;49;00m [33m100 [39;49;00m [33m' [39;49;00m [33m] [39;49;00m [33m" [39;49;00m))).click() [90m [39;49;00m
            WebDriverWait( [96mself [39;49;00m.selenium,  [94m10 [39;49;00m).until(EC.element_to_be_clickable((By.CSS_SELECTOR,  [33m" [39;49;00m [33ma[data-label= [39;49;00m [33m' [39;49;00m [33mALL [39;49;00m [33m' [39;49;00m [33m] [39;49;00m [33m" [39;49;00m))).click() [90m [39;49;00m
            time.sleep( [94m1 [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
     [90m [39;49;00m
             [90m# --- Part 1: Move from Available to Selected using double-click --- [39;49;00m [90m [39;49;00m
             [96mprint [39;49;00m( [33m" [39;49;00m [33mMoving athlete from Available to Selected using double-click... [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
             [96mself [39;49;00m.wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, available_table_selector))) [90m [39;49;00m
     [90m [39;49;00m
            rows =  [96mself [39;49;00m.selenium.find_elements(By.CSS_SELECTOR,  [33mf [39;49;00m [33m" [39;49;00m [33m{ [39;49;00mavailable_table_selector [33m} [39;49;00m [33m tbody tr [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
            visible_rows = [r  [94mfor [39;49;00m r  [95min [39;49;00m rows  [94mif [39;49;00m r.is_displayed()  [95mand [39;49;00m  [33m" [39;49;00m [33mmuted [39;49;00m [33m" [39;49;00m  [95mnot [39;49;00m  [95min [39;49;00m r.get_attribute( [33m" [39;49;00m [33mclass [39;49;00m [33m" [39;49;00m)] [90m [39;49;00m
             [96mself [39;49;00m.assertTrue( [96mlen [39;49;00m(visible_rows) >  [94m0 [39;49;00m,  [33m" [39;49;00m [33mNo available eligible athletes found [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
            athlete_row = visible_rows[ [94m0 [39;49;00m] [90m [39;49;00m
            athlete_text = athlete_row.text [90m [39;49;00m
             [90m# Extract the name - skip first word (badge ID) and get remaining text [39;49;00m [90m [39;49;00m
            words = athlete_text.split() [90m [39;49;00m
            athlete_name =  [33m' [39;49;00m [33m  [39;49;00m [33m' [39;49;00m.join(words[ [94m1 [39;49;00m:])  [94mif [39;49;00m  [96mlen [39;49;00m(words) >  [94m1 [39;49;00m  [94melse [39;49;00m words[ [94m0 [39;49;00m] [90m [39;49;00m
             [96mprint [39;49;00m( [33mf [39;49;00m [33m" [39;49;00m [33mDouble-clicking on athlete:  [39;49;00m [33m{ [39;49;00mathlete_name [33m} [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
             [90m# Get count of available athletes before double-click [39;49;00m [90m [39;49;00m
            available_count_before =  [96mlen [39;49;00m(visible_rows) [90m [39;49;00m
     [90m [39;49;00m
             [90m# Use double-click to assign directly (bypasses highlight/promote flow) [39;49;00m [90m [39;49;00m
             [94mfrom [39;49;00m [90m  [39;49;00m [04m [96mselenium [39;49;00m [04m [96m. [39;49;00m [04m [96mwebdriver [39;49;00m [04m [96m. [39;49;00m [04m [96mcommon [39;49;00m [04m [96m. [39;49;00m [04m [96maction_chains [39;49;00m [90m  [39;49;00m [94mimport [39;49;00m ActionChains [90m [39;49;00m
            actions = ActionChains( [96mself [39;49;00m.selenium) [90m [39;49;00m
            actions.move_to_element(athlete_row).double_click().perform() [90m [39;49;00m
             [90m# If double click is flaky, try execute_script for dblclick [39;49;00m [90m [39;49;00m
             [96mself [39;49;00m.selenium.execute_script( [33m" [39;49;00m [33mvar evt = new MouseEvent( [39;49;00m [33m' [39;49;00m [33mdblclick [39;49;00m [33m' [39;49;00m [33m,  [39;49;00m [33m{ [39;49;00m [33mbubbles: true, cancelable: true, view: window}); arguments[0].dispatchEvent(evt); [39;49;00m [33m" [39;49;00m, athlete_row) [90m [39;49;00m
            time.sleep( [94m1 [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
     [90m [39;49;00m
             [90m# Verify athlete was moved (available count should decrease by 1) [39;49;00m [90m [39;49;00m
            available_rows_after =  [96mself [39;49;00m.selenium.find_elements(By.CSS_SELECTOR,  [33mf [39;49;00m [33m" [39;49;00m [33m{ [39;49;00mavailable_table_selector [33m} [39;49;00m [33m tbody tr [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
            available_count_after =  [96mlen [39;49;00m([r  [94mfor [39;49;00m r  [95min [39;49;00m available_rows_after  [94mif [39;49;00m r.is_displayed()  [95mand [39;49;00m  [33m" [39;49;00m [33mmuted [39;49;00m [33m" [39;49;00m  [95mnot [39;49;00m  [95min [39;49;00m r.get_attribute( [33m" [39;49;00m [33mclass [39;49;00m [33m" [39;49;00m)]) [90m [39;49;00m
             [96mself [39;49;00m.assertEqual(available_count_after, available_count_before -  [94m1 [39;49;00m,  [33mf [39;49;00m [33m" [39;49;00m [33mAthlete  [39;49;00m [33m' [39;49;00m [33m{ [39;49;00mathlete_name [33m} [39;49;00m [33m' [39;49;00m [33m should have been removed from available table [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
             [90m# --- Additional: Test Promote button functionality --- [39;49;00m [90m [39;49;00m
     [90m [39;49;00m
            selected_rows =  [96mself [39;49;00m.selenium.find_elements(By.CSS_SELECTOR,  [33mf [39;49;00m [33m" [39;49;00m [33m{ [39;49;00mselected_table_selector [33m} [39;49;00m [33m tbody tr [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
            selected_text = [r.text  [94mfor [39;49;00m r  [95min [39;49;00m selected_rows] [90m [39;49;00m
     [90m [39;49;00m
             [90m# demote test [39;49;00m [90m [39;49;00m
     [90m [39;49;00m
             [94mif [39;49;00m  [96mlen [39;49;00m(selected_rows): [90m [39;49;00m
                row_to_demote = selected_rows[ [96mlen [39;49;00m(selected_rows)- [94m1 [39;49;00m] [90m [39;49;00m
     [90m [39;49;00m
                 [90m# click it to highlight it [39;49;00m [90m [39;49;00m
                row_to_demote.click() [90m [39;49;00m
     [90m [39;49;00m
                 [90m# then click demote button [39;49;00m [90m [39;49;00m
                 [96mself [39;49;00m.selenium.find_elements(By.CSS_SELECTOR,  [33mf [39;49;00m [33m" [39;49;00m [33m{ [39;49;00mdemote_btn_selector [33m} [39;49;00m [33m" [39;49;00m)[ [94m0 [39;49;00m].click() [90m [39;49;00m
     [90m [39;49;00m
                 [90m# check they are no longer in select table [39;49;00m [90m [39;49;00m
                recheck_selected_text = [r.text  [94mfor [39;49;00m r  [95min [39;49;00m  [96mself [39;49;00m.selenium.find_elements(By.CSS_SELECTOR,  [33mf [39;49;00m [33m" [39;49;00m [33m{ [39;49;00mselected_table_selector [33m} [39;49;00m [33m tbody tr [39;49;00m [33m" [39;49;00m)] [90m [39;49;00m
                found =  [96many [39;49;00m(athlete_name  [95min [39;49;00m text  [94mfor [39;49;00m text  [95min [39;49;00m recheck_selected_text) [90m [39;49;00m
                 [96mself [39;49;00m.assertFalse(found,  [33mf [39;49;00m [33m" [39;49;00m [33mAthlete  [39;49;00m [33m{ [39;49;00mathlete_name [33m} [39;49;00m [33m was found in the selected table after being demoted! [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
            time.sleep( [94m0.5 [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
            rows =  [96mself [39;49;00m.selenium.find_elements(By.CSS_SELECTOR,  [33mf [39;49;00m [33m" [39;49;00m [33m{ [39;49;00mavailable_table_selector [33m} [39;49;00m [33m tbody tr [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
            visible_rows = [r  [94mfor [39;49;00m r  [95min [39;49;00m rows  [94mif [39;49;00m r.is_displayed()  [95mand [39;49;00m  [33m" [39;49;00m [33mmuted [39;49;00m [33m" [39;49;00m  [95mnot [39;49;00m  [95min [39;49;00m r.get_attribute( [33m" [39;49;00m [33mclass [39;49;00m [33m" [39;49;00m)] [90m [39;49;00m
     [90m [39;49;00m
             [90m# Select second athlete for promote test [39;49;00m [90m [39;49;00m
             [94mif [39;49;00m  [96mlen [39;49;00m(visible_rows): [90m [39;49;00m
                second_athlete_row = visible_rows[ [94m0 [39;49;00m] [90m [39;49;00m
                second_athlete_text = second_athlete_row.text [90m [39;49;00m
                 [90m# Extract the name - skip first word (badge ID) and get remaining text [39;49;00m [90m [39;49;00m
                words = second_athlete_text.split() [90m [39;49;00m
                second_athlete_name =  [33m' [39;49;00m [33m  [39;49;00m [33m' [39;49;00m.join(words[ [94m1 [39;49;00m:])  [94mif [39;49;00m  [96mlen [39;49;00m(words) >  [94m1 [39;49;00m  [94melse [39;49;00m words[ [94m0 [39;49;00m] [90m [39;49;00m
                 [96mprint [39;49;00m( [33mf [39;49;00m [33m" [39;49;00m [33mTesting promote button with second athlete:  [39;49;00m [33m{ [39;49;00msecond_athlete_name [33m} [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
                 [90m# Highlight the athlete using click [39;49;00m [90m [39;49;00m
                second_athlete_row.click() [90m [39;49;00m
                time.sleep( [94m0.5 [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
                 [90m# Check if promote button is available and click it [39;49;00m [90m [39;49;00m
                 [94mtry [39;49;00m: [90m [39;49;00m
                    promote_btn =  [96mself [39;49;00m.selenium.find_element(By.CSS_SELECTOR, promote_btn_selector) [90m [39;49;00m
                     [94mif [39;49;00m promote_btn.is_enabled()  [95mand [39;49;00m  [33m" [39;49;00m [33mdisabled [39;49;00m [33m" [39;49;00m  [95mnot [39;49;00m  [95min [39;49;00m promote_btn.get_attribute( [33m" [39;49;00m [33mclass [39;49;00m [33m" [39;49;00m): [90m [39;49;00m
                         [96mprint [39;49;00m( [33m" [39;49;00m [33mClicking promote button... [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
                         [96mself [39;49;00m.selenium.execute_script( [33m" [39;49;00m [33marguments[0].click(); [39;49;00m [33m" [39;49;00m, promote_btn) [90m [39;49;00m
                         [96mprint [39;49;00m( [33m" [39;49;00m [33mClicked promote button. [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
                        time.sleep( [94m1 [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
     [90m [39;49;00m
                         [90m# Verify it moved to the selected table [39;49;00m [90m [39;49;00m
                        selected_rows =  [96mself [39;49;00m.selenium.find_elements(By.CSS_SELECTOR,  [33mf [39;49;00m [33m" [39;49;00m [33m{ [39;49;00mselected_table_selector [33m} [39;49;00m [33m tbody tr [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
                        selected_text = [r.text  [94mfor [39;49;00m r  [95min [39;49;00m selected_rows] [90m [39;49;00m
                        found_second =  [96many [39;49;00m(second_athlete_name  [95min [39;49;00m text  [94mfor [39;49;00m text  [95min [39;49;00m selected_text) [90m [39;49;00m
                         [96mself [39;49;00m.assertTrue(found_second,  [33mf [39;49;00m [33m" [39;49;00m [33mSecond athlete  [39;49;00m [33m' [39;49;00m [33m{ [39;49;00msecond_athlete_name [33m} [39;49;00m [33m' [39;49;00m [33m was not moved to the selected table using promote button [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
                         [96mprint [39;49;00m( [33m" [39;49;00m [33mSecond athlete successfully tested with promote button [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
                 [94mexcept [39;49;00m  [96mException [39;49;00m  [94mas [39;49;00m e: [90m [39;49;00m
                     [96mprint [39;49;00m(e) [90m [39;49;00m
                     [96mprint [39;49;00m( [33m" [39;49;00m [33mPromote button test skipped - button not available or not functional [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
             [94melse [39;49;00m: [90m [39;49;00m
                 [96mprint [39;49;00m( [33m" [39;49;00m [33mNot enough athletes to test promote button [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
             [96mprint [39;49;00m( [33m" [39;49;00m [33mAthletes successfully tested with both double-click and promote button (when available) [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
             [90m# --- Part 2: Add via Grid --- [39;49;00m [90m [39;49;00m
             [96mprint [39;49;00m( [33m" [39;49;00m [33mOpening  [39;49;00m [33m' [39;49;00m [33mBulk Entry (Excel) [39;49;00m [33m' [39;49;00m [33m grid... [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
            add_athlete_btn =  [96mself [39;49;00m.selenium.find_element(By.XPATH,  [33m" [39;49;00m [33m//button[contains(text(),  [39;49;00m [33m' [39;49;00m [33mBulk Entry (Excel) [39;49;00m [33m' [39;49;00m [33m)] [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
            add_athlete_btn.click() [90m [39;49;00m
            time.sleep( [94m0.5 [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
            ht_container_selector =  [33m" [39;49;00m [33m#addCompetitorContainer [39;49;00m [33m" [39;49;00m [90m [39;49;00m
             [96mself [39;49;00m.wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, ht_container_selector))) [90m [39;49;00m
     [90m [39;49;00m
            ht_selector =  [33mf [39;49;00m [33m" [39;49;00m [33m{ [39;49;00mht_container_selector [33m} [39;49;00m [33m table.htCore [39;49;00m [33m" [39;49;00m [90m [39;49;00m
             [96mself [39;49;00m.wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, ht_selector))) [90m [39;49;00m
     [90m [39;49;00m
             [90m# Use more persons from fixture for grid input [39;49;00m [90m [39;49;00m
            p6 = Person.objects.filter(first_name= [33m" [39;49;00m [33mJames [39;49;00m [33m" [39;49;00m, last_name= [33m" [39;49;00m [33mSmith [39;49;00m [33m" [39;49;00m, gender= [33m' [39;49;00m [33mF [39;49;00m [33m' [39;49;00m).first() [90m [39;49;00m
            p7 = Person.objects.filter(first_name= [33m" [39;49;00m [33mJohn [39;49;00m [33m" [39;49;00m, last_name= [33m" [39;49;00m [33mMartinez [39;49;00m [33m" [39;49;00m).first() [90m [39;49;00m
            p8 = Person.objects.filter(first_name= [33m" [39;49;00m [33mMichael [39;49;00m [33m" [39;49;00m, last_name= [33m" [39;49;00m [33mMiller [39;49;00m [33m" [39;49;00m, gender= [33m' [39;49;00m [33mF [39;49;00m [33m' [39;49;00m).first() [90m [39;49;00m
     [90m [39;49;00m
             [96mself [39;49;00m.assertIsNotNone(p6,  [33m" [39;49;00m [33mPerson  [39;49;00m [33m' [39;49;00m [33mJames Smith (F) [39;49;00m [33m' [39;49;00m [33m not found in fixture [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
             [96mself [39;49;00m.assertIsNotNone(p7,  [33m" [39;49;00m [33mPerson  [39;49;00m [33m' [39;49;00m [33mJohn Martinez [39;49;00m [33m' [39;49;00m [33m not found in fixture [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
             [96mself [39;49;00m.assertIsNotNone(p8,  [33m" [39;49;00m [33mPerson  [39;49;00m [33m' [39;49;00m [33mMichael Miller (F) [39;49;00m [33m' [39;49;00m [33m not found in fixture [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
            new_athletes = [ [90m [39;49;00m
                (p6.first_name, p6.last_name, p6.date_of_birth.isoformat(), p6.gender), [90m [39;49;00m
                (p7.first_name, p7.last_name, p7.date_of_birth.isoformat(), p7.gender), [90m [39;49;00m
                (p8.first_name, p8.last_name, p8.date_of_birth.isoformat(), p8.gender), [90m [39;49;00m
            ] [90m [39;49;00m
     [90m [39;49;00m
             [94mfrom [39;49;00m [90m  [39;49;00m [04m [96mselenium [39;49;00m [04m [96m. [39;49;00m [04m [96mwebdriver [39;49;00m [04m [96m. [39;49;00m [04m [96mcommon [39;49;00m [04m [96m. [39;49;00m [04m [96maction_chains [39;49;00m [90m  [39;49;00m [94mimport [39;49;00m ActionChains [90m [39;49;00m
     [90m [39;49;00m
             [94mfor [39;49;00m i, (first, last, dob, gender)  [95min [39;49;00m  [96menumerate [39;49;00m(new_athletes): [90m [39;49;00m
                 [96mprint [39;49;00m( [33mf [39;49;00m [33m" [39;49;00m [33mInputting grid athlete  [39;49;00m [33m{ [39;49;00mi+ [94m1 [39;49;00m [33m} [39;49;00m [33m:  [39;49;00m [33m{ [39;49;00mfirst [33m} [39;49;00m [33m  [39;49;00m [33m{ [39;49;00mlast [33m} [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
                cell_xpath =  [33mf [39;49;00m [33m" [39;49;00m [33m//div[@id= [39;49;00m [33m' [39;49;00m [33mcompetitors_ht [39;49;00m [33m' [39;49;00m [33m]//table[@class= [39;49;00m [33m' [39;49;00m [33mhtCore [39;49;00m [33m' [39;49;00m [33m]/tbody/tr[ [39;49;00m [33m{ [39;49;00mi+ [94m1 [39;49;00m [33m} [39;49;00m [33m]/td[1] [39;49;00m [33m" [39;49;00m [90m [39;49;00m
                cell =  [96mself [39;49;00m.wait.until(EC.element_to_be_clickable((By.XPATH, cell_xpath))) [90m [39;49;00m
     [90m [39;49;00m
                actions = ActionChains( [96mself [39;49;00m.selenium) [90m [39;49;00m
                actions.move_to_element(cell).click().perform() [90m [39;49;00m
                time.sleep( [94m0.3 [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
                actions = ActionChains( [96mself [39;49;00m.selenium) [90m [39;49;00m
                actions.send_keys(first) [90m [39;49;00m
                actions.send_keys(Keys.TAB) [90m [39;49;00m
                actions.send_keys(last) [90m [39;49;00m
                actions.send_keys(Keys.TAB) [90m [39;49;00m
                actions.send_keys(dob) [90m [39;49;00m
                actions.send_keys(Keys.TAB) [90m [39;49;00m
                actions.send_keys(gender) [90m [39;49;00m
                actions.send_keys(Keys.ENTER) [90m [39;49;00m
                actions.perform() [90m [39;49;00m
                time.sleep( [94m0.5 [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
             [90m# Note: The "Add Athletes" button doesn't exist in the template. [39;49;00m [90m [39;49;00m
             [90m# Bulk entry data is saved via the handsontable's internal state when saveEntry is called. [39;49;00m [90m [39;49;00m
     [90m [39;49;00m
             [90m# Verify grid is in sync with selected athletes after double-click [39;49;00m [90m [39;49;00m
             [96mprint [39;49;00m( [33m" [39;49;00m [33mVerifying grid is in sync with selected athletes... [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
            time.sleep( [94m0.5 [39;49;00m)   [90m# Wait for Vue to update [39;49;00m [90m [39;49;00m
            grid_data =  [96mself [39;49;00m.selenium.execute_script( [33m" [39;49;00m [33mreturn window.competitor_ht ? window.competitor_ht.getData() : []; [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
            selected_rows_after_dblclick =  [96mself [39;49;00m.selenium.find_elements(By.CSS_SELECTOR,  [33mf [39;49;00m [33m" [39;49;00m [33m{ [39;49;00mselected_table_selector [33m} [39;49;00m [33m tbody tr:not(.muted) [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
            selected_names = [r.text  [94mfor [39;49;00m r  [95min [39;49;00m selected_rows_after_dblclick] [90m [39;49;00m
     [90m [39;49;00m
             [90m# Check that all selected athletes appear in the grid [39;49;00m [90m [39;49;00m
             [94mfor [39;49;00m row  [95min [39;49;00m selected_rows_after_dblclick: [90m [39;49;00m
                row_text = row.text [90m [39;49;00m
                words = row_text.split() [90m [39;49;00m
                 [90m# Skip badge ID (first word) [39;49;00m [90m [39;49;00m
                athlete_name =  [33m' [39;49;00m [33m  [39;49;00m [33m' [39;49;00m.join(words[ [94m1 [39;49;00m:])  [94mif [39;49;00m  [96mlen [39;49;00m(words) >  [94m1 [39;49;00m  [94melse [39;49;00m words[ [94m0 [39;49;00m] [90m [39;49;00m
                found_in_grid =  [96many [39;49;00m(athlete_name.split()[ [94m0 [39;49;00m]  [95min [39;49;00m  [96mstr [39;49;00m(row)  [95mor [39;49;00m athlete_name.split()[- [94m1 [39;49;00m]  [95min [39;49;00m  [96mstr [39;49;00m(row)  [94mfor [39;49;00m row  [95min [39;49;00m grid_data  [94mif [39;49;00m row) [90m [39;49;00m
                 [96mprint [39;49;00m( [33mf [39;49;00m [33m" [39;49;00m [33mChecking athlete  [39;49;00m [33m' [39;49;00m [33m{ [39;49;00mathlete_name [33m} [39;49;00m [33m' [39;49;00m [33m in grid:  [39;49;00m [33m{ [39;49;00m [33m' [39;49;00m [33mFound [39;49;00m [33m' [39;49;00m [90m  [39;49;00m [94mif [39;49;00m [90m  [39;49;00mfound_in_grid [90m  [39;49;00m [94melse [39;49;00m [90m  [39;49;00m [33m' [39;49;00m [33mNot found [39;49;00m [33m' [39;49;00m [33m} [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
             [96mprint [39;49;00m( [33m" [39;49;00m [33mGrid is in sync with selected athletes after double-click. [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
             [90m# --- Part 2b: Add new athletes via grid and verify they appear in selected table --- [39;49;00m [90m [39;49;00m
     [90m [39;49;00m
             [90m# Wait for Vue to process the grid changes and trigger update [39;49;00m [90m [39;49;00m
             [96mprint [39;49;00m( [33m" [39;49;00m [33mWaiting for grid sync... [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
            time.sleep( [94m1 [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
             [90m# Trigger the Vue update manually to ensure sync [39;49;00m [90m [39;49;00m
             [90m# self.selenium.execute_script("if (window.app && window.app.updateCompetitorsHandsOnTable) { window.app.updateCompetitorsHandsOnTable(); }") [39;49;00m [90m [39;49;00m
             [90m# time.sleep(1) [39;49;00m [90m [39;49;00m
     [90m [39;49;00m
             [90m# Check grid data to see what's actually in it [39;49;00m [90m [39;49;00m
            grid_data =  [96mself [39;49;00m.selenium.execute_script( [33m" [39;49;00m [33mreturn window.competitor_ht ? window.competitor_ht.getData() : []; [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
            grid_count =  [96mlen [39;49;00m([r  [94mfor [39;49;00m r  [95min [39;49;00m grid_data  [94mif [39;49;00m r  [95mand [39;49;00m r.get( [33m' [39;49;00m [33mfirst_name [39;49;00m [33m' [39;49;00m)]) [90m [39;49;00m
             [96mprint [39;49;00m( [33mf [39;49;00m [33m" [39;49;00m [33mGrid has  [39;49;00m [33m{ [39;49;00mgrid_count [33m} [39;49;00m [33m athletes after entry [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
             [90m# Verify selected table and grid are in sync [39;49;00m [90m [39;49;00m
             [96mprint [39;49;00m( [33m" [39;49;00m [33mVerifying grid and selected table are in sync... [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
            selected_rows =  [96mself [39;49;00m.selenium.find_elements(By.CSS_SELECTOR,  [33mf [39;49;00m [33m" [39;49;00m [33m{ [39;49;00mselected_table_selector [33m} [39;49;00m [33m tbody tr:not(.muted) [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
            selected_count =  [96mlen [39;49;00m(selected_rows) [90m [39;49;00m
             [96mprint [39;49;00m( [33mf [39;49;00m [33m" [39;49;00m [33mSelected table has  [39;49;00m [33m{ [39;49;00mselected_count [33m} [39;49;00m [33m athletes, Grid has  [39;49;00m [33m{ [39;49;00mgrid_count [33m} [39;49;00m [33m athletes [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
             [90m# The grid should be in sync with selected athletes (grid is loaded from selectedAthletes) [39;49;00m [90m [39;49;00m
             [90m# Note: Some athletes from grid may have been filtered if they don't meet criteria [39;49;00m [90m [39;49;00m
             [90m# So we just verify that both have athletes and grid/selected are consistent [39;49;00m [90m [39;49;00m
             [96mself [39;49;00m.assertGreater(selected_count,  [94m0 [39;49;00m,  [33m" [39;49;00m [33mSelected table should have athletes [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
             [96mself [39;49;00m.assertEqual(grid_count, selected_count,  [33m" [39;49;00m [33mGrid row count should match selected table count [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
             [96mprint [39;49;00m( [33m" [39;49;00m [33mGrid and selected table are in sync. [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
             [90m# Also verify grid is in sync with selected table [39;49;00m [90m [39;49;00m
            grid_data =  [96mself [39;49;00m.selenium.execute_script( [33m" [39;49;00m [33mreturn window.competitor_ht ? window.competitor_ht.getData() : []; [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
            final_selected_count =  [96mlen [39;49;00m( [96mself [39;49;00m.selenium.find_elements(By.CSS_SELECTOR,  [33mf [39;49;00m [33m" [39;49;00m [33m{ [39;49;00mselected_table_selector [33m} [39;49;00m [33m tbody tr:not(.muted) [39;49;00m [33m" [39;49;00m)) [90m [39;49;00m
            expected_grid_rows = final_selected_count [90m [39;49;00m
             [96mprint [39;49;00m( [33mf [39;49;00m [33m" [39;49;00m [33mGrid has  [39;49;00m [33m{ [39;49;00m [96mlen [39;49;00m([r [90m  [39;49;00m [94mfor [39;49;00m [90m  [39;49;00mr [90m  [39;49;00m [95min [39;49;00m [90m  [39;49;00mgrid_data [90m  [39;49;00m [94mif [39;49;00m [90m  [39;49;00mr [90m  [39;49;00m [95mand [39;49;00m [90m  [39;49;00m [96many [39;49;00m(r.values())]) [33m} [39;49;00m [33m rows, selected table has  [39;49;00m [33m{ [39;49;00mfinal_selected_count [33m} [39;49;00m [33m rows [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
             [90m# --- Part 3: Save and Refresh --- [39;49;00m [90m [39;49;00m
             [96mprint [39;49;00m( [33m" [39;49;00m [33mSaving... [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
             [96mself [39;49;00m.selenium.execute_script( [33m" [39;49;00m [33mwindow.scrollTo(0, 0); [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
            time.sleep( [94m0.5 [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
             [90m# The save button has class "btn-primary btn-sm rounded-pill px-4 shadow-sm" [39;49;00m [90m [39;49;00m
            save_btn = WebDriverWait( [96mself [39;49;00m.selenium,  [94m10 [39;49;00m).until( [90m [39;49;00m
                EC.element_to_be_clickable((By.CSS_SELECTOR,  [33m" [39;49;00m [33mbutton.btn-primary.btn-sm [39;49;00m [33m" [39;49;00m)) [90m [39;49;00m
            ) [90m [39;49;00m
             [96mself [39;49;00m.selenium.execute_script( [33m" [39;49;00m [33marguments[0].click(); [39;49;00m [33m" [39;49;00m, save_btn) [90m [39;49;00m
     [90m [39;49;00m
             [94mtry [39;49;00m: [90m [39;49;00m
                 [96mself [39;49;00m.wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR,  [33m" [39;49;00m [33mspan.bg-success-light span.fa-check [39;49;00m [33m" [39;49;00m))) [90m [39;49;00m
             [94mexcept [39;49;00m: [90m [39;49;00m
                 [94mpass [39;49;00m [90m [39;49;00m
     [90m [39;49;00m
            time.sleep( [94m1 [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
             [90m# --- Part 4: Refresh and verify persistence --- [39;49;00m [90m [39;49;00m
             [96mprint [39;49;00m( [33m" [39;49;00m [33mRefreshing page... [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
             [96mself [39;49;00m.selenium.refresh() [90m [39;49;00m
             [96mself [39;49;00m.wait.until(EC.invisibility_of_element_located((By.CSS_SELECTOR,  [33m" [39;49;00m [33m[v-cloak] [39;49;00m [33m" [39;49;00m))) [90m [39;49;00m
            time.sleep( [94m1 [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
             [96mprint [39;49;00m( [33m" [39;49;00m [33mRe-selecting event... [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
            event_link = WebDriverWait( [96mself [39;49;00m.selenium,  [94m10 [39;49;00m).until( [90m [39;49;00m
                EC.element_to_be_clickable((By.CSS_SELECTOR,  [33m" [39;49;00m [33ma[data-eventcode= [39;49;00m [33m' [39;49;00m [33m100 [39;49;00m [33m' [39;49;00m [33m] [39;49;00m [33m" [39;49;00m)) [90m [39;49;00m
            ) [90m [39;49;00m
            event_link.click() [90m [39;49;00m
            time.sleep( [94m1 [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
             [96mprint [39;49;00m( [33m" [39;49;00m [33mVerifying persistence after refresh... [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
             [90m# Get selected table state after refresh [39;49;00m [90m [39;49;00m
            selected_rows_after_refresh =  [96mself [39;49;00m.selenium.find_elements(By.CSS_SELECTOR,  [33mf [39;49;00m [33m" [39;49;00m [33m{ [39;49;00mselected_table_selector [33m} [39;49;00m [33m tbody tr [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
            selected_text_after_refresh = [r.text  [94mfor [39;49;00m r  [95min [39;49;00m selected_rows_after_refresh] [90m [39;49;00m
     [90m [39;49;00m
             [90m# Verify some athletes persisted (not checking specific ones since test flow may have changed selection) [39;49;00m [90m [39;49;00m
            selected_count_after_refresh =  [96mlen [39;49;00m(selected_rows_after_refresh) [90m [39;49;00m
             [96mprint [39;49;00m( [33mf [39;49;00m [33m" [39;49;00m [33mSelected table has  [39;49;00m [33m{ [39;49;00mselected_count_after_refresh [33m} [39;49;00m [33m athletes after refresh [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
             [90m# Verify grid is in sync with selected table after refresh [39;49;00m [90m [39;49;00m
             [90m# Re-open the bulk entry grid to check its contents [39;49;00m [90m [39;49;00m
            add_athlete_btn =  [96mself [39;49;00m.selenium.find_element(By.XPATH,  [33m" [39;49;00m [33m//button[contains(text(),  [39;49;00m [33m' [39;49;00m [33mBulk Entry (Excel) [39;49;00m [33m' [39;49;00m [33m)] [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
            add_athlete_btn.click() [90m [39;49;00m
            time.sleep( [94m0.5 [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
             [96mself [39;49;00m.wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, ht_container_selector))) [90m [39;49;00m
     [90m [39;49;00m
            grid_data_after_refresh =  [96mself [39;49;00m.selenium.execute_script( [33m" [39;49;00m [33mreturn window.competitor_ht ? window.competitor_ht.getData() : []; [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
            grid_count_after_refresh =  [96mlen [39;49;00m([r  [94mfor [39;49;00m r  [95min [39;49;00m grid_data_after_refresh  [94mif [39;49;00m r  [95mand [39;49;00m  [96many [39;49;00m(r.values())]) [90m [39;49;00m
     [90m [39;49;00m
             [96mprint [39;49;00m( [33mf [39;49;00m [33m" [39;49;00m [33mAfter refresh - Selected table:  [39;49;00m [33m{ [39;49;00mselected_count_after_refresh [33m} [39;49;00m [33m athletes, Grid:  [39;49;00m [33m{ [39;49;00mgrid_count_after_refresh [33m} [39;49;00m [33m rows [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
             [90m# Verify selected table has athletes (at least some should have been saved) [39;49;00m [90m [39;49;00m
             [96mself [39;49;00m.assertGreater(selected_count_after_refresh,  [94m0 [39;49;00m,  [33m" [39;49;00m [33mSelected table should have athletes after refresh [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
             [90m# Grid and selected table should be in sync [39;49;00m [90m [39;49;00m
             [96mself [39;49;00m.assertEqual(grid_count_after_refresh, selected_count_after_refresh, [90m [39;49;00m
                            [33mf [39;49;00m [33m" [39;49;00m [33mGrid row count ( [39;49;00m [33m{ [39;49;00mgrid_count_after_refresh [33m} [39;49;00m [33m) should match selected table count ( [39;49;00m [33m{ [39;49;00mselected_count_after_refresh [33m} [39;49;00m [33m) [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
             [96mprint [39;49;00m( [33m" [39;49;00m [33mGrid and selected table are in sync after refresh. [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
             [96mprint [39;49;00m( [33m" [39;49;00m [33mAll athletes persisted correctly! [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
             [96mprint [39;49;00m( [33m" [39;49;00m [33mTest completed successfully! [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
     [90m [39;49;00m
         [94mexcept [39;49;00m  [96mException [39;49;00m  [94mas [39;49;00m e: [90m [39;49;00m
             [96mself [39;49;00m.selenium.save_screenshot( [33m" [39;49;00m [33mtest_adhoc_indiv_failure.png [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
             [94mwith [39;49;00m  [96mopen [39;49;00m( [33m" [39;49;00m [33mtest_adhoc_indiv_failure_source.html [39;49;00m [33m" [39;49;00m,  [33m" [39;49;00m [33mw [39;49;00m [33m" [39;49;00m)  [94mas [39;49;00m f: [90m [39;49;00m
                f.write( [96mself [39;49;00m.selenium.page_source) [90m [39;49;00m
             [96mprint [39;49;00m( [33mf [39;49;00m [33m" [39;49;00m [33mTest failed:  [39;49;00m [33m{ [39;49;00me [33m} [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
>            [94mraise [39;49;00m e [90m [39;49;00m
project/comp/tests/test_team_declarations.py:735:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
project/comp/tests/test_team_declarations.py:722: in test_adhoc_individual_declarations
     [0m [96mself [39;49;00m.assertEqual(grid_count_after_refresh, selected_count_after_refresh, [90m [39;49;00m
E   AssertionError: 0 != 1 : Grid row count (0) should match selected table count (1)
____________ SeedingE2ETests.test_three_round_championship_seeding _____________
[gw2] linux -- Python 3.13.2 /opt/hostedtoolcache/Python/3.13.2/x64/bin/python
self = <project.comp.tests.test_seeding.SeedingE2ETests testMethod=test_three_round_championship_seeding>
     [0m [94mdef [39;49;00m [90m  [39;49;00m [92mtest_three_round_championship_seeding [39;49;00m( [96mself [39;49;00m): [90m [39;49;00m
     [90m     [39;49;00m [33m""" [39;49;00m
     [33m    E2E test for multi-round seeding. [39;49;00m
     [33m    """ [39;49;00m [90m [39;49;00m
        wait = WebDriverWait( [96mself [39;49;00m.selenium,  [94m10 [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
         [90m# Login [39;49;00m [90m [39;49;00m
         [94mtry [39;49;00m: [90m [39;49;00m
             [94mfrom [39;49;00m [90m  [39;49;00m [04m [96mdjango [39;49;00m [04m [96m. [39;49;00m [04m [96murls [39;49;00m [90m  [39;49;00m [94mimport [39;49;00m reverse [90m [39;49;00m
            login_path = reverse( [33m' [39;49;00m [33mlogin [39;49;00m [33m' [39;49;00m) [90m [39;49;00m
            login_url =  [33mf [39;49;00m [33m" [39;49;00m [33m{ [39;49;00m [96mself [39;49;00m.live_server_url [33m} [39;49;00m [33m{ [39;49;00mlogin_path [33m} [39;49;00m [33m" [39;49;00m [90m [39;49;00m
         [94mexcept [39;49;00m  [96mException [39;49;00m  [94mas [39;49;00m e: [90m [39;49;00m
             [96mprint [39;49;00m( [33mf [39;49;00m [33m" [39;49;00m [33mError reversing login URL:  [39;49;00m [33m{ [39;49;00me [33m} [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
            login_url =  [33mf [39;49;00m [33m" [39;49;00m [33m{ [39;49;00m [96mself [39;49;00m.live_server_url [33m} [39;49;00m [33m/en-gb/accounts/login/ [39;49;00m [33m" [39;49;00m [90m [39;49;00m
     [90m [39;49;00m
         [96mprint [39;49;00m( [33mf [39;49;00m [33m" [39;49;00m [33mLogging in at  [39;49;00m [33m{ [39;49;00mlogin_url [33m} [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
         [96mself [39;49;00m.selenium.get(login_url) [90m [39;49;00m
         [96mprint [39;49;00m( [33mf [39;49;00m [33m" [39;49;00m [33mCurrent URL:  [39;49;00m [33m{ [39;49;00m [96mself [39;49;00m.selenium.current_url [33m} [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
         [94mfrom [39;49;00m [90m  [39;49;00m [04m [96mproject [39;49;00m [04m [96m. [39;49;00m [04m [96mreference [39;49;00m [04m [96m. [39;49;00m [04m [96mmongo [39;49;00m [04m [96m. [39;49;00m [04m [96munit [39;49;00m [90m  [39;49;00m [94mimport [39;49;00m Unit [90m [39;49;00m
         [96mprint [39;49;00m( [33mf [39;49;00m [33m" [39;49;00m [33mDEBUG TEST: Competitor count for event 1:  [39;49;00m [33m{ [39;49;00mCompetitor.objects(competition= [96mself [39;49;00m.c, [90m  [39;49;00mevents_entered__event_id= [33m' [39;49;00m [33m1 [39;49;00m [33m' [39;49;00m).count() [33m} [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
        ev1 =  [96mself [39;49;00m.c.get_event( [33m' [39;49;00m [33m1 [39;49;00m [33m' [39;49;00m) [90m [39;49;00m
         [96mprint [39;49;00m( [33mf [39;49;00m [33m" [39;49;00m [33mDEBUG TEST: Unit count for event 1:  [39;49;00m [33m{ [39;49;00mUnit.objects(competition= [96mself [39;49;00m.c, [90m  [39;49;00mevent=ev1).count() [33m} [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
         [94mtry [39;49;00m: [90m [39;49;00m
            username_field = wait.until(EC.presence_of_element_located((By.ID,  [33m" [39;49;00m [33mid_username [39;49;00m [33m" [39;49;00m))) [90m [39;49;00m
             [96mprint [39;49;00m( [33m" [39;49;00m [33mFound username field [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
            username_field.send_keys( [33m" [39;49;00m [33msuperuser [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
             [96mself [39;49;00m.selenium.find_element(By.ID,  [33m" [39;49;00m [33mid_password [39;49;00m [33m" [39;49;00m).send_keys( [33m" [39;49;00m [33msecret [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
             [96mself [39;49;00m.selenium.find_element(By.ID,  [33m" [39;49;00m [33mloginBtn [39;49;00m [33m" [39;49;00m).click() [90m [39;49;00m
         [94mexcept [39;49;00m: [90m [39;49;00m
             [96mprint [39;49;00m( [33mf [39;49;00m [33m" [39;49;00m [33mTimeout or Error at login. Current URL:  [39;49;00m [33m{ [39;49;00m [96mself [39;49;00m.selenium.current_url [33m} [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
             [96mprint [39;49;00m( [33mf [39;49;00m [33m" [39;49;00m [33mPage source snippet:  [39;49;00m [33m{ [39;49;00m [96mself [39;49;00m.selenium.page_source[: [94m2000 [39;49;00m] [33m} [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
             [94mraise [39;49;00m [90m [39;49;00m
     [90m [39;49;00m
         [90m# Wait for login to complete by checking for a logout link or a specific dashboard element [39;49;00m [90m [39;49;00m
        wait.until( [94mlambda [39;49;00m d: d.current_url != login_url) [90m [39;49;00m
         [96mprint [39;49;00m( [33mf [39;49;00m [33m" [39;49;00m [33mLogged in, current URL:  [39;49;00m [33m{ [39;49;00m [96mself [39;49;00m.selenium.current_url [33m} [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
         [90m# Navigate to event detail private page [39;49;00m [90m [39;49;00m
        event_private_url =  [33mf [39;49;00m [33m" [39;49;00m [33m{ [39;49;00m [96mself [39;49;00m.live_server_url [33m} [39;49;00m [33m{ [39;49;00mcomp_reverse( [33m' [39;49;00m [33mcomp-event-detail-private [39;49;00m [33m' [39;49;00m, [90m  [39;49;00m [96mself [39;49;00m.sql_comp, [90m  [39;49;00mevent_id= [33m' [39;49;00m [33m1 [39;49;00m [33m' [39;49;00m) [33m} [39;49;00m [33m" [39;49;00m [90m [39;49;00m
         [96mprint [39;49;00m( [33mf [39;49;00m [33m" [39;49;00m [33mNavigating to  [39;49;00m [33m{ [39;49;00mevent_private_url [33m} [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
         [96mself [39;49;00m.selenium.get(event_private_url) [90m [39;49;00m
        time.sleep( [94m3 [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
         [90m# Round 1: Heats [39;49;00m [90m [39;49;00m
         [90m# make sure heats tab is selected [39;49;00m [90m [39;49;00m
        heats_tab = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR,  [33m" [39;49;00m [33ma[href= [39;49;00m [33m' [39;49;00m [33m#pills-heats [39;49;00m [33m' [39;49;00m [33m] [39;49;00m [33m" [39;49;00m))) [90m [39;49;00m
        heats_tab.click() [90m [39;49;00m
        time.sleep( [94m1 [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
         [96mprint [39;49;00m( [33m" [39;49;00m [33mRound 1: Heats [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
         [90m# Open Seeding Options (it's a collapsible) [39;49;00m [90m [39;49;00m
        seeding_options_btn = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR,  [33m" [39;49;00m [33mbutton[data-target= [39;49;00m [33m' [39;49;00m [33m#seeding_options_r1 [39;49;00m [33m' [39;49;00m [33m] [39;49;00m [33m" [39;49;00m))) [90m [39;49;00m
         [96mprint [39;49;00m( [33m" [39;49;00m [33mFound seeding options button [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
         [94mif [39;49;00m  [33m" [39;49;00m [33mcollapsed [39;49;00m [33m" [39;49;00m  [95min [39;49;00m seeding_options_btn.get_attribute( [33m" [39;49;00m [33mclass [39;49;00m [33m" [39;49;00m): [90m [39;49;00m
             [96mprint [39;49;00m( [33m" [39;49;00m [33mExpanding seeding options [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
            ActionChains( [96mself [39;49;00m.selenium).click(seeding_options_btn).perform() [90m [39;49;00m
            time.sleep( [94m1 [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
         [90m# Click 'Seed heats' [39;49;00m [90m [39;49;00m
         [96mprint [39;49;00m( [33m" [39;49;00m [33mClicking Seed heats [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
        seed_btn = wait.until(EC.element_to_be_clickable((By.NAME,  [33m" [39;49;00m [33mseed [39;49;00m [33m" [39;49;00m))) [90m [39;49;00m
        seed_btn.click() [90m [39;49;00m
        time.sleep( [94m1 [39;49;00m)  [90m# Wait for seeding to complete (it reloads the page) [39;49;00m [90m [39;49;00m
     [90m [39;49;00m
        heats_tab = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR,  [33m" [39;49;00m [33ma[href= [39;49;00m [33m' [39;49;00m [33m#pills-heats [39;49;00m [33m' [39;49;00m [33m] [39;49;00m [33m" [39;49;00m))) [90m [39;49;00m
        heats_tab.click() [90m [39;49;00m
        time.sleep( [94m1 [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
         [90m# Accept the upcoming confirm dialog [39;49;00m [90m [39;49;00m
         [96mself [39;49;00m.accept_confirm() [90m [39;49;00m
     [90m [39;49;00m
         [90m# Click 'Fake results' [39;49;00m [90m [39;49;00m
         [96mprint [39;49;00m( [33m" [39;49;00m [33mClicking Fake results [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
        fake_btn = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR,  [33m" [39;49;00m [33m#pills-heats .fake_results [39;49;00m [33m" [39;49;00m))) [90m [39;49;00m
        fake_btn.click() [90m [39;49;00m
        time.sleep( [94m1 [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
        heats_tab = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR,  [33m" [39;49;00m [33ma[href= [39;49;00m [33m' [39;49;00m [33m#pills-heats [39;49;00m [33m' [39;49;00m [33m] [39;49;00m [33m" [39;49;00m))) [90m [39;49;00m
        heats_tab.click() [90m [39;49;00m
        time.sleep( [94m1 [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
     [90m [39;49;00m
         [90m# Accept the upcoming confirm dialog [39;49;00m [90m [39;49;00m
         [96mself [39;49;00m.accept_confirm() [90m [39;49;00m
     [90m [39;49;00m
         [90m# Click 'Mark qualifiers' [39;49;00m [90m [39;49;00m
         [96mprint [39;49;00m( [33m" [39;49;00m [33mClicking Mark qualifiers [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
        mark_btn = wait.until(EC.element_to_be_clickable((By.NAME,  [33m" [39;49;00m [33mmark_qualifiers [39;49;00m [33m" [39;49;00m))) [90m [39;49;00m
        mark_btn.click() [90m [39;49;00m
        time.sleep( [94m1 [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
         [90m# Round 2: Semi-finals [39;49;00m [90m [39;49;00m
         [96mprint [39;49;00m( [33m" [39;49;00m [33mRound 2: Semi-finals [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
         [90m# Switch to Semi-finals tab [39;49;00m [90m [39;49;00m
        semi_tab = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR,  [33m" [39;49;00m [33ma[href= [39;49;00m [33m' [39;49;00m [33m#pills-semifinal [39;49;00m [33m' [39;49;00m [33m] [39;49;00m [33m" [39;49;00m))) [90m [39;49;00m
        semi_tab.click() [90m [39;49;00m
        time.sleep( [94m1 [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
         [90m# Re-open Seeding Options if needed (it might be different form) [39;49;00m [90m [39;49;00m
         [90m# For Round 2, the form is in #pills-semifinal [39;49;00m [90m [39;49;00m
        semi_seeding_btn =  [96mself [39;49;00m.selenium.find_element(By.CSS_SELECTOR,  [33m" [39;49;00m [33m#pills-semifinal button[data-target= [39;49;00m [33m' [39;49;00m [33m#seeding_options_r2 [39;49;00m [33m' [39;49;00m [33m] [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
         [94mif [39;49;00m  [33m" [39;49;00m [33mcollapsed [39;49;00m [33m" [39;49;00m  [95min [39;49;00m semi_seeding_btn.get_attribute( [33m" [39;49;00m [33mclass [39;49;00m [33m" [39;49;00m): [90m [39;49;00m
            semi_seeding_btn.click() [90m [39;49;00m
            time.sleep( [94m1 [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
         [90m# Seed heats for semi-finals [39;49;00m [90m [39;49;00m
        seed_semi_btn =  [96mself [39;49;00m.selenium.find_element(By.CSS_SELECTOR,  [33m" [39;49;00m [33m#pills-semifinal button[name= [39;49;00m [33m' [39;49;00m [33mseed [39;49;00m [33m' [39;49;00m [33m] [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
        seed_semi_btn.click() [90m [39;49;00m
        time.sleep( [94m1 [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
         [90m# Accept the upcoming confirm dialog [39;49;00m [90m [39;49;00m
         [96mself [39;49;00m.accept_confirm() [90m [39;49;00m
     [90m [39;49;00m
         [90m# Fake results for semi-finals [39;49;00m [90m [39;49;00m
        fake_semi_btn =  [96mself [39;49;00m.selenium.find_element(By.CSS_SELECTOR,  [33m" [39;49;00m [33m#pills-semifinal .fake_results [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
        fake_semi_btn.click() [90m [39;49;00m
        time.sleep( [94m1 [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
         [90m# Mark qualifiers for semi-finals [39;49;00m [90m [39;49;00m
         [96mself [39;49;00m.wait.until( [94mlambda [39;49;00m _ :  [96mself [39;49;00m.selenium.find_element(By.CSS_SELECTOR,  [33m" [39;49;00m [33m#pills-semifinal button[name= [39;49;00m [33m' [39;49;00m [33mmark_qualifiers [39;49;00m [33m' [39;49;00m [33m] [39;49;00m [33m" [39;49;00m).is_displayed()) [90m [39;49;00m
        mark_semi_btn =  [96mself [39;49;00m.selenium.find_element(By.CSS_SELECTOR,  [33m" [39;49;00m [33m#pills-semifinal button[name= [39;49;00m [33m' [39;49;00m [33mmark_qualifiers [39;49;00m [33m' [39;49;00m [33m] [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
        mark_semi_btn.click() [90m [39;49;00m
        time.sleep( [94m1 [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
         [96mself [39;49;00m.reset_mouse_position() [90m [39;49;00m
     [90m [39;49;00m
         [90m# Round 3: Finals [39;49;00m [90m [39;49;00m
         [90m# Switch to Finals tab [39;49;00m [90m [39;49;00m
        final_tab =  [96mself [39;49;00m.selenium.find_element(By.CSS_SELECTOR,  [33m" [39;49;00m [33ma[href= [39;49;00m [33m' [39;49;00m [33m#pills-final [39;49;00m [33m' [39;49;00m [33m] [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
        final_tab.click() [90m [39;49;00m
        time.sleep( [94m1 [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
         [90m# Seed final [39;49;00m [90m [39;49;00m
         [90m# Re-open Seeding Options if needed (it might be different form) [39;49;00m [90m [39;49;00m
        final_seeding_btn =  [96mself [39;49;00m.selenium.find_element(By.CSS_SELECTOR,  [33m" [39;49;00m [33m#pills-final button[data-target= [39;49;00m [33m' [39;49;00m [33m#finalSeedingForm [39;49;00m [33m' [39;49;00m [33m] [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
         [94mif [39;49;00m  [33m" [39;49;00m [33mcollapsed [39;49;00m [33m" [39;49;00m  [95min [39;49;00m final_seeding_btn.get_attribute( [33m" [39;49;00m [33mclass [39;49;00m [33m" [39;49;00m): [90m [39;49;00m
            final_seeding_btn.click() [90m [39;49;00m
            time.sleep( [94m1 [39;49;00m) [90m [39;49;00m
        seed_final_btn =  [96mself [39;49;00m.selenium.find_element(By.CSS_SELECTOR,  [33m" [39;49;00m [33m#finalSeedingForm button[name= [39;49;00m [33m' [39;49;00m [33mseed [39;49;00m [33m' [39;49;00m [33m] [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
        seed_final_btn.click() [90m [39;49;00m
        time.sleep( [94m1 [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
         [90m# Verify athletes in final heat table [39;49;00m [90m [39;49;00m
         [90m# There should be a table within #pills-final [39;49;00m [90m [39;49;00m
         [90m# The final round table should have 8 athletes [39;49;00m [90m [39;49;00m
        final_results =  [96mself [39;49;00m.selenium.find_elements(By.CSS_SELECTOR,  [33m" [39;49;00m [33m#pills-final tbody tr [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
         [90m# Filter for rows that have a bib (not empty lanes) [39;49;00m [90m [39;49;00m
        athletes_in_final = [row  [94mfor [39;49;00m row  [95min [39;49;00m final_results  [94mif [39;49;00m row.find_elements(By.XPATH,  [33m" [39;49;00m [33m./td[2][normalize-space()] [39;49;00m [33m" [39;49;00m)] [90m [39;49;00m
     [90m [39;49;00m
         [96mprint [39;49;00m( [33mf [39;49;00m [33m" [39;49;00m [33mAthletes in final:  [39;49;00m [33m{ [39;49;00m [96mlen [39;49;00m(athletes_in_final) [33m} [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
>        [96mself [39;49;00m.assertEqual( [96mlen [39;49;00m(athletes_in_final),  [94m8 [39;49;00m,  [33m" [39;49;00m [33mShould have 8 athletes in the final [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
E       AssertionError: 0 != 8 : Should have 8 athletes in the final
project/comp/tests/test_seeding.py:1912: AssertionError
______________ CompE2ETests.test_record_length_and_height_events _______________
[gw3] linux -- Python 3.13.2 /opt/hostedtoolcache/Python/3.13.2/x64/bin/python
self = <project.comp.tests.test_comp_e2e.CompE2ETests testMethod=test_record_length_and_height_events>
     [0m [94mdef [39;49;00m [90m  [39;49;00m [92mtest_record_length_and_height_events [39;49;00m( [96mself [39;49;00m): [90m [39;49;00m
     [90m     [39;49;00m [33m"""E2E test for recording results on length (LJ) and height (HJ) events.""" [39;49;00m [90m [39;49;00m
         [96mself [39;49;00m.login() [90m [39;49;00m
     [90m [39;49;00m
         [90m# ============================================== [39;49;00m [90m [39;49;00m
         [90m# PART 1: Long Jump (Length Event with Wind) [39;49;00m [90m [39;49;00m
         [90m# ============================================== [39;49;00m [90m [39;49;00m
        lj_url =  [33mf [39;49;00m [33m" [39;49;00m [33m{ [39;49;00m [96mself [39;49;00m.live_server_url [33m} [39;49;00m [33m{ [39;49;00mcomp_reverse( [33m' [39;49;00m [33mcomp-unit-record [39;49;00m [33m' [39;49;00m, [90m  [39;49;00m [96mself [39;49;00m.sql_comp, [90m  [39;49;00mevent_id= [33m' [39;49;00m [33m3 [39;49;00m [33m' [39;49;00m, [90m  [39;49;00m [96mround [39;49;00m= [33m' [39;49;00m [33m1 [39;49;00m [33m' [39;49;00m, [90m  [39;49;00mheat= [33m' [39;49;00m [33m1 [39;49;00m [33m' [39;49;00m) [33m} [39;49;00m [33m" [39;49;00m [90m [39;49;00m
         [96mprint [39;49;00m( [33mf [39;49;00m [33m" [39;49;00m [33mLJ recording URL:  [39;49;00m [33m{ [39;49;00mlj_url [33m} [39;49;00m [33m" [39;49;00m, flush= [94mTrue [39;49;00m) [90m [39;49;00m
         [96mself [39;49;00m.selenium.get(lj_url) [90m [39;49;00m
        time.sleep( [94m3 [39;49;00m) [90m [39;49;00m
         [96mprint [39;49;00m( [33mf [39;49;00m [33m" [39;49;00m [33mCurrent URL:  [39;49;00m [33m{ [39;49;00m [96mself [39;49;00m.selenium.current_url [33m} [39;49;00m [33m" [39;49;00m, flush= [94mTrue [39;49;00m) [90m [39;49;00m
         [96mprint [39;49;00m( [33mf [39;49;00m [33m" [39;49;00m [33mPage title:  [39;49;00m [33m{ [39;49;00m [96mself [39;49;00m.selenium.title [33m} [39;49;00m [33m" [39;49;00m, flush= [94mTrue [39;49;00m) [90m [39;49;00m
         [94mtry [39;49;00m: [90m [39;49;00m
            body =  [96mself [39;49;00m.selenium.find_element(By.TAG_NAME,  [33m" [39;49;00m [33mbody [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
             [96mprint [39;49;00m( [33mf [39;49;00m [33m" [39;49;00m [33mBody text:  [39;49;00m [33m{ [39;49;00mbody.text[: [94m1500 [39;49;00m] [33m} [39;49;00m [33m" [39;49;00m, flush= [94mTrue [39;49;00m) [90m [39;49;00m
         [94mexcept [39;49;00m  [96mException [39;49;00m  [94mas [39;49;00m e: [90m [39;49;00m
             [96mprint [39;49;00m( [33mf [39;49;00m [33m" [39;49;00m [33mFailed to get body:  [39;49;00m [33m{ [39;49;00me [33m} [39;49;00m [33m" [39;49;00m, flush= [94mTrue [39;49;00m) [90m [39;49;00m
         [96mself [39;49;00m.selenium.save_screenshot( [33m' [39;49;00m [33m/tmp/lj_page.png [39;49;00m [33m' [39;49;00m) [90m [39;49;00m
         [96mself [39;49;00m.accept_confirm() [90m [39;49;00m
        time.sleep( [94m1 [39;49;00m) [90m [39;49;00m
         [96mself [39;49;00m.wait.until( [94mlambda [39;49;00m _:  [96mlen [39;49;00m( [96mself [39;49;00m._length_starter_rows()) >  [94m0 [39;49;00m) [90m [39;49;00m
        athletes =  [96mself [39;49;00m._length_starter_rows() [90m [39;49;00m
         [96mself [39;49;00m.assertGreater( [96mlen [39;49;00m(athletes),  [94m0 [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
         [90m# Click first athlete and record a valid jump with wind [39;49;00m [90m [39;49;00m
        athletes[ [94m0 [39;49;00m].click() [90m [39;49;00m
        time.sleep( [94m0.5 [39;49;00m) [90m [39;49;00m
         [96mself [39;49;00m._record_length_trial(distance= [33m" [39;49;00m [33m6.50 [39;49;00m [33m" [39;49;00m, wind= [33m" [39;49;00m [33m+1.2 [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
        time.sleep( [94m0.5 [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
         [90m# Verify the trial was saved [39;49;00m [90m [39;49;00m
        first_bib = athletes[ [94m0 [39;49;00m].find_element(By.CSS_SELECTOR,  [33m" [39;49;00m [33m.col-xs-1:nth-child(2) [39;49;00m [33m" [39;49;00m).text.strip() [90m [39;49;00m
        trial_container =  [96mself [39;49;00m.selenium.find_element(By.CSS_SELECTOR,  [33mf [39;49;00m [33m" [39;49;00m [33mdiv[id= [39;49;00m [33m' [39;49;00m [33m{ [39;49;00mfirst_bib [33m} [39;49;00m [33m' [39;49;00m [33m] [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
         [96mself [39;49;00m.assertIn( [33m" [39;49;00m [33m6.50 [39;49;00m [33m" [39;49;00m, trial_container.text) [90m [39;49;00m
     [90m [39;49;00m
         [90m# Delete the result by clicking remove last observation button [39;49;00m [90m [39;49;00m
        remove_btn =  [96mself [39;49;00m.wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR,  [33m" [39;49;00m [33m.del.focused .btn-danger [39;49;00m [33m" [39;49;00m))) [90m [39;49;00m
        remove_btn.click() [90m [39;49;00m
        time.sleep( [94m1 [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
         [90m# Re-record a different distance for first athlete [39;49;00m [90m [39;49;00m
        rows =  [96mself [39;49;00m._length_starter_rows() [90m [39;49;00m
        rows[ [94m0 [39;49;00m].click() [90m [39;49;00m
        time.sleep( [94m0.5 [39;49;00m) [90m [39;49;00m
         [96mself [39;49;00m._record_length_trial(distance= [33m" [39;49;00m [33m6.80 [39;49;00m [33m" [39;49;00m, wind= [33m" [39;49;00m [33m+0.5 [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
        time.sleep( [94m0.5 [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
         [90m# Record results for remaining athletes [39;49;00m [90m [39;49;00m
         [94mfor [39;49;00m idx  [95min [39;49;00m  [96mrange [39;49;00m( [94m1 [39;49;00m,  [96mlen [39;49;00m(athletes)): [90m [39;49;00m
            rows =  [96mself [39;49;00m._length_starter_rows() [90m [39;49;00m
            rows[idx].click() [90m [39;49;00m
            time.sleep( [94m0.3 [39;49;00m) [90m [39;49;00m
             [94mif [39;49;00m idx ==  [94m1 [39;49;00m: [90m [39;49;00m
                 [96mself [39;49;00m._record_length_trial(is_foul= [94mTrue [39;49;00m) [90m [39;49;00m
             [94melse [39;49;00m: [90m [39;49;00m
                 [96mself [39;49;00m._record_length_trial(distance= [33m" [39;49;00m [33m5.00 [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
            time.sleep( [94m0.3 [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
         [90m# Click Finish [39;49;00m [90m [39;49;00m
        finish_btn =  [96mself [39;49;00m.wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR,  [33m" [39;49;00m [33mbutton[title= [39;49;00m [33m' [39;49;00m [33mFinish [39;49;00m [33m' [39;49;00m [33m] [39;49;00m [33m" [39;49;00m))) [90m [39;49;00m
        finish_btn.click() [90m [39;49;00m
        time.sleep( [94m1.5 [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
         [90m# ============================================== [39;49;00m [90m [39;49;00m
         [90m# PART 2: High Jump (Height Event) [39;49;00m [90m [39;49;00m
         [90m# ============================================== [39;49;00m [90m [39;49;00m
        hj_url =  [33mf [39;49;00m [33m" [39;49;00m [33m{ [39;49;00m [96mself [39;49;00m.live_server_url [33m} [39;49;00m [33m{ [39;49;00mcomp_reverse( [33m' [39;49;00m [33mcomp-unit-record [39;49;00m [33m' [39;49;00m, [90m  [39;49;00m [96mself [39;49;00m.sql_comp, [90m  [39;49;00mevent_id= [33m' [39;49;00m [33m9 [39;49;00m [33m' [39;49;00m, [90m  [39;49;00m [96mround [39;49;00m= [33m' [39;49;00m [33m1 [39;49;00m [33m' [39;49;00m, [90m  [39;49;00mheat= [33m' [39;49;00m [33m1 [39;49;00m [33m' [39;49;00m) [33m} [39;49;00m [33m" [39;49;00m [90m [39;49;00m
         [96mself [39;49;00m.selenium.get(hj_url) [90m [39;49;00m
         [96mself [39;49;00m.accept_confirm() [90m [39;49;00m
        time.sleep( [94m3 [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
         [90m# Set bar height [39;49;00m [90m [39;49;00m
        form_well =  [96mself [39;49;00m.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR,  [33m" [39;49;00m [33mform.well [39;49;00m [33m" [39;49;00m))) [90m [39;49;00m
        raise_btn = form_well.find_element(By.CSS_SELECTOR,  [33m" [39;49;00m [33mbutton.btn-info [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
         [96mself [39;49;00m.selenium.execute_script( [33m" [39;49;00m [33marguments[0].click(); [39;49;00m [33m" [39;49;00m, raise_btn) [90m [39;49;00m
        time.sleep( [94m0.5 [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
        new_height_input = form_well.find_element(By.NAME,  [33m" [39;49;00m [33mnew_height [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
        new_height_input.clear() [90m [39;49;00m
        new_height_input.send_keys( [33m" [39;49;00m [33m1.80 [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
        time.sleep( [94m0.5 [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
        confirm_btn =  [96mself [39;49;00m.wait.until(EC.element_to_be_clickable((By.XPATH,  [33m" [39;49;00m [33m//button[text()= [39;49;00m [33m' [39;49;00m [33mConfirm [39;49;00m [33m' [39;49;00m [33m] [39;49;00m [33m" [39;49;00m))) [90m [39;49;00m
        confirm_btn.click() [90m [39;49;00m
        time.sleep( [94m1.5 [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
         [90m# Find jumper rows [39;49;00m [90m [39;49;00m
        jumper_rows =  [94mlambda [39;49;00m:  [96mself [39;49;00m.selenium.find_elements(By.CSS_SELECTOR,  [33m" [39;49;00m [33mtable.table tbody > tr:not(.obs) [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
         [96mself [39;49;00m.wait.until( [94mlambda [39;49;00m _:  [96mlen [39;49;00m(jumper_rows()) >  [94m0 [39;49;00m) [90m [39;49;00m
        jumpers = jumper_rows() [90m [39;49;00m
         [96mself [39;49;00m.assertGreater( [96mlen [39;49;00m(jumpers),  [94m0 [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
         [90m# Record O/X for each jumper [39;49;00m [90m [39;49;00m
         [94mfor [39;49;00m idx  [95min [39;49;00m  [96mrange [39;49;00m( [96mlen [39;49;00m(jumpers)): [90m [39;49;00m
            jumpers = jumper_rows() [90m [39;49;00m
            jumpers[idx].click() [90m [39;49;00m
            time.sleep( [94m0.5 [39;49;00m) [90m [39;49;00m
            obs_record_btn =  [96mself [39;49;00m.wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR,  [33m" [39;49;00m [33mtr.obs a.btn-warning [39;49;00m [33m" [39;49;00m))) [90m [39;49;00m
            obs_record_btn.click() [90m [39;49;00m
            modal =  [96mself [39;49;00m.wait.until(EC.visibility_of_element_located((By.ID,  [33m" [39;49;00m [33mobsEntry [39;49;00m [33m" [39;49;00m))) [90m [39;49;00m
             [94mif [39;49;00m idx ==  [94m1 [39;49;00m: [90m [39;49;00m
                modal.find_element(By.CSS_SELECTOR,  [33m" [39;49;00m [33m.btn-danger [39;49;00m [33m" [39;49;00m).click() [90m [39;49;00m
     [90m [39;49;00m
                obs_record_btn =  [96mself [39;49;00m.wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR,  [33m" [39;49;00m [33mtr.obs a.btn-warning [39;49;00m [33m" [39;49;00m))) [90m [39;49;00m
                obs_record_btn.click() [90m [39;49;00m
                modal =  [96mself [39;49;00m.wait.until(EC.visibility_of_element_located((By.ID,  [33m" [39;49;00m [33mobsEntry [39;49;00m [33m" [39;49;00m))) [90m [39;49;00m
     [90m [39;49;00m
                modal.find_element(By.CSS_SELECTOR,  [33m" [39;49;00m [33m.btn-danger [39;49;00m [33m" [39;49;00m).click() [90m [39;49;00m
     [90m [39;49;00m
                obs_record_btn =  [96mself [39;49;00m.wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR,  [33m" [39;49;00m [33mtr.obs a.btn-warning [39;49;00m [33m" [39;49;00m))) [90m [39;49;00m
                obs_record_btn.click() [90m [39;49;00m
                modal =  [96mself [39;49;00m.wait.until(EC.visibility_of_element_located((By.ID,  [33m" [39;49;00m [33mobsEntry [39;49;00m [33m" [39;49;00m))) [90m [39;49;00m
     [90m [39;49;00m
                modal.find_element(By.CSS_SELECTOR,  [33m" [39;49;00m [33m.btn-success [39;49;00m [33m" [39;49;00m).click() [90m [39;49;00m
     [90m [39;49;00m
     [90m [39;49;00m
             [94melse [39;49;00m: [90m [39;49;00m
                modal.find_element(By.CSS_SELECTOR,  [33m" [39;49;00m [33m.btn-success [39;49;00m [33m" [39;49;00m).click() [90m [39;49;00m
            time.sleep( [94m1 [39;49;00m) [90m [39;49;00m
             [96mself [39;49;00m.wait.until(EC.invisibility_of_element_located((By.ID,  [33m" [39;49;00m [33mobsEntry [39;49;00m [33m" [39;49;00m))) [90m [39;49;00m
     [90m [39;49;00m
         [90m# Finish the height event [39;49;00m [90m [39;49;00m
        hj_finish_btn =  [96mself [39;49;00m.wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR,  [33m" [39;49;00m [33mbutton.btn-primary[title= [39;49;00m [33m' [39;49;00m [33mFinish [39;49;00m [33m' [39;49;00m [33m] [39;49;00m [33m" [39;49;00m))) [90m [39;49;00m
        hj_finish_btn.click() [90m [39;49;00m
        time.sleep( [94m1.5 [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
         [90m# ============================================== [39;49;00m [90m [39;49;00m
         [90m# PART 3: Verify HJ results persistence [39;49;00m [90m [39;49;00m
         [90m# ============================================== [39;49;00m [90m [39;49;00m
        time.sleep( [94m2 [39;49;00m)   [90m# ensure finish API call has completed [39;49;00m [90m [39;49;00m
     [90m [39;49;00m
         [90m# Re-navigate to HJ recording page to verify data persisted [39;49;00m [90m [39;49;00m
         [96mself [39;49;00m.selenium.get(hj_url) [90m [39;49;00m
         [96mself [39;49;00m.accept_confirm() [90m [39;49;00m
        time.sleep( [94m3 [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
         [90m# Wait for jumper rows and verify count [39;49;00m [90m [39;49;00m
        jumper_rows =  [94mlambda [39;49;00m:  [96mself [39;49;00m.selenium.find_elements(By.CSS_SELECTOR,  [33m" [39;49;00m [33mtable.table tbody > tr:not(.obs) [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
>        [96mself [39;49;00m.wait.until( [94mlambda [39;49;00m _:  [96mlen [39;49;00m(jumper_rows()) >  [94m0 [39;49;00m) [90m [39;49;00m
project/comp/tests/test_comp_e2e.py:205:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <selenium.webdriver.support.wait.WebDriverWait (session="840bf2c50c41e402619d88d0a72af9a8")>
method = <function CompE2ETests.test_record_length_and_height_events.<locals>.<lambda> at 0x7f3992e940e0>
message = ''
     [0m [94mdef [39;49;00m [90m  [39;49;00m [92muntil [39;49;00m( [96mself [39;49;00m, method: Callable[[D], Union[Literal[ [94mFalse [39;49;00m], T]], message:  [96mstr [39;49;00m =  [33m" [39;49;00m [33m" [39;49;00m) -> T: [90m [39;49;00m
     [90m     [39;49;00m [33m"""Wait until the method returns a value that is not False. [39;49;00m
     [33m [39;49;00m
     [33m    Calls the method provided with the driver as an argument until the [39;49;00m
     [33m    return value does not evaluate to ``False``. [39;49;00m
     [33m [39;49;00m
     [33m    Parameters: [39;49;00m
     [33m    ----------- [39;49;00m
     [33m    method: callable(WebDriver) [39;49;00m
     [33m        - A callable object that takes a WebDriver instance as an argument. [39;49;00m
     [33m [39;49;00m
     [33m    message: str [39;49;00m
     [33m        - Optional message for :exc:`TimeoutException` [39;49;00m
     [33m [39;49;00m
     [33m    Return: [39;49;00m
     [33m    ------- [39;49;00m
     [33m    object: T [39;49;00m
     [33m        - The result of the last call to `method` [39;49;00m
     [33m [39;49;00m
     [33m    Raises: [39;49;00m
     [33m    ------- [39;49;00m
     [33m    TimeoutException [39;49;00m
     [33m        - If 'method' does not return a truthy value within the WebDriverWait [39;49;00m
     [33m        object's timeout [39;49;00m
     [33m [39;49;00m
     [33m    Example: [39;49;00m
     [33m    -------- [39;49;00m
     [33m    >>> from selenium.webdriver.common.by import By [39;49;00m
     [33m    >>> from selenium.webdriver.support.ui import WebDriverWait [39;49;00m
     [33m    >>> from selenium.webdriver.support import expected_conditions as EC [39;49;00m
     [33m [39;49;00m
     [33m    # Wait until an element is visible on the page [39;49;00m
     [33m    >>> wait = WebDriverWait(driver, 10) [39;49;00m
     [33m    >>> element = wait.until(EC.visibility_of_element_located((By.ID, "exampleId"))) [39;49;00m
     [33m    >>> print(element.text) [39;49;00m
     [33m    """ [39;49;00m [90m [39;49;00m
        screen =  [94mNone [39;49;00m [90m [39;49;00m
        stacktrace =  [94mNone [39;49;00m [90m [39;49;00m
     [90m [39;49;00m
        end_time = time.monotonic() +  [96mself [39;49;00m._timeout [90m [39;49;00m
         [94mwhile [39;49;00m  [94mTrue [39;49;00m: [90m [39;49;00m
             [94mtry [39;49;00m: [90m [39;49;00m
                value = method( [96mself [39;49;00m._driver) [90m [39;49;00m
                 [94mif [39;49;00m value: [90m [39;49;00m
                     [94mreturn [39;49;00m value [90m [39;49;00m
             [94mexcept [39;49;00m  [96mself [39;49;00m._ignored_exceptions  [94mas [39;49;00m exc: [90m [39;49;00m
                screen =  [96mgetattr [39;49;00m(exc,  [33m" [39;49;00m [33mscreen [39;49;00m [33m" [39;49;00m,  [94mNone [39;49;00m) [90m [39;49;00m
                stacktrace =  [96mgetattr [39;49;00m(exc,  [33m" [39;49;00m [33mstacktrace [39;49;00m [33m" [39;49;00m,  [94mNone [39;49;00m) [90m [39;49;00m
             [94mif [39;49;00m time.monotonic() > end_time: [90m [39;49;00m
                 [94mbreak [39;49;00m [90m [39;49;00m
            time.sleep( [96mself [39;49;00m._poll) [90m [39;49;00m
>        [94mraise [39;49;00m TimeoutException(message, screen, stacktrace) [90m [39;49;00m
E       selenium.common.exceptions.TimeoutException: Message:
/opt/hostedtoolcache/Python/3.13.2/x64/lib/python3.13/site-packages/selenium/webdriver/support/wait.py:146: TimeoutException
____________ TeamDeclarationsE2ETests.test_adhoc_relay_declarations ____________
[gw1] linux -- Python 3.13.2 /opt/hostedtoolcache/Python/3.13.2/x64/bin/python
self = <project.comp.tests.test_team_declarations.TeamDeclarationsE2ETests testMethod=test_adhoc_relay_declarations>
     [0m [37m@superuser_login_required [39;49;00m [90m [39;49;00m
     [94mdef [39;49;00m [90m  [39;49;00m [92mtest_adhoc_relay_declarations [39;49;00m( [96mself [39;49;00m): [90m [39;49;00m
     [90m     [39;49;00m [33m""" [39;49;00m
     [33m    Test adhoc team declarations relays. [39;49;00m
     [33m [39;49;00m
     [33m    Summary: [39;49;00m
     [33m        - Change the team category for a relay team should save after change. [39;49;00m
     [33m        - Select 4 athletes to fill a 4x400 relay event from the available athletes table and move them across to selected table. [39;49;00m
     [33m            - Click the save button and check that they persist after save and refreshing page. [39;49;00m
     [33m        - Use the up and down arrows to change the order of the relay athletes [39;49;00m
     [33m            - Click save and check that the ordering persists after refreshing page. [39;49;00m
     [33m        - Use the relay grid to input two athletes. Check that any existing athletes are not editible through the grid. [39;49;00m
     [33m            - Once finished inputting, check that the athletes show in the selected athletes table and their ordering can be changed. [39;49;00m
     [33m            - Click save and check that the ordering persists after refreshing page. [39;49;00m
     [33m        - Create two new relay team by clicking the plus button. [39;49;00m
     [33m            - Check that the relayteam_id is set to the next character in the alpherbet. [39;49;00m
     [33m            - Click save and check that the ordering persists after refreshing page. [39;49;00m
     [33m            - Add some athletes to the last team then remove the middle relay team and check that the last team is updated. [39;49;00m
     [33m                - If relayteam_id was ADHOC1-C then it should now be ADHOC1-B and the athletes event entries for this relay team should be updated also. [39;49;00m
     [33m    """ [39;49;00m [90m [39;49;00m
         [90m# 1. Setup: Create a new adhoc TeamEntry (no organisation) [39;49;00m [90m [39;49;00m
        team_id =  [33m' [39;49;00m [33mADHOC1 [39;49;00m [33m' [39;49;00m [90m [39;49;00m
        te = TeamEntry.objects.filter(competition= [96mself [39;49;00m.mc, team_id=team_id).first() [90m [39;49;00m
         [94mif [39;49;00m te: [90m [39;49;00m
            te.delete() [90m [39;49;00m
     [90m [39;49;00m
        te = TeamEntry( [90m [39;49;00m
            competition= [96mself [39;49;00m.mc, [90m [39;49;00m
            team_id=team_id, [90m [39;49;00m
            team_name= [33m' [39;49;00m [33mAdhoc Team 1 [39;49;00m [33m' [39;49;00m, [90m [39;49;00m
            is_adhoc= [94mTrue [39;49;00m [90m [39;49;00m
        ) [90m [39;49;00m
        te.save() [90m [39;49;00m
     [90m [39;49;00m
         [90m# 1.5 Set some competition settings [39;49;00m [90m [39;49;00m
         [96mself [39;49;00m.mc.relay_extra_reserves =  [94m4 [39;49;00m [90m [39;49;00m
         [96mself [39;49;00m.mc.save() [90m [39;49;00m
     [90m [39;49;00m
         [90m# 2. Setup: Create competitors for this team [39;49;00m [90m [39;49;00m
         [90m# We need at least 4 for a relay (4x400) [39;49;00m [90m [39;49;00m
        runners_data = [ [90m [39;49;00m
            ( [94m1 [39;49;00m,  [33m' [39;49;00m [33mAthlete [39;49;00m [33m' [39;49;00m,  [33m' [39;49;00m [33mOne [39;49;00m [33m' [39;49;00m,  [33m' [39;49;00m [33m2015-01-01 [39;49;00m [33m' [39;49;00m,  [33m' [39;49;00m [33mM [39;49;00m [33m' [39;49;00m), [90m [39;49;00m
            ( [94m2 [39;49;00m,  [33m' [39;49;00m [33mAthlete [39;49;00m [33m' [39;49;00m,  [33m' [39;49;00m [33mTwo [39;49;00m [33m' [39;49;00m,  [33m' [39;49;00m [33m2015-01-02 [39;49;00m [33m' [39;49;00m,  [33m' [39;49;00m [33mF [39;49;00m [33m' [39;49;00m), [90m [39;49;00m
            ( [94m3 [39;49;00m,  [33m' [39;49;00m [33mAthlete [39;49;00m [33m' [39;49;00m,  [33m' [39;49;00m [33mThree [39;49;00m [33m' [39;49;00m,  [33m' [39;49;00m [33m2010-01-03 [39;49;00m [33m' [39;49;00m,  [33m' [39;49;00m [33mM [39;49;00m [33m' [39;49;00m), [90m [39;49;00m
            ( [94m4 [39;49;00m,  [33m' [39;49;00m [33mAthlete [39;49;00m [33m' [39;49;00m,  [33m' [39;49;00m [33mFour [39;49;00m [33m' [39;49;00m,  [33m' [39;49;00m [33m2010-01-04 [39;49;00m [33m' [39;49;00m,  [33m' [39;49;00m [33mF [39;49;00m [33m' [39;49;00m), [90m [39;49;00m
        ] [90m [39;49;00m
     [90m [39;49;00m
        reserve_runners_data = [ [90m [39;49;00m
            ( [94m5 [39;49;00m,  [33m' [39;49;00m [33mAthlete [39;49;00m [33m' [39;49;00m,  [33m' [39;49;00m [33mFive [39;49;00m [33m' [39;49;00m,  [33m' [39;49;00m [33m2015-01-01 [39;49;00m [33m' [39;49;00m,  [33m' [39;49;00m [33mM [39;49;00m [33m' [39;49;00m), [90m [39;49;00m
            ( [94m6 [39;49;00m,  [33m' [39;49;00m [33mAthlete [39;49;00m [33m' [39;49;00m,  [33m' [39;49;00m [33mSix [39;49;00m [33m' [39;49;00m,  [33m' [39;49;00m [33m2015-01-02 [39;49;00m [33m' [39;49;00m,  [33m' [39;49;00m [33mF [39;49;00m [33m' [39;49;00m), [90m [39;49;00m
            ( [94m7 [39;49;00m,  [33m' [39;49;00m [33mAthlete [39;49;00m [33m' [39;49;00m,  [33m' [39;49;00m [33mSeven [39;49;00m [33m' [39;49;00m,  [33m' [39;49;00m [33m2010-01-03 [39;49;00m [33m' [39;49;00m,  [33m' [39;49;00m [33mM [39;49;00m [33m' [39;49;00m), [90m [39;49;00m
            ( [94m8 [39;49;00m,  [33m' [39;49;00m [33mAthlete [39;49;00m [33m' [39;49;00m,  [33m' [39;49;00m [33mEight [39;49;00m [33m' [39;49;00m,  [33m' [39;49;00m [33m2010-01-04 [39;49;00m [33m' [39;49;00m,  [33m' [39;49;00m [33mF [39;49;00m [33m' [39;49;00m), [90m [39;49;00m
        ] [90m [39;49;00m
     [90m [39;49;00m
        competitors = [] [90m [39;49;00m
         [94mfor [39;49;00m idx, first, last, dob, gender  [95min [39;49;00m runners_data: [90m [39;49;00m
            bib =  [33mf [39;49;00m [33m" [39;49;00m [33mA [39;49;00m [33m{ [39;49;00midx [33m} [39;49;00m [33m" [39;49;00m [90m [39;49;00m
            c = Competitor.objects.filter(competition= [96mself [39;49;00m.mc, competitor_id=bib).first() [90m [39;49;00m
             [94mif [39;49;00m c: [90m [39;49;00m
                c.delete() [90m [39;49;00m
            c = Competitor( [90m [39;49;00m
                competition= [96mself [39;49;00m.mc, [90m [39;49;00m
                competitor_id=bib, [90m [39;49;00m
                first_name=first, [90m [39;49;00m
                last_name=last, [90m [39;49;00m
                gender=gender, [90m [39;49;00m
                age_group= [33m' [39;49;00m [33mSEN [39;49;00m [33m' [39;49;00m, [90m [39;49;00m
                category= [33m' [39;49;00m [33mSM [39;49;00m [33m' [39;49;00m, [90m [39;49;00m
                team_id=team_id, [90m [39;49;00m
                date_of_birth=date.fromisoformat(dob), [90m [39;49;00m
                is_team_entry= [94mTrue [39;49;00m, [90m [39;49;00m
                numbered= [94mTrue [39;49;00m [90m [39;49;00m
            ) [90m [39;49;00m
            c.save() [90m [39;49;00m
            competitors.append(c) [90m [39;49;00m
     [90m [39;49;00m
         [90m# Ensure a relay event exists [39;49;00m [90m [39;49;00m
        relay_event =  [96mnext [39;49;00m((ev  [94mfor [39;49;00m ev  [95min [39;49;00m  [96mself [39;49;00m.mc.get_events()  [94mif [39;49;00m ev.event_code.startswith( [33m' [39;49;00m [33m4x [39;49;00m [33m' [39;49;00m)),  [94mNone [39;49;00m) [90m [39;49;00m
         [94mif [39;49;00m  [95mnot [39;49;00m relay_event: [90m [39;49;00m
              [90m# try to find any event with 'relay' in name [39;49;00m [90m [39;49;00m
             relay_event =  [96mnext [39;49;00m((ev  [94mfor [39;49;00m ev  [95min [39;49;00m  [96mself [39;49;00m.mc.get_events()  [94mif [39;49;00m  [33m' [39;49;00m [33mrelay [39;49;00m [33m' [39;49;00m  [95min [39;49;00m ev.name.lower()),  [94mNone [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
         [96mself [39;49;00m.assertIsNotNone(relay_event,  [33m" [39;49;00m [33mNo relay event found in competition [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
        relay_event_id = relay_event.event_id [90m [39;49;00m
     [90m [39;49;00m
         [90m# Ensure the relay team exists for this adhoc team [39;49;00m [90m [39;49;00m
        created, rt = get_or_create_relayteam( [96mself [39;49;00m.mc, relay_event_id, team_id) [90m [39;49;00m
     [90m [39;49;00m
         [90m# sync to TeamEntry object for team declarations [39;49;00m [90m [39;49;00m
        te.sync_from_relay_teams(force= [94mTrue [39;49;00m) [90m [39;49;00m
        te.save() [90m [39;49;00m
        te = TeamEntry.objects.filter(competition= [96mself [39;49;00m.mc, team_id=team_id).first() [90m [39;49;00m
     [90m [39;49;00m
         [96mprint [39;49;00m( [33m" [39;49;00m [33mTeamEntry relay teams [39;49;00m [33m" [39;49;00m, te.relay_teams) [90m [39;49;00m
     [90m [39;49;00m
         [96mself [39;49;00m.assertIsNotNone(rt,  [33m" [39;49;00m [33mFailed to get or create relay team [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
        rt.bib =  [33m' [39;49;00m [33m100 [39;49;00m [33m' [39;49;00m  [90m# Give it a bib [39;49;00m [90m [39;49;00m
        rt.save() [90m [39;49;00m
     [90m [39;49;00m
         [90m# 3. Navigate to the adhoc relay entry page [39;49;00m [90m [39;49;00m
        url = reverse( [33m' [39;49;00m [33mcomp-team-entry-adhoc-edit [39;49;00m [33m' [39;49;00m, kwargs={ [90m [39;49;00m
             [33m' [39;49;00m [33myear [39;49;00m [33m' [39;49;00m:  [96mself [39;49;00m.mc.year, [90m [39;49;00m
             [33m' [39;49;00m [33mcountry [39;49;00m [33m' [39;49;00m:  [96mself [39;49;00m.mc.country, [90m [39;49;00m
             [33m' [39;49;00m [33mslug [39;49;00m [33m' [39;49;00m:  [96mself [39;49;00m.mc.slug, [90m [39;49;00m
             [33m' [39;49;00m [33mteam_id [39;49;00m [33m' [39;49;00m: team_id [90m [39;49;00m
        }) [90m [39;49;00m
        full_url =  [33mf [39;49;00m [33m" [39;49;00m [33m{ [39;49;00m [96mself [39;49;00m.live_server_url [33m} [39;49;00m [33m{ [39;49;00murl [33m} [39;49;00m [33m" [39;49;00m [90m [39;49;00m
         [96mprint [39;49;00m( [33mf [39;49;00m [33m" [39;49;00m [33mNavigating to:  [39;49;00m [33m{ [39;49;00mfull_url [33m} [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
         [96mself [39;49;00m.selenium.get(full_url) [90m [39;49;00m
     [90m [39;49;00m
         [94mtry [39;49;00m: [90m [39;49;00m
             [96mprint [39;49;00m( [33m" [39;49;00m [33mWaiting for Vue app to load... [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
             [96mself [39;49;00m.wait.until(EC.invisibility_of_element_located((By.CSS_SELECTOR,  [33m" [39;49;00m [33m[v-cloak] [39;49;00m [33m" [39;49;00m))) [90m [39;49;00m
     [90m [39;49;00m
             [90m# --- Step 1: Relay Teams Tab --- [39;49;00m [90m [39;49;00m
            relay_tab_link =  [96mself [39;49;00m.selenium.find_element(By.CSS_SELECTOR,  [33m" [39;49;00m [33ma[href= [39;49;00m [33m' [39;49;00m [33m#relay_teams_tab [39;49;00m [33m' [39;49;00m [33m] [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
            relay_tab_link.click() [90m [39;49;00m
            time.sleep( [94m1 [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
             [90m# Select the relay event [39;49;00m [90m [39;49;00m
             [96mprint [39;49;00m( [33mf [39;49;00m [33m" [39;49;00m [33mSelecting relay event  [39;49;00m [33m{ [39;49;00mrelay_event.name [33m} [39;49;00m [33m... [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
            event_btn_xpath =  [33mf [39;49;00m [33m" [39;49;00m [33m//a[@data-eventid= [39;49;00m [33m' [39;49;00m [33m{ [39;49;00mrelay_event_id [33m} [39;49;00m [33m' [39;49;00m [33m] [39;49;00m [33m" [39;49;00m [90m [39;49;00m
            event_btn =  [96mself [39;49;00m.wait.until(EC.element_to_be_clickable((By.XPATH, event_btn_xpath))) [90m [39;49;00m
            event_btn.click() [90m [39;49;00m
            time.sleep( [94m1 [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
             [90m# --- New: Select athletes from available table using promote button --- [39;49;00m [90m [39;49;00m
             [96mprint [39;49;00m( [33m" [39;49;00m [33m--- Testing selection from available table --- [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
            relay_available_table_selector =  [33m" [39;49;00m [33m#relay_teams_tab .card-body .row:last-child .col-lg-5:nth-child(3) table [39;49;00m [33m" [39;49;00m [90m [39;49;00m
            relay_selected_table_selector =  [33m" [39;49;00m [33m#relay_teams_tab .card-body .row:last-child .col-lg-5:nth-child(1) table [39;49;00m [33m" [39;49;00m [90m [39;49;00m
            relay_promote_btn_selector =  [33m" [39;49;00m [33m#relayPromoteBtn [39;49;00m [33m" [39;49;00m [90m [39;49;00m
            relay_demote_btn_selector =  [33m" [39;49;00m [33m#relayDemoteBtn [39;49;00m [33m" [39;49;00m [90m [39;49;00m
     [90m [39;49;00m
             [96mself [39;49;00m.wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, relay_available_table_selector))) [90m [39;49;00m
     [90m [39;49;00m
             [90m# Select 4 runners [39;49;00m [90m [39;49;00m
             [94mfor [39;49;00m i  [95min [39;49;00m  [96mrange [39;49;00m( [94m4 [39;49;00m): [90m [39;49;00m
                rows =  [96mself [39;49;00m.selenium.find_elements(By.CSS_SELECTOR,  [33mf [39;49;00m [33m" [39;49;00m [33m{ [39;49;00mrelay_available_table_selector [33m} [39;49;00m [33m tbody tr [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
                visible_rows = [r  [94mfor [39;49;00m r  [95min [39;49;00m rows  [94mif [39;49;00m r.is_displayed()  [95mand [39;49;00m  [33m" [39;49;00m [33mmuted [39;49;00m [33m" [39;49;00m  [95mnot [39;49;00m  [95min [39;49;00m r.get_attribute( [33m" [39;49;00m [33mclass [39;49;00m [33m" [39;49;00m)] [90m [39;49;00m
                 [96mself [39;49;00m.assertTrue( [96mlen [39;49;00m(visible_rows) >  [94m0 [39;49;00m,  [33mf [39;49;00m [33m" [39;49;00m [33mNo available eligible athletes found at step  [39;49;00m [33m{ [39;49;00mi [33m} [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
                athlete_row = visible_rows[ [94m0 [39;49;00m] [90m [39;49;00m
                athlete_name = athlete_row.text [90m [39;49;00m
                 [96mprint [39;49;00m( [33mf [39;49;00m [33m" [39;49;00m [33mSelecting athlete  [39;49;00m [33m{ [39;49;00mi+ [94m1 [39;49;00m [33m} [39;49;00m [33m:  [39;49;00m [33m{ [39;49;00mathlete_name [33m} [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
                athlete_row.click() [90m [39;49;00m
                time.sleep( [94m0.5 [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
                promote_btn =  [96mself [39;49;00m.selenium.find_element(By.CSS_SELECTOR, relay_promote_btn_selector) [90m [39;49;00m
                 [96mself [39;49;00m.selenium.execute_script( [33m" [39;49;00m [33marguments[0].click(); [39;49;00m [33m" [39;49;00m, promote_btn) [90m [39;49;00m
                time.sleep( [94m0.5 [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
             [90m# Verify 4 runners in selected table [39;49;00m [90m [39;49;00m
            selected_rows =  [96mself [39;49;00m.selenium.find_elements(By.CSS_SELECTOR,  [33mf [39;49;00m [33m" [39;49;00m [33m{ [39;49;00mrelay_selected_table_selector [33m} [39;49;00m [33m tbody tr:not(.muted) [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
             [96mself [39;49;00m.assertEqual( [96mlen [39;49;00m(selected_rows),  [94m4 [39;49;00m,  [33m" [39;49;00m [33mExpected 4 athletes in selected table [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
             [90m# Test demote [39;49;00m [90m [39;49;00m
             [96mprint [39;49;00m( [33m" [39;49;00m [33mTesting demote button... [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
            last_selected_row = selected_rows[- [94m1 [39;49;00m] [90m [39;49;00m
            last_selected_name = last_selected_row.text [90m [39;49;00m
            last_selected_row.click() [90m [39;49;00m
            time.sleep( [94m0.5 [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
            demote_btn =  [96mself [39;49;00m.selenium.find_element(By.CSS_SELECTOR, relay_demote_btn_selector) [90m [39;49;00m
             [96mself [39;49;00m.selenium.execute_script( [33m" [39;49;00m [33marguments[0].click(); [39;49;00m [33m" [39;49;00m, demote_btn) [90m [39;49;00m
            time.sleep( [94m0.5 [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
            selected_rows =  [96mself [39;49;00m.selenium.find_elements(By.CSS_SELECTOR,  [33mf [39;49;00m [33m" [39;49;00m [33m{ [39;49;00mrelay_selected_table_selector [33m} [39;49;00m [33m tbody tr:not(.muted) [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
             [96mself [39;49;00m.assertEqual( [96mlen [39;49;00m(selected_rows),  [94m3 [39;49;00m,  [33m" [39;49;00m [33mExpected 3 athletes in selected table after demote [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
             [90m# Promote back to 4 [39;49;00m [90m [39;49;00m
            rows =  [96mself [39;49;00m.selenium.find_elements(By.CSS_SELECTOR,  [33mf [39;49;00m [33m" [39;49;00m [33m{ [39;49;00mrelay_available_table_selector [33m} [39;49;00m [33m tbody tr [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
            visible_rows = [r  [94mfor [39;49;00m r  [95min [39;49;00m rows  [94mif [39;49;00m r.is_displayed()  [95mand [39;49;00m  [33m" [39;49;00m [33mmuted [39;49;00m [33m" [39;49;00m  [95mnot [39;49;00m  [95min [39;49;00m r.get_attribute( [33m" [39;49;00m [33mclass [39;49;00m [33m" [39;49;00m)] [90m [39;49;00m
            visible_rows[ [94m0 [39;49;00m].click() [90m [39;49;00m
            time.sleep( [94m0.5 [39;49;00m) [90m [39;49;00m
             [96mself [39;49;00m.selenium.execute_script( [33m" [39;49;00m [33marguments[0].click(); [39;49;00m [33m" [39;49;00m, promote_btn) [90m [39;49;00m
            time.sleep( [94m1 [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
             [90m# --- New: Test ordering with arrows --- [39;49;00m [90m [39;49;00m
             [96mprint [39;49;00m( [33m" [39;49;00m [33m--- Testing ordering with arrows --- [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
            selected_rows =  [96mself [39;49;00m.selenium.find_elements(By.CSS_SELECTOR,  [33mf [39;49;00m [33m" [39;49;00m [33m{ [39;49;00mrelay_selected_table_selector [33m} [39;49;00m [33m tbody tr:not(.muted) [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
            runner_to_move = selected_rows[ [94m1 [39;49;00m] [90m [39;49;00m
            runner_name = runner_to_move.text [90m [39;49;00m
             [96mprint [39;49;00m( [33mf [39;49;00m [33m" [39;49;00m [33mMoving runner  [39;49;00m [33m{ [39;49;00mrunner_name [33m} [39;49;00m [33m up [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
            runner_to_move.click() [90m [39;49;00m
            time.sleep( [94m0.5 [39;49;00m) [90m [39;49;00m
             [90m# Find the up arrow specifically in the highlighted row [39;49;00m [90m [39;49;00m
            ath_id = runner_to_move.get_attribute( [33m" [39;49;00m [33mdata-id [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
            up_arrow_selector =  [33mf [39;49;00m [33m" [39;49;00m [33mtr[data-id= [39;49;00m [33m' [39;49;00m [33m{ [39;49;00math_id [33m} [39;49;00m [33m' [39;49;00m [33m] .fa-chevron-up [39;49;00m [33m" [39;49;00m [90m [39;49;00m
            up_arrow =  [96mself [39;49;00m.wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, up_arrow_selector))) [90m [39;49;00m
             [96mself [39;49;00m.selenium.execute_script( [33m" [39;49;00m [33marguments[0].click(); [39;49;00m [33m" [39;49;00m, up_arrow) [90m [39;49;00m
            time.sleep( [94m1 [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
             [90m# Verify it moved to first position [39;49;00m [90m [39;49;00m
            reordered_rows =  [96mself [39;49;00m.selenium.find_elements(By.CSS_SELECTOR,  [33mf [39;49;00m [33m" [39;49;00m [33m{ [39;49;00mrelay_selected_table_selector [33m} [39;49;00m [33m tbody tr:not(.muted) [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
             [96mself [39;49;00m.assertEqual(reordered_rows[ [94m0 [39;49;00m].text, runner_name,  [33m" [39;49;00m [33mRunner did not move up correctly [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
             [90m# --- Step 2: Input athletes via Handsontable grid --- [39;49;00m [90m [39;49;00m
             [96mprint [39;49;00m( [33m" [39;49;00m [33mInteracting with Handsontable for relay runners... [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
            bulk_relay_btn_selector =  [33m" [39;49;00m [33m#bulkRelayBtn [39;49;00m [33m" [39;49;00m [90m [39;49;00m
            bulk_relay_btn =  [96mself [39;49;00m.wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, bulk_relay_btn_selector))) [90m [39;49;00m
            bulk_relay_btn.click() [90m [39;49;00m
            time.sleep( [94m2 [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
            ht_selector =  [33m" [39;49;00m [33m#runners_ht table.htCore [39;49;00m [33m" [39;49;00m [90m [39;49;00m
             [96mself [39;49;00m.wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, ht_selector))) [90m [39;49;00m
     [90m [39;49;00m
             [90m# Check grid editability: existing runners should be readOnly [39;49;00m [90m [39;49;00m
             [96mprint [39;49;00m( [33m" [39;49;00m [33mChecking grid editability... [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
            is_readonly =  [96mself [39;49;00m.selenium.execute_script( [33m" [39;49;00m [33mreturn window.relay_ht.getCellMeta(0, 1).readOnly; [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
             [96mself [39;49;00m.assertTrue(is_readonly,  [33m" [39;49;00m [33mExisting runner in grid should be readOnly [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
             [90m# --- Adding some reserve runners --- [39;49;00m [90m [39;49;00m
             [94mfor [39;49;00m i, (idx, first, last, dob, gender)  [95min [39;49;00m  [96menumerate [39;49;00m(reserve_runners_data, start= [94m4 [39;49;00m): [90m [39;49;00m
                 [96mprint [39;49;00m( [33mf [39;49;00m [33m" [39;49;00m [33mInputting runner  [39;49;00m [33m{ [39;49;00mi+ [94m1 [39;49;00m [33m} [39;49;00m [33m:  [39;49;00m [33m{ [39;49;00mfirst [33m} [39;49;00m [33m  [39;49;00m [33m{ [39;49;00mlast [33m} [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
                cell_xpath =  [33mf [39;49;00m [33m" [39;49;00m [33m//div[@id= [39;49;00m [33m' [39;49;00m [33mrunners_ht [39;49;00m [33m' [39;49;00m [33m]//table[@class= [39;49;00m [33m' [39;49;00m [33mhtCore [39;49;00m [33m' [39;49;00m [33m]/tbody/tr[ [39;49;00m [33m{ [39;49;00mi+ [94m1 [39;49;00m [33m} [39;49;00m [33m]/td[1] [39;49;00m [33m" [39;49;00m [90m [39;49;00m
                cell =  [96mself [39;49;00m.wait.until(EC.element_to_be_clickable((By.XPATH, cell_xpath))) [90m [39;49;00m
     [90m [39;49;00m
                actions = ActionChains( [96mself [39;49;00m.selenium) [90m [39;49;00m
                actions.move_to_element(cell).click().perform() [90m [39;49;00m
                time.sleep( [94m1 [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
                actions = ActionChains( [96mself [39;49;00m.selenium) [90m [39;49;00m
                actions.send_keys( [96mstr [39;49;00m(idx)) [90m [39;49;00m
                actions.send_keys(Keys.TAB) [90m [39;49;00m
                actions.send_keys(first) [90m [39;49;00m
                actions.send_keys(Keys.TAB) [90m [39;49;00m
                actions.send_keys(last) [90m [39;49;00m
                actions.send_keys(Keys.TAB) [90m [39;49;00m
                actions.send_keys(dob) [90m [39;49;00m
                actions.send_keys(Keys.TAB) [90m [39;49;00m
                actions.send_keys(gender) [90m [39;49;00m
                actions.send_keys(Keys.ENTER) [90m [39;49;00m
                actions.perform() [90m [39;49;00m
                time.sleep( [94m0.5 [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
             [90m# --- Step 3: Save --- [39;49;00m [90m [39;49;00m
             [96mprint [39;49;00m( [33m" [39;49;00m [33mSaving team A... [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
            save_btn =  [96mself [39;49;00m.wait.until(EC.element_to_be_clickable((By.XPATH,  [33m" [39;49;00m [33m//div[@id= [39;49;00m [33m' [39;49;00m [33mrelay_teams_tab [39;49;00m [33m' [39;49;00m [33m]//button[contains(.,  [39;49;00m [33m' [39;49;00m [33mSave Changes [39;49;00m [33m' [39;49;00m [33m)] [39;49;00m [33m" [39;49;00m))) [90m [39;49;00m
             [96mself [39;49;00m.selenium.execute_script( [33m" [39;49;00m [33marguments[0].click(); [39;49;00m [33m" [39;49;00m, save_btn) [90m [39;49;00m
     [90m [39;49;00m
             [96mprint [39;49;00m( [33m" [39;49;00m [33mWaiting for save confirmation... [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
             [96mself [39;49;00m.wait.until(EC.presence_of_element_located((By.XPATH,  [33m" [39;49;00m [33m//*[contains(translate(text(),  [39;49;00m [33m' [39;49;00m [33mABCDEFGHIJKLMNOPQRSTUVWXYZ [39;49;00m [33m' [39;49;00m [33m,  [39;49;00m [33m' [39;49;00m [33mabcdefghijklmnopqrstuvwxyz [39;49;00m [33m' [39;49;00m [33m),  [39;49;00m [33m' [39;49;00m [33mentries saved [39;49;00m [33m' [39;49;00m [33m)] [39;49;00m [33m" [39;49;00m))) [90m [39;49;00m
             [96mprint [39;49;00m( [33m" [39;49;00m [33mEntries saved. [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
            rt.reload() [90m [39;49;00m
            rt.fetch_runners() [90m [39;49;00m
            rt.save() [90m [39;49;00m
     [90m [39;49;00m
             [90m# --- Step 4: Add two more relay teams --- [39;49;00m [90m [39;49;00m
             [96mprint [39;49;00m( [33m" [39;49;00m [33m=== PART 1: Adding relay teams B and C === [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
            time.sleep( [94m1 [39;49;00m) [90m [39;49;00m
            event_btn =  [96mself [39;49;00m.wait.until(EC.element_to_be_clickable((By.XPATH, event_btn_xpath))) [90m [39;49;00m
            event_btn.click() [90m [39;49;00m
            time.sleep( [94m1 [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
            add_team_btn =  [96mself [39;49;00m.wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR,  [33m" [39;49;00m [33m#relay_teams_tab button.btn-outline-success [39;49;00m [33m" [39;49;00m))) [90m [39;49;00m
            add_team_btn.click() [90m [39;49;00m
            time.sleep( [94m1 [39;49;00m) [90m [39;49;00m
             [96mprint [39;49;00m( [33m" [39;49;00m [33mAdded team B [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
            add_team_btn.click() [90m [39;49;00m
            time.sleep( [94m1 [39;49;00m) [90m [39;49;00m
             [96mprint [39;49;00m( [33m" [39;49;00m [33mAdded team C [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
             [90m# Verify alphabetical order in UI tabs [39;49;00m [90m [39;49;00m
            team_tabs =  [96mself [39;49;00m.selenium.find_elements(By.CSS_SELECTOR,  [33m" [39;49;00m [33m#relay_teams_tab ul.nav-tabs li.nav-item a [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
            team_labels = [t.text.strip()  [94mfor [39;49;00m t  [95min [39;49;00m team_tabs  [94mif [39;49;00m  [33m" [39;49;00m [33mTeam [39;49;00m [33m" [39;49;00m  [95min [39;49;00m t.text] [90m [39;49;00m
             [96mprint [39;49;00m( [33mf [39;49;00m [33m" [39;49;00m [33mTeam tabs found:  [39;49;00m [33m{ [39;49;00mteam_labels [33m} [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
             [96mself [39;49;00m.assertIn( [33m" [39;49;00m [33mTeam A [39;49;00m [33m" [39;49;00m, team_labels) [90m [39;49;00m
             [96mself [39;49;00m.assertIn( [33m" [39;49;00m [33mTeam B [39;49;00m [33m" [39;49;00m, team_labels) [90m [39;49;00m
             [96mself [39;49;00m.assertIn( [33m" [39;49;00m [33mTeam C [39;49;00m [33m" [39;49;00m, team_labels) [90m [39;49;00m
     [90m [39;49;00m
             [90m# Switch to team C and add some runners [39;49;00m [90m [39;49;00m
            team_tabs[- [94m1 [39;49;00m].click() [90m [39;49;00m
            time.sleep( [94m1 [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
            team_c_runners = [ [90m [39;49;00m
                ( [94m1 [39;49;00m,  [33m' [39;49;00m [33mCharlie [39;49;00m [33m' [39;49;00m,  [33m' [39;49;00m [33mOne [39;49;00m [33m' [39;49;00m,  [33m' [39;49;00m [33m2015-01-01 [39;49;00m [33m' [39;49;00m,  [33m' [39;49;00m [33mM [39;49;00m [33m' [39;49;00m), [90m [39;49;00m
                ( [94m2 [39;49;00m,  [33m' [39;49;00m [33mCharlie [39;49;00m [33m' [39;49;00m,  [33m' [39;49;00m [33mTwo [39;49;00m [33m' [39;49;00m,  [33m' [39;49;00m [33m2015-01-02 [39;49;00m [33m' [39;49;00m,  [33m' [39;49;00m [33mF [39;49;00m [33m' [39;49;00m), [90m [39;49;00m
            ] [90m [39;49;00m
     [90m [39;49;00m
             [96mself [39;49;00m.selenium.execute_script( [33m" [39;49;00m [33m$( [39;49;00m [33m' [39;49;00m [33m#addRelayTeamContainer [39;49;00m [33m' [39;49;00m [33m).collapse( [39;49;00m [33m' [39;49;00m [33mshow [39;49;00m [33m' [39;49;00m [33m) [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
             [96mself [39;49;00m.selenium.execute_script( [33m" [39;49;00m [33mwindow.app.initRelayCompetitorsHandsOnTable() [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
            time.sleep( [94m1 [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
             [94mfor [39;49;00m i, (idx, first, last, dob, gender)  [95min [39;49;00m  [96menumerate [39;49;00m(team_c_runners): [90m [39;49;00m
                 [96mprint [39;49;00m( [33mf [39;49;00m [33m" [39;49;00m [33mInputting runner  [39;49;00m [33m{ [39;49;00mi+ [94m1 [39;49;00m [33m} [39;49;00m [33m for team C:  [39;49;00m [33m{ [39;49;00mfirst [33m} [39;49;00m [33m  [39;49;00m [33m{ [39;49;00mlast [33m} [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
                cell_xpath =  [33mf [39;49;00m [33m" [39;49;00m [33m//div[@id= [39;49;00m [33m' [39;49;00m [33mrunners_ht [39;49;00m [33m' [39;49;00m [33m]//table[@class= [39;49;00m [33m' [39;49;00m [33mhtCore [39;49;00m [33m' [39;49;00m [33m]/tbody/tr[ [39;49;00m [33m{ [39;49;00mi+ [94m1 [39;49;00m [33m} [39;49;00m [33m]/td[1] [39;49;00m [33m" [39;49;00m [90m [39;49;00m
                cell =  [96mself [39;49;00m.wait.until(EC.element_to_be_clickable((By.XPATH, cell_xpath))) [90m [39;49;00m
                actions = ActionChains( [96mself [39;49;00m.selenium) [90m [39;49;00m
                actions.move_to_element(cell).click().perform() [90m [39;49;00m
                time.sleep( [94m0.3 [39;49;00m) [90m [39;49;00m
                actions = ActionChains( [96mself [39;49;00m.selenium) [90m [39;49;00m
                actions.send_keys( [96mstr [39;49;00m(idx +  [94m20 [39;49;00m)) [90m [39;49;00m
                actions.send_keys(Keys.TAB) [90m [39;49;00m
                actions.send_keys(first) [90m [39;49;00m
                actions.send_keys(Keys.TAB) [90m [39;49;00m
                actions.send_keys(last) [90m [39;49;00m
                actions.send_keys(Keys.TAB) [90m [39;49;00m
                actions.send_keys(dob) [90m [39;49;00m
                actions.send_keys(Keys.TAB) [90m [39;49;00m
                actions.send_keys(gender) [90m [39;49;00m
                actions.send_keys(Keys.ENTER) [90m [39;49;00m
                actions.perform() [90m [39;49;00m
                time.sleep( [94m0.5 [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
             [96mprint [39;49;00m( [33m" [39;49;00m [33mSaving teams... [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
            save_btn =  [96mself [39;49;00m.wait.until(EC.element_to_be_clickable((By.XPATH,  [33m" [39;49;00m [33m//div[@id= [39;49;00m [33m' [39;49;00m [33mrelay_teams_tab [39;49;00m [33m' [39;49;00m [33m]//button[contains(.,  [39;49;00m [33m' [39;49;00m [33mSave Changes [39;49;00m [33m' [39;49;00m [33m)] [39;49;00m [33m" [39;49;00m))) [90m [39;49;00m
             [96mself [39;49;00m.selenium.execute_script( [33m" [39;49;00m [33marguments[0].click(); [39;49;00m [33m" [39;49;00m, save_btn) [90m [39;49;00m
            time.sleep( [94m2 [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
             [90m# --- Step 5: Refresh and verify athletes and order persist --- [39;49;00m [90m [39;49;00m
             [96mprint [39;49;00m( [33m" [39;49;00m [33m=== PART 2: Refreshing and verifying persistence === [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
             [96mself [39;49;00m.selenium.refresh() [90m [39;49;00m
             [96mself [39;49;00m.wait.until(EC.invisibility_of_element_located((By.CSS_SELECTOR,  [33m" [39;49;00m [33m[v-cloak] [39;49;00m [33m" [39;49;00m))) [90m [39;49;00m
            time.sleep( [94m1 [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
            relay_tab_link =  [96mself [39;49;00m.selenium.find_element(By.CSS_SELECTOR,  [33m" [39;49;00m [33ma[href= [39;49;00m [33m' [39;49;00m [33m#relay_teams_tab [39;49;00m [33m' [39;49;00m [33m] [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
            relay_tab_link.click() [90m [39;49;00m
            time.sleep( [94m1 [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
            event_btn =  [96mself [39;49;00m.wait.until(EC.element_to_be_clickable((By.XPATH, event_btn_xpath))) [90m [39;49;00m
            event_btn.click() [90m [39;49;00m
             [96mself [39;49;00m.wait.until( [94mlambda [39;49;00m d:  [33m" [39;49;00m [33m--- [39;49;00m [33m" [39;49;00m  [95mnot [39;49;00m  [95min [39;49;00m d.find_element(By.CSS_SELECTOR, relay_selected_table_selector +  [33m" [39;49;00m [33m tbody tr:not(.muted) [39;49;00m [33m" [39;49;00m).text) [90m [39;49;00m
            selected_rows =  [96mself [39;49;00m.selenium.find_elements(By.CSS_SELECTOR,  [33mf [39;49;00m [33m" [39;49;00m [33m{ [39;49;00mrelay_selected_table_selector [33m} [39;49;00m [33m tbody tr:not(.muted) [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
             [90m# Check team A order persistence [39;49;00m [90m [39;49;00m
            selected_rows =  [96mself [39;49;00m.selenium.find_elements(By.CSS_SELECTOR,  [33mf [39;49;00m [33m" [39;49;00m [33m{ [39;49;00mrelay_selected_table_selector [33m} [39;49;00m [33m tbody tr:not(.muted) [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
             [96mprint [39;49;00m( [33mf [39;49;00m [33m" [39;49;00m [33mFirst runner at refresh:  [39;49;00m [33m{ [39;49;00mselected_rows[ [94m0 [39;49;00m].text.strip()[: [94m20 [39;49;00m] [33m} [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
             [96mself [39;49;00m.assertEqual(selected_rows[ [94m0 [39;49;00m].text, runner_name,  [33m" [39;49;00m [33mRunner order did not persist after refresh [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
             [90m# --- Step 6: Remove middle team (B) and check renaming --- [39;49;00m [90m [39;49;00m
             [96mprint [39;49;00m( [33m" [39;49;00m [33m=== PART 3: Removing Team B and checking renaming === [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
            team_tabs =  [96mself [39;49;00m.selenium.find_elements(By.CSS_SELECTOR,  [33m" [39;49;00m [33m#relay_teams_tab ul.nav-tabs li.nav-item a [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
             [90m# Team B is index 1 [39;49;00m [90m [39;49;00m
             [94mfor [39;49;00m tab  [95min [39;49;00m team_tabs: [90m [39;49;00m
                 [94mif [39;49;00m  [33m" [39;49;00m [33mTeam B [39;49;00m [33m" [39;49;00m  [95min [39;49;00m tab.text: [90m [39;49;00m
                    tab.click() [90m [39;49;00m
                    time.sleep( [94m0.5 [39;49;00m) [90m [39;49;00m
                     [94mbreak [39;49;00m [90m [39;49;00m
     [90m [39;49;00m
            remove_btn =  [96mself [39;49;00m.wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR,  [33m" [39;49;00m [33m#removeRelayTeamBtn [39;49;00m [33m" [39;49;00m))) [90m [39;49;00m
             [96mself [39;49;00m.selenium.execute_script( [33m" [39;49;00m [33marguments[0].click(); [39;49;00m [33m" [39;49;00m, remove_btn) [90m [39;49;00m
            time.sleep( [94m0.5 [39;49;00m) [90m [39;49;00m
             [94mtry [39;49;00m: [90m [39;49;00m
                 [96mself [39;49;00m.selenium.switch_to.alert.accept() [90m [39;49;00m
                time.sleep( [94m0.5 [39;49;00m) [90m [39;49;00m
             [94mexcept [39;49;00m: [90m [39;49;00m
                 [94mpass [39;49;00m [90m [39;49;00m
     [90m [39;49;00m
             [90m# Verify UI renaming before save [39;49;00m [90m [39;49;00m
            team_tabs_after =  [96mself [39;49;00m.selenium.find_elements(By.CSS_SELECTOR,  [33m" [39;49;00m [33m#relay_teams_tab ul.nav-tabs li.nav-item a [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
            team_labels_after = [t.text.strip()  [94mfor [39;49;00m t  [95min [39;49;00m team_tabs_after  [94mif [39;49;00m  [33m" [39;49;00m [33mTeam [39;49;00m [33m" [39;49;00m  [95min [39;49;00m t.text] [90m [39;49;00m
             [96mprint [39;49;00m( [33mf [39;49;00m [33m" [39;49;00m [33mTeam tabs after removal:  [39;49;00m [33m{ [39;49;00mteam_labels_after [33m} [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
             [96mself [39;49;00m.assertNotIn( [33m" [39;49;00m [33mTeam C [39;49;00m [33m" [39;49;00m, team_labels_after,  [33m" [39;49;00m [33mTeam C should have been renamed [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
             [96mself [39;49;00m.assertIn( [33m" [39;49;00m [33mTeam B [39;49;00m [33m" [39;49;00m, team_labels_after,  [33m" [39;49;00m [33mTeam B should still exist (now representing what was C) [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
             [90m# Check Charlie moved to B [39;49;00m [90m [39;49;00m
             [94mfor [39;49;00m tab  [95min [39;49;00m team_tabs_after: [90m [39;49;00m
                 [94mif [39;49;00m  [33m" [39;49;00m [33mTeam B [39;49;00m [33m" [39;49;00m  [95min [39;49;00m tab.text: [90m [39;49;00m
                    tab.click() [90m [39;49;00m
                    time.sleep( [94m0.5 [39;49;00m) [90m [39;49;00m
                     [94mbreak [39;49;00m [90m [39;49;00m
     [90m [39;49;00m
            page_text =  [96mself [39;49;00m.selenium.find_element(By.TAG_NAME,  [33m" [39;49;00m [33mbody [39;49;00m [33m" [39;49;00m).text [90m [39;49;00m
             [96mself [39;49;00m.assertIn( [33m" [39;49;00m [33mCharlie [39;49;00m [33m" [39;49;00m, page_text,  [33m" [39;49;00m [33mFormer Team C athletes (Charlie) not found in renamed Team B [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
            save_btn =  [96mself [39;49;00m.wait.until(EC.element_to_be_clickable((By.XPATH,  [33m" [39;49;00m [33m//div[@id= [39;49;00m [33m' [39;49;00m [33mrelay_teams_tab [39;49;00m [33m' [39;49;00m [33m]//button[contains(.,  [39;49;00m [33m' [39;49;00m [33mSave Changes [39;49;00m [33m' [39;49;00m [33m)] [39;49;00m [33m" [39;49;00m))) [90m [39;49;00m
             [96mself [39;49;00m.selenium.execute_script( [33m" [39;49;00m [33marguments[0].click(); [39;49;00m [33m" [39;49;00m, save_btn) [90m [39;49;00m
            time.sleep( [94m2 [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
             [90m# Verify in database [39;49;00m [90m [39;49;00m
            all_teams_db = RelayTeam.objects.filter(competition= [96mself [39;49;00m.mc, event_id=relay_event_id, team_id=team_id).order_by( [33m' [39;49;00m [33mrelayteam_id [39;49;00m [33m' [39;49;00m) [90m [39;49;00m
            db_ids = [t.relayteam_id  [94mfor [39;49;00m t  [95min [39;49;00m all_teams_db] [90m [39;49;00m
             [96mprint [39;49;00m( [33mf [39;49;00m [33m" [39;49;00m [33mRelay teams in DB:  [39;49;00m [33m{ [39;49;00mdb_ids [33m} [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
             [96mself [39;49;00m.assertIn( [33mf [39;49;00m [33m" [39;49;00m [33m{ [39;49;00mteam_id [33m} [39;49;00m [33m-A [39;49;00m [33m" [39;49;00m, db_ids) [90m [39;49;00m
             [96mself [39;49;00m.assertIn( [33mf [39;49;00m [33m" [39;49;00m [33m{ [39;49;00mteam_id [33m} [39;49;00m [33m-B [39;49;00m [33m" [39;49;00m, db_ids) [90m [39;49;00m
             [96mself [39;49;00m.assertNotIn( [33mf [39;49;00m [33m" [39;49;00m [33m{ [39;49;00mteam_id [33m} [39;49;00m [33m-C [39;49;00m [33m" [39;49;00m, db_ids) [90m [39;49;00m
     [90m [39;49;00m
            team_b_db = all_teams_db.filter(relayteam_id= [33mf [39;49;00m [33m" [39;49;00m [33m{ [39;49;00mteam_id [33m} [39;49;00m [33m-B [39;49;00m [33m" [39;49;00m).first() [90m [39;49;00m
            team_b_runners = [r.first_name  [94mfor [39;49;00m r  [95min [39;49;00m team_b_db.runners] [90m [39;49;00m
             [96mself [39;49;00m.assertIn( [33m" [39;49;00m [33mCharlie [39;49;00m [33m" [39;49;00m, team_b_runners,  [33m" [39;49;00m [33mDatabase did not update runners for renamed team [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
             [96mprint [39;49;00m( [33m" [39;49;00m [33mAll relay tests completed successfully! [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
     [90m [39;49;00m
             [90m# --- Step 7: Number all athletes before seeding --- [39;49;00m [90m [39;49;00m
             [96mprint [39;49;00m( [33m" [39;49;00m [33m=== PART 4: Numbering athletes === [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
            numbering_url = reverse( [33m' [39;49;00m [33mcomp-edit-numbering [39;49;00m [33m' [39;49;00m, kwargs={ [90m [39;49;00m
                 [33m' [39;49;00m [33myear [39;49;00m [33m' [39;49;00m:  [96mself [39;49;00m.mc.year, [90m [39;49;00m
                 [33m' [39;49;00m [33mcountry [39;49;00m [33m' [39;49;00m:  [96mself [39;49;00m.mc.country, [90m [39;49;00m
                 [33m' [39;49;00m [33mslug [39;49;00m [33m' [39;49;00m:  [96mself [39;49;00m.mc.slug [90m [39;49;00m
            }) [90m [39;49;00m
            numbering_full_url =  [33mf [39;49;00m [33m" [39;49;00m [33m{ [39;49;00m [96mself [39;49;00m.live_server_url [33m} [39;49;00m [33m{ [39;49;00mnumbering_url [33m} [39;49;00m [33m" [39;49;00m [90m [39;49;00m
             [96mprint [39;49;00m( [33mf [39;49;00m [33m" [39;49;00m [33mNavigating to numbering page:  [39;49;00m [33m{ [39;49;00mnumbering_full_url [33m} [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
             [96mself [39;49;00m.selenium.get(numbering_full_url) [90m [39;49;00m
     [90m [39;49;00m
             [94mif [39;49;00m  [33m' [39;49;00m [33m/accounts/login/ [39;49;00m [33m' [39;49;00m  [95min [39;49;00m  [96mself [39;49;00m.selenium.current_url: [90m [39;49;00m
                 [96mself [39;49;00m.login(redirect=numbering_url) [90m [39;49;00m
     [90m [39;49;00m
             [96mself [39;49;00m.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR,  [33m" [39;49;00m [33mbutton[name= [39;49;00m [33m' [39;49;00m [33mapply_numbers [39;49;00m [33m' [39;49;00m [33m] [39;49;00m [33m" [39;49;00m))) [90m [39;49;00m
            time.sleep( [94m1 [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
            apply_numbers_btn =  [96mself [39;49;00m.wait.until(EC.element_to_be_clickable( [90m [39;49;00m
                (By.CSS_SELECTOR,  [33m" [39;49;00m [33mbutton[name= [39;49;00m [33m' [39;49;00m [33mapply_numbers [39;49;00m [33m' [39;49;00m [33m] [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
            )) [90m [39;49;00m
             [96mself [39;49;00m.selenium.execute_script( [33m" [39;49;00m [33marguments[0].scrollIntoView(true); [39;49;00m [33m" [39;49;00m, apply_numbers_btn) [90m [39;49;00m
            time.sleep( [94m0.5 [39;49;00m) [90m [39;49;00m
            apply_numbers_btn.click() [90m [39;49;00m
            time.sleep( [94m2 [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
            page_text =  [96mself [39;49;00m.selenium.page_source [90m [39;49;00m
             [94mif [39;49;00m  [33m' [39;49;00m [33msuccess [39;49;00m [33m' [39;49;00m  [95min [39;49;00m page_text.lower()  [95mor [39;49;00m  [33m' [39;49;00m [33mapplied [39;49;00m [33m' [39;49;00m  [95min [39;49;00m page_text.lower(): [90m [39;49;00m
                 [96mprint [39;49;00m( [33m" [39;49;00m [33mNumbers applied successfully [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
             [94melse [39;49;00m: [90m [39;49;00m
                 [96mprint [39;49;00m( [33m" [39;49;00m [33mNumbering page content - checking for confirmation... [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
             [96mprint [39;49;00m( [33m" [39;49;00m [33mAthletes numbered! [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
     [90m [39;49;00m
             [90m# --- Step 8: Seed and Publish via Event Detail Private --- [39;49;00m [90m [39;49;00m
             [96mprint [39;49;00m( [33m" [39;49;00m [33m=== PART 4: Seeding and publishing === [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
            private_event_url = reverse( [33m' [39;49;00m [33mcomp-event-detail-private [39;49;00m [33m' [39;49;00m, kwargs={ [90m [39;49;00m
                 [33m' [39;49;00m [33myear [39;49;00m [33m' [39;49;00m:  [96mself [39;49;00m.mc.year, [90m [39;49;00m
                 [33m' [39;49;00m [33mcountry [39;49;00m [33m' [39;49;00m:  [96mself [39;49;00m.mc.country, [90m [39;49;00m
                 [33m' [39;49;00m [33mslug [39;49;00m [33m' [39;49;00m:  [96mself [39;49;00m.mc.slug, [90m [39;49;00m
                 [33m' [39;49;00m [33mevent_id [39;49;00m [33m' [39;49;00m: relay_event_id [90m [39;49;00m
            }) [90m [39;49;00m
             [96mprint [39;49;00m( [33mf [39;49;00m [33m" [39;49;00m [33mNavigating to private event detail:  [39;49;00m [33m{ [39;49;00mprivate_event_url [33m} [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
             [96mself [39;49;00m.selenium.get( [33mf [39;49;00m [33m" [39;49;00m [33m{ [39;49;00m [96mself [39;49;00m.live_server_url [33m} [39;49;00m [33m{ [39;49;00mprivate_event_url [33m} [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
             [94mif [39;49;00m  [33m' [39;49;00m [33m/accounts/login/ [39;49;00m [33m' [39;49;00m  [95min [39;49;00m  [96mself [39;49;00m.selenium.current_url: [90m [39;49;00m
                 [96mprint [39;49;00m( [33m" [39;49;00m [33mOn login page [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
                 [96mself [39;49;00m.login(redirect=private_event_url) [90m [39;49;00m
     [90m [39;49;00m
            time.sleep( [94m0.5 [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
            heats_tab =  [96mself [39;49;00m.wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR,  [33m" [39;49;00m [33ma[href= [39;49;00m [33m' [39;49;00m [33m#pills-heats [39;49;00m [33m' [39;49;00m [33m] [39;49;00m [33m" [39;49;00m))) [90m [39;49;00m
            heats_tab.click() [90m [39;49;00m
            time.sleep( [94m1 [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
            seeding_options_container =  [96mself [39;49;00m.wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR,  [33m" [39;49;00m [33m#pills-heats form#seeding_options_r1 [39;49;00m [33m" [39;49;00m))) [90m [39;49;00m
             [94mif [39;49;00m  [95mnot [39;49;00m seeding_options_container.is_displayed(): [90m [39;49;00m
                seeding_options_link =  [96mself [39;49;00m.wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR,  [33m" [39;49;00m [33m#pills-heats button[data-target= [39;49;00m [33m' [39;49;00m [33m#seeding_options_r1 [39;49;00m [33m' [39;49;00m [33m] [39;49;00m [33m" [39;49;00m))) [90m [39;49;00m
                seeding_options_link.click() [90m [39;49;00m
                time.sleep( [94m0.5 [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
            seeding_select =  [96mself [39;49;00m.wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR,  [33m" [39;49;00m [33m#pills-heats button[name= [39;49;00m [33m' [39;49;00m [33mseed [39;49;00m [33m' [39;49;00m [33m] [39;49;00m [33m" [39;49;00m))) [90m [39;49;00m
            seeding_select.click() [90m [39;49;00m
             [96mprint [39;49;00m( [33m" [39;49;00m [33mSelected open_meet seeding method. [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
            lanes_input =  [96mself [39;49;00m.wait.until(EC.visibility_of_element_located((By.NAME,  [33m" [39;49;00m [33mlanes [39;49;00m [33m" [39;49;00m))) [90m [39;49;00m
            lanes_input.clear() [90m [39;49;00m
            lanes_input.send_keys( [33m" [39;49;00m [33m8 [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
             [96mprint [39;49;00m( [33m" [39;49;00m [33mSet lanes to 8. [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
            seed_btn =  [96mself [39;49;00m.wait.until(EC.element_to_be_clickable((By.NAME,  [33m" [39;49;00m [33mseed [39;49;00m [33m" [39;49;00m))) [90m [39;49;00m
            seed_btn.click() [90m [39;49;00m
             [96mprint [39;49;00m( [33m" [39;49;00m [33mSeeding triggered. [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
            heats_tab =  [96mself [39;49;00m.wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR,  [33m" [39;49;00m [33ma[href= [39;49;00m [33m' [39;49;00m [33m#pills-heats [39;49;00m [33m' [39;49;00m [33m] [39;49;00m [33m" [39;49;00m))) [90m [39;49;00m
            heats_tab.click() [90m [39;49;00m
     [90m [39;49;00m
            seeding_options_container =  [96mself [39;49;00m.wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR,  [33m" [39;49;00m [33m#pills-heats form#seeding_options_r1 [39;49;00m [33m" [39;49;00m))) [90m [39;49;00m
             [94mif [39;49;00m  [95mnot [39;49;00m seeding_options_container.is_displayed(): [90m [39;49;00m
                seeding_options_link =  [96mself [39;49;00m.wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR,  [33m" [39;49;00m [33m#pills-heats button[data-target= [39;49;00m [33m' [39;49;00m [33m#seeding_options_r1 [39;49;00m [33m' [39;49;00m [33m] [39;49;00m [33m" [39;49;00m))) [90m [39;49;00m
                seeding_options_link.click() [90m [39;49;00m
                time.sleep( [94m0.5 [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
             [96mself [39;49;00m.wait.until(EC.presence_of_element_located((By.NAME,  [33m" [39;49;00m [33mpublic [39;49;00m [33m" [39;49;00m))) [90m [39;49;00m
     [90m [39;49;00m
            publish_btn =  [96mself [39;49;00m.wait.until(EC.element_to_be_clickable((By.NAME,  [33m" [39;49;00m [33mpublic [39;49;00m [33m" [39;49;00m))) [90m [39;49;00m
            publish_btn.click() [90m [39;49;00m
             [96mprint [39;49;00m( [33m" [39;49;00m [33mPublished results. [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
            time.sleep( [94m1 [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
             [90m# --- Step 9: Verify on unit detail page --- [39;49;00m [90m [39;49;00m
             [94mfrom [39;49;00m [90m  [39;49;00m [04m [96mproject [39;49;00m [04m [96m. [39;49;00m [04m [96mreference [39;49;00m [04m [96m. [39;49;00m [04m [96mmongo [39;49;00m [90m  [39;49;00m [94mimport [39;49;00m Unit [90m [39;49;00m
            unit = Unit.objects.filter(competition= [96mself [39;49;00m.mc, event=relay_event).first() [90m [39;49;00m
             [96mself [39;49;00m.assertIsNotNone(unit,  [33m" [39;49;00m [33mNo unit found for the relay event after seeding. [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
            unit_url = reverse( [33m' [39;49;00m [33mcomp-unit-detail [39;49;00m [33m' [39;49;00m, kwargs={ [90m [39;49;00m
                 [33m' [39;49;00m [33myear [39;49;00m [33m' [39;49;00m:  [96mself [39;49;00m.mc.year, [90m [39;49;00m
                 [33m' [39;49;00m [33mcountry [39;49;00m [33m' [39;49;00m:  [96mself [39;49;00m.mc.country, [90m [39;49;00m
                 [33m' [39;49;00m [33mslug [39;49;00m [33m' [39;49;00m:  [96mself [39;49;00m.mc.slug, [90m [39;49;00m
                 [33m' [39;49;00m [33mevent_id [39;49;00m [33m' [39;49;00m: unit.event.event_id, [90m [39;49;00m
                 [33m' [39;49;00m [33mround [39;49;00m [33m' [39;49;00m: unit.round, [90m [39;49;00m
                 [33m' [39;49;00m [33mheat [39;49;00m [33m' [39;49;00m: unit.heat [90m [39;49;00m
            }) [90m [39;49;00m
             [96mprint [39;49;00m( [33mf [39;49;00m [33m" [39;49;00m [33mNavigating to unit detail:  [39;49;00m [33m{ [39;49;00munit_url [33m} [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
             [96mself [39;49;00m.selenium.get( [33mf [39;49;00m [33m" [39;49;00m [33m{ [39;49;00m [96mself [39;49;00m.live_server_url [33m} [39;49;00m [33m{ [39;49;00munit_url [33m} [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
            page_text =  [96mself [39;49;00m.selenium.find_element(By.TAG_NAME,  [33m" [39;49;00m [33mbody [39;49;00m [33m" [39;49;00m).text [90m [39;49;00m
             [94mfor [39;49;00m idx, first, last, dob, gender  [95min [39;49;00m runners_data: [90m [39;49;00m
                 [96mself [39;49;00m.assertIn(first, page_text,  [33mf [39;49;00m [33m" [39;49;00m [33mAthlete  [39;49;00m [33m{ [39;49;00mfirst [33m} [39;49;00m [33m  [39;49;00m [33m{ [39;49;00mlast [33m} [39;49;00m [33m not found in unit detail page [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
                 [96mself [39;49;00m.assertIn(last, page_text,  [33mf [39;49;00m [33m" [39;49;00m [33mAthlete  [39;49;00m [33m{ [39;49;00mfirst [33m} [39;49;00m [33m  [39;49;00m [33m{ [39;49;00mlast [33m} [39;49;00m [33m not found in unit detail page [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
             [96mprint [39;49;00m( [33m" [39;49;00m [33mVerified athletes in unit detail page. [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
             [96mprint [39;49;00m( [33m" [39;49;00m [33m=== TEST PASSED: Add/Remove relay team with athlete persistence and seeding verified === [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
     [90m [39;49;00m
         [94mexcept [39;49;00m  [96mException [39;49;00m  [94mas [39;49;00m e: [90m [39;49;00m
             [96mprint [39;49;00m( [33mf [39;49;00m [33m" [39;49;00m [33mTest failed:  [39;49;00m [33m{ [39;49;00me [33m} [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
            time.sleep( [94m1 [39;49;00m) [90m [39;49;00m
             [96mself [39;49;00m.selenium.save_screenshot( [33m" [39;49;00m [33mrelay_test_failure.png [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
             [94mwith [39;49;00m  [96mopen [39;49;00m( [33m" [39;49;00m [33mrelay_test_failure.html [39;49;00m [33m" [39;49;00m,  [33m" [39;49;00m [33mw [39;49;00m [33m" [39;49;00m)  [94mas [39;49;00m f: [90m [39;49;00m
                f.write( [96mself [39;49;00m.selenium.page_source) [90m [39;49;00m
>            [94mraise [39;49;00m e [90m [39;49;00m
project/comp/tests/test_team_declarations.py:1245:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
project/comp/tests/test_team_declarations.py:1116: in test_adhoc_relay_declarations
     [0m [96mself [39;49;00m.assertNotIn( [33mf [39;49;00m [33m" [39;49;00m [33m{ [39;49;00mteam_id [33m} [39;49;00m [33m-C [39;49;00m [33m" [39;49;00m, db_ids) [90m [39;49;00m
E   AssertionError: 'ADHOC1-C' unexpectedly found in ['ADHOC1-A', 'ADHOC1-B', 'ADHOC1-C']
__________________ BMCE2ETests.test_associate_membership_join __________________
[gw2] linux -- Python 3.13.2 /opt/hostedtoolcache/Python/3.13.2/x64/bin/python
self = <project.federation.tests.test_bmc.BMCE2ETests testMethod=test_associate_membership_join>
     [0m [37m@superuser_login_required [39;49;00m [90m [39;49;00m
     [94mdef [39;49;00m [90m  [39;49;00m [92mtest_associate_membership_join [39;49;00m( [96mself [39;49;00m): [90m [39;49;00m
     [90m [39;49;00m
         [96mself [39;49;00m.person.roles().filter(organisation__code= [33m' [39;49;00m [33mBMC [39;49;00m [33m' [39;49;00m).delete() [90m [39;49;00m
     [90m [39;49;00m
        fed_index_url =  [33mf [39;49;00m [33m" [39;49;00m [33m{ [39;49;00m [96mself [39;49;00m.live_server_url [33m} [39;49;00m [33m{ [39;49;00mreverse( [33m' [39;49;00m [33mfed-index [39;49;00m [33m' [39;49;00m) [33m} [39;49;00m [33m" [39;49;00m [90m [39;49;00m
         [96mself [39;49;00m.selenium.get(fed_index_url) [90m [39;49;00m
     [90m [39;49;00m
         [90m# join button [39;49;00m [90m [39;49;00m
        join_btn =  [96mself [39;49;00m.selenium.find_element(By.ID,  [33m" [39;49;00m [33mjoinMembershipBtn [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
        join_btn.click() [90m [39;49;00m
     [90m [39;49;00m
     [90m [39;49;00m
         [90m# contact details popup will show, hide it [39;49;00m [90m [39;49;00m
        contact_details_close_btn =  [96mself [39;49;00m.selenium.find_element(By.CSS_SELECTOR,  [33m" [39;49;00m [33m#inputMissingDataModal .close-btn [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
>       contact_details_close_btn.click() [90m [39;49;00m
project/federation/tests/test_bmc.py:438:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
/opt/hostedtoolcache/Python/3.13.2/x64/lib/python3.13/site-packages/selenium/webdriver/remote/webelement.py:119: in click
     [0m [96mself [39;49;00m._execute(Command.CLICK_ELEMENT) [90m [39;49;00m
/opt/hostedtoolcache/Python/3.13.2/x64/lib/python3.13/site-packages/selenium/webdriver/remote/webelement.py:572: in _execute
     [0m [94mreturn [39;49;00m  [96mself [39;49;00m._parent.execute(command, params) [90m [39;49;00m
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [90m [39;49;00m
/opt/hostedtoolcache/Python/3.13.2/x64/lib/python3.13/site-packages/selenium/webdriver/remote/webdriver.py:448: in execute
     [0m [96mself [39;49;00m.error_handler.check_response(response) [90m [39;49;00m
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <selenium.webdriver.remote.errorhandler.ErrorHandler object at 0x7f8c74ac6780>
response = {'status': 404, 'value': '{"value":{"error":"stale element reference","message":"stale element reference: stale elemen...\\n#17 0x563ffb47ff88 \\u003Cunknown>\\n#18 0x563ffb4915de \\u003Cunknown>\\n#19 0x7fbd7a294ac3 \\u003Cunknown>\\n"}}'}
     [0m [94mdef [39;49;00m [90m  [39;49;00m [92mcheck_response [39;49;00m( [96mself [39;49;00m, response: Dict[ [96mstr [39;49;00m, Any]) ->  [94mNone [39;49;00m: [90m [39;49;00m
     [90m     [39;49;00m [33m"""Checks that a JSON response from the WebDriver does not have an [39;49;00m
     [33m    error. [39;49;00m
     [33m [39;49;00m
     [33m    :Args: [39;49;00m
     [33m     - response - The JSON response from the WebDriver server as a dictionary [39;49;00m
     [33m       object. [39;49;00m
     [33m [39;49;00m
     [33m    :Raises: If the response contains an error message. [39;49;00m
     [33m    """ [39;49;00m [90m [39;49;00m
        status = response.get( [33m" [39;49;00m [33mstatus [39;49;00m [33m" [39;49;00m,  [94mNone [39;49;00m) [90m [39;49;00m
         [94mif [39;49;00m  [95mnot [39;49;00m status  [95mor [39;49;00m status == ErrorCode.SUCCESS: [90m [39;49;00m
             [94mreturn [39;49;00m [90m [39;49;00m
        value =  [94mNone [39;49;00m [90m [39;49;00m
        message = response.get( [33m" [39;49;00m [33mmessage [39;49;00m [33m" [39;49;00m,  [33m" [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
        screen:  [96mstr [39;49;00m = response.get( [33m" [39;49;00m [33mscreen [39;49;00m [33m" [39;49;00m,  [33m" [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
        stacktrace =  [94mNone [39;49;00m [90m [39;49;00m
         [94mif [39;49;00m  [96misinstance [39;49;00m(status,  [96mint [39;49;00m): [90m [39;49;00m
            value_json = response.get( [33m" [39;49;00m [33mvalue [39;49;00m [33m" [39;49;00m,  [94mNone [39;49;00m) [90m [39;49;00m
             [94mif [39;49;00m value_json  [95mand [39;49;00m  [96misinstance [39;49;00m(value_json,  [96mstr [39;49;00m): [90m [39;49;00m
                 [94mimport [39;49;00m [90m  [39;49;00m [04m [96mjson [39;49;00m [90m [39;49;00m
     [90m [39;49;00m
                 [94mtry [39;49;00m: [90m [39;49;00m
                    value = json.loads(value_json) [90m [39;49;00m
                     [94mif [39;49;00m  [96mlen [39;49;00m(value) ==  [94m1 [39;49;00m: [90m [39;49;00m
                        value = value[ [33m" [39;49;00m [33mvalue [39;49;00m [33m" [39;49;00m] [90m [39;49;00m
                    status = value.get( [33m" [39;49;00m [33merror [39;49;00m [33m" [39;49;00m,  [94mNone [39;49;00m) [90m [39;49;00m
                     [94mif [39;49;00m  [95mnot [39;49;00m status: [90m [39;49;00m
                        status = value.get( [33m" [39;49;00m [33mstatus [39;49;00m [33m" [39;49;00m, ErrorCode.UNKNOWN_ERROR) [90m [39;49;00m
                        message = value.get( [33m" [39;49;00m [33mvalue [39;49;00m [33m" [39;49;00m)  [95mor [39;49;00m value.get( [33m" [39;49;00m [33mmessage [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
                         [94mif [39;49;00m  [95mnot [39;49;00m  [96misinstance [39;49;00m(message,  [96mstr [39;49;00m): [90m [39;49;00m
                            value = message [90m [39;49;00m
                            message = message.get( [33m" [39;49;00m [33mmessage [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
                     [94melse [39;49;00m: [90m [39;49;00m
                        message = value.get( [33m" [39;49;00m [33mmessage [39;49;00m [33m" [39;49;00m,  [94mNone [39;49;00m) [90m [39;49;00m
                 [94mexcept [39;49;00m  [96mValueError [39;49;00m: [90m [39;49;00m
                     [94mpass [39;49;00m [90m [39;49;00m
     [90m [39;49;00m
        exception_class: Type[WebDriverException] [90m [39;49;00m
        e = ErrorCode() [90m [39;49;00m
        error_codes = [item  [94mfor [39;49;00m item  [95min [39;49;00m  [96mdir [39;49;00m(e)  [94mif [39;49;00m  [95mnot [39;49;00m item.startswith( [33m" [39;49;00m [33m__ [39;49;00m [33m" [39;49;00m)] [90m [39;49;00m
         [94mfor [39;49;00m error_code  [95min [39;49;00m error_codes: [90m [39;49;00m
            error_info =  [96mgetattr [39;49;00m(ErrorCode, error_code) [90m [39;49;00m
             [94mif [39;49;00m  [96misinstance [39;49;00m(error_info,  [96mlist [39;49;00m)  [95mand [39;49;00m status  [95min [39;49;00m error_info: [90m [39;49;00m
                exception_class =  [96mgetattr [39;49;00m(ExceptionMapping, error_code, WebDriverException) [90m [39;49;00m
                 [94mbreak [39;49;00m [90m [39;49;00m
         [94melse [39;49;00m: [90m [39;49;00m
            exception_class = WebDriverException [90m [39;49;00m
     [90m [39;49;00m
         [94mif [39;49;00m  [95mnot [39;49;00m value: [90m [39;49;00m
            value = response[ [33m" [39;49;00m [33mvalue [39;49;00m [33m" [39;49;00m] [90m [39;49;00m
         [94mif [39;49;00m  [96misinstance [39;49;00m(value,  [96mstr [39;49;00m): [90m [39;49;00m
             [94mraise [39;49;00m exception_class(value) [90m [39;49;00m
         [94mif [39;49;00m message ==  [33m" [39;49;00m [33m" [39;49;00m  [95mand [39;49;00m  [33m" [39;49;00m [33mmessage [39;49;00m [33m" [39;49;00m  [95min [39;49;00m value: [90m [39;49;00m
            message = value[ [33m" [39;49;00m [33mmessage [39;49;00m [33m" [39;49;00m] [90m [39;49;00m
     [90m [39;49;00m
        screen =  [94mNone [39;49;00m   [90m# type: ignore[assignment] [39;49;00m [90m [39;49;00m
         [94mif [39;49;00m  [33m" [39;49;00m [33mscreen [39;49;00m [33m" [39;49;00m  [95min [39;49;00m value: [90m [39;49;00m
            screen = value[ [33m" [39;49;00m [33mscreen [39;49;00m [33m" [39;49;00m] [90m [39;49;00m
     [90m [39;49;00m
        stacktrace =  [94mNone [39;49;00m [90m [39;49;00m
        st_value = value.get( [33m" [39;49;00m [33mstackTrace [39;49;00m [33m" [39;49;00m)  [95mor [39;49;00m value.get( [33m" [39;49;00m [33mstacktrace [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
         [94mif [39;49;00m st_value: [90m [39;49;00m
             [94mif [39;49;00m  [96misinstance [39;49;00m(st_value,  [96mstr [39;49;00m): [90m [39;49;00m
                stacktrace = st_value.split( [33m" [39;49;00m [33m\n [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
             [94melse [39;49;00m: [90m [39;49;00m
                stacktrace = [] [90m [39;49;00m
                 [94mtry [39;49;00m: [90m [39;49;00m
                     [94mfor [39;49;00m frame  [95min [39;49;00m st_value: [90m [39;49;00m
                        line = frame.get( [33m" [39;49;00m [33mlineNumber [39;49;00m [33m" [39;49;00m,  [33m" [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
                        file = frame.get( [33m" [39;49;00m [33mfileName [39;49;00m [33m" [39;49;00m,  [33m" [39;49;00m [33m<anonymous> [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
                         [94mif [39;49;00m line: [90m [39;49;00m
                            file =  [33mf [39;49;00m [33m" [39;49;00m [33m{ [39;49;00mfile [33m} [39;49;00m [33m: [39;49;00m [33m{ [39;49;00mline [33m} [39;49;00m [33m" [39;49;00m [90m [39;49;00m
                        meth = frame.get( [33m" [39;49;00m [33mmethodName [39;49;00m [33m" [39;49;00m,  [33m" [39;49;00m [33m<anonymous> [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
                         [94mif [39;49;00m  [33m" [39;49;00m [33mclassName [39;49;00m [33m" [39;49;00m  [95min [39;49;00m frame: [90m [39;49;00m
                            meth =  [33mf [39;49;00m [33m" [39;49;00m [33m{ [39;49;00mframe[ [33m' [39;49;00m [33mclassName [39;49;00m [33m' [39;49;00m] [33m} [39;49;00m [33m. [39;49;00m [33m{ [39;49;00mmeth [33m} [39;49;00m [33m" [39;49;00m [90m [39;49;00m
                        msg =  [33m" [39;49;00m [33m    at  [39;49;00m [33m%s [39;49;00m [33m ( [39;49;00m [33m%s [39;49;00m [33m) [39;49;00m [33m" [39;49;00m [90m [39;49;00m
                        msg = msg % (meth, file) [90m [39;49;00m
                        stacktrace.append(msg) [90m [39;49;00m
                 [94mexcept [39;49;00m  [96mTypeError [39;49;00m: [90m [39;49;00m
                     [94mpass [39;49;00m [90m [39;49;00m
         [94mif [39;49;00m exception_class == UnexpectedAlertPresentException: [90m [39;49;00m
            alert_text =  [94mNone [39;49;00m [90m [39;49;00m
             [94mif [39;49;00m  [33m" [39;49;00m [33mdata [39;49;00m [33m" [39;49;00m  [95min [39;49;00m value: [90m [39;49;00m
                alert_text = value[ [33m" [39;49;00m [33mdata [39;49;00m [33m" [39;49;00m].get( [33m" [39;49;00m [33mtext [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
             [94melif [39;49;00m  [33m" [39;49;00m [33malert [39;49;00m [33m" [39;49;00m  [95min [39;49;00m value: [90m [39;49;00m
                alert_text = value[ [33m" [39;49;00m [33malert [39;49;00m [33m" [39;49;00m].get( [33m" [39;49;00m [33mtext [39;49;00m [33m" [39;49;00m) [90m [39;49;00m
             [94mraise [39;49;00m exception_class(message, screen, stacktrace, alert_text)   [90m# type: ignore[call-arg]  # mypy is not smart enough here [39;49;00m [90m [39;49;00m
>        [94mraise [39;49;00m exception_class(message, screen, stacktrace) [90m [39;49;00m
E       selenium.common.exceptions.StaleElementReferenceException: Message: stale element reference: stale element not found in the current frame
E         (Session info: chrome=147.0.7727.55); For documentation on this error, please visit: https://www.selenium.dev/documentation/webdriver/troubleshooting/errors#stale-element-reference-exception
E       Stacktrace:
E       #0 0x563ffb492b6a <unknown>
E       #1 0x563ffae94265 <unknown>
E       #2 0x563ffae9b111 <unknown>
E       #3 0x563ffae9d95b <unknown>
E       #4 0x563ffae9da03 <unknown>
E       #5 0x563ffaee8cd0 <unknown>
E       #6 0x563ffaedc437 <unknown>
E       #7 0x563ffaedbe07 <unknown>
E       #8 0x563ffaf2f969 <unknown>
E       #9 0x563ffaeda5cf <unknown>
E       #10 0x563ffaedb391 <unknown>
E       #11 0x563ffb45804b <unknown>
E       #12 0x563ffb45b00d <unknown>
E       #13 0x563ffb444808 <unknown>
E       #14 0x563ffb45bba0 <unknown>
E       #15 0x563ffb42b280 <unknown>
E       #16 0x563ffb47fdb8 <unknown>
E       #17 0x563ffb47ff88 <unknown>
E       #18 0x563ffb4915de <unknown>
E       #19 0x7fbd7a294ac3 <unknown>
/opt/hostedtoolcache/Python/3.13.2/x64/lib/python3.13/site-packages/selenium/webdriver/remote/errorhandler.py:232: StaleElementReferenceException
=========================== short test summary info ============================
[31mFAILED [0m project/comp/tests/test_team_declarations.py:: [1mTeamDeclarationsE2ETests::test_adhoc_individual_declarations [0m - AssertionError: 0 != 1 : Grid row count (0) should match selected table count (1)
[31mFAILED [0m project/comp/tests/test_seeding.py:: [1mSeedingE2ETests::test_three_round_championship_seeding [0m - AssertionError: 0 != 8 : Should have 8 athletes in the final
[31mFAILED [0m project/comp/tests/test_comp_e2e.py:: [1mCompE2ETests::test_record_length_and_height_events [0m - selenium.common.exceptions.TimeoutException: Message:
[31mFAILED [0m project/comp/tests/test_team_declarations.py:: [1mTeamDeclarationsE2ETests::test_adhoc_relay_declarations [0m - AssertionError: 'ADHOC1-C' unexpectedly found in ['ADHOC1-A', 'ADHOC1-B', 'ADHOC1-C']
[31mFAILED [0m project/federation/tests/test_bmc.py:: [1mBMCE2ETests::test_associate_membership_join [0m - selenium.common.exceptions.StaleElementReferenceException: Message: stale element reference: stale element not found in the current frame
  (Session info: chrome=147.0.7727.55); For documentation on this error, please visit: https://www.selenium.dev/documentation/webdriver/troubleshooting/errors#stale-element-reference-exception
Stacktrace:
#0 0x563ffb492b6a <unknown>
#1 0x563ffae94265 <unknown>
#2 0x563ffae9b111 <unknown>
#3 0x563ffae9d95b <unknown>
#4 0x563ffae9da03 <unknown>
#5 0x563ffaee8cd0 <unknown>
#6 0x563ffaedc437 <unknown>
#7 0x563ffaedbe07 <unknown>
#8 0x563ffaf2f969 <unknown>
#9 0x563ffaeda5cf <unknown>
#10 0x563ffaedb391 <unknown>
#11 0x563ffb45804b <unknown>
#12 0x563ffb45b00d <unknown>
#13 0x563ffb444808 <unknown>
#14 0x563ffb45bba0 <unknown>
#15 0x563ffb42b280 <unknown>
#16 0x563ffb47fdb8 <unknown>
#17 0x563ffb47ff88 <unknown>
#18 0x563ffb4915de <unknown>
#19 0x7fbd7a294ac3 <unknown>
====  [31m [1m5 failed [0m,  [32m610 passed [0m,  [33m16 skipped [0m,  [33m20285 warnings [0m [31m in 580.30s (0:09:40) [0m [31m =====
Filename: None. Size: 148kb. View raw, , hex, or download this file.

This paste expires on 2026-05-19 16:49:11.262152+00:00. Pasted through deprecated-web.