Protractorul este un cadru de testare end-to-end popular, care vă permite să vă testați aplicația Angular într-un browser real care simulează interacțiunile browserului, așa cum ar interacționa un utilizator real cu acesta. Testele end-to-end sunt concepute astfel încât să se asigure că aplicația se comportă conform așteptărilor din perspectiva utilizatorului. Mai mult, testele nu sunt preocupate de implementarea actuală a codului.
Protractorul rulează pe partea de sus a popularului Selenium WebDriver, un API pentru automatizarea și testarea browserului. În plus față de caracteristicile oferite de Selenium WebDriver, Protractor oferă locatori și metode pentru captarea componentelor UI ale aplicației Angular.
În acest tutorial, veți afla despre:
Nu sună interesant? Cu toate acestea, mai întâi, mai întâi.
Dacă ați folosit Angular-CLI, s-ar putea să știți că, în mod implicit, acesta este livrat împreună cu două cadre pentru testare. Sunt:
Diferența aparentă dintre cele două este că prima este folosită pentru a testa logica componentelor și serviciilor, în timp ce aceasta din urmă este utilizată pentru a se asigura că funcționalitatea la nivel înalt (care implică elementele UI) a aplicației funcționează așa cum se aștepta.
Dacă sunteți nou la testarea în Angular, aș recomanda citirea Componentelor de testare din seria Angular Using Jasmine pentru a obține o idee mai bună despre locul unde să desenați linia.
În primul caz, puteți utiliza puterea utilitarelor de testare unghiulară și Jasmine pentru a scrie nu doar testele unității pentru componente și servicii, ci și testele UI de bază. Cu toate acestea, dacă trebuie să testați funcționalitatea front-end a aplicației de la început până la sfârșit, Protractorul este calea de urmat. API-ul protocorului, combinat cu modele de design, cum ar fi obiectele de pagină, face mai ușor să scrieți teste care sunt mai ușor de citit. Iată un exemplu pentru a face rostul lucrurilor.
/ * 1. Ar trebui să aibă un buton de creare a lipirii 2. Faceți clic pe butonul trebuie să aducă o fereastră modală * / it ('ar trebui să aibă un buton Creare lipire și fereastră modală', () => expect (addPastePage.isCreateButtonPresent () ) .toBeTruthy ( "butonul ar trebui să existe"), se așteaptă (addPastePage.isCreatePasteModalPresent ()) toBeFalsy ( "fereastra modal nu ar trebui să existe, nu încă!");. addPastePage.clickCreateButton (), se așteaptă (addPastePage.isCreatePasteModalPresent ( )) toBeTruthy ("Fereastra modală ar trebui să apară acum"););
Configurarea Protractorului este ușor dacă utilizați Angular-CLI pentru a vă genera proiectul. Structura directorului creată de ng nou
este după cum urmează.
. ├── e2e │ ├── app.e2e-spec.ts │ ├── app.po.ts │ └── tsconfig.e2e.json ├── karma.conf.js ├── package.json ├── pachet-lock.json ├── protractor.conf.js ├── README.md ├── src │ ├── app │ ├── active │ ├── medii │ ├── favicon.ico │ ├── indice .html │ ├── main.ts │ ├── polyfills.ts │ ├── styles.css │ ├── test.ts │ ├── tsconfig.app.json │ ├── tsconfig.spec.json │ └ ──────────────────────────────────────────────────────────────────────────
Șablonul de proiect implicit creat de către Protractor depinde de două fișiere pentru a rula testele: fișierele spec. Care se află în interiorul e2e și fișierul de configurare (protractor.conf.js). Să vedem cum configurabil protractor.conf.js este:
/ * Cale: protractor.conf.ts * / // Fișier de configurare a protractorului, vezi link-ul pentru mai multe informații // https://github.com/angular/protractor/blob/master/lib/config.ts const SpecReporter = require ( 'iasomie-spec-reporter'); exports.config = allScriptsTimeout: 11000, specs: capabilități [ './e2e/**/*.e2e-spec.ts']: 'browserName': 'crom', DirectConnect: true, baseUrl:' http : // localhost: 4200 /“, cadru: 'iasomie.', jasmineNodeOpts: showColors: true, defaultTimeoutInterval: 30000, imprimare: function () , onPrepare () necesită ( 'ts-nod') se înregistrează ( proiect: 'e2e / tsconfig.e2e.json'); ()) addReporter (noul SpecReporter (spec: displayStacktrace: true)); ;
Dacă sunteți în regulă cu rularea testului pe browserul web Chrome, puteți lăsa acest lucru ca atare și sări peste restul acestei secțiuni.
directConnect: true
permite ca Protractorul să se conecteze direct la driverele browserului. Cu toate acestea, în momentul în care scrieți acest tutorial, Chrome este singurul browser acceptat. Dacă aveți nevoie de asistență pentru mai multe browsere sau dacă rulați un alt browser decât Chrome, va trebui să configurați serverul standalone Selenium. Pașii sunt după cum urmează.
Instalați protractorul global folosind npm:
npm install -g protractor
Aceasta instalează instrumentul pentru linia de comandă pentru webdriver-manager împreună cu cel al proiectorului. Actualizați managerul webdriver pentru a utiliza ultimele binare și apoi porniți serverul standalone Selenium.
webdriver-manager actualizare webdriver-manager începe
În cele din urmă, setați directConnect: false
și adăugați seleniumAddress
proprietate după cum urmează:
: http: // localhost: 4200 / ', seleniumAdresa:' http: // localhost: 4444 / wd / hub ', cadru:' iasomie ' , jasmineNodeOpts: showColori: true, implicitTimeoutInterval: 30000, print: function () ,
Fișierul de configurare de pe GitHub oferă mai multe informații despre opțiunile de configurare disponibile pe Protractor. Voi folosi opțiunile implicite pentru acest tutorial.
ng e2e
este singura comandă de care aveți nevoie pentru a începe să executați testele dacă folosiți unghiular CLI. Dacă testele par a fi lent, este pentru că Angular trebuie să compileze codul de fiecare dată când executați ng e2e
. Dacă doriți să accelerați puțin, iată ce ar trebui să faceți. Servește aplicația utilizând ng servi
.
Apoi aprindeți o nouă filă a consolei și rulați:
ng e2e -s false
Testele ar trebui să se încarce mai repede acum.
Vom scrie teste E2E pentru o aplicație de bază Pastebin. Clonați proiectul de la repo GitHub.
Atât versiunile, cât și versiunea finală (cea cu teste) sunt disponibile pe sucursale separate. Clonați ramura de pornire pentru moment. Opțional, deserviți proiectul și treceți prin cod pentru a vă familiariza cu aplicația la îndemână.
Să descriem pe scurt aplicația noastră Pastebin. Aplicația va încărca inițial o listă de paste (extrase dintr-un server fals) într-un tabel. Fiecare rând din tabel va avea a Afișați Paste care, atunci când se face clic, deschide o fereastră modală pentru bootstrap. Fereastra modală afișează datele de lipire cu opțiunile de editare și ștergere a pastei. Spre sfârșitul mesei, există a Creați Paste care poate fi utilizat pentru a adăuga paste noi.
Aplicația eșantion.Restul tutorialului este dedicat scrierii testelor de protractor în Angular.
Fișierul spec, terminând cu .E2E-spec.ts, va găzdui testele reale pentru aplicația noastră. Vom plasa toate specificațiile de testare în interiorul e2e deoarece acesta este locul pe care l-am configurat pe Protractor să caute specificațiile.
Există două lucruri pe care trebuie să le luați în considerare în timp ce scrieți testele Protractor:
Creați un fișier nou numit test.e2e-spec.ts cu următorul cod pentru a începe.
/ * Cale: e2e / test.e2e-spec.ts * / import browser, by, element de la "protractor"; descrie ('Protractor Demo', () => beforeEach (() => // Codul aici va fi executat înainte ca fiecare bloc să fie numit //browser.get('/ ';); ar trebui să afișeze numele aplicației ', () => / * Așteptările acceptă parametrii care vor fi potriviți cu valoarea reală folosind funcțiile matcher ale lui Jasmine, de exemplu, toEqual (), toContain (), toBe (), toBeTruthy . * / așteptați ("Aplicație Pastebin") toEqual ("Pastebin Application");)) ("ar trebui să faceți clic pe butonul creați Pastebin", () => // spec merge aici);
Aceasta descrie modul în care testele noastre vor fi organizate în fișierul spec folosind sintaxa lui Jasmine. descrie()
, beforeEach ()
și aceasta()
sunt funcții globale de iasomie.
Jasmine are o sintaxă grozavă pentru scrierea testelor și funcționează la fel de bine cu Protractorul. Dacă sunteți nou la Jasmine, aș recomanda întâi să treceți prin pagina GitHub a Jasmine.
descrie bloc este folosit pentru a împărți testele în suite de testare logică. Fiecare descrie bloc (sau test suite) pot avea mai multe aceasta blocuri (sau specificații de testare). Testele reale sunt definite în interiorul specificațiilor de testare.
"De ce să-mi structrez testele în acest fel?" puteți întreba. O suită de testare poate fi utilizată pentru a descrie logic o anumită caracteristică a aplicației dvs. De exemplu, toate specificațiile referitoare la componenta Pastebin ar trebui, în mod ideal, să fie acoperite în interiorul unui bloc de descriere numit Pastebin Page. Deși acest lucru poate duce la teste care sunt redundante, testele dvs. vor fi mai ușor de citit și pot fi întreținute.
Un bloc descrie poate avea a beforeEach ()
care va fi executată o singură dată, înainte de fiecare spec în acel bloc. Deci, dacă aveți nevoie de browser pentru a naviga la o adresă URL înainte de fiecare test, plasând codul pentru navigare în interior beforeEach ()
este ceea ce trebuie să faceți.
Așteptați declarațiile, care acceptă o valoare, sunt înlănțuite cu unele funcții de matcher. Valorile reale și cele așteptate sunt comparate și se întoarce un boolean care determină dacă testul nu reușește sau nu.
Acum, să punem niște carne pe ea.
/ * Cale: e2e / test.e2e-spec.ts * / import browser, by, element de la "protractor"; descrie ('Protractor Demo', () => beforeEach (() => browser.get ('/')); elementul ('. pastebin')) getText ()) toContain ('Pastebin Application'); id ('source-modal')) isPresent ()) toBeFalsy ("fereastra modală nu trebuie să apară acum"); element (by.buttonText (by_id ('source-modal')) isPresent ()) toBeTruthy ('Fereastra modală ar trebui să apară acum');););
browser.get ( '/')
și Element (by.css (“. PasteBin')). getText ()
fac parte din API-ul Protractor. Să ne murdărim mâinile și să mergem direct în ceea ce le oferă Protractorului.
Componentele proeminente exportate de API-ul Protractor sunt enumerate mai jos.
browser-ul ()
: Ar trebui să sunați browser-ul ()
pentru toate operațiile la nivel de browser, cum ar fi navigarea, depanarea, etc. element()
: Se utilizează pentru a căuta un element în DOM pe baza unei condiții de căutare sau a unui lanț de condiții. Acesta returnează un obiect ElementFinder și puteți efectua acțiuni cum ar fi getText ()
sau clic()
pe ei.element.all ()
: Acesta este folosit pentru a căuta o serie de elemente care se potrivesc cu un lanț de condiții. Returnează un obiect ElementArrayFinder. Toate acțiunile care pot fi efectuate pe elementul ElementFinder pot fi efectuate și pe ElementArrayFinder.Deoarece vom folosi locatori foarte des, iată câteva dintre locatorii folosiți în mod obișnuit.
by.css ( 'selector name')
: Acesta este de departe cel mai frecvent utilizat locator pentru găsirea unui element bazat pe numele selectorului CSS.by.name ( 'name-value')
: Localizează un element cu o valoare de potrivire pentru atributul name.by.buttonText (butonul „valoare“)
: Afișează un element de buton sau o serie de elemente de buton bazate pe textul interior. Notă: Locatorii by.model, by.binding și by.repeater nu funcționează cu aplicațiile Angular 2+ în momentul elaborării acestui tutorial. Folosește CSS-în loc.
Să scriem mai multe teste pentru aplicația noastră Pastebin.
(') ar trebui să accepte și să salveze valori de intrare, () => element (by.buttonText (' create Paste ')) click (); // trimite valori de intrare la formular folosind elementul sendKeys (byname (' )) elementul (by'name ('language')) element (by 'csContentingText (' opțiune ',' Ruby ')) .name ('paste')) sendKeys ("pune" Hello world ";); element (by.buttonText ('Salveaza')) element.all (by.tagName ('tr')) ultim (); așteptați (lastRow.getText ()) toContain ("Bună ziua în Ruby");
Codul de mai sus funcționează, iar tu poți să-l verifici singur. Cu toate acestea, nu te-ai simți mai confortabil în ceea ce privește scrierea testelor fără vocabularul specific în Protractor în fișierul de spec? Iată despre ce vorbesc:
("ar trebui să aibă un buton Creare lipire și fereastră modală", () => așteptați (addPastePage.isCreateButtonPresent ()) toBeTruthy ("butonul ar trebui să existe"); Fereastra modală nu ar trebui să apară, nu încă! "); AddPastePage.clickCreateButton (); așteptați (addPastePage.isCreatePasteModalPresent ()) toBeTruthy (" Fereastra modală ar trebui să apară acum ");); ("ar trebui să accepte și să salveze valorile de intrare", () => addPastePage.clickCreateButton (); // câmpul de introducere ar trebui să fie gol inițial const emptyInputValues = ["", "", ""]; expect (addPastePage.getInputPasteValues )) toEqual (emptyInputValues); // Acum actualizați câmpurile de intrare addPastePage.addNewPaste (); addPastePage.clickSaveButton (); așteptați (addPastePage.isCreatePasteModalPresent ()) .BeFalsy ("Fereastra modală ar trebui să dispară"); mainPage.getLastRowData ()). toContain ("Bună ziua în Ruby"););
Specificațiile par mai clare fără bagajul extractorului. Cum am făcut asta? Permiteți-mi să vă prezint Obiectele de pagină.
Obiectul paginii este un model de design care este popular în cercurile de automatizare a testării. Un obiect de pagină modelează o pagină sau o parte dintr-o aplicație utilizând o clasă orientată pe obiecte. Toate obiectele (care sunt relevante pentru testele noastre) precum textul, titlurile, tabelele, butoanele și legăturile pot fi captate într-un obiect de pagină. Apoi putem importa aceste obiecte de pagină în fișierul spec și le putem invoca metodele. Acest lucru reduce duplicarea codului și facilitează menținerea codului.
Creați un director numit Pagina obiecte și adăugați un fișier nou în el numit pastebin.po.ts. Toate obiectele interesate de componenta Pastebin vor fi captate aici. După cum am menționat anterior, am împărțit întreaga aplicație în trei componente diferite și fiecare componentă va avea un obiect de pagină dedicat acesteia. Schema de numire .vase este pur convențional, iar tu poți să-i numești orice vrei.
Iată un plan al proiectului pagina pe care o testăm.
Iată codul.
/ * Calea e2e / page-objects / pastebin.po.ts * / import browser, by, element, promit, ElementFinder, ElementArrayFinder din "protractor"; clasa de export Pastebin extinde baza navigateToHome (): promise.Promiseretur browser.get ('/'); getPastebin (): ElementFinder element return (by.css ('. pastebin')); / * Pastebin Încărcare * / getPastebinHeading (): promise.Promise return this.getPastebin () elementul (by.css ("h2")). getText (); / * Date tabelă * / getTable (): ElementFinder returnați acest element.getTable (). (By.css ('table')); getTableHeader (): promise.Promise return this.getPastebin () toate (by.tagName ('tr')) get (0) .getText (); getTableRow (): ElementArrayFinder returnați acest.getPastebin (). toate (by.tagName ('tr')); getFirstRowData (): promise.Promise returnați acest.getTableRow (). get (1) .getText (); getLastRowData (): promise.Promise return this.getTableRow () ultima (). getText (); / * tag app-add-paste * / getAddPasteTag (): ElementFinder returnați acest element.getPastebin () element (by.tagName ('app-add-paste')); isAddPasteTagPresent (): promise.Promise returnați acest.getAddPasteTag (). isPresent ();
Să trecem peste ceea ce am învățat până acum. API-ul protagonistului returnează obiecte și am întâlnit până acum trei tipuri de obiecte. Sunt:
Pe scurt, element()
returnează un ElementFinder și elementul (). toate
returnează un ElementArrayFinder. Puteți utiliza locatorii (by.css
, by.tagName
, etc) pentru a găsi locația elementului în DOM și a trece la acesta element()
sau element.all ()
.
ElementFinder și ElementArrayFinder pot fi apoi înlănțuite cu acțiuni, cum ar fi este prezent()
, getText ()
, clic()
, etc. Aceste metode returneaza o promisiune care se rezolva atunci cand acea actiune a fost finalizata.
Motivul pentru care nu avem un lanț de atunci()
în testul nostru este că Protractorul are grijă de el pe plan intern. Testele par să fie sincrone, chiar dacă nu sunt; prin urmare, rezultatul final este o experiență de codare liniară. Cu toate acestea, vă recomandăm să utilizați sintaxa async / await pentru a vă asigura că codul este dovada viitoare.
Aveți posibilitatea să lanțați mai multe ElementFinder
obiecte, după cum se arată mai jos. Acest lucru este util în special în cazul în care DOM are mai mulți selectori cu același nume și trebuie să capturăm cea potrivită.
getTable (): ElementFinder retur acest element.getPastebin () element (by.css ('table'));
Acum, când avem pregătit codul pentru obiectul de pagină, să îl importăm în spec. Iată codul pentru testele noastre inițiale.
/ * Cale: e2e / mainPage.e2e-spec.ts * / import Pastebin din "./page-objects/pastebin.po"; import browser, protractor de la "protractor"; / * Scenarii pentru a fi testate 1. Pastebin Page ar trebui să afișeze o rubrică cu text Pastebin Application 2. Ar trebui să aibă un antet de tabel 3. Tabelul ar trebui să aibă rânduri 4. App-add-paste tag-ul ar trebui să existe * / describe ('Pastebin Page '(): const mainpage: Pastebin = new Pastebin (); beforeEach (() => mainPage.navigateToHome ();); () ()) ()) () () () () () ") ())))) (" ar trebui să aibă cel puțin un rând ", () => așteptați (mainPage.getFirstRowData ()) toContain (" Hello world " -paste tag ', () => așteptați (mainPage.isAddPasteTagPresent ()) toBeTruthy ();));
Testele ar trebui să fie organizate astfel încât structura generală să fie semnificativă și simplă. Iată câteva recomandări pe care trebuie să le țineți minte în timp ce organizați testele E2E.
navigateToHome ()
), creați un obiect de bază. Alte modele de pagini pot moșteni de la modelul paginii de bază. Urmând regulile de mai sus, iată ce ar trebui să arate ierarhia de obiecte de pagină și organizarea fișierului.
Am acoperit deja pastebin.po.ts și mainPage.e2e-spec.ts. Iată restul fișierelor.
/ * cale: e2e / page-objects / base.po.ts * / import browser, by, element, promit, ElementFinder, ElementArrayFinder de la 'protractor'; clasa de export Baza / * Metode de navigare * / navigateToHome (): promise.Promiseretur browser.get ('/'); navigateToAbout (): promise.Promise retur browser.get ('/ about'); navigareToContact (): promise.Promise retur browser.get ('/ contact'); / * Mock date pentru crearea unei noi paste și editarea pastei existente * / getMockPaste (): orice lasă paste: any = title: "Ceva aici", limbă: "Ruby", pastă: "Test" pastă de întoarcere; getEditedMockPaste (): orice lasa paste: any = title: "Paste 2", limba: "JavaScript", pasta: "Test2" pasta de returnare; / * Metode partajate de addPaste și viewPaste * / getInputTitle (): ElementFinder return element (byname ("title")); getInputLanguage (): ElementFinder return element (prin nume ("limba")); getInputPaste (): ElementFinder return element (byname ("pastă"));
/ * Cale: e2e / page-objects / add-paste.po.ts * / import browser, element, promit, ElementFinder, ElementArrayFinder de la "protractor"; import Base din "./base.po"; clasa de export AddPaste extinde baza getAddPaste (): ElementFinder return element (by.tagName ('app-add-paste')); / * Crează butonul Paste * / getCreateButton (): ElementFinder returnați acest element.getAddPaste () element (by.buttonText ("create Paste")); isCreateButtonPresent (): promise.Promisereturn this.getCreateButton () estePresent (); clickCreateButton (): promise.Promise return this.getCreateButton (). faceți clic pe (); / * Creare Paste modal * / getCreatePasteModal (): ElementFinder retur acest element.getAddPaste () element (by.id ("source-modal")); isCreatePasteModalPresent (): promise.Promise return this.getCreatePasteModal () estePresent (); / * Butonul Salvare * / getSaveButton (): ElementFinder returnați acest element.getAddPaste () element (by.buttonText ("Salvează")); clickSaveButton (): promise.Promise return this.getSaveButton (). faceți clic pe (); / * Butonul Închidere * / getCloseButton (): ElementFinder returnați acest element.getAddPaste () element (by.buttonText ("Close")); clicCloseButton (): promise.Promise return this.getCloseButton (). faceți clic pe (); / * Get Input Paste valori din fereastra Modal * / getInputPasteValues (): Promise introduceți inputTitle, inputLanguage, inputPaste; // Returnați valorile de intrare după ce promisiunea a fost rezolvată // Rețineți că this.getInputTitle (). GetText nu funcționează // Utilizați getAttribute ('value') în loc să reveniți la Promise.all ([this.getInputTitle (). GetAttribute "value"), this.getInputLanguage () getAttribute ("value"), this.getInputPaste (). getAttribute ("value")]) .then ((valori) => return values; / * Adăugați un nou Paste * / addNewPaste (): orice let newPaste: any = this.getMockPaste (); // Trimiteți valorile de intrare this.getInputTitle () sendKeys (newPaste.title); acest.getInputLanguage () .element (by.cssContainingText ('opțiune', newPaste.language)). clic (); . This.getInputPaste () sendKeys (newPaste.paste); // Conversia obiectului paste intr-un array retur Object.keys (newPaste) .map (key => newPaste [key]);
/ * Cale: e2e / addNewPaste.e2e-spec.ts * / import Pastebin din "./page-objects/pastebin.po"; importați AddPaste de la "./page-objects/add-paste.po"; import browser, protractor de la "protractor"; / * Scenariile care vor fi testate 1. Pagina AddPaste trebuie să aibă un buton atunci când se face clic pe ea ar trebui să prezinte o fereastră modală 2. Fereastra modală ar trebui să accepte noile valori și să le salveze 4. Datele salvate ar trebui să apară în MainPage 3. Butonul Close de lucru * / descrie ('Add-New-Paste page', () => constPastePage: AddPaste = newPastePage () );); acesta ("ar trebui să aibă un buton Creare lipire și fereastră modală", () => așteptați (addPastePage.isCreateButtonPresent ()) toBeTruthy (" ) toBeFalsy ("Fereastra modală nu trebuie să apară, nu încă!"); addPastePage.clickCreateButton (); așteptați (addPastePage.isCreatePasteModalPresent ()) .BeTruthy ("Fereastra modală ar trebui să apară acum"); ("ar trebui să accepte și să salveze valorile de intrare", () => addPastePage.clickCreateButton (); const emptyInputValues = ["", "", " (emptyInputValues); const newInputValues = addPastePage.addNewPaste (); aștepta (addPastePage.getInputPasteValues ()) toEqual (newInputValues).; addPastePage.clickSaveButton (); așteptați (addPastePage.isCreatePasteModalPresent ()). toBeFalsy ("Fereastra modală ar trebui să dispară"); așteptați (mainPage.getLastRowData ()) toContain ("Ceva aici"); ); ("butonul de închidere ar trebui să funcționeze", () => addPastePage.clickCreateButton (); addPastePage.clickCloseButton (); așteptați (addPastePage.isCreatePasteModalPresent ()) .BeFalsy ("fereastra modală ar trebui să dispară"); );
Există câteva lucruri care lipsesc, totuși: testele pentru Afișați Paste butonul și fereastra modală care apare după ce faceți clic pe buton. Am de gând să las asta ca un exercițiu pentru tine. Cu toate acestea, vă voi da un indiciu.
Structura obiectelor de pagină și specificațiile pentru ViewPastePage sunt similare cu cele ale AddPastePage.
Blueprint pentru componenta ViewPasteIată scenariile pe care trebuie să le testați:
Încercați să respectați liniile directoare ori de câte ori este posibil. Dacă aveți dubii, treceți la ramura finală pentru a vedea schița finală a codului.
Deci, tu o ai. În acest articol, am acoperit scrierea de teste end-to-end pentru aplicația noastră Angular utilizând Protractor. Am inceput o discutie despre testele unitare vs. testele e2e si apoi am invatat despre configurarea, configurarea si rularea Protractorului. Restul tutorialului sa concentrat pe scrierea testelor reale pentru aplicația demo Pastebin.
Te rog să-mi spui gândurile și experiențele tale despre scrierea testelor folosind Protractor sau teste de scriere pentru Angular în general. Mi-ar plăcea să le aud. Vă mulțumim pentru lectură!