import os from pathlib import Path import runpy,json,sys,uuid from pathlib import Path sys.path.insert(0,str(Path(__file__).parent));from framebuffer import Screen,ROOT,shuffle,EVIDENCE c=runpy.run_path(str(Path(__file__).with_name('playtest_support.py')));state=json.load(open(os.environ['CAPITAL_TEST_STATE'])) r=c['call']('start_playtest',dict(project_id=state['cap']['project_id'],revision=state['cap']['revision'],artifact_id=state['artifact']['artifact_id'],request_id=str(uuid.uuid4()))) run=r['run_id'];state['typed_run']=r;Path(os.environ['CAPITAL_TEST_STATE']).write_text(json.dumps(state)) rows=[x.split('\t') for x in (ROOT/'data/states.tsv').read_text().splitlines()];order,_=shuffle();report=[];score=corrections=0 def press(k,hold=4,settle=12):return c['key'](run,k,hold,settle) def settle():c['inp'](run,[{'keys':[],'fields':600},{'keys':[],'fields':300}]) def cap(name):c['capture'](run,name);return Screen(str(EVIDENCE)+'/'+name+'.png') def type_text(s): steps=[] for ch in s: keys=['space'] if ch==' ' else ['shift',ch.lower()] if ch.isupper() else [ch] steps.extend([dict(keys=keys,fields=4),dict(keys=[],fields=12)]) if len(steps)>=60:c['inp'](run,steps);steps=[] if steps:c['inp'](run,steps) def clear_input(n): for start in range(0,n,25):c['inp'](run,[x for _ in range(min(25,n-start)) for x in [dict(keys=['left'],fields=4),dict(keys=[],fields=12)]]) settle();c['key'](run,'2',8,600);settle();c['inp'](run,[dict(keys=['shift','0'],fields=8),dict(keys=[],fields=12)]) # First alias in mixed case. Capture input before submission to verify the real keys. type_text('sT pAuL');s=cap('typed-alias-input');print('input=',s.text(8,158,45),flush=True) assert s.text(8,158,45)=='sT pAuL_',s.text(8,158,45) press('enter',8,600);s=cap('typed-alias-feedback');assert s.text(8,158,30)=='CORRECT!',s.text(8,158,30) score=1;report.append(dict(question=1,code='MN',answer='sT pAuL',correct=True,correction=False));c['key'](run,'space',8,600);settle() for q in range(1,50): st=order[q];code,name,capital=rows[st];s=cap(f'typed-q{q+1:02d}-{code}') assert s.text(8,19,45)==f'Question {q+1} of 50 Score {score}',(q,s.text(8,19,45),score) assert s.text(8,145,50)==name+': what is its capital?' spelling=False;right=True if q==1 or code=='CO': # Adjacent transpositions, followed by a real Backspace correction. answer='Chalreston' if q==1 else 'Dnever';type_text(answer);press('enter',8,600) s=cap('typed-near-'+code) assert s.text(8,158,48)=='That looks close. Check your spelling' assert s.text(8,182,35)=='No point awarded yet.' assert s.text(8,19,45)==f'Question {q+1} of 50 Score {score}' c['key'](run,'r',8,600);settle();clear_input(len(answer));type_text(capital);spelling=True elif q==2: answer='Austin';type_text(answer);right=False elif q==3: answer='X'*39;type_text('X'*45);settle();s=cap('typed-long-input') assert s.text(8,158,45)=='X'*39+'_',s.text(8,158,45) assert s.text(8,171,48)=='39 characters maximum. Backspace to edit.' right=False elif q==4: answer='Tallhassee';type_text(answer);press('enter',8,600);s=cap('typed-near-reveal') assert s.text(8,158,48)=='That looks close. Check your spelling' c['key'](run,'v',8,600);right=False elif q==5: answer=' bAtOn rOuGe ';type_text(answer) else: answer=capital.lower() if q%3==0 else capital.upper() if q%3==1 else ''.join(ch.lower() if i%2 else ch.upper() for i,ch in enumerate(capital)) type_text(answer) if q!=4:press('enter',8,600) s=cap(f'typed-feedback-{q+1:02d}') assert s.text(8,158,30)==('CORRECT!' if right else 'NOT THIS TIME.'),(q,s.text(8,158,30),answer) assert s.text(8,170,50)==f'The capital is {capital}.' if right:score+=1 if spelling:corrections+=1 report.append(dict(question=q+1,code=code,answer=answer,correct=right,correction=spelling)) c['key'](run,'space',8,600) if q!=49:settle() print(q+1,code,score,corrections,flush=True) s=cap('typed-results');assert s.text(8,48,48)==f'{score} / 50 correct {score*2}%',s.text(8,48,48) assert s.text(8,70,48)==f'{corrections} correct after spelling correction' assert score==47 and corrections==2 c['key'](run,'r',8,600);s=cap('typed-review') misses=[x for x in report if not x['correct']] for i,m in enumerate(misses): name,capital=next((r[1],r[2]) for r in rows if r[0]==m['code']) assert s.text(8,39+i*43,48)==name assert s.text(8,50+i*43,48)=='You: '+m['answer'] assert s.text(8,61+i*43,48)=='Capital: '+capital c['key'](run,'b',8,600);c['key'](run,'q',8,300);final=c['capture'](run,'typed-exit') assert final.get('screen_text_unavailable') is None json.dump(dict(score=score,corrections=corrections,questions=report,run_id=run,exit=final),open(str(EVIDENCE)+'/typed-report.json','w'),indent=2) print('PASS full typed quiz: casing, spaces, alias, transpositions, corrected spelling, wrong capital, long input, reveal, score/review/exit.',flush=True);c['v'].close()