Usaldusväärsete kasutajaliideste loomise strateegiad

Original: https://blog.berniesumption.com/software/building-reliable-user-interfaces/

Bernie Sumption

Või „UI-koodi ühiku testimine on ülehinnatud”

See artikkel räägib, kuidas teha UI kood usaldusväärne. Autor usaldusväärne mõtlen vaba vead. See tähendab, et tarkvara peab olema hooldatav mis on öelda, et saate hõlpsasti lisada uusi funktsioone ilma ka lisades uusi vigu. Autor UI kood mõtlen kood, mis aktsepteerib sisendit kasutajad ja kuvab tulemused. Kaasaegses JavaScript jõul veebirakenduse, palju äriloogika on kolinud brauserit. Näiteks võib veebimängus olla virtuaalne vastane, kes saab teie vastu mängida, valides igal käigul parima käigu. Rumal, kuna võib tunduda, et male nimetatakse ettevõtluseks, on selle virtuaalse vastase kood pigem äriloogika kui kasutajaliides ja tõenäoliselt peaksite selle katsetama. UI-kood hõlmab selliseid asju nagu tagasiside andmine, kui mängija lohistab oma malelaua kogu mängulauale.

Tarkvaraarenduses on midagi testitavat religiooni. Enamasti on see hea asi – ühiktestid on tõenäoliselt kõige olulisemad tööriistad usaldusväärse tarkvara loomisel. Selles artiklis väidan, et UI arendamise kontekstis on usaldusväärsuse saavutamiseks paremaid tehnikaid.

1. osa: miks ühikatestid UI-koodiga ei tööta

See on artikkel usaldusväärsete kasutajaliideste loomise kohta, kuid kavatsen artikli esimese poole kulutada oma väitele, et ühikatsetused pole selle jaoks heaks tööriistaks. See ütleb midagi üksuse testimise positsiooni kohta tarkvaraarenduses – paljudele inimestele on ühiku testimine sünonüüm usaldusväärsusele. Tegelikult on minu motiiv selle artikli kirjutamiseks see, et olen töötanud mõne projekti kallal, kus ettevõte pidas toote usaldusväärsust tavapärasest veelgi olulisemaks ja see tulenes otseselt nõudest ühiktesti teha tavapärasest rohkem, ilma alternatiive palju ilmselgelt kaalumata. Kui olete rohkem huvitatud positiivsetest nõuannetest, võiksite minna üle 2. osa.

Põhjalike testide soe udune tunne

Esiteks tunnistagem, et ühiku testimise populaarsuse tagamiseks on mõjuv põhjus: mõnes kontekstis on sellised testid vajalikud ja vigadevaba programmi loomiseks piisavad. Vajalik, kuna need on kõige tõhusam vahend vigadest vabanemiseks; piisav, sest üksi testid on peaaegu kõik, mida vajate.

Mõningaid programme, eriti neid, mis töötavad serveripoolses veebirakenduses, saab täielikult määratleda sisendi ja väljundi osas. REST API serveri spetsifikatsioon võib koosneda järgmistest lausetest: Kui POST-päring tehakse päringute kehas kehtiva JSON-kodeeritud Smurf /api/smurfs, peaks Smurf jääma andmehoidlasse ja tagastama 200 OK vastuse koos uue Smurf andmebaasi ID-ga kui vastusekeha. See tähendab *väga* hästi ühikatset:

function api_should_create_a_new_smurf(api, backend) {
  let smurf = {
    name: "Papa Smurf",
    age: 546,
    biography: "https://en.wikipedia.org/wiki/Papa_Smurf"
  };
  let response = api.smurfs.handlePost(papaSmurf);
  expect(response.status).to.be(200);
  expect(response.body).to.be.a.number;
  expect(backend.getSmurf(response.body).to.equal(smurf);
}

Edasine test juhtudel võib sätestada, mida on oodata, kui vale Smurf on sätestatud, või kui andmed pood on saadaval. Kui teil on test iga oluline aspekt süsteemi, saad sooja hägune tunne terviklik teste. See on kindlus, et saate tarkvara ükskõik millise osaga vabalt töötada, teades, et kui midagi kogemata rikkuda, annavad testid teile sellest märku.

See on oluline kraam – just see teeb projekti õnnestumise või ebaõnnestumise või arendajate õnne ja ärrituse vahel vahet.

Miks ei saa kasutajaliideseid terviklikult testida

Serverid on liidesed, mis on loodud masinate kasutamiseks, ja seetõttu on mõistlik, et masinad oskavad neid hästi testida. Ülaltoodud Smurfi API-liidese saab täpsustada sissetulevate ja väljaminevate andmete osas. Kui sisend toodab usaldusväärselt eeldatava väljundi, siis server töötab, loo lõpp. Teisest küljest kasutavad UI-sid inimesed, mis muudab masina nõuetekohase testimise sõna otseses mõttes võimatuks. Kasutajaliidese sisend on inimese kavatsus ja väljund on inimese mõistmine. Ainult viis testida seda, vähemalt praeguse tehnoloogia on, et inimene seda teha.

Lubage mul seda illustreerida näitega. Oletame, et meil on see kasutajaliides:

+ ikoonil klõpsamine laiendab elulugu ja muudab plussmärgi miinuseks:

Selle funktsiooni toimimise proovilepanek seisneb selles, et kui inimene üritab klõpsata eluloo nuppu, saab inimene elulugu lugeda. Kõige lähedasem, milleni ühiskatse pääseb, on midagi sellist:

function smurf_biography_should_expand(browser) {
  // assume that browser is a selenium object that
  // remote-controls a real web browser
  let page = browser.navigateTo(SMURF_VIEW_PAGE);
  let bio = page.findElement("#biography");
  let bioButton = page.findElement("#biography-toggle-btn");
  expect(bioButton).to.containText("Show biography");
  expect(bio.style.visibility).to.be("hidden");
  bioButton.click();
  expect(bioButton).to.containText("Hide biography");
  expect(bio.style.visibility).to.be("visible");
}

See test pole kahel põhjusel väga kasulik.

1. Kui kasutajaliidese test läbib, ei tähenda see, et funktsioon töötab. Põhimõtteliselt kehtib see kõigi testide kohta – isegi hästi testitud serveripoolses koodis võib alati esineda servi, kus mõni funktsioon mõnikord ebaõnnestub, isegi kui kõik selle testid läbivad. Kuid kasutajaliidesed on erilised selle poolest, et testid on läbitavad ja funktsioon on endiselt täiesti täielik, 100% katki.

See võib juhtuda kas sisendi või väljundi lõpus, kui midagi läheb valesti inimeste ja masinate katsetamisvõimaluste vahel. Sisendi lõpus rida bioButton.click();kipub töötama ka siis, kui päris inimene ei saanud nuppu klõpsata. Näiteks Smurfi foto võib olla pakitud täisnurkse läbipaistva taustaga konteinerisse, mis ulatub üle nupu, nii et kui proovite hiirega nuppu klõpsata, siis klõpsate hoopis fotokonteineril. Brauseri automatiseerimistööriist otsib aga elemente lihtsalt ID järgi ja annab neile virtuaalseid klõpse, nii et see pole temast lahti. Väljundi lõpus kontrollib test, kas CSS-i nähtavuse atribuudil on õige väärtus, mitte et silm näeks ekraanil kõiki paremaid piksleid. Võib-olla on elulooelemendil fikseeritud kõrgus, nii et isegi kui sisu näidatakse, lõigatakse see ära ja saate lugeda ainult paar esimest rida.

Lõpptulemus on see, et ükskõik kui palju üksuse testid teil on, siis kunagi saavutada soe hägune tunne terviklik teste.

2. Kasutajaliideses olevad asjad, mida saab testida, pole tavaliselt väärt katsetamist. Sellise nõude taga nagu „klõpsates nuppu Näita elulugu” laiendatakse eluloolist teksti ”on kümneid pisikesi nõudeid, mõned nähtavad kujunduse maketis, teised kaudselt: tekst peaks olema täielikult nähtav; ikoon ja nupu silt peaksid laiendatud olekus olema erinevad; kui tekst on liiga pikk, peaks see tõenäoliselt kerima, mitte lehe põhjast välja sirutuma; polsterdus teksti ümber peaks olema ülejäänud kujundusega kooskõlas; teksti laiendamiseks kasutatav animatsioon peaks olema kooskõlas mujal animatsioonidega; jne jne. Üldiselt on nende alamfunktsioonide rakendamine triviaalne (alates ühest HTML-atribuudist kuni mõne koodiridani), neid on lihtne käsitsi testida ja isoleerida (ühe funktsiooni lisamine või muutmine tõenäoliselt ei riku teisi).

Teatud alamfunktsioone, näiteks nupu sildi muutmist, on lihtne automaatselt testida. Teisi, näiteks animatsiooni sujuvust, on keeruline või võimatu testida.

Mõned inimesed pooldavad katsetamist, mida saab testida, kuna mõned testid on paremad kui mitte ükski. Olen seisukohal, et see annab väära turvatunde ja parem on olla järjepidev: katsetage mitte ühtegi neist alamfunktsioonidest ja keskenduge muudele usaldusväärsete kasutajaliideste loomise tehnikatele.

2. osa: Kuidas luua usaldusväärseid kasutajaliideseid

Ära karda! Hea uudis on see, et on võimalik kirjutada usaldusväärseid kasutajaliidese programme. Automatiseeritud testimisel on oma roll, kuid see pole kaugeltki nii oluline kui serveripoolne arendamine.

Need on tehnikad, mida olen kasutanud usaldusväärsemate kasutajaliideste loomiseks, mitte mingis kindlas järjekorras. Loend võib aja jooksul kasvada ja on veebirakenduste suhtes kallutatud, kuna just selle kallal olen hiljuti töötanud.

Kas teil on usaldusväärsuse strateegia

Tunnistage usaldusväärsus selgesõnaliselt oma tarkvara mittefunktsionaalse nõudena, näiteks jõudlus. Otsustage, milliseid tehnikaid kavatsete kasutada töökindluse suurendamiseks, ja veenduge, et kogu meeskond on otsuse tegemisel kiire.

Selle artikli ülejäänud osas sisalduv tehnikate loetelu on hea koht alustamiseks. Iga tehnika puhul analüüsige konkreetse projekti maksumust ja tulusid ning otsustage, kas ja kuidas seda rakendada.

Hea vanaaegne tarkvara meisterdamine

Hästi kirjutatud tarkvara sisaldab vähem varjatud vigu ja hiljem on seda hõlpsam muuta ilma uusi vigu sisestamata. Lõppude lõpuks loodi see tarkvara juba 20. sajandil enne seda, kui Kent Beck muutis seadme testimise populaarseks. Sellel teemal on kirjutatud terveid raamatuid, nii et ma ei saa siin isegi pinda kriimustada, kuid kõik klassikalised rusikareeglid hea tarkvara kirjutamiseks on UI-koodis veelgi olulisemad: kirjutage lühikesi meetodeid, mis teevad ühte asja hästi; kasutage järjepidevat kodeerimisstiili; kasutage hoolikalt valitud kirjeldavat meetodit ja muutujate nimesid; vältige koodide dubleerimist, eelistage kommentaaridele lihtsat ja arusaadavat koodi, kuid kasutage kommentaare, kui see pole võimalik; jagada funktsioonid iseseisvateks mooduliteks, mille vahel on selgesti määratletud liides; vältida jagatud muutlikku olekut; jne jne. Selliseid asju saab kõige paremini õppida vigadest õppimise ajal, kuid “tarkvaratehnika parimate tavade” Google pakub palju loendeid.

Kui piisavalt kogenud arendaja valmistab ise toote, saavad nad lihtsalt otsustada kasutada head tarkvaraoskust. Meeskonnas ei juhtu hea viimistlemine lihtsalt heade arendajate palkamise tagajärjel – see nõuab õiget juhtimist ja kultuuri. Jällegi, sellel teemal on kirjutatud palju raamatuid, kuid mõned olulised aspektid on järgmised: kodeerimisstandardite kujundamine ja edastamine; üksteise koodi lugemine (eelistatumalt võrdõiguslikul viisil, kui termin „koodi ülevaade” viitab); ekspertteadmiste jagamine; koostöö kujundamine; ja paaride programmeerimine.

Automatiseeritud ühiku test

Pärast kogu selle aja pesuühiku testide tegemist pedaalin natuke.

Mõnikord leiate, et testipõhise arenduse abil on lihtsalt natuke lihtsam koodi välja töötada kui käsitsi testimise abil. Sel juhul ei ole vaja midagi vaeva näha: ühikatsete kirjutamine säästab teie aega ja annab teile tasuta ühiskatsete eelised.

Teinekord võib tekkida tunne, et natuke kirjutatav kood on eriti veatu või vigane. Teie esimene mõte peaks siin olema kodeerimismustrite muutmine nii, et te ei peaks sisestama veatuid ega tõrkeid tekitavaid koode. Aga mõnikord te kasutate raamistik, mis on suurt kasu, kuid vajab natuke Aeganõudev kodeerimine (Redux mõõtevahendikaubanduses otsin sind), et sa hindame soe hägune tunne põhjalikult testida, et natuke koodi baasi.

Komponentide väljatöötamise rakmed

Üks ühiktestide peamistest eelistest on see, et need sunnivad teie koodi olema modulaarsed, tehes sõltuvused selgeks. Iga ühiskatse on isoleeritud keskkond, mis loob klassist uue eksemplari ja pakub talle kõiki vajalikke sõltuvusi. Kui klassil on palju sõltuvusi, on selle jaoks testide kirjutamine keeruline ja see julgustab arendajat korraldama süsteemi klassidevaheliste sõltuvuste minimeerimiseks, muutes koodialuse hooldatavamaks.

Visuaalsete komponentide ekvivalent on arendusrihmad. Oletame, et arendate rippmenüü komponenti. Selle arendusrihm on ekraan, kuhu on paigutatud mitu menüükomponendi esinemisjuhtu erinevate sätete, stiilide ja andmetega. Komponendi arendamisel ja selle keerukuse suurenemisel lisate arendusvöödele rohkem eksemplare, nagu ka mittevisuaalse komponendi testkomplekti testid. Kui teete komponendi kallal suuri töid, teete seda pigem arendusvöödes, mitte rakenduses. See muudab teid produktiivsemaks, kuna koodimuudatused ja nende mõju näitamine on kiirem. See rakendab ka modulaarsust – kuna teie komponent peab olema võimeline töötama nii rakenduses kui ka arendusköites, peab see olema eraldiseisev komponent.

Tugevalt trükitud JavaScript

JavaScript on dünaamiline keeles, nii teatud vigu ei avastata enne programmi jookseb. Natuke kood, mis näeb välja õige – alert("Hello" + user.name)– võib katki, sest userobjekti ei ole namevaldkonnas, vaid firstname ja lastname. See viga võiks ilmuda varem tööpäeva koodi kui kasutaja objekti refactored kuid mitte kõik koodi kasutades nimetatud objekt on uuendatud. Üks argument ulatusliku üksus katsetamine on see, et testid leiavad need vead, kuid staatilise tüüp kontrollimine on parem viis sama eesmärgi saavutamiseks.

TypeScript ja Flow on mõlemad suurepärased staatilist tüüpi kabe JavaScript jaoks. Isiklikult kasutan TypeScript. Need kõrvaldavad terve hulga vigu ja muudavad redigeerimise palju produktiivsemaks ja nauditavamaks, rõhutades vigu ja sisestades nimesid automaatselt.

Kasutage modulaarsust soodustavat arhitektuuri

On mitmeid erinevaid tehnikaid asjakohane siin, et üldiselt kuuluvad Inversioon kontroll, mida tuntakse ka Hollywoodi Põhimõte: “ei helista, helistame teile”. Komponendid ei tohiks ulatuda väljaspool piire ega suhelda ülejäänud süsteemiga. Selle asemel peaksid nad olema sõltuvused, mida pakutakse sõltuvuse süstimise kaudu, ja signaalitoimingud sündmuste edastamise teel.

Näiteks kasutajaliidese kujundamisel võib juhtuda, et kui kasutaja klõpsab nuppu „Salvesta“, salvestab mudel end serverisse. Ei mudel ega nupp peaksid üksteise kohta teadma. Selle asemel peaks nupp saatma “salvestamise taotlemise” sündmuse kesksesse sündmusbussi, mille mudel võtab vastu ja käivitab selle salvestamiseks. See annab teile paindlikkuse uute funktsioonide lisamiseks – näiteks automaatne salvestamine iga paari minuti tagant või laadimiskere, mis teavitab kasutajat dokumendi salvestamisest – ilma olemasolevaid komponente muutmata.

Jagage kood deklaratsioonidesse ja mootoritesse

Kui te ei saa programmi osa terviklikult testida, on eriti oluline, et kood oleks lihtne, loetav ja funktsioneeriks iseenesestmõistetavalt. Selle saavutamiseks on tõesti võimas vahend jagada kood deklaratsioonideks ja mootoriteks.

Deklaratsioon on andmestruktuur, mis määrab täpselt, mida sa üritad saavutada, kus ei rakendamise üksikasju. Mootor on kood, mis tõlgendab neid andmeid struktuuri ja muudab nii.

Selle heaks näiteks on Django vormide süsteem. Vorm on määratletud ühes kohas klassina:

class NameForm(forms.Form):
  name = forms.CharField(label='Your name', max_length=100);
  bmu = forms.BooleanField(label='Beam me up?', required=False);

Seda andmestruktuuri kasutab mootorikood mitmes kohas: serveris, et genereerida HTML-i vorm, mis saadetakse brauserile; brauseris JavaScripti abil viivitamatu kinnituse saamiseks; ja tagasi serverisse, et esitatud vastuseid turvaliselt kinnitada.

Deklaratsioon/mootori muster töötab kõige paremini siis, kui mootori pakub hästi kavandatud ja dokumenteeritud raamistik nagu Django, sellisel juhul ei pea te ühtegi mootori koodi kirjutama. Kuid kui teie juhtumi jaoks pole raamistikku saadaval, on see piisavalt lihtne enda kirjutada. Võtke siiski arvesse, et see maksab: uued arendajad peavad enne deklaratsiooni vormingu kasutamist õppima, seega tasub seda teha ainult siis, kui kavatsete seda märkimisväärselt kasutada.

Kasutage tõu parimaid raamistikke, mis leevendavad brauserite ebakõlasid

Kaasaegsete veebirakenduste loomisel on asendamatud sellised raamistikud nagu React, Angular, Bootstrap ja jQuery. Neid mõeldakse enamasti tööriistadena, mis suurendavad tootlikkust või soodustavad mooduliarhitektuuri, kuid suur osa nende väärtusest seisneb selles, et nad pakuvad brauseri funktsioonide vahel ühtlast API.

Kui lisate changekorral käitlejat <select>(rippmenüüst) element, kasutades JavaScript, tulemused on vastuolulised. Mõned brauserid saadavad muudatussündmuse, kui uus üksus on valitud, teised aga ainult siis, kui kasutaja valib uuesti juhtnupu. Mõnes brauseris mullitab muutussündmus, teistes mitte. Ja ilmselt on rohkem ebakõlasid, millest ma ei tea. Ülalnimetatud populaarsed raamistikud on spetsiifiliste brauserite jaoks erijuhud täis ning tuhandeid kaastöid on neid testitud ja täpsustatud, et veenduda, et Samsungi uusimas WiFi-toega külmikus olev varjatud DOM-i viga ei põhjusta viga oma rakenduses.

Automatiseeritud suitsutestid

Suitsutestid on täielikult töötava tarkvara kõrgetasemelised testid, mis kontrollivad, kas enamik olulisi funktsioone näib töötavat. Brauseripõhise kasutajaliidese rakenduste jaoks kirjutatakse need tavaliselt brauseri kaugjuhtimispuldi tööriista, näiteks Seleeni abil. Suitsutestide ja ühiku, integreerimis või vastuvõtutestide erinevus seisneb selles, et suitsutesti eesmärk ei ole tõestada, et süsteem töötab täpselt täpsustatud viisil, eeldades, et see üldse töötab.

Suitsukatsete juures on suurepärane see, et natuke läheb tõesti väga kaugele. Kirjeldate oma rakenduse kaudu mõnda lihtsat teed: näiteks logige sisse, lisage uus Smurf, leidke otsingulehel uus Smurf, vaadake uue Smurfi elulugu, eemaldage Smurf, logige välja. Selle testi kirjutamine võtab vaid paar tundi, kui olete Seleeniga juba tuttav, kuid kui see läbib, ütleb see teile, et rakenduse kõik suuremad osad töötavad – JavaScriptil pole ühtegi saatuslikku viga, rakendusserver on käivitunud, andmebaasi ühendus töötab jne.

Suitsuproov on kasulik asi pärast ehitamist kohe joosta, et kontrollida, kas ehitis töötab. Kuid see on ka asi, millele saate osutada lavastuste või tootmissüsteemide osas. Võite isegi seda iga tunni aja tagant käivitada, kuna see on täpsema versiooniga pingist, et kontrollida serveri tööaega.

Kliendipoolne vigade kogumine

Kliendipoolne tõrgete kogumine hõlmab koodi sisestamist rakendusse, et tuvastada kasutaja arvutis ilmnevad vead ja teid sellest teavitada. Saate seda ise teha, kuulates Window.onerrori sündmust või tellides sellist teenust nagu RaygunSentry või errorception.

Need töötavad nii hästi, kuna muudavad QA-st möödunud vigade ökonoomikat. Ütle, et on viga, mis juhtub ainult Firefox Mobile’is ja mis pole ükski teie testitavatest brauseritest. Ilma kliendipoolsete vigade kogumiseta teavad Firefox Mobile’i kasutajad lihtsalt, et teie sait ei tööta, ja lähevad mujale või on pisut pahane ja vahetavad mõnda muud brauserit. Kliendipoolse veakogumisega saate veast teate ja saate selle parandada, kui olete vaid mõnda kasutajat ärritanud.

Need teenused püüavad JavaScripti vigu ainult veebirakendustes, mitte muud tüüpi vigu, näiteks pilte, mida ei õnnestu laadida. Kuid JavaScript vead on sageli kõige kahjulikumad, kuna need rikuvad funktsiooni täielikult, mitte ei muuda seda lihtsalt pisut välja.

Käsitsi testimine

Mõningane käsitsi testimine on enamiku tarkvaraprojektide jaoks tavaliselt hea mõte.

Traditsiooniliselt tähendab „käsitsi testimine” seda, et teie või mõni teine meeskonnaliige või kolmanda osapoole ettevõte läbib iga funktsiooni võimaliku permutatsiooni, üritades vigu leida. Kuna testimist tegeval inimesel puuduvad eriteadmised, mis võimaldavad funktsiooni rikkumist intuitiivselt ära tunda, on tal vaja dokumenti, milles öeldakse, kuidas testida. Seda nimetatakse katseprotseduur spetsifikatsioon ja oli surematuks filmis Office Space embleem tüütu Raataja-töö tarkvarafirma.

Käsitsi testimise palju meeldivam versioon on Dogfooding: tava kasutada oma tarkvara väljalaske-eelseid versioone sisemiselt. Ilmselt töötab see ainult siis, kui teie ettevõte loob toote, mida tal endal on vaja. Tuumaelektrijaama juhtimistarkvara ei saa kasutada. Kuid kui teete projektihaldusrakendust, saab enamik teie töötajatest teie tarkvara käsitsi testida lihtsalt oma igapäevast tööd tehes. Selle eeliseks on ka see, et teie töötajad on kursis teie tarkvara uusimate arengutega.

Kasutajate testimine

Kasutajate testimist peetakse tavaliselt UX-i poleerimise poleerimisvahendiks, mitte vigade otsimiseks. Kuid mis puutub kasutajatesse, siis pole vahet väiksema tehnilise vea ja UX-i probleemi vahel – mõlemad saavad takistuseks ülesandele, mida nad proovivad täita. Ja kuna kasutajatestimist on kõige parem teha varase prototüübi tarkvara korral, avastab kasutaja testimise protsess mõnikord uusi tehnilisi vigu.

Kõige tõhusam kasutajatestimine seisneb katseisikute kutsumises läbi vaatama tarkvara ülesannete loend ja rääkima valjuhäälselt oma mõtteprotsessist.

Kasutajate tagasiside

Lõpuks, kui kõik ülaltoodud tehnikad ei suuda tõrke või ukseprobleemide sattumist tootmistarkvarasse, veenduge, et kasutajatel oleks hõlbus viisidest teada anda ja soovitusi anda. Andke vähemalt e-posti aadress, kuhu aruandeid saata. Palju parem on kasutada tagasiside tööriista, näiteks Usersnap, mis teeb lehe ekraanipildi ja salvestab automaatselt teabe nagu kasutatav brauser ja opsüsteem.

Lõpp…

Kas ma olen millestki ilma jäänud? Millised on teie lemmikvõtted usaldusväärsete kasutajaliideste tegemiseks? Kommenteerige oma eelistatud tehnikaid usaldusväärsete kasutajaliideste tegemiseks või saatke mulle e-kiri aadressil [email protected].