- import_nota / import_adauga_factura: flux unificat note contabile + articole, sincronizare automata, total factura automat, TVA impartit pe conturile creditoare ale notelor (rundele 1-31) - runda 32: totalurile import_nota pe _label/_textbox din _baza.vcx (Arial 10), fara containerele clb_tx_simplu si fara bifa "Recalculeaza TVA" (recalcul mereu automat); "Sincronizeaza facturile secundare" mutata sub butoane - utile/Teste: harness UI VFP headless (vfp_ui_harness.ps1, ui_harness.prg, precompilare, mock-uri) in radacina; suitele e2e pentru achizitia din import grupate in utile/Teste/achizitie_import/ - docs: flux editare text vcx/scx, testare-ui-vfp si testare-vfp-mcp (mutate din ROAGEST, general valabile), inventar comun, orchestrare subagenti, conventie GO recno, depanare testare VFP Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019DGibQKa5uK4P2TNSFFQNh
11 KiB
Testare UI VFP headless cu harness + screenshots
Harness reutilizabil pentru teste care afiseaza un formular REAL (fara Oracle/UI complet),
executa actiuni si valideaza vizual prin capturi de ecran. Valabil pentru toate proiectele
VFP din suita ROA; exemplele sunt pe ROAGEST. Alternativa la
testare-vfp-mcp.md (care ruleaza programul principal intreg + windows-mcp): aici instantiem o
singura clasa cu dependinte dummy, deci pornire rapida si izolata.
Fisiere in COMUN\utile\Teste\: vfp_ui_harness.ps1 (orchestrator generic, parametrizat),
_precompile.ps1 (precompilare izolata), ui_harness.prg (proceduri VFP de handshake),
mock_amessagebox.prg (mock dialog) — infrastructura sta in radacina Teste\; suitele de
teste stau grupate pe subiect in subfoldere (ex. achizitie_import\). Exemplu complet:
achizitie_import\test_import_nota_ui.prg + .ps1 (wrapper).
REGULA (Marius, 17/07/2026): testele pe aplicatii frontend exerseaza fluxul REAL al utilizatorului — formularul/butonul/metoda pe care o apeleaza aplicatia (ex. "Adauga repere", do_adauga_factura prin dialogul real), NU introducerea directa a randurilor in cursoare/tabele (ex. Append in rul_temp/introdc). Popularea directa sare peste validari/filtre/efecte laterale si da PASS fals (caz real: articol cont 212 "adaugat" direct in rul_temp trecea testul, dar prin fluxul real nu se adauga deloc). Mock-urile raman permise doar pe INFRASTRUCTURA (Oracle, dialoguri modale), nu pe pasii fluxului testat.
Test UI nou in 5 pasi
.prgde test:PUBLIC gcUILog, gcSyncDir;SET TALK OFF(obligatoriu, vezi cap. c);SET PATH/CLASSLIB/PROCEDUREca in aplicatie; incarca ULTIMELESET PROCEDURE TO ...\ui_harness.prg ADDITIVEsi (daca ai dialoguri)...\mock_amessagebox.prg ADDITIVE.- Creeaza dummy-urile minime (
goExecutor,goApp, etc.) + cursoarele pe care le cere Init-ul clasei (RecordSource-urile grilelor). - Instantiaza clasa,
WindowType=0,Show(),WindowState=2. - Pe fiecare pas: fa actiunea, verifica efectul in date (loghezi PASS/FAIL cu un helper
local), apoi
DO HarnessStep WITH <n>, '<mesaj>'(scrie ready_n, asteapta cont_n de la orchestrator sau auto-continua dupa ~30s). La finalDO HarnessDone WITH 'done'. - Ruleaza:
powershell -File vfp_ui_harness.ps1 -TestPrg <test.prg> -Steps @('pas0',...). PNG-urile ies inscreenshots\step_<n>_<eticheta>.png; log in<test>_log.txt.
Capcane / deblocari (toate confirmate in sesiune)
a. Lansarea orchestratorului: prin tool cu timeout implicit (~120s) e omorat inainte de
finalul pasilor. Ruleaza-l in background (run_in_background) + asteapta done.txt, sau
timeout mare (300-600s). NU porni VFP separat de orchestrator - si-l lanseaza singur.
b. Asteptarea ready_0 prea scurta omoara VFP exact inainte sa scrie semaforul. Cu .fxp
cald START apare in ~2s, la rece mult mai mult (zeci de classlib-uri). Foloseste
-ReadyTimeoutSec generos (implicit 180s). Retry NUMAI la "start ratat", nu la "start lent".
c. SET SAFETY OFF + SET TALK OFF ca PRIMUL lucru, inainte de primul STRTOFILE.
Cu SAFETY ON, prima scriere a log-ului (daca fisierul exista deja) scoate dialogul modal
"...already exists, overwrite it?" care BLOCHEAZA rularea headless (VFP pare "instanta moarta"
- proces viu, blocat pe dialog). Cu TALK ON,
SUM/CALCULATE(ex. dinrecalculeaza) isi echo-eaza rezultatele pe desktop, ca text parazit peste formular in screenshots. Harness-ul areHarnessInit/SET SAFETY OFFdefensiv in procedurile de scriere, dar testul trebuie sa facaSET SAFETY OFForicum INAINTE de scrierea lui proprie de "START". d.vfp9 -A test.prgdeschide INTERMITENT editorul in loc sa ruleze (log ramane gol). Lanseaza.fxp-ul PRECOMPILAT (vfp9 -A test.fxp) - se executa mereu. e. Precompilarea (COMPILE+QUIT) atarna dupa COMPILE; rulata in ACEEASI sesiune powershell care lanseaza apoi testul, otraveste lansarile (nu executa). Ruleaza precompilarea intr-un proces copil izolat (Start-Process -Wait _precompile.ps1). f. Detectia "a pornit" pe mtime-ul log-ului > momentul lansarii, nu pe "log ne-gol": un log stale tinut deschis de unvfp9zombi pacaleste verificarea. Omoara zombii inainte de fiecare lansare. g. Formulare modale (WindowType) -Show()nu returneaza si blocheaza testul. SeteazagoObj.WindowType = 0(modeless) pentru afisare non-modala automatizata. h. Dummy minimali:goExecutor(oExecute/oExecuta creeaza cursorul cerut),goApp(ReadInipt. font din settings.ini),poAct/oact(curs valutar). Init-ul recreeaza cursoarele goale din Oracle daca nu-s pre-populate - pre-populeaza-le INAINTE de instantiere. i.LOCATE FOR camp == 'literal'pe campC(n)padded cu spatii nu gaseste nimic (==e exact) - folosesteALLTRIM(camp) == 'literal'. j. Messagebox-uri de confirmare blocheaza testul headless. Mock:FUNCTION amessageboxcare returneaza direct valoarea butonului (6=Da). Incarca-l cuSET PROCEDURE ... ADDITIVEPRIMUL (inainteaoproceduri_comune.prg): la nume duplicat de procedura, VFP foloseste fisierul cautat PRIMUL in lista (verificat empiric: incarcat ultimul PIERDE, primul CASTIGA). Mock-ul umbresteamessageboxsi din apelurile din metodele de clasa (ex.do_sterge_articol). k. Cursor bound la o grila, recreat de Init -> grila pierde coloanele (eroare 1925). Ex.:import_nota.Initrecreeazarul_tempdin Oracle daca e gol la instantiere; GridArt ramane fara coloanele denumite. Fix: pune 1 rand placeholder in cursor INAINTE de instantiere (ca Init sa nu-l recreeze), apoiZAPdupaShow()(NUDELETE ALL: acesta lasa un rand fizic sters si pointerul la EOF, iar cod gendo_calculeaza_diferentefaceGO recno_EOF-> eroare 5). l. CREATEOBJECT/ADDPROPERTY se comporta gresit apelate DINTR-O PROCEDURA in acest runtime headless (obiect returnat ca string de 30 spatii;ADDPROPERTYda eroare 11). Din programul PRINCIPAL merg. Construieste obiectele (ex.toDlgpt.do_adauga_factura) INLINE in main, nu intr-un helper. (Cauza exacta neclara; reproductibil.) m.gl406=.T.(SAFT) activeaza cod care are nevoie de mock-uri suplimentare, altfel crapa sau taxcode ramane mereu NULL:GetTaxCode/GetTaxCodeIdPart(oproceduri_comune.prg) apeleazaupdate_jtva_coloane()care ruleazagoExecutor.oExecute("select * from vjtva_coloane ...", <cursor>) - mock-ul de
oExecutetrebuie sa intercepteze'VJTVA_COLOANE' $ UPPER(tcSql)si sa returneze un cursor cu structura reala a view-ului (id_jtva_coloana, denumire, jc, jv, coloana_jc, coloana_jv, cota_tva, id_tva, taxcode, taxcode_neexigibil, taxcode_n50, taxcode_n50_tvai, taxcode_n100, taxcode_regularizare, taxcode_tvai, afisat, ordine) - codul apelant face singurINDEX ON id_jtva_coloana TAG id_jtvadupa, nu trebuie indexat in mock. Candid_partc/id_partdpe rand nu e gol,GetTaxCodeIdPartcauta si codul fiscal al partenerului pringoExecutor.oSelect2Value(sql, @toValue)(GetCodFiscalPartenerById) - metoda lipseste din dummyexecutor-ul de baza, trebuie adaugata (returneaza cod fiscal gol ->VERIFICA_RTVAIsare peste apelul Oracle real de validare CIF). n. Explicatia TVA de pe randul T ("in tandem" cu baza) NU se scrie prin cod separat - e pur si simplu o A DOUA intrare injtva_coloane2/vjtva_coloane(coloanaid_tvade pe randul bazei punctand spreid_jtva_coloanaal randului TVA);aplica_explicatie_tva(ointroduceri.prg ~9807) citeste acea a doua intrare si o scrie pe rand T. Populeaza mock-ul cu ambele randuri (baza + TVA), nu doar cu unul si nu concatena manual "TVA " + denumirea bazei in test. o. FARA FURT DE FOCUS (17/07/2026):Graphics.CopyFromScreen(captura ecranului intreg) fura focus si se corupe daca utilizatorul lucreaza in paralel - NU se mai foloseste. Capturile se fac cuPrintWindow(user32, P/Invoke) pe HANDLE-ul ferestrei vfp9 (Process.MainWindowHandle), flag2=PW_RENDERFULLCONTENT(fallback flag0daca iese goala); merge cu fereastra acoperita/in fundal, dar NU minimizata. Fereastra e mutata OFF-SCREEN (SetWindowPoscuHWND_BOTTOM, x=-4000,SWP_NOACTIVATE) imediat ce apare handle-ul si RE-IMPINSA la fiecare pas (VFP isi reactiveaza singur fereastra laShow()/dialoguri modale - o singura impingere la lansare nu e suficienta). Toate consolele powershell copil (precompilare) sunt-WindowStyle Hidden(sigur pentru consola; NU si pentru GUI VFP, care trebuie doar off-screen, nu ascuns - PrintWindow pe fereastra minimizata/ascunsa poate reda gol). Implementat invfp_ui_harness.ps1(Take-Screenshot, Push-Offscreen, Wait-MainWindowHandle) si_precompile.ps1(acelasi Push-Offscreen pe fereastra tranzitorie de compilare). p. CursorulintrodcFARA indexulord_docschimba documentul "principal" la sincronizari repetate (sincronizeaza() apelat de mai multe ori, ex. la adaugarea a 2+ articole succesiv prin fluxul real): identificarea documentului principal (Go Top+Locate For Inlist(tip_rand,'B','S') And !Empty(doc_key)) e stabila in productie doar pentru caachizitie_import(COMUN\programe\ointroduceri.prg:1616-1618) creeaza, INAINTE deCREATEOBJECT('import_nota'), indexulord_doc(Str(Nvl(nr_doc,0),8) + Icase(tip_rand='B','1', tip_rand='S','2', tip_rand='T','3', tip_rand='D','4', '5') + Padr(Nvl(scd,''),10)) +SET ORDER TO ord_doc, care ordoneaza dupanr_doc(S-urile principalei mostenesc nr_doc-ul ei) - documentul cunr_docminim ramane mereu acelasi. Un test care creeazaintrodcfara acest index ramane pe ordinea FIZICA (recno): dupa ce principala e sparta (randul B sters, randurile S adaugate la coada), urmatorul apel de sincronizeaza() gaseste gresit ca "principal" documentul cu cel mai mic recno ramas si il sparge si pe el - cascada se repeta la fiecare apel ulterior. Fix: dupa populareaintrodc, INAINTE deCREATEOBJECT('import_nota', ...), adauga exact acelasiINDEX ON ... TAG ord_doc
SET ORDER TO ord_docca inachizitie_import. q. Suitele de teste NU se ruleaza in paralel:_precompile.ps1faceGet-Process vfp9 | Stop-Process -Force(omoara TOATE instantele vfp9 de pe masina), deci un test lansat in paralel ucide testul aflat in curs si corupe rezultatele. Ruleaza suitele strict secvential. r. Fara SELECT-SQL pe cursorul legat de grid: SELECT ... FROM introdc INTO CURSOR intr-un test, cat timp introdc e RecordSource-ul unui grid viu, poate omori procesul vfp9 silentios (fara eroare catchabila, fara semafor). Foloseste xBase nativ (COUNT FOR ... TO, CALCULATE, LOCATE) care nu comuta zona de lucru a grid-ului.