{"id":19490820,"url":"https://github.com/andrelkj/gravidadezero","last_synced_at":"2026-04-08T21:32:33.142Z","repository":{"id":65831647,"uuid":"588835698","full_name":"andrelkj/GravidadeZero","owner":"andrelkj","description":"Project to automate GetGeeks, a service providers platform with Robot Framework to create the tests, Fly to deploy the application, REST API to validate backend requests, PostgreSQL managed through ElephantSQL to store and manage data, and github actions as pipeline for continuous integration.","archived":false,"fork":false,"pushed_at":"2023-03-25T03:25:47.000Z","size":21193,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-10-19T07:22:19.472Z","etag":null,"topics":["css","github-actions","html","postgresql","python","robot-framework","test-automation","testing"],"latest_commit_sha":null,"homepage":"https://geeks-web-andre.fly.dev/","language":"HTML","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/andrelkj.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2023-01-14T07:03:02.000Z","updated_at":"2024-08-13T17:43:06.000Z","dependencies_parsed_at":"2024-04-24T18:00:39.767Z","dependency_job_id":"b7d4fc74-f7c6-4f9a-908d-6a5ede7880d9","html_url":"https://github.com/andrelkj/GravidadeZero","commit_stats":null,"previous_names":["andrelkj/gravidadezero"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/andrelkj/GravidadeZero","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andrelkj%2FGravidadeZero","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andrelkj%2FGravidadeZero/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andrelkj%2FGravidadeZero/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andrelkj%2FGravidadeZero/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/andrelkj","download_url":"https://codeload.github.com/andrelkj/GravidadeZero/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andrelkj%2FGravidadeZero/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31575584,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-08T14:31:17.711Z","status":"ssl_error","status_checked_at":"2026-04-08T14:31:17.202Z","response_time":54,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["css","github-actions","html","postgresql","python","robot-framework","test-automation","testing"],"created_at":"2024-11-10T21:14:28.044Z","updated_at":"2026-04-08T21:32:33.121Z","avatar_url":"https://github.com/andrelkj.png","language":"HTML","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Summary\n\n- [Summary](#summary)\n- [Notes](#notes)\n- [Structure](#structure)\n  - [Actions](#actions)\n    - [Arguments](#arguments)\n    - [Tags](#tags)\n  - [Factories](#factories)\n    - [Faker Library](#faker-library)\n  - [Template](#template)\n- [Base.robot](#baserobot)\n- [Database.robot](#databaserobot)\n- [SignupRequired.robot](#signuprequiredrobot)\n- [Temp.robot](#temprobot)\n  - [Loops](#loops)\n  - [Keywords](#keywords)\n- [Login.robot](#loginrobot)\n- [BeGeek.robot](#begeekrobot)\n  - [Filling type input elements](#filling-type-input-elements)\n  - [Filling type select elements](#filling-type-select-elements)\n  - [Submitting geek form and message validation](#submitting-geek-form-and-message-validation)\n  - [Improvements](#improvements)\n    - [Test template improvement](#test-template-improvement)\n- [Smoke test](#smoke-test)\n- [Screen Resolution](#screen-resolution)\n- [Clearing HTML forms](#clearing-html-forms)\n- [Dealing with conditional elements (IFs)](#dealing-with-conditional-elements-ifs)\n- [Challenge 1 - module PRO](#challenge-1---module-pro)\n  - [Automate 3 new test cases inside the login suite:](#automate-3-new-test-cases-inside-the-login-suite)\n- [API Testing](#api-testing)\n  - [Thunder Client](#thunder-client)\n    - [Collections](#collections)\n    - [HTML Methods](#html-methods)\n    - [Status Codes](#status-codes)\n    - [Tests](#tests)\n      - [Validating token](#validating-token)\n      - [Front-end x back-end](#front-end-x-back-end)\n  - [Automating API Tests with robot](#automating-api-tests-with-robot)\n    - [Alternative scenarios (sad paths)](#alternative-scenarios-sad-paths)\n  - [Sessions.robot](#sessionsrobot)\n  - [Users.robot](#usersrobot)\n    - [Validating ID](#validating-id)\n    - [POST Request](#post-request)\n    - [GET Request](#get-request)\n      - [Validating user infos](#validating-user-infos)\n    - [DELETE Request](#delete-request)\n    - [PUT Request](#put-request)\n  - [Geeks](#geeks)\n    - [Validating geek creation](#validating-geek-creation)\n    - [Getting geeks list](#getting-geeks-list)\n  - [Regression test](#regression-test)\n  - [Github Actions](#github-actions)\n    - [Creating python dependencies file](#creating-python-dependencies-file)\n    - [Regression backend workflow](#regression-backend-workflow)\n    - [Uploading robot report](#uploading-robot-report)\n    - [jUnit reports](#junit-reports)\n    - [Frontend workflow](#frontend-workflow)\n      - [Adding configuration and dependencies](#adding-configuration-and-dependencies)\n    - [Important commands](#important-commands)\n  - [Geek search](#geek-search)\n    - [Creating the geek user seed](#creating-the-geek-user-seed)\n    - [Search geeks test suite](#search-geeks-test-suite)\n    - [Turning user into geek](#turning-user-into-geek)\n    - [Adding searcher](#adding-searcher)\n      - [We have a bug](#we-have-a-bug)\n    - [Validating geek list](#validating-geek-list)\n- [Usefull terminal commands](#usefull-terminal-commands)\n  - [Git](#git)\n  - [Linux](#linux)\n  - [chmod +x run.sh](#chmod-x-runsh)\n  - [Request Library](#request-library)\n- [Important links](#important-links)\n  - [Pabot](#pabot)\n    - [Pabot screenshot path improvement](#pabot-screenshot-path-improvement)\n  - [Typora](#typora)\n\n---\n\n# Notes\n\nHere we're going to automate the web application [Getgeeks](https://geeks-web-andre.fly.dev/) geratered using Fly that is connect to a PostgreSQL database. We're also using ElephantSQL to manage our database.\n\nWe'll use Gherkin to define our test scenarios following Behaviour Driven Development (BDD) standards.\n\nBDD is a colaborative specification technique where development driven scenarios are written considering the final users' point of view in order to give a better undertanding and clear informations to facilitate development and also testing after all. BDD is used to guide the development, not only testing automation and should be defined before starting the development.\n\n**Example:** [Register notes](docs/signup.md)\n\n```md\n\u003e Sendo um visitante que deseja contratar serviços de TI\n\u003e Posso fazer o meu cadastro\n\u003e Para que eu possa buscar por prestadores de serviços (Geeks)\n\n##### Cenário: Cadastro de cliente\n\nDado que acesso a página de cadastro\nQuando faço o meu cadastro com o nome completo, e-mail e senha\nEntão vejo a mensagem de boas vindas:\n\"Agora você faz parte da Getgeeks. Tenha uma ótima experiência.\"\n```\n\nWe can list here our Sad scenarios, those cases that can lead to erros, as well:\n\n```md\n##### Cenário: E-mail duplicado\n\nDado que acesso a página de cadastro\nPorem o meu e-mail já foi cadastrado\nQuando faço o meu cadastro com o nome completo, e-mail e senha\nEntão vejo a mensagem de alerta:\n\"Oops! Já temos um usuário com o e-mail informado.\"\n\n##### Cenário: Email com formato incorreto\n\nDado que acesso a página de cadastro\nQuando faço o meu cadastro com um email incorreto\nEntão vejo a mensagem de alerta \"O email está estranho\"\n\n##### Cenário: Campos obrigatórios\n\nDado que acesso a página de cadastro\nQuando submeto o cadastro sem preencher o formulário\nEntão devo ver uma mensagem informando que todos os campos são obrigatórios\n```\n\n---\n\n# Structure\n\nWe'll use _PascalCase_ in here to define files and _snake_case_ to define methods,\n\n## Actions\n\nIt's very important to consider the creation of actions for repetitive steps from the start in order to arrange the code properly and make it easier to use the same actions in different scenarios.\n\n**Note:**\n\n\u003cp\u003eOne way of doing it is by considering each step of the BDD as an action.\u003c/p\u003e\n\n**For example:** [Actions](resources/Actions.robot)\n\n- Dado que acesso a página de cadastro\n\n```\n*** Keywords ***\nGo to signup form\n    Go To    https://geeks-web-andre.fly.dev/signup\n\n    Wait For Elements State    .signup-form h1 \u003e\u003e text=\"Cadastro\"    visible    5\n\n```\n\n- Quando faço o meu cadastro com o nome completo, e-mail e senha\n\n```\n# When entering input information\n    # Checkpoint - going to register page\n    Fill Signup Form    ${user}\n\n# And submiting valid information\n    Submit Signut Form\n```\n\n- Então vejo a mensagem de boas vindas:\n\n```\n# Then I should register the user and see the welcome message\n    User Should Be Registered\n```\n\n**Important**\n\n\u003cp\u003eOne important thing to remember is that as actions will be defined inside a different file you need to add this as a Resource inside the Test case file and inside the Base file as well in order to integrate all the information.\u003c/p\u003e\n\n### Arguments\n\nArguments can be used to reduce reusage and facilitate code maintenance once it's necessary to update and/or change only one element. For example:\n\n```\nModal Content Should Be\n    [Arguments]    ${expected_text}\n\n    ${title}    Set Variable    css=.swal2-title\n    ${container}    Set Variable    css=.swal2-html-container\n\n    Wait For Elements State    ${title}    visible    5\n    Get Text    ${title}    equal    Oops...\n\n    Wait For Elements State    ${container}    visible    5\n    Get Text    ${container}    equal    ${expected_text}\n```\n\n### Tags\n\nTags can be used to identify one specific test case and allow you to run individual test cases by calling they're tags using:\n`-i tag_name` before the test case name. For example:\n\n```\nDuplicate user\n    [Tags]    dup_email\n\n    Go To Signup Form\n    Fill Signup Form    ${user}\n    Submit Signut Form\n    Modal Content Should Be    Já temos um usuário com o e-mail informado.\n```\n\n## Factories\n\nFactories are used to avoid the need of entering all testing set of information individually for each element. It works by defining a factory (factory_user) and then defining all arguments (test dough) inside a variable (user) which will be returned as response. **For example:** [Users factory](resources/factories/Users.py)\n\n```\n# Defining test dough\n\ndef factory_user():\n    user = {\n        'name': 'Tony',\n        'lastname': 'Stark',\n        'email': 'tony@stark.com',\n        'password': 'pwd123'\n    }\n\n    return user\n```\n\nThen we add this variable to an action, defining which argument to use for each case:\n\n```\n    Fill Text    input[id=\"name\"]    ${user}[name]\n    Fill Text    input[id=\"lastname\"]    ${user}[lastname]\n    Fill Text    input[id=\"email\"]    ${user}[email]\n    Fill Text    input[id=\"password\"]    ${user}[password]\n```\n\nAnd finally we integrate this factory and insert it inside our test cases:\n\n```\n*** Test Cases ***\nRegister a new user\n# Entering test data mass\n    ${user}    Factory User\n\n# Once I enter register page\n    Go To Signup Form\n\n# When entering input information\n    # Checkpoint - going to register page\n    Fill Signup Form    ${user}\n```\n\n### Faker Library\n\nFaker is a python package used to create dynamic arguments, making it possible to test scenarios with different sets of information automatically created. This can be really usefull for scenarios where you need unique data like registering users, validating emails and so on.\n\nTo use it we just need to configure faker inside our factory file:\n[Using Faker Library](https://pypi.org/project/Faker/)\n\n```py\n# Defining test dough\n\nfrom faker  import  Faker\nfake = Faker()\n\ndef factory_user():\n    user = {\n        'name': fake.first_name(),\n        'lastname': fake.last_name(),\n        'email': fake.free_email(),\n        'password': 'pwd123'\n    }\n\n    return user\n```\n\nAnd then run the project again now using dynamic mass of test.\n\n**Obs.:** using faker can create a massive amont of information inside the database, in order to avoid this we install 2 libraries:\n\n- `pip install robotframework-databaselibrary` - Gives access to the database through robot\n- `pip3 install psycopg2` - Connect to the postgreSQL database natively\n\n## Template\n\nIt is possible to define several steps inside a keyword and then use template to indicate that the test case should follow those steps one at a time following exactly what is defined inside the keyword. This allows us to repeat several scenarios changing only the test variables.\n\nTo create a template we first need to define a new keyword with all desired steps:\n\n```\n*** Keywords ***\nSignup With Short Password\n    [Arguments]    ${short_pass}\n\n    ${user}    Create Dictionary\n    ...    name=Test    lastname=User\n    ...    email=test@email.com\n    ...    password=${short_pass}\n\n    Go To Signup Form\n    Fill Signup Form    ${user}\n    Submit Signut Form\n    Alert Span Should Be    Informe uma senha com pelo menos 6 caracteres\n```\n\nAnd then, call this template inside the test case that should reproduce the template behavior, informing all inputs as well:\n\n```\nShort Password\n    [Tags]    attempt_signup    short_pass\n    [Template]    Signup With Short Password\n    1\n    12\n    123\n    1234\n    12345\n    a\n    a2\n    ab3\n    ab3c\n    a23bc\n    -1\n    acb#1\n```\n\n**Important:** template will only work if all conditions are exactly equal to one another. Another way to add these repetitive structures, is by using suite setups that will be explained better in the Signup.robot section.\n\n---\n\n# Base.robot\n\nHere's all the base data for testing\n\nIt's possible to add dynamic variables to Robot's Command Line Interface (CLI) by defining a variable and calling it with -v VARIABLE:value inside the console:\n\n1. Here we first defined the variables inside de [Base File](resources/Base.robot)\n\n```\n*** Keywords ***\nStart Session\n    New Browser    ${BROWSER}    headless=${HEADLESS}    slowMo=00:00:00.3\n    New Page    ${BASE_URL}\n    Set Viewport Size    1440    900\n```\n\n2. Then added it to our console shortcut inside our [Terminal Shortcut File](run.sh) with -v\n\n```\nrobot -d ./logs -v BROWSER:chromium -v HEADLESS:true -i smoke tests\n```\n\n**Note:** This is important while running continuos testing with Jenkins but will only work while running the application through the terminal. Running this with VS Code for example will return error because ${BROWSER} and ${HEADLESS} variables aren't defined inside the code.\n\n# Database.robot\n\nHere we're creating a task that will return the application to default and then configurate all required information to run our tests. Meaning that after running all database entered information will be deleted and the application will run as if it was the first time.\n\n**Important:** always disconnect from the database after finishing the task. (BEST PRACTICE)\n\n**Note:** always consider password_dash instead of password while dealing with databases as it uses encripted passwords\n\nFor the register test cases we were using a forced hardcoded password inside the database.\n\n```\nInsert User\n    [Arguments]    ${u}\n# password must be encripted to a successfull login\n\n    ${q}    Set Variable\n    ...    INSERT INTO public.users (name, email, password_hash, is_geek) values ('${u}[name] ${u}[lastname]', '${u}[email]', '${u}[password]', false)\n\n    Execute SQL String    ${q}\n```\n\nWhile creating the login test case it won't work because of the password integrity, so we'll need to now update our test to consider the encripted password instead of the hardcoded one.\n\n---\n\n# SignupRequired.robot\n\nThe use of templates are not recommended in same cases because it will run all the steps as they come. Using required fields as example the test case will open a new browser tab and start the hole process all over for each conditions, instead of validation all of them at once, even considering that they're all being shown in the same page.\n\nOne way to deal and improve templates is by defining a hole test suite using test cases and defining all steps inside a keyword. The key here is to use the `Suite Setup` as start point instead of the previous Test Setup. It will allow all test cases to run one after another without the need to close and open tabs every time.\n\n**Example:**\n\n```\n*** Settings ***\nDocumentation       Signup Test Suite\n\nResource            ../resources/Base.robot\n\nSuite Setup         Signup Without Fill Form    # execute the setup once for suite and not for test as used before\nTest Teardown       Finish Section\n\n*** Test Cases ***\nName is required\n    Alert Span Should Be    Cadê o seu nome?\nLastname is required\n    Alert Span Should Be    -Remover esse texto- E o sobrenome?\nEmail is required\n    Alert Span Should Be    O email é importante também!\nPassword is required\n    Alert Span Should Be    Agora só falta a senha!\n\n# Testing all forms all together using form\n*** Keywords ***\nSignup Without Fill Form\n\n    Start Section\n    Go To Signup Form\n    Submit Signut Form\n```\n\n---\n\n# Temp.robot\n\nTabulation is essential for lists functioning\n\nBy using `@{name}` robot will consider this as a temporary element. Yet to call it as a variable we need to use the `${name}`\n\n## Loops\n\nIt is possible to use FOR to create a loop that will be executed until there's no more element available to use\n\n**Example:**\n\n```\nWorking with Lists\n\n    @{avengers}    Create List    Tony Stak    Kamalakhan    Steve Rogers\n\n    Append To List    ${avengers}    Hulk\n\n      FOR    ${a}    IN    @{avengers}\n          Log To Console   ${a}\n\n      END\n```\n\n**Note:** Here all individual list elements 'a' inside the avengers list will be logged into console one by one until there's no more elements available.\n\n## Keywords\n\nAppend To List allows us to add a new element to the list. Example:\n\n```\nWorking with Lists\n\n    @{avengers}    Create List    Tony Stak    Kamalakhan    Steve Rogers\n\n    Append To List    ${avengers}    Hulk\n\n    Log To Console  ${avengers}[0]\n    Log To Console  ${avengers}[1]\n    Log To Console  ${avengers}[2]\n```\n\n**Important:** to use this arguments we need to import the library Collections from Robot\n\n---\n\n# Login.robot\n\nTooltip cannot be inspected as it isn't part of the code, instead it is a browser navigator response that cannot be used as selector. This happens when the input type is defined as email and one solution is to:\n\nCreate a new keyword to validate the element type inside the code:\n\n```\nShould Be Type Email\n    Get Property    id=email    type    equal    email\n```\n\nAnd adding it to our test case:\n\n```\nIncorrect Email\n    [Tags]    incorrect_email\n\n    ${user}    Create Dictionary    email=invalid.format.com    password=pwd123\n\n    Go To Login Page\n    Fill Credentials    ${user}\n    Submit Credentials\n    Should Be Type Email\n```\n\n**Important:** global variables should be used wisely, but one best practice is to create a global variable for global elements that repeat themselves throughout the code. In this example we created the `${INPUT_PASS}` global variable as well just for a visual purpose.\n\n---\n\n# BeGeek.robot\n\nDo Login variable is added in order to facilitate login functionality. The login helper cannot be used inside login test suite because we're validating the login process step by step, then we'll need separated steps to test all functionality coverage.\n\nFor the geek form we'll need to fill 5 different fields, in order to do it properly we'll update our test dough generator factory_user_be_geek by adding the sub-dictionary geek_profile inside Users.py:\n\n```\ndef factory_user_be_geek():\n    return {\n        'name': 'Kim',\n        'lastname': 'Dotcom',\n        'email': 'kim@dot.com',\n        'password': 'pwd123',\n        'geek_profile': {\n            'whats': '11999999999',\n            'desc': 'Seu computador está lento? Reiniciando do nada? Talvez seja um vírus, ou algum hardware com defeito. Posso fazer a manutenção no seu PC, formatando, reinstalando o SO, trocando algum componente físico e porque não remover o baidu ou qualquer outro malware.',\n            'printer_repair': 'Sim',\n            'work': 'Remote',\n            'cost': '100'\n        }\n    }\n```\n\nTo define the elements name we normally use those defined inside the HTML, for example:\n\nDescription's field HTML:\n\n```html\n\u003cdiv class=\"sc-kstqJO hxBQUd\"\u003e\n  \u003clabel for=\"desc\"\u003eDescrição\u003c/label\u003e\u003ctextarea id=\"desc\"\u003e\u003c/textarea\u003e\n\u003c/div\u003e\n```\n\n**Note:** here the id selector \"desc\" is used.\n\n## Filling type input elements\n\n```\nFill Geek Form\n    [Arguments]    ${geek_profile}\n\n    Fill Text    id=whatsapp    ${geek_profile}[whats]\n    Fill Text    id=desc    ${geek_profile}[desc]\n\n    Fill Text    id=cost    ${geek_profile}[cost]\n```\n\n## Filling type select elements\n\n```\nFill Geek Form\n    [Arguments]    ${geek_profile}\n\n    Select Options By    id=printer_repair    value    ${geek_profile}[printer_repair]\n    Select Options By    id=work    value    ${geek_profile}[work]\n```\n\n## Submitting geek form and message validation\n\nFirst we'll define new keywords for the desired steps:\n\n```\nSubmit Geek Form\n    Click    css=button \u003e\u003e text=Quero ser um Geek\n\nGeek Form Should Be Success\n    ${expected_message}    Set Variable\n    ...    Seu cadastro está na nossa lista de geeks. Agora é só ficar de olho no seu WhatsApp.\n\n    Wait For Elements State    css=p \u003e\u003e text=${expected_message}    visible    5\n```\n\nFinally we'll add it to our, now complete, be a geek test case:\n\n```\n*** Test Cases ***\nBe a Geek\n    # Given that I have a common user\n    ${user}    Factory User Be Geek\n\n    # And I log in the plataform Getgeeks\n    Do Login    ${user}\n\n    # When submitting the form to become a Geek (service provider)\n    Go To Geek Form\n    Fill Geek Form    ${user}[geek_profile]\n    Submit Geek Form\n\n    # Then I should see the success message\n    Geek Form Should Be Success\n```\n\n**Note:** the gherkin BDD usage here isn't considering the automation but the expected scenario behavior, that then guide the automation development. Differently from cucumber that needs structured language to define the automation steps, this method gives a more flexible way of steps description without lossing functionality and flow details.\n\n## Improvements\n\nFor every alternative scenario where the user don't actually successfully turn into a geek we need to login for every test case unnecessarily considering that where're already inside the forms page.\n\nTo optimize it we'll update our scenarios to new variant scenarios:\n\n1. First we'll create a new test suite AttemptBeGeek.robot that will contain our dynamic keyword\n\n```\n*** Keywords ***\nAttempt Be a Geek\n    [Arguments]    ${key}    ${input_field}    ${output_message}\n\n    ${user}    Factory User    attempt_be_geek\n\n    Fill Geek Form    ${user}[geek_profile]\n    Submit Geek Form\n    Alert Span Should Be    ${output_message}\n```\n\nand starter session\n\n```\nStart Session For Attempt Be Geek\n    ${user}    Factory User    attempt_be_geek\n\n    Start Session\n    Do Login    ${user}\n    Go To Geek Form\n```\n\n**Obs.:** This will then be used as test setup hook to initialise the test suite.\n\n2. We now link it to our new updated attempt be geek factory\n\n```\n'attempt_be_geek': {\n    'name': 'Dio',\n    'lastname': 'Linux',\n    'email': 'dio@linux.com',\n    'password': 'pwd123',\n    'geek_profile': {\n        'whats': '11999999999',\n        'desc': 'Instalo Distros Ubuntu, Debian, ElementaryOS, PopOS, Linux Mint, Kurumin, Mandrake, ConnectivaFedora, RedHat, CentOS, Slackware, Genton, Archlinux, Kubuntu, Xubuntu, Suze, Mandriva, Edubuntu, KateOSSabayon Linux, Manjaro Linux, BigLinux, ZorinOS, Unit',\n        'printer_repair': 'Não',\n        'work': 'Ambos',\n        'cost': '200'\n    }\n}\n```\n\n3. We'll then add a dictonary to our attempt be a geek function add the key and input_field arguments whom will change the values inside the factory to simulate user attempts to be a geek with invalid forms.\n\n```\n    Set To Dictionary    ${user}[geek_profile]    ${key}    ${input_field}\n```\n\n4. After all that we'll finally add our attempt test cases inside the suite using our pre-defined keywords as templateand only informing the test dough\n\n```\n*** Test Cases ***\nShould Not be a Geek\n    [Template]    Attempt Be a Geek\n\n    desc    Formato o seu PC    A descrição deve ter no minimo 80 caracteres\n    desc    ${long_desc}    A descrição deve ter no máximo 255 caracteres\n    desc    ${EMPTY}    Informe a descrição do seu trabalho\n    whats    11    O Whatsapp deve ter 11 digitos contando com o DDD\n    whats    999999999    O Whatsapp deve ter 11 digitos contando com o DDD\n    whats    ${EMPTY}    O Whatsapp deve ter 11 digitos contando com o DDD\n    cost    aaaa    Valor hora deve ser numérico\n    cost    aa12    Valor hora deve ser numérico\n    cost    \u0026!*%ˆ    Valor hora deve ser numérico\n    cost    ${EMPTY}    Valor hora deve ser numérico\n```\n\nand updated the factory in our users seed function inside database.robot\n\n```\nUsers Seed\n    ${user}    Factory User    login\n    Insert User    ${user}\n\n    ${user2}    Factory User    be_geek\n    Insert User    ${user2}\n\n    ${user3}    Factory User    attempt_be_geek\n    Insert User    ${user3}\n```\n\n**Obs.:** we created the `${long_desc}` variable to store a long description text, just for aesthetic reasons.\n\nUsing this improved format we'll now test all defined sad paths in the same window, one after other.\n\n### Test template improvement\n\nOne problem of using the template in a test case level is that we'll now have several different scenarios (all of them with different inputs and outputs) but only one described test case, meaning that after running all those different scenarios it'll all be considered as an individual test case. To improve it, showing the test status for every of those scenarios we'll define a test template in a suite level.\n\n1. First we'll define our template inside the settings section:\n\n```\n*** Settings ***\nDocumentation       Attempt BeGeek Test Suite\n\nResource            ../resources/Base.robot\nLibrary             Users\n\nTest Setup          Start Session For Attempt Be Geek\nTest Template       Attempt Be a Geek\n```\n\n2. Now we define a specific test case for each of our previous variable scenarios:\n\n```\n*** Test Cases ***\nShort desc    desc    Formato o seu PC    A descrição deve ter no minimo 80 caracteres\nLong desc    desc    ${long_desc}    A descrição deve ter no máximo 255 caracteres\nEmpty desc    desc    ${EMPTY}    Informe a descrição do seu trabalho\nWhats only ddd    whats    11    O Whatsapp deve ter 11 digitos contando com o DDD\nWhats only number    whats    999999999    O Whatsapp deve ter 11 digitos contando com o DDD\nEmpty whats    whats    ${EMPTY}    O Whatsapp deve ter 11 digitos contando com o DDD\nCost letters    cost    aaaa    Valor hora deve ser numérico\nCost alpha    cost    aa12    Valor hora deve ser numérico\nCost special    cost    \u0026!*%ˆ    Valor hora deve ser numérico\nEmpty cost    cost    ${EMPTY}    Valor hora deve ser numérico\n```\n\n3. After all that we'll change our test setup hook to a suite setup inside the settings section in order to run the test initiator only once for all the test suite.\n\n```\n*** Settings ***\nDocumentation       Attempt BeGeek Test Suite\n\nResource            ../resources/Base.robot\nLibrary             Users\n\nSuite Setup         Start Session For Attempt Be Geek\nTest Template       Attempt Be a Geek\n```\n\n---\n\n# Smoke test\n\nSmoke test isn't a critical test but it allows us to find potencial issues before proceeding to new steps. This test is used for critical scenarios and its purpose is to verify application health before a release or delivery.\n\nTo create smoke tests we:\n\n1. Define critical important scenarios (generally speaking, the inicial and/or basic flows of the application that gives access to all other scenarios)\n\n- Register a new user\n- Login\n- Be a Geek\n\n2. We then set a `[Tags]    smoke` to each of those scenarios\n\n```\n*** Test Cases ***\nRegister a new user\n    [Tags]    smoke\n    ...\n```\n\n3. Add the indication `-i smoke` to our console shortcut\n\n```\nrobot -d ./logs -i smoke tests\n```\n\n4. Run the smoke test through console\n\nWe'll now have an idea about the health of the application to allow or not its evolution\n\n---\n\n# Screen Resolution\n\nAlways consider the screen resolution while creating and executing test cases. To optimize it you can consider the lower required resolution so all elements can be displayed and tested properly for diverent devices.\n\nWe define which resolution we want by adding the function `Set Viewport Size    1440    900` inside our test initiator\n\n---\n\n# Clearing HTML forms\n\nWhile executing our tests inside /be-geek to register a new geek all forms information is being kept before the library actually inserts its input. Here this is not a problem because the factory overwrite the old data from the necessary fields, but the ideal scenario is to clear the forms for each new test case scenario.\n\nIn order to do this we have a JavaScript function that find and clear web applications forms data by class name `document.getElementsByClassName(\"be-geek-form\")[0].reset();`. Although robot cannot read JavaScript so we'll add a keyword (Execute JavaScript) so robot can use the function properly.\n\n1. We'll create a new action called Reset Geek Form\n\nUsed inside the course (OUTDATED)\n\n```\nReset Geek Form\n    Execute JavaScript    document.getElementsByClassName(\"be-geek-form\")[0].reset();\n```\n\nUp to date alternative method\n\n```\nReset Geek Form\n    Evaluate JavaScript    css=.be-geek-form    document.getElementsByClassName(\"be-geek-form\")[0].reset()\n```\n\n**Important:** Execute JavaScript method was outdated so I updated it to the Evaluate JavaScript method entering the selector and then the JS function.\n\n2. Then we're going to call this new keyword inside the Fill Geek Form action\n\n```\nFill Geek Form\n    [Arguments]    ${geek_profile}\n\n    Reset Geek Form\n\n    Fill Text    id=whatsapp    ${geek_profile}[whats]\n    Fill Text    id=desc    ${geek_profile}[desc]\n\n    Select Options By    id=printer_repair    text    ${geek_profile}[printer_repair]\n    Select Options By    id=work    text    ${geek_profile}[work]\n\n    Fill Text    id=cost    ${geek_profile}[cost]\n```\n\nThis way every time that a form is filled all previous data is cleared before entering the new data.\n\n---\n\n# Dealing with conditional elements (IFs)\n\nUntil this point we've created a bunch of test case considering all basic inputs but we now need to deal with the conditional selectors, for example:\n\n\u003e For the print_repair select element. While true the user is classified as supreme geek, while false the user is classified as a normal geek only so...\n\u003e\n\u003e - If print_repair is true or equal to \"Sim\" then user is a supreme geek;\n\u003e - If print_repair is false or equal to \"Não\" then user is a normal geek;\n\u003e - If there's no option selected the span message \"Por favor, informe se você é um Geek Supremo\" is displayed\n\n1. We created 2 new test cases to test submiting empty select fields to our [Attempt Be Geek Suite](tests/AttemptBeGeek.robot):\n\n```\n*** Test Cases ***\n...\nNo printer repair    printer_repair    ${EMPTY}    Por favor, informe se você é um Geek Supremo\nNo word    work    ${EMPTY}    Por favor, selecione o modelo de trabalho\n```\n\n**OBS.:** using the `${EMPTY}` variable in here it's not recommended once it will also remove the standard option leaving and empty field, instead of the normal Select option that can lead to future bugs in the application.\n\nIn order to improve this empty field we'll define a conditional statement inside the [Fill Geek Form](resources/actions/GeekActions.robot) action:\n\n```\n    IF    '${geek_profile}[printer_repair]'\n        Select Options By    id=printer_repair    text    ${geek_profile}[printer_repair]\n    END\n\n    IF    ${geek_profile}[work]\n        Select Options By    id=work    text    ${geek_profile}[work]\n    END\n```\n\nBy doing it the select fields will be selected only if there's a valid option for it and if not this steps is not considered, we skip it to the next step.\n\n---\n\n# Challenge 1 - module PRO\n\n## Automate 3 new test cases inside the login suite:\n\n- Required email\n\nGiven that I an inside the login page\nWhen the email field is empty\nAnd I submit the login button\nThen a required field message is displayed\n\"E-mail obrigatório\"\n\n- Required password\n\nGiven that I an inside the login page\nWhen the password field is empty\nAnd I submit the login button\nThen a required field message is displayed\n\"Senha obrigatório\"\n\n- Required fields\n\nGiven that I an inside the login page\nWhen both email and password fields are empty\nAnd I submit the login button\nThen a required field message is displayed for each field\n\"E-mail obrigatório\" and \"Senha obrigatório\"\n\n---\n\n# API Testing\n\nHere all backend testing and database connection will be analysed and validated though plataforms lile Postman, Insomnia and/or, as used here, Thunder Client (VS Code extention). The principle of API testing it to validate servers requirements linking it to the GUI's expected behaviour.\n\n## Thunder Client\n\nThunder client is a VS Code extention used to manage and test server queries without the need of an external application like Postman and/or Insomnia.\n\n**IMPORTANT:** Thunder Client execute requirements and validations but it cannot deal with data workload properly so it will be converted to robot as well for automated testing. Although Thunder Client can still be usefull and interesting for API Testing.\n\n### Collections\n\nAll start by creating a collection like the \"Getgeeks\" created in here.\n\nTo create a collection:\n\n1. Open the Collections tab;\n2. Select the 3 lines buttom on the right of the filter field;\n3. Select to create a New Collection or to Import an existing according to your needs.\n\n**Obs.:** selecting the \"...\" button on the right of a collection will display it's possible options/actions.\n\n### HTML Methods\n\nAll HTML requests use methods in order to understand what it should actually do. Some of these requests are:\n\n- GET\n- POST\n- PUT\n- DELETE\n\n**Note:** all new created request come with GET as standart, change it to your needs.\n\n### Status Codes\n\n[Mozilla Web Docs - Status Code](https://developer.mozilla.org/pt-BR/docs/Web/HTTP/Status?_gl=1*1uzgpt1*_ga*MTQyNjEyMjEwMi4xNjcxNDYzODUw*_ga_37GXT4VGQK*MTY3ODA3NjI5MC42NS4xLjE2NzgwNzcxNDYuMC4wLjA.)\n\n1. Informative responses (100 - 199)\n2. Success responses (200 - 299)\n3. Redirectioning (300-399)\n4. Client errors (400 - 499)\n5. Server errors (500 - 599)\n\n**Notes:**\n200 - OK, everything was executed correctly\n400 - Credentials and/or information do not exist o\n401 - Credentials and/or information exists but is not authorized or is invalid\n\n**OBS.:** devtools can be used in web applications in order to get all required requests information\n\n### Tests\n\n#### Validating token\n\nToken validation is possible by verifying token's character count (in this case 140) and then using the function .lengh indicating the json token inside the tests tab from thunder client: `json.token.length` and the value of character it should receive (140 characters in our case)\n\nIn addition to it we can also validate if the token is expired or not by using `json.expires_in` with the value of days until it expires (10d for the application we're considering)\n\nTo call the elemts it's very simple we just need to look for it's name displayed inside the response, then consider the output type and describing the functions we want it to use after the . like in the where we're creating our own json queries.\n\n#### Front-end x back-end\n\nAPI validation is important eventhough the front-end already validate and blocks API requirements when the informations or inputs don't meet expectancies, this is important because eventhough that the application works fine, when sharing or delivering the API forward to a customer or client it must consider all succesfull and alternative scenarios validations to behave as expected.\n\n## Automating API Tests with robot\n\nThe API tests created until now we're basically done manually throw Thunder Client, although it's possible to automate them using robot as well, in order to do it:\n\n1. First of all we'll need to use a library that will allow us to send our HTTP requests from robot. In here we'll be using the library [HTTP RequestsLibrary (Python)](https://github.com/MarketSquare/robotframework-requests#readme)\n\n**OBS.:** this can be found by filtering HTTP resources inside [Robot Framework webpage](https://robotframework.org/)\n\n2. We'll then import the installed library to our [Sessions test file](../backend/users/tests/Sessions.robot) where we're going to define the automation.\n\n```\n*** Settings ***\nDocumentation       Session route test suite\n\nLibrary    RequestsLibrary\n```\n\n3. We add our requests with all the body and header data from the API and also inform the path in it:\n\n```\n*** Test Cases ***\nUser session\n  ${payload}    Create Dictionary    email=test2@email.com    password=pwd123\n  ${header}    Create Dictionary    Content-Type=application/json\n\n  POST    ${API_USERS}/sessions    data=${payload}    headers=${header}\n```\n\n4. After all we'll add our validations:\n\n```\n*** Test Cases ***\n    ...\n\n    Status Should Be    200    ${response}\n```\n\n**OBS.:** header variable won't be needed here because the Request Library have a function json that already converts information to json, eliminating the need to inform the header's content-type. Considering it we'll remore the header and change the data function for the new json one.\n\nThe new test case will be:\n\n```\n*** Test Cases ***\nUser session\n    ${payload}    Create Dictionary    email=test2@email.com    password=pwd123\n\n    ${response}    POST    ${API_USERS}/sessions    json=${payload}\n\n    Status Should Be    200    ${response}\n```\n\n5. And all the other validations as well:\n\n```\n*** Test Cases ***\nUser session\n    ${payload}    Create Dictionary    email=test2@email.com    password=pwd123\n\n    ${response}    POST    ${API_USERS}/sessions    json=${payload}\n\n    Status Should Be    200    ${response}\n\n    ${size}    Get Length    ${response.json()}[token]\n    ${expected_size}    Convert To Integer    140\n\n    Should Be Equal    ${expected_size}    ${size}\n    Should Be Equal    10d    ${response.json()}[expires_in]\n```\n\n**OBS.:** it's important to consider using ${expected_size} integer convertion once the Get Length function returns a integer number of characters and Should Be Equal function expects a string by default.\n\n### Alternative scenarios (sad paths)\n\nRequest Library returns error whenever the request response differs from status 200 causing the execution to end before the actual validation, in order to allow the validation of alternative scenarios (that are intended to return errors) we need to add a new argument `expected_status=any` to the response:\n\n```\n${response}    POST    ${API_USERS}/sessions    json=${payload}    expected_status=any\n```\n\nThis allows us to move on to the next steps of the test regardless of response status.\n\n## Sessions.robot\n\nTo use the template we'll create variables to each of our test cases inputs. Here we're using the \u0026 in order to use each variable as a dictionary for our test cases\n\n## Users.robot\n\nWhile adding a new user we need to keep in mind that the user can only be registered once. This means that using the same payload won't work. One option to it is using faker to generate random payloads which causes massive information input inside the database, making it impracticable. Another option is to create Delorean that refresh the database before every execution as we did for the front-end, although as where dealing with API requests, there's an even better way to do it by defining DELETE requests to the API while executing it.\n\nIn order to do it:\n\n1. First we execute the deletion manually to visualize the API Request\n2. From the request we get the token that is generated while the user is created, it will give each created user one specific id\n3. After all that we add new steps to our [User.robot](../backend/users/tests/Users.robot) suite to identify the token and execute the delete request\n\n**OBS.:** it's possible to visualize the id analysing the encoder web token with [JWT](https://jwt.io/)\n\nAnd then we add all that to our test case:\n\n1. We define a new dictionary to fill the email and password with New User factory's data:\n\n```\n*** Test Cases ***\nAdd new user\n    ${user}    Factory New User\n\n    # Getting the user token\n    ${payload}    Create Dictionary    email=${user}[email]    password=${user}[password]\n    ${response}    POST Session    ${payload}\n```\n\n2. We get the token from API's body as json:\n\n```\n${token}    Set Variable    Bearer ${response.json()}[token]\n```\n\n3. Define what is the desired action/request to the API, validating it's response:\n\n```\nDELETE User    ${token}\n\n${response}    POST User    ${user}\nStatus Should Be    201    ${response}\n```\n\n4. Once this scenario will only happen for existing users, we'll add our delete request to a condition statement allowing validation only if the user actually exists:\n\n```\nIF  \"200\" in \"${response}\"\n    # We also need to inform the Bearer carrier as well\n    ${token}    Set Variable    Bearer ${response.json()}[token]\n\n    # Delete from /user\n    DELETE User    ${token}\nEND\n```\n\n**OBS.:** this way once the user exists, validation will be executed, if not return the status code\n\n### Validating ID\n\nAfter considering the user token we now should have an id for each registered user, and then we should be able to validate it, although this id should be a positive number then we'll validate it as well:\n\n1. First we've created a variable to store the received id from API's response:\n\n```\n${user_id}    Set Variable    ${response.json()}[id]\n```\n\n2. Then we added the condition that the id should be greater than 0:\n\n```\nShould Be True    ${user_id} \u003e 0\n```\n\n**OBS.:** this is important to validade when considering the usage of those IDs in the application, negative and/or alphanumeric values for ID may cause problems while using it.\n\n### POST Request\n\nA POST Request to the API will allow us to insert new information to the database by executing the defined actions. For POST Requests we need to enter the body so the information can be created\n\n### GET Request\n\nA GET Request to a route from the API will try to return infos from the desired path. Differently from the POST Request, executing a GET Request don't need body information yet we still need to identify the user id through the header's authorization token\n\nTo get the token from devtools:\n\n1. With a logged user, go to application\n2. Find the session storage, you'll find the token and all user informations\n\n**Note:** Looking into POST header's response or executing the login through API requests will return the authentication token as well.\n\nTo define it inside the GET Request:\n\n1. Go to Auth tab inside the request\n2. Select the Authentication carrier (in this case Bearer)\n3. Paste the token\n\n**OBS.:** token identification is necessary for allowing user access through the application.\n\nWhile executing the GET Request its important to keep in mind that already registered users won't be registered again instead the method will only log in and return all users information.\n\n**IMPORTANT:** password changes can break the test so we need to have password data control in order to make it work properly. One way to deal with scenarios that have constant password changes is to add the `Remove User    ${user}` function to the test case, this way everytime the test case runs a new user is created before actually executing its steps. Here we're useing standard passwords so this won't be needed.\n\n#### Validating user infos\n\nInside GET HTML method we can also validate users information. Considering all the standard expected fields we define the expected response versus the actual returned data:\n\n```\nShould Be Equal As Strings    ${user}[name]    ${response.json()}[name]\nShould Be Equal As Strings    ${user}[email]    ${response.json()}[email]\n\nShould Be Equal As Strings    None    ${response.json()}[whatsapp]\nShould Be Equal As Strings    None    ${response.json()}[avatar]\nShould Be Equal As Strings    False    ${response.json()}[is_geek]\n```\n\n**OBS.:** it's important to remember that whatsapp and avatar data is null for standard, considering the response is a string value we use the expected responses as none, and False for is_geek once it is the standard responsem.\n\n`Dictionary Should Contain Value    ${response.json()}   ${user}[name]` keyword can also be used for fields validation, although it just validate if the field exists inside users information. It's not a best practice to use it once the actual field value isn't being validated.\n\n### DELETE Request\n\nDelete request user authenticator in order to identify which element should be deleted. It's successful execution returns a status code 204 (No Content), although it do not garantee that the user ou data was actually deleted so we need additional validation steps to confirm that.\n\nOne way of verifying it the user was deleted for sure is calling the GET request once more with the same (deleted user) token authenticator. If all behave as expected and the user was successful deleted then a status code 404 (not found) will be returned\n\n```\nRemove user\n    # Given that the user exists in the system\n    ${user}    Factory Remove User\n    # Remove User    ${user}\n    POST User    ${user}\n\n    # And I have this user token\n    ${token}    Get Token    ${user}\n\n    # When executing a delete request to /users\n    ${response}    DELETE User    ${token}\n\n    # Status code 204 (no contet) should be returned\n    Status Should Be    204    ${response}\n\n    # And while executing a new GET request to /users with the same token should return status code 404 (not found)\n    ${response}    GET User    ${token}\n    Status Should Be    404    ${response}\n```\n\n**OBS.:** A new factory was created exclusive for the remove user test case.\n\n### PUT Request\n\nPUT request allows entry of data either to create or change data from users. Desired content should the entered inside the body of the request and it will replace as well as the user token so all previous existing information of it will be replaced for the new one inside the PUT request body.\n\nAfter allowing PUT request execution\n\n```\n    ${user}    Factory Update User\n    POST User    ${user}[before]\n\n    ${token}    Get Token    ${user}[before]\n\n    ${response}    PUT User    ${token}    ${user}[after]\n\n    Status Should Be    200    ${response}\n```\n\nWe send a new GET request to validate all new changes\n\n```\n    ${response}    GET User    ${token}\n\n    Should Be Equal As Strings    ${user}[after][name]    ${response.json()}[name]\n    Should Be Equal As Strings    ${user}[after][email]    ${response.json()}[email]\n\n    Should Be Equal As Strings    ${user}[after][whatsapp]    ${response.json()}[whatsapp]\n    Should Be Equal As Strings    ${user}[after][avatar]    ${response.json()}[avatar]\n    Should Be Equal As Strings    False    ${response.json()}[is_geek]\n```\n\n**OBS.:** PUT requests operates similarly to POST requests, we just need the token to identify what should be changed.\n\n## Geeks\n\nInside the applation there's service providers that needs to be added and stored in a list which should be displayed inside the page with all their information. In order to garantee geeks functionality we'll test for both it's creation and displayability inside the page\n\n### Validating geek creation\n\nHere we're going to validate the creation of geek user inside the application. Geeks are service providers that will add informations that allow people to reach them old for their services.\n\nHere we basically:\n\n1. Created the [Geek test suite](../backend/tests/Geeks.robot)\n2. Created a new [GeekRoute](../backend/resources/routes/GeeksRoute.robot) to manage our geeks test suite requests\n3. Created a new factory to geek users with will generate all our inputs\n4. We then clear our user in order to restart the API\n5. Then we send POST and GET requests with users token in order to create and validate users data\n\n**OBS.:** values like cost may need convertion once it needs to be identical in order to pass the test. Here we're going to convert both user data and API response with `${expected_float}    Convert To Number    ${user}[geek_profile][cost]` and `${got_float}    Convert To Number    ${response.json()}[cost]`.\n\n### Getting geeks list\n\nHere we're validating getting the geeks list for all displayed information visualization.\n\nFirst we'll generate a new user and through a normal user point of view we'll look for\n\n```\nGet Geek List\n    ${user}    Factory Search For Geeks\n    POST User    ${user}\n\n    ${token}    Get Token    ${user}\n\n    ${response}    GET Geeks    ${token}\n\n    Status Should Be    200    ${response}\n```\n\nIn order to validate the list we'll use the log function to get and validate the length of the list, once all geeks will be shown inside the same list.\n\n```\n    Log    ${response.json()}[Geeks]\n\n    ${total}    Get Length    ${response.json()}[Geeks]\n    Should Be True    ${total} \u003e 0\n```\n\n**OBS.:** once we need to log in inside a user to visualise geeks list we create a dependency, so we're going to improve it to a more independent test case.\n\n1. We'll enter geeks information to the factory:\n\n```\n     'geeks': [\n            {\n                'name': 'Riri Willians',\n                'email': 'riri@marvel.com',\n                'password': 'pwd123',\n                'geek_profile': {\n                        'whatsapp': '11999999999',\n                        'desc': 'Seu computador está lento? Reiniciando do nada? Talvez seja um vírus, ou algum hardware com defeito. Posso fazer a manutenção no seu PC, formando, reinstalando o SO, trocando algum componente físico e porque não remover o baidu ou qualquer outro malware.',\n                        'printer_repair': 'Não',\n                        'work': 'Presencial',\n                        'cost': '100'\n                }\n            },\n            {\n                'name': 'Arnim Zola',\n                'email': 'zola@hidra.com',\n                'password': 'pwd123',\n                'geek_profile': {\n                        'whatsapp': '21999999999',\n                        'desc': 'Instalo Distros Ubuntu, Debian, ElementaryOS, PopOS, Linux Mint, Kurumin, Mandrake, Connectiva, Fedora, RedHat, CentOS, Slackware, Gentoo, Archlinux, Kubuntu, Xubuntu, Suze, Mandriva, Edubuntu, KateOS, Sabayon Linux, Manjaro Linux, BigLinux, ZorinOS, Unit',\n                        'printer_repair': 'Não',\n                        'work': 'Ambos',\n                        'cost': '110'\n                }\n            },\n            {\n                'name': 'Reed Richards',\n                'email': 'reed@fantastic4.com',\n                'password': 'pwd123',\n                'geek_profile': {\n                        'whatsapp': '31999999999',\n                        'desc': 'Instalado e faço manutenção em qualquer tipo de impressora, matricial padrão, matricial fiscal, a jato, a cera, a laser e até 3D. Também faço remold de fitas coloridas para Epson LX300. ',\n                        'printer_repair': 'Sim',\n                        'work': 'Remoto',\n                        'cost': '120'\n                }\n            }\n        ]\n```\n\n2. We'll set a behaviour for each of the geeks in order to create them:\n\n```\n    FOR    ${geek}    IN    @{data}[geeks]\n        POST User    ${geek}\n        ${token}    Get Token    ${geek}\n\n        POST Geek    ${token}    ${geek}[geek_profile]\n    END\n```\n\n3. Then we validate the token, status code response and length of the list\n\n```\n    ${token}    Get Token    ${data}[user]\n\n    ${response}    GET Geeks    ${token}\n    Status Should Be    200    ${response}\n\n    ${total}    Get Length    ${response.json()}[Geeks]\n    Should Be True    ${total} \u003e 0\n```\n\n## Regression test\n\nWe'll define a new run.sh file with the command `robot -d ./logs tests` and then activate it with `chmod +x run.sh`. By running `./run.sh` into the terminal we'll execute all the described commands inside of it.\n\n**OBS.:** in this case all backend tests.\n\n## Github Actions\n\nGitHub Actions allow continuous integration with lots of cloud services. Since we're using Robot Framework we'll create our own template.\n\nGithub actions allow the creation of workflows that will execute jobs with all the automation test steps inside of it.\n\n### Creating python dependencies file\n\n1. Create a file to store all dependencies generated with `pip freeze \u003e requirements.txt`\n2. Remove all unnecessary libraries\n\n**OBS.:** now requirements.txt should be available inside the projects file. Running `pip install -r requirements.txt` can be used to install all required libraries/dependencies from the file.\n\n### Regression backend workflow\n\nWe'll define when we want this workflow to be executed:\n\n```\non:\n  push:\n    branches: [ \"master\" ]\n```\n\nThen we'll define our jobs which stands for the steps it needs to execute:\n\nEntering running informations\n\n```\n    runs-on: ubuntu-latest\n    strategy:\n      fail-fast: false\n      matrix:\n        python-version: [\"3.9\"]\n```\n\nAnd our actual steps\n\n```\n    steps:\n    - uses: actions/checkout@v3 // add all code to the virtual machine\n    - name: Set up Python ${{ matrix.python-version }}\n      uses: actions/setup-python@v3 // install the python version described inside the with\n      with:\n        python-version: ${{ matrix.python-version }}\n    - name: Install dependencies\n      run: |\n        python -m pip install --upgrade pip // keeps pip package updated\n        pip install build // install all needed dependencies and libraries to run the project\n        pip install -r requirements.txt // install all dependencies from requirements.txt\n    - name: Run API Tests\n      run: |\n        chmod +x ./backend/run.sh // gives permition to execute run.sh file\n        cd backend/ \u0026\u0026 ./run.sh // move to backend file and execute ./run.sh\n```\n\n**OBS.:** the workflow starts inside the basic project file (projec), also we need to give permission to our run.sh file adding the `chmod +x ./backend/run.sh` before the run.\n\nWe then execute the commit and them check for the status inside actions.\n\n### Uploading robot report\n\nAfter running all tests a report is generated, here we're going to import our html report file to the workflow.\n\n1. We're going to add Upload a Build Artifact resource to the regression workflow\n\n````\n    - name: Upload API Test Results\n      uses: actions/upload-artifact@v3.1.2 // install the artifact dependencies\n      if: always() // define when to execute it, in this case always\n      with: // define the name and from where the data would come from\n        name: api-reports\n        path: backend/logs\n````\n\n2. Visualize the artifacts generated from execution.\n\n**OBS.:** here we can now download the report.\n\n### jUnit reports\n\nWe're going to use jUnit reports format to integrate reports to the pipeline allowing outputs visualization through dashboard.\n\n1. Adding Test Reporter to regression workflow:\n\n````\n    - name: Test Reporter\n      uses: dorny/test-reporter@v1.6.0\n      with:\n        name: API Test Result\n        path: backend/logs/xunit.xml\n        reporter: java-junit // specify the format of the test result\n````\n\n### Frontend workflow\n\nHere we're going to create a pipelane to whom execute all our frontend tests. We'll use the same basic structure from the backend regression workflow just changing:\n\n1. Files path to frontend/\n2. Adding the jUnit report generation command to run.sh/ `pabot -x xunit.xml -d ./logs -v BROWSER:chromium -v HEADLESS:true -e smoke tests`\n3. Adds a `needs: api_tests`step so that the frontend workflows awaits for the backend workflow to be successfully tested in order to run\n\n#### Adding configuration and dependencies\n\nIn here we're going to install all node and libraries required for our test to run\n\n````\n    - name: Set up Node 14\n      run: |\n      curl -fsSL https://deb.nodesource.com/setup_14.x | sudo -E bash - \u0026\u0026\\\n      sudo apt-get install -y nodejs\n    - name: Preparation\n      run: |\n        sudo apt-get update\n        sudo apt-get install -y xvfb ca-certificates net-tools netcat gnupg ffmpeg libgtk-3-0 libgdk-pixbuf2.0-dev libxcomposite-dev libdbus-glib-1-2 libatk-bridge2.0-0 wget libgbm1 libnss3 libxss1 libasound2\n````\n\n**OBS.:** Evaluate Javascript keyword was changed back to Execute Javascript once we rolled back the library and robot versions to allow better compatibility.\n\n### Important commands\n\n- `on` - defines when to run the workflow\n- `runs-on` - defines the virtuar machine agent whom should run the workflow\n- `fail-fast` - while false allows the flow to continue after a failed response, while true end the execution once a fail is respose is returned.\n- `pip install build` - install the project dependencies inside the virtual machine\n\n\n## Geek search\n\nHere we're going to automate the geek search by generating a base user through API requests and then validating it through web page elements.\n\nFirst we'll create 3 API requests into Thunder Client to manually test: user creation (POST New user), login (POST Session) and geek registry, both for alien (POST Alien Geek Profile) and common geeks (POST Common Geek Profile).\n\nBy doing that we now have all necessary steps to automate our tests with predefined independent users.\n\n### Creating the geek user seed\n\nOnce we now have our steps defined we need to prepared the environment with users data. We'll create 2 new users within users factory:\n\nOne Alien Geek that would repair printers\n\n````\n        'search_alien': {\n            'name': 'Dok',\n            'lastname': 'Ock',\n            'email': 'dok@oscorp.com',\n            'password': 'pwd123'\n        }\n````\n\nAnd one common geek that wouldn't repair printers\n\n````\n         'search_common': {\n            'name': 'Peter', \n            'lastname': 'Parker',\n            'email': 'peter@oscorp.com',\n            'password': 'pwd123'\n        }\n````\n\n**OBS.:** there's a business rule that defined geeks that execute printer repair as Aliens and the ones that cannot as common geeks so this differentiation is important.\n\n### Search geeks test suite\n\nHere we'll automate all te steps we manually executed through thunder client. To do it we'll:\n\n1. Create a new [SearchForGeeks.robot file](frontend/tests/SearchForGeeks.robot) with all our test steps\n2. Create a new [service](frontend/resources/Service.robot) which will contain all our API services\n3. Then we define the steps to get all needed information to execute the test\n\n````\n*** Settings ***\nDocumentation    Services API\n\nLibrary    RequestsLibrary\n\n*** Variables ***\n${API_GATEWAY}    https://geeks-api-andre.fly.dev\n\n*** Keywords ***\nGet Token\n    [Arguments]    ${user}\n\n    ${payload}    Create Dictionary\n    ...    email=${user}[email]\n    ...    password=${user}[password]\n\n    ${response}    POST    ${API_GATEWAY}/sessions    json=${payload}\n\n    ${token}    Set Variable    Bearer ${response.json()}[token]\n\n    [Return]    ${token}\n````\n\n4. And create our test case steps as well\n\n````\n*** Test Cases ***\nSearch for Alien Geek\n    ${alien}    Factory User   search_alien\n\n    Get Token    ${alien}\n````\n\n**OBS.:** here we're only validating it the user was created and is actually registered.\n\n### Turning user into geek\n\nOnce that we now have a brand new and indepentend registered user we'll create steps to turn him into a service provider. For this we also need to consider both alien and common geeks.\n\n**OBS.:** where adding Service to our keywords in order to indicate that it will consume the API service\n\n### Adding searcher\n\nWe'll need to login with a different user in order to search and visualize available geeks so we're going to:\n\n1. Add a new searcher factory:\n\n````\n        'searcher': {\n            'name': 'Johnny',\n            'lastname': 'Lawrence',\n            'email': 'johnny@cobrakai.com',\n            'password': 'pwd123'\n        }\n````\n\n2. Identifing it inside the page using pre defined actions\n\n````\n    Go To Geeks\n    Fill Search Form    ${EMPTY}    Formato PC\n    Submit Search Form\n````\n\n**OBS.:** we'll use a conditionals inside the fill search form action to only execute the validation if there's data inside of it. As a optional field it's important to allow it to be empty as well.\n\n````\nFill Search Form\n    [Arguments]    ${target_option}    ${target_text}\n\n    IF    '${target_option}'\n        Select Options By    id=printer_repair    value    ${target_option}\n    END\n\n    Fill Text    id=desc    ${target_text}\n````\n\n#### We have a bug\n\nAutomation executes each step a lot faster than a manual execution, sometimes this can cause issues in the system or break displayed elements. For this Wait for elements state and sleep can be used in order to allow a delay in the execution making it closer to reality. Although this is still a bug that must be fixed.\n\nAlso sleep isn't recommended to be used once it freezes all execution for that time.\n \n**IMPORTANT** while considering page object models it would be necessary to create a new file which would be related to the search geek page once it's one element inside of this page.\n\n### Validating geek list\n\nWe're going validate all geeks information using xpath to find the article elements that is what refers to the hole component, and them we're going to validate each of it's elements individually.\n\n**OBS.:** CSS Selector cannot be used in here once it would return unespecific elements. In here we're trying to find a generic element and to make it specific xpath allow us to find an inside elements and them go back to the origin keeping it's specificity. For example:\n\n1. Finding the geek article element with class geek-item:\n\n````\n    Get Element    xpath=//strong[contains(text(), 'Dok Ock')]/../../..\n````\n\nHere we start for the text inside of the element that would be unique and them we return back all files till the article where it's possible to access all other elements like \"Valor/hora\",\"Description\", etc. Plus we also validate the name.\n\n2. Now we validate each element individually:\n\n````\n    ${target}    Get Element    xpath=//strong[contains(text(), 'Dok Ock')]/../../..\n\n    Get Text    ${target}    contains    Atendimento ${geek}[geek_profile][work]\n    Get Text    ${target}    contains    ${geek}[geek_profile][desc]\n    Get Text    ${target}    contains    ${geek}[geek_profile][cost]\n````\n\nThe work data is converted to lowercase so we also need to convert it by using:\n\n````\n    ${work}    Convert To Lower Case    ${geek}[geek_profile][work]\n\n    Get Text    ${target}    contains    Atendimento ${work}\n````\n\n**OBS.:** for Convert To Lower Case to work we need to import `Library  String` as well.\n\n3. Then for last we'll validate the alien emoji to see if the user is actually a alien geek:\n\n````\nAlien Icon Should Be Visible\n    Get Text    ${target_geek}    contains    👽\n````\n**OBS.:** Robot can understand emojis so we just need to copy and paste it as value inside the Get Text function.\n\nHere it's important to remember that Alien Icon Should Be Visible do not cannot find elements in the page so we'll define the variable ${target_geek} which contain the selector locator as a suite variable, making it possible to be used across different test cases as long as they're in the same test suite.\n\n````\n    ${target_geek}    Get Element    xpath=//strong[contains(text(), 'Dok Ock')]/../../..\n    Set Suite Variable    ${target_geek}\n````\n\n---\n\n# Usefull terminal commands\n\n- robot -l NONE tasks/Delorean.robot - runs the file without generating log. It's mostly used for tasks.\n- robot --help - show all possible commands with detailed description\n- robot -o NONE - runs the file without generating output\n- robot -r NONE - runs the file witout generating report\n- robot -i tags_name - runs only test cases that contains the defined tag\n- robot -e tags_name - excludes tests that contains the defined tag\n- chmod +x run.sh - gives file \"run.sh\" permition to be executed. Turns the file executable. **Works for linux based terminals**\n- ./run.sh - runs a file inside the actual file path\n- run.bat - runs terminal shortcuts **This works for MS-DOS based terminals**\n- .lower() - python method to turn text onto lower case only\n- fullPage - it's a browser library feature normally used for screenshots, expects true (to show the hole page) or false (to show only a piece of the page)\n- robot -v BROWSER:chromium -v HEADLESS:true - define a variable and gives a value to it\n- pip freeze \u003e requirements.txt - frozen python libraries version and add it to the file requirements.txt\n- pip install -r requirements.txt - install all dependencies from requirements.txt file\n- robot --help - open robot command line options with description\n- robot -x xunit.xml - display report in jUnit format\n\n## Git\n\ngit init - initialize the git repository\ngit status - return repository and files status\ngit add . - add all pending changes to the staged file\ngit commit -m 'message' - commit all staged changes and define a message to the commit\ngit pull - pull github updates to the local environment\ngit push - push local updates to the github environment\n\n## Linux\n\nfind ./logs/pabot_results -type f -name \"\\*.png\" - will find all files that have .png extension\ncp $(find ./logs/pabot_results -type f -name \"\\*.png\") ./logs/browser/screenshot - copy the list to the screenshot file\n\n## chmod +x run.sh\n\nfiles.sh only runs in linux compatible terminals, for terminals MS-DOS based we need to use a file.bat which will then need additional changes. **For example:**\n\nFiles path uses \\ instead of /:\n\n```\nrobot -l NONE -o NONE -r NONE tasks\\Delorean.robot\nrobot -d ./logs tests\\Signup.robot\n```\n\nAnd also a different way of calling it:\n\n```\nrun.bat\n```\n\n## Request Library\n\n- `json=${variable}` - convert the request content-type to json\n\n# Important links\n\n- [Web application](https://geeks-web-andre.fly.dev/signup)\n- [ElephantSQL](https://api.elephantsql.com/console/51ccfaa2-d261-4503-b858-da3b75125790/browser?#)\n- [QAcademy course](https://app.qacademy.io/area/produto/item/149046)\n- [Faker Library](https://pypi.org/project/Faker/)\n- [Typora md editor](https://typora.io/)\n- [JSON Web Tokens](https://jwt.io/)\n\n## Pabot\n\n- [Pabot Parallel Executor](https://pabot.org/)\n\nIs a parallel executor that runs several tests at once based on the number of available processors. It's important to note that using cloud environment only gives 1 available processor so it won't work for cloud environment.\n\n### Pabot screenshot path improvement\n\nThe problem of Pabot is that the Browser and/or Selenium libraries cannot access the screenshot file path so the report won't display the screenshot images.\n\nIn order for the screenshots to be displayed correctly we'll rearrange the output files in a way that the browser lib can understand. To do it:\n\n1. We added 4 new preparation steps for the run.sh terminal shortcut that will delete old files and create brand new ones\n\n```\n# delete the old browser file and create a brand new one\nrm -rf ./logs/browser\nmkdir ./logs/browser\nmkdir ./logs/browser/screenshot\n\n# list all type png files from pabot_results and copy it to the new screenshot file\ncp $(find ./logs/pabot_results -type f -name \"*.png\") ./logs/browser/screenshot\n```\n\n2. We created a new faker [Utility generator](resources/Utility.py) that will create unique ids to each screenshot\n\n```py\nfrom faker import Faker\nfake = Faker()\n\n\ndef screenshot_name():\n    return fake.sha1()\n```\n\n3. And then added this generated id function to a new variable inside our test case closer, defining a new argument filename to set a name to each screenshot file that will receive the random generated id.\n\n```\nAfter Test\n    ${shot_name}    Screenshot Name\n    Take Screenshot    fullPage=true    filename=${shot_name}\n```\n\nBy doing this we now refresh browsers lib screenshot file before each execution, saving all report screenshots inside a brand new file with unique identifiers, making it possible to visualize the screenshots inside the report even when using the pabot.\n\n## Typora\n\n[Typora](https://typora.io/) is a paid md files editor that allows easier ways to create readable and writable md notes\n\n**Obs.:** i'm not using it in here because it's now paid","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fandrelkj%2Fgravidadezero","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fandrelkj%2Fgravidadezero","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fandrelkj%2Fgravidadezero/lists"}