<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by André on Medium]]></title>
        <description><![CDATA[Stories by André on Medium]]></description>
        <link>https://medium.com/@andrebolivia?source=rss-cd82928ec4a------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*HHQIoVdjLZxZ_kZHTN_tvg.png</url>
            <title>Stories by André on Medium</title>
            <link>https://medium.com/@andrebolivia?source=rss-cd82928ec4a------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Wed, 03 Jun 2026 00:05:29 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@andrebolivia/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[I Built an MCP Server for AnythingLLM — Here’s How AI Agents can Manage Your Entire Knowledge Base]]></title>
            <link>https://andrebolivia.medium.com/i-built-an-mcp-server-for-anythingllm-heres-how-ai-agents-can-manage-your-entire-knowledge-base-26c791e3214b?source=rss-cd82928ec4a------2</link>
            <guid isPermaLink="false">https://medium.com/p/26c791e3214b</guid>
            <category><![CDATA[anythingllm]]></category>
            <category><![CDATA[llm-agent]]></category>
            <category><![CDATA[python]]></category>
            <category><![CDATA[llm]]></category>
            <category><![CDATA[agentic-rag]]></category>
            <dc:creator><![CDATA[André]]></dc:creator>
            <pubDate>Thu, 26 Feb 2026 14:09:26 GMT</pubDate>
            <atom:updated>2026-02-26T14:09:26.340Z</atom:updated>
            <content:encoded><![CDATA[<h3>I Built an MCP Server for AnythingLLM — Here’s How AI Agents Can Now Manage Your Entire Knowledge Base</h3><figure><img alt="Accessing AnythingLLM with MCP" src="https://cdn-images-1.medium.com/max/1024/1*Q6oCGyP5_WvtAiPe4uLGTg.png" /><figcaption>Accessing AnythingLLM with MCP</figcaption></figure><h3>TL;DR</h3><p>I built an open-source MCP server that lets AI agents (GitHub Copilot, Claude Desktop, Cursor) control AnythingLLM programmatically — managing workspaces, chatting with documents, uploading content, running vector searches, and inspecting system settings. All through 23 typed tools, using a single Python file and stdio transport.</p><p><strong>Repository:</strong> <a href="https://github.com/andreperez/anythingllm-mcp">github.com/andreperez/anythingllm-mcp</a></p><h3>The Problem: AI Assistants Can’t Reach Your Knowledge Base</h3><p>If you use AnythingLLM to organize documents into workspaces, chat with them, and run vector searches, you probably interact with it through its web UI. That’s fine for manual work — but what happens when you want an AI assistant to do it <em>for you</em>?</p><p>Imagine telling your coding agent:</p><blockquote><em>“Search my research papers workspace for everything related to neurology, then summarize the top results.”</em></blockquote><p>Or:</p><blockquote><em>“Create a new workspace called ‘Q1 Reports’, upload this financial document, embed it, and give me a summary.”</em></blockquote><p>Without a bridge between the AI agent and AnythingLLM, you’d have to do each step manually: open the UI, click through menus, copy and paste results back. That’s the gap I wanted to close.</p><h3>The Solution: Model Context Protocol (MCP)</h3><p>The <strong>Model Context Protocol</strong> (MCP), created by Anthropic, is an open standard that allows AI models to interact with external tools and data sources through a structured interface. Think of it as a USB-C port for AI — a standardized way for any compatible client to discover and call tools from any compatible server.</p><p>An MCP server exposes <strong>tools</strong> (functions the AI can call), <strong>resources</strong> (data the AI can read), and <strong>prompts</strong> (reusable message templates). The client — VS Code, Claude Desktop, Cursor, or any MCP-compatible host — discovers them automatically and uses them in context.</p><p>I built an MCP server that wraps the entire AnythingLLM REST API into 23 typed tools that any MCP client can call.</p><h3>What the Server Does</h3><p>The server covers the full lifecycle of knowledge management in AnythingLLM:</p><h3>Workspace Management</h3><ul><li>List, create, update, delete workspaces</li><li>Configure chat mode (conversational vs. query-only), temperature, similarity threshold, and LLM provider per workspace</li></ul><h3>Chat &amp; Threads</h3><ul><li>Send messages to any workspace (chat or query mode)</li><li>Retrieve conversation history with full metadata</li><li>Create and manage threads for parallel conversation tracks within a workspace</li></ul><h3>Document Operations</h3><ul><li>List all documents in the system</li><li>Upload content from URLs (web pages, PDFs) — AnythingLLM fetches and processes them</li><li>Upload raw text directly as a document</li><li>Update embeddings — move documents in or out of a workspace’s vector store</li></ul><h3>Vector Search</h3><ul><li>Semantic search across embedded documents with configurable top_n and similarity thresholds</li></ul><h3>System &amp; Administration</h3><ul><li>Inspect system settings — LLM provider, embedding engine, vector DB, model preferences</li><li>Get vector counts across the system</li><li>Export all chat logs for backup or analysis</li><li>List embed configurations (public chat widgets)</li><li>List available models via the OpenAI-compatible endpoint</li></ul><h3>Architecture: Single File, Zero Complexity</h3><p>The entire server is a single Python file (anythingllm_mcp.py, ~660 lines) built on top of:</p><ul><li>FastMCP — the high-level Python SDK for MCP servers</li><li>httpx — async HTTP client for calling AnythingLLM’s REST API</li><li>Pydantic — for type validation and schema generation</li></ul><pre>┌──────────────────────┐       stdio        ┌──────────────────────┐<br>│  MCP Client          │◄──────────────────►│  anythingllm_mcp.py  │<br>│  (VS Code / Claude / │   JSON-RPC         │  (FastMCP Server)    │<br>│   Cursor)            │                    │                      │<br>└──────────────────────┘                    └──────────┬───────────┘<br>                                                       │ httpx<br>                                                       ▼<br>                                            ┌──────────────────────┐<br>                                            │  AnythingLLM         │<br>                                            │  REST API (:3001)    │<br>                                            └──────────────────────┘</pre><p>Each tool is a decorated async function with full type hints. FastMCP reads the type annotations and docstrings to automatically generate the JSON schemas that clients use for tool discovery. This means the code <em>is</em> the documentation — no separate schema files to maintain.</p><h3>Example: Vector Search Tool</h3><pre>@mcp.tool(<br>    name=&quot;anythingllm_search&quot;,<br>    annotations={<br>        &quot;readOnlyHint&quot;: True,<br>        &quot;destructiveHint&quot;: False,<br>        &quot;idempotentHint&quot;: True,<br>    },<br>)<br>async def search_workspace(<br>    slug: str,<br>    query: str,<br>    top_n: int = 4,<br>    score_threshold: Optional[float] = None,<br>) -&gt; str:<br>    &quot;&quot;&quot;Search for relevant document chunks within a workspace <br>    using vector similarity.&quot;&quot;&quot;<br>    body: dict = {&quot;query&quot;: query, &quot;topN&quot;: top_n}<br>    if score_threshold is not None:<br>        body[&quot;scoreThreshold&quot;] = score_threshold<br>    result = await _api(<br>        f&quot;/workspace/{slug}/vector-search&quot;,<br>        method=&quot;POST&quot;,<br>        body=body,<br>    )<br>    return json.dumps(result, indent=2)</pre><p>The annotations dict tells the client that this tool is read-only and safe to call repeatedly — which helps the AI decide when and how to use it.</p><h3>How to Set It Up (5 Minutes)</h3><h3>Prerequisites</h3><ul><li>Python 3.10+</li><li><a href="https://docs.astral.sh/uv/">uv</a> (modern Python package manager)</li><li>AnythingLLM running (locally or remote)</li><li>An AnythingLLM API key (Settings → Developer)</li></ul><h3>Install</h3><pre>git clone https://github.com/andreperez/anythingllm-mcp.git<br>cd anythingllm-mcp<br>uv sync</pre><h3>Configure Your Client</h3><p>VS Code — Add to your mcp.json (Command Palette → &quot;MCP: Open User Configuration&quot;):</p><pre>{<br>  &quot;servers&quot;: {<br>    &quot;anythingllm&quot;: {<br>      &quot;type&quot;: &quot;stdio&quot;,<br>      &quot;command&quot;: &quot;uv&quot;,<br>      &quot;args&quot;: [<br>        &quot;run&quot;,<br>        &quot;--directory&quot;,<br>        &quot;/path/to/anythingllm-mcp&quot;,<br>        &quot;anythingllm-mcp&quot;<br>      ],<br>      &quot;env&quot;: {<br>        &quot;ANYTHINGLLM_API_KEY&quot;: &quot;${input:anythingllm_api_key}&quot;,<br>        &quot;ANYTHINGLLM_BASE_URL&quot;: &quot;http://localhost:3001&quot;<br>      }<br>    }<br>  }<br>}</pre><p>Claude Desktop — Edit claude_desktop_config.json:</p><pre>{<br>  &quot;mcpServers&quot;: {<br>    &quot;anythingllm&quot;: {<br>      &quot;command&quot;: &quot;uv&quot;,<br>      &quot;args&quot;: [<br>        &quot;run&quot;,<br>        &quot;--directory&quot;,<br>        &quot;/path/to/anythingllm-mcp&quot;,<br>        &quot;anythingllm-mcp&quot;<br>      ],<br>      &quot;env&quot;: {<br>        &quot;ANYTHINGLLM_API_KEY&quot;: &quot;YOUR_API_KEY&quot;,<br>        &quot;ANYTHINGLLM_BASE_URL&quot;: &quot;http://localhost:3001&quot;<br>      }<br>    }<br>  }<br>}</pre><p>Replace /path/to/anythingllm-mcp with the absolute path where you cloned the repository.</p><h3>Real-World Use Case</h3><p>Here’s a concrete example. I had a workspace with academic papers about technical analysis and wanted to find specific content:</p><blockquote><em>Me (in VS Code, using GitHub Copilot): “Search my ‘papers’ workspace for RSI divergence strategies.”</em></blockquote><p>Copilot automatically called anythingllm_search with slug=&quot;papers&quot; and query=&quot;RSI divergence strategies&quot;, got back the top vector-matched document chunks with similarity scores, and then summarized the findings — all without me ever leaving my editor.</p><p>The same workflow works for any knowledge base: internal documentation, legal contracts, research papers, product specs. If it’s in AnythingLLM, the AI can now reach it.</p><h3>Design Decisions Worth Mentioning</h3><p>Why stdio transport? It’s the simplest and most universal. Every MCP client supports it. The server starts as a subprocess of the client, communicates over stdin/stdout, and dies when the client disconnects. No ports to manage, no authentication layer to build on top.</p><p>Why a single file? An MCP server should be easy to audit. With 660 lines in one file, anyone can read the entire codebase in minutes and understand exactly what the AI agent can and cannot do. No hidden abstractions.</p><p>Why tool annotations? The readOnlyHint, destructiveHint, and idempotentHint annotations inform the client about the tool&#39;s behavior. A client can use this to auto-approve read-only tools while requiring confirmation for destructive ones (like delete_workspace). This is a small detail that significantly improves the user experience.</p><p>Why async? AnythingLLM API calls can take time, especially when processing documents or running LLM inference. Async functions with httpx ensure the server doesn&#39;t block while waiting for responses.</p><h3>What’s Next</h3><p>The server currently wraps the most common AnythingLLM API endpoints. Future additions could include:</p><ul><li>File upload support (binary documents, not just URLs and raw text)</li><li>Workspace-level permission management for multi-user instances</li><li>Streaming chat responses</li><li>PyPI distribution for simpler installation (uvx anythingllm-mcp)</li></ul><h3>Try It</h3><p>The project is MIT-licensed and available on GitHub:</p><p><a href="https://github.com/andreperez/anythingllm-mcp">github.com/andreperez/anythingllm-mcp</a></p><p>If you use AnythingLLM as your knowledge base and want your AI assistants to interact with it natively, give it a try. Issues and contributions are welcome.</p><p><em>Specialist in software development and product support. If you found the article useful, follow me on my social networks. If you didn’t like it, or found something wrong, that’s okay… constructive criticism is welcome.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=26c791e3214b" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Usando o restic para realizar backups de forma simples]]></title>
            <link>https://andrebolivia.medium.com/usando-o-restic-para-realizar-backups-de-forma-simples-2e9a5e981372?source=rss-cd82928ec4a------2</link>
            <guid isPermaLink="false">https://medium.com/p/2e9a5e981372</guid>
            <category><![CDATA[backup]]></category>
            <category><![CDATA[automation-tools]]></category>
            <category><![CDATA[restic]]></category>
            <category><![CDATA[backup-software]]></category>
            <category><![CDATA[backup-and-restore]]></category>
            <dc:creator><![CDATA[André]]></dc:creator>
            <pubDate>Sat, 09 Apr 2022 23:27:28 GMT</pubDate>
            <atom:updated>2022-04-09T23:27:28.632Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*jHPGTZ_1b2Oe43Zb" /><figcaption>Photo by <a href="https://unsplash.com/@benjaminlehman?utm_source=medium&amp;utm_medium=referral">benjamin lehman</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p><strong>Introdução</strong></p><p>O tempo é a moeda de troca mais valiosa… talvez não seria se tivéssemos uma vida infinita, mas esse não é o caso. Por isso, quanto mais tempo você gasta com determinado trabalho, com mais afinco você deveria protegê-lo. Imagine a dor de cabeça de ter que recomeçar um trabalho que levou anos para ser concluído. É por isso que existem diversas ferramentas de <em>backup</em>.</p><p>Estas ferramentas possuem recursos e estratégias diferentes, no que tange “o que”, “como” e “quando”. A escolha de tais recursos depende da necessidade do usuário.</p><ul><li>“O que?” — Do que o backup será feito? Disco, partição, ou arquivos?</li><li>“Como?” — Backup completo, diferencial ou incremental? Qual o nível de compressão?</li><li>“Quando?” — Qual a periodicidade?</li></ul><p><strong>O que é o restic</strong></p><p>O <a href="https://github.com/restic/restic"><strong>restic </strong></a>é um programa de backup de código aberto escrito na linguagem de programação <a href="https://go.dev/"><strong>Go</strong></a>, que funciona em diversos sistemas operacionais. É capaz de realizar backups de arquivos, mas não de discos e partições.</p><p>Ele funciona com o conceito de <em>repositório</em> e <em>snapshots</em>, e por isso lembra muito o <a href="https://git-scm.com/"><strong>git</strong></a>.</p><p>Se fossemos comparar “a grosso modo”, a diferença básica é que o <em>restic</em> é exclusivamente voltado para o <strong>controle de backups</strong>, enquanto o <em>git</em><strong> </strong>é exclusivamente voltado para o <strong>controle de versão</strong>. Além disso, o repositório do <em>restic</em> é encriptado e necessita de uma senha, coisa que não existe no git.</p><p>Ainda não existe uma ferramenta gráfica, mas para quem está acostumado com a linha de comando, isso não é um problema. Nada de outro mundo.</p><p>Na minha experiência pessoal, notei que ele é rápido e eficiente na economia de espaço, visto que a cada backup que é realizado, apenas a diferença é salva.</p><p><strong>Pressupostos</strong></p><p>Nível de dificuldade: médio para avançado</p><p>Conhecimentos: linha de comando do seu sistema operacional favorito (Windows, Linux, MacOS, BSDs).</p><ol><li><strong>Instalação</strong></li></ol><p>A instalação é igual uma receita de bolo: basta entrar no site oficial, ler a documentação e seguir os passos. Aqui eu coloquei um resumo de todos os comandos, de acordo com o sistema operacional:</p><pre># Alpine Linux<br> apk add restic</pre><pre># Arch Linux<br> pacman -S restic</pre><pre># Debian, Ubuntu e derivados<br> apt-get install restic</pre><pre># Fedora<br> dnf install restic</pre><pre># MacOS via Homebrew<br> brew install restic</pre><pre># MacOS via MacPorts<br> sudo port install restic</pre><pre># Nix &amp; NixOS<br> nix-env — install restic</pre><pre># OpenBSD<br> pkg_add restic</pre><pre># FreeBSD<br> pkg install restic</pre><pre># OpenSuse<br> zypper install restic</pre><pre># RHEL &amp; CentOS Stream 8 &amp; 9<br> dnf install epel-release<br> dnf install restic</pre><pre># RHEL7/CentOS<br> yum install yum-plugin-copr<br> yum copr enable copart/restic<br> yum install restic</pre><pre># Solus<br> eopkg install restic</pre><pre># Windows via Scoop<br> scoop install restic</pre><p><strong>2. Criando um backup</strong></p><p>Para criar um backup novo, primeiro precisamos criar o repositório de backups, usando a seguinte sintaxe:</p><pre><strong>$</strong> restic init --repo &lt;pasta_do_repositório&gt;</pre><p>Exemplo:</p><pre><strong>$</strong> restic init --repo /mnt/disco_1/restic-repo<br>enter password for new repository:<br>enter password again:<br>created restic repository 085b3c76b9 at /mnt/disco_1/restic-repo<br>Please note that knowledge of your password is required to access the repository.<br>Losing your password means that your data is irrecoverably lost.</pre><p>O próximo passo é fazer o backup.</p><p>Digamos que você queira fazer um backup de uma lista de pastas e arquivos. Então o mais recomendável é colocar essa lista num TXT. Isso facilita a manutenção.</p><p>O mesmo pode ser feito para os arquivos e pastas a serem ignorados, colocando-os em outro TXT.</p><p>A senha também pode ser armazenada num outro arquivo TXT (num local seguro e que ninguém consiga acessar)… para evitar o trabalho chato de toda hora ter que digitá-la.</p><p>Após termos os TXTs em mãos, poderíamos fazer o backup mais ou menos com esse comando:</p><pre><strong>$ </strong>restic -r &lt;pasta_do_repositório&gt; backup --files-from &lt;arquivo_TXT_1&gt; --exclude-file=&lt;arquivo_TXT_2&gt; --password-file &lt;arquivo_TXT_3&gt;</pre><p>Exemplo:</p><pre><strong>$</strong> restic -r /media/usuario/Disco_Externo/Backups/restic backup --files-from /home/usuario/restic/include.txt --exclude-file =/home/usuario/restic/exclude.txt --password-file /etc/restic/.restic_pwd</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/886/1*pMsmGPXoaP1c835onNUB_Q.png" /><figcaption>Um exemplo de backup que realizei</figcaption></figure><p>Feito. Simples assim.</p><p>Toda vez que for realizado um backup, um novo snapshot é criado no repositório. Para listá-los usamos este comando:</p><pre><strong>$ </strong>restic -r &lt;pasta_do_repositório&gt; snapshots</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/886/1*pySqQu52JGvbfTm7bbK2kg.png" /><figcaption>Snapshots</figcaption></figure><p><strong>3. E agora, se eu precisar restaurar?</strong></p><p>No Linux, podemos montar um determinado snapshot via FUSE:</p><pre><strong>$</strong> mkdir /mnt/restic<br><strong>$</strong> restic -r /srv/restic-repo mount /mnt/restic<br>enter password for repository:<br>Now serving /srv/restic-repo at /mnt/restic<br>Use another terminal or tool to browse the contents of this folder.<br>When finished, quit with Ctrl-c here or umount the mountpoint.</pre><p>No Windows ainda não temos esse recurso… mas podemos usar um comando que lista os arquivos de um snapshot:</p><pre><strong>$</strong> restic -r E:\Backup-restic\Sources ls latest</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/886/1*vkd_nsWk2pUdmOBDD9dKnw.png" /><figcaption>Listando os arquivos</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/886/1*qKdfkF81zf1v6maBEZRv3w.png" /><figcaption>Listando os arquivos</figcaption></figure><p>O comando <strong>ls</strong> pode resultar numa lista muito extensa. Se quisermos procurar um arquivo, é mais fácil redirecionar a saída para um arquivo TXT, e depois neste arquivo realizar a busca:</p><pre><strong>$</strong> restic -r E:\Backup-restic\Sources ls latest &gt; arquivos.txt</pre><p>E para simplesmente restaurar tudo, bastaria executar esse comando:</p><pre><strong>$</strong> restic -r /srv/restic-repo restore latest --target /tmp/restore-art</pre><p>… salientando que “latest” se refere ao último snapshot. Para restaurar um snapshot anterior, basta referenciar o ID do mesmo.</p><p><strong>4. Automatizando tudo</strong></p><p>Para não ter que entrar na linha de comando toda hora e manualmente ter que fazer o backup, é melhor agendar a tarefa numa execução automática.</p><p>Assim o risco do esquecimento é eliminado, além de economizar o tempo que seria gasto fazendo essa tarefa.</p><p>No Windows temos o <strong>Agendador de Tarefas</strong>. Aqui eu mostro um script Powershell que executa todo dia às 18:00:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/886/1*0xFgGZdR31CEnz_Y6rsJSw.png" /><figcaption>Agendando script</figcaption></figure><p>No script Powershell apenas há o comando para fazer o backup, nada além disso. Lembre-se, na correria do seu dia-a-dia, você não quer ser importunado com essa coisa chata.</p><p>No Linux criei o agendamento via <a href="https://debian-handbook.info/browse/pt-BR/stable/sect.asynchronous-task-scheduling-anacron.html"><strong>Anacron</strong></a><strong>. </strong>Configurei para executar uma vez por dia, toda vez que a máquina iniciar, após 5 minutos. Nesse caso é um script <strong>Bash</strong>:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/852/1*DXTVXUJWyFYy59_VxwUcUQ.png" /><figcaption>Script bash com o comando do restic</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/886/1*8E2hQHtKtf1SZuT07D6Z7A.png" /><figcaption>Script bash com o comando do restic</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/886/1*DzZiGZIVA-UJfz-vTXuGLQ.png" /><figcaption>Agendando a tarefa via Anacron:</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/886/1*TQfftT9ZYnHLdKkbNXrbXQ.png" /><figcaption>Edição da tarefa no Anacron</figcaption></figure><p><strong>5. Considerações Finais</strong></p><p>Ao longo da minha experiência com TI, já testei diversas ferramentas de backup. Para o backup de arquivos rotineiros, o <em>restic</em> supre a exigência.</p><p>Ele oferece possibilidades mais avançadas, como por exemplo usar um servidor <em>REST</em>, fazer backup na nuvem, etc.</p><p>Todavia é recomendável usar outra ferramenta para fazer o backup de disco e de partições, possuindo um plano de recuperação de desastre mais robusto. Alguns exemplos são <a href="https://clonezilla.org/downloads.php">Clonezilla</a>, <a href="https://www.partimage.org/">partimage</a>, <a href="https://partclone.org/">partclone</a>, <a href="https://br.easeus.com/">EaseUS</a>. No Windows recomendo o <a href="https://br.easeus.com/">EaseUS </a>pela sua facilidade.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=2e9a5e981372" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Protractor]]></title>
            <link>https://andrebolivia.medium.com/protractor-analisando-os-testes-e2e-de-forma-mais-efetiva-784583a4a468?source=rss-cd82928ec4a------2</link>
            <guid isPermaLink="false">https://medium.com/p/784583a4a468</guid>
            <category><![CDATA[test-automation]]></category>
            <category><![CDATA[jasmine]]></category>
            <category><![CDATA[angular]]></category>
            <category><![CDATA[protractor]]></category>
            <category><![CDATA[totvs-developers]]></category>
            <dc:creator><![CDATA[André]]></dc:creator>
            <pubDate>Thu, 10 Sep 2020 03:41:27 GMT</pubDate>
            <atom:updated>2020-09-17T18:13:14.490Z</atom:updated>
            <cc:license>http://creativecommons.org/licenses/by/4.0/</cc:license>
            <content:encoded><![CDATA[<h3>Protractor — Analisando os testes E2E de forma mais efetiva</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*EqCKfzYGdhp7l7Pd" /><figcaption>Photo by <a href="https://unsplash.com/@sctgrhm?utm_source=medium&amp;utm_medium=referral">Scott Graham</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><h3>Sumário</h3><ol><li>Definições básicas do Protractor (pode pular se já sabe!)</li><li>Imprimindo stack traces</li><li>Gerando um relatório de pizza para impressionar a diretoria🍕</li><li>Debugando o teste E2E no VS Code</li><li>Debugando o teste E2E no Chrome</li></ol><h3>1. Definições básicas do <strong>Protractor</strong></h3><ul><li>O que é o Protractor?</li></ul><p>Conforme o <a href="https://www.protractortest.org">site oficial</a>, é um framework de teste ponta-a-ponta para aplicações Angular e AngularJS. A sua utilidade é rodar testes automatizados nas aplicações web, interagindo como se fosse o usuário, e assim descobrindo comportamentos inesperados.</p><ul><li>Como o Protractor funciona?</li></ul><p>Basicamente, ele funciona como uma aplicação Node.js, que engloba um framework de testes (exemplo: <a href="https://jasmine.github.io/">Jasmine </a>e <a href="https://mochajs.org/api/mocha">Mocha</a>) e um framework de automação de browser (<a href="https://www.seleniumhq.org">Selenium</a>).</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/621/0*Ds1l0i5YFkpfKykp.png" /><figcaption>Fonte: <a href="https://www.protractortest.org/#/infrastructure">https://www.protractortest.org/#/infrastructure</a></figcaption></figure><p>O funcionamento dos testes é definido por dois itens principais: <strong>os arquivos de testes (arquivos spec)</strong> e o <strong>arquivo de configuração</strong>.</p><ul><li>Um exemplo rápido</li></ul><p>A execução dos testes é impressa no console. Por padrão, a biblioteca para imprimir estes resultados é o <a href="https://github.com/bcaudan/jasmine-spec-reporter">jasmine-spec-reporter</a>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*1tMUJ6IXuNkdZmE5NgraiA.png" /><figcaption>Exemplo da execução do Protractor</figcaption></figure><h3>2. <strong>Imprimindo stack traces</strong></h3><ul><li><strong>displayStacktrace — para que serve?</strong></li></ul><p>No arquivo de configuração do Protractor, é possível definir a opção <strong>displayStacktrace</strong>.</p><p>A opção <strong>displayStacktrace</strong>, da biblioteca <a href="https://github.com/bcaudan/jasmine-spec-reporter">jasmine-spec-reporter</a>, permite o ajuste do detalhamento dos erros encontrados.</p><p>Quando está configurada com a opção <strong>‘pretty’</strong>, ela apresenta a pilha de chamadas causadora do erro (<em>stack trace</em>), <strong>organizadas por contexto</strong>. O mais importante desse recurso são duas informações: <strong>código-fonte</strong> e <strong>linha\coluna</strong> onde ocorreu o erro.</p><p>Um exemplo:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1013/1*X7LbxLBlTLXb2aIfTqJlOA.png" /><figcaption>displayStacktrace: StacktraceOption.PRETTY</figcaption></figure><p>O que acontece quando esta opção está desligada? O mesmo erro é apresentado dessa forma:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1022/1*JccIq7tEhw7-bd7NB8edhw.png" /><figcaption>displayStacktrace: StacktraceOption.NONE</figcaption></figure><p>Note que o <strong>código-fonte</strong> e a respectiva <strong>linha\coluna</strong> geradores do erro <strong>não foram apresentados</strong>, apenas a descrição do teste.</p><ul><li>É importante ressaltar a <a href="https://github.com/bcaudan/jasmine-spec-reporter/pull/467"><strong>Pull Request 467 de 22 de março-2020</strong></a><strong>. </strong>As opções válidas são <strong>none</strong>, <strong>raw</strong> e <strong>pretty</strong>. Antes disso, eram <strong>true</strong> ou <strong>false</strong>.</li><li>Desde então, se a opção está como <strong>true </strong>ou <strong>false</strong> (ou qualquer outro valor inválido), o valor default é <strong>none.</strong></li></ul><h3>3. Gerando um relatório de pizza para impressionar a diretoria🍕</h3><p>Podemos automatizar ainda mais o teste automatizado…😂</p><p>Existe uma biblioteca, denominada <a href="https://www.npmjs.com/package/protractor-html-reporter-2">protractor-html-reporter-2</a>, que emite um relatório analítico dos testes, com gráficos de pizza e a totalização de acertos e defeitos.</p><p>Além disso, ele consegue tirar <em>screenshots </em>das telas, no momento exato em que algum teste falhou.</p><p>O relatório é muito didático, podendo ser entendido por qualquer pessoa que não seja da área de desenvolvimento.</p><p><strong>Criando um relatório</strong></p><p>Primeiro é necessário instalar as bibliotecas na aplicação:</p><pre>npm i protractor-html-reporter-2 --save-dev<br>npm i fs-extra --save-dev</pre><p>Ok. Agora vamos configurar a ferramenta.</p><p>Para não impactar o que já funciona (supondo que sua aplicação possua um <em>pipeline </em>com <em>continuous integration</em>), recomendo que seja criado um arquivo de configuração à parte para o Protractor, exclusivamente para emitir este relatório.</p><p>Se o arquivo padrão de configuração é <em>protractor.conf.js</em>, outro arquivo com nome <em>protractor-report.conf.js</em> poderia ser criado:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/af0c8de7b247a9344befe12e48ec0421/href">https://medium.com/media/af0c8de7b247a9344befe12e48ec0421/href</a></iframe><p>Para facilitar a vida, no <em>package.json poderia ser </em>adicionado um novo atalho de <em>script</em>:</p><pre>“e2e:report”: “protractor ./e2e/protractor-report.conf.js”,</pre><p>Teoricamente está tudo pronto. Agora é só rodar o comando:</p><pre>npm run e2e:report</pre><p><strong>Resultado:</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*vZ2-poGFYz8GH4S64Ewbzg.png" /><figcaption>Relatório de pizza 🍕🍕🍕</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*kNXs8GLBGszM0auMhe6Dnw.png" /><figcaption>Screenshot da tela com erro</figcaption></figure><h3>4. Debugando o teste E2E no VS Code</h3><p>Acredito que esta informação é pouco disseminada, mas é possível realizar o debug do teste E2E no VS Code.</p><p>Basta configurar o VS Code conforme abaixo:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/959/1*SasJ8_bDJdkvyWT-coRMlw.png" /></figure><p>Recomendo a criação de um arquivo de configuração a parte (exemplo: <em>protractor-debug.conf.js</em>), apenas com um <em>timeout </em>maior, para o sistema não fechar durante a depuração.</p><p>No trecho a ser debugado, troque <strong>it </strong>por <strong>fit</strong>, e coloque um <strong>breakpoint</strong> logo abaixo:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*5gjLA4c8gTB-Sc7vhJBgYA.png" /><figcaption>O debug em ação</figcaption></figure><h3>5. Debugando o teste E2E no Chrome</h3><p>“Ah, mas eu não uso o VS Code, prefiro o vi!”</p><p>Ok, também é possível debugar no Chrome, independente de qualquer editor ou IDE.</p><ul><li>No seu editor, no trecho a ser debugado, troque <strong>it</strong> por <strong>fit</strong>, e logo abaixo coloque a <em>keyword</em> <strong>debugger:</strong></li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/765/1*YUbXJM9Y2UnrFUBzToaRGA.png" /><figcaption>Keyword debugger</figcaption></figure><ul><li>Rode este comando no terminal:</li></ul><pre>node --inspect-brk ./node_modules/protractor/bin/protractor ./e2e/protractor-debug.conf.js</pre><ul><li>Abra o Chrome no endereço <strong>chrome://inspect/#devices</strong></li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*65B-vu5Ig07YNL61UEqcAQ.png" /><figcaption>Tela do Chrome — aqui começa o debug</figcaption></figure><ul><li>Clique no link <strong>inspect</strong></li><li>Uma janela do Chrome abrirá:</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Qk0QH1jiW4nIiZHiMEzq8Q.png" /><figcaption>Primeira etapa do debug</figcaption></figure><ul><li>Tecle F8</li><li>Após isso, o <em>debugger </em>do Chrome cairá na linha onde foi escrito <em>debugger</em>:</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*gcR8_d_rrg34DeD5fmykDA.png" /><figcaption>Debugando no Chrome</figcaption></figure><p>Pronto. Outra maneira de fazer a mesma coisa.</p><h3>Referências:</h3><ol><li><a href="https://github.com/bcaudan/jasmine-spec-reporter/pull/467">https://github.com/bcaudan/jasmine-spec-reporter/pull/467</a></li><li><a href="https://www.protractortest.org/#/debugging#disabled-control-flow">https://www.protractortest.org/#/debugging#disabled-control-flow</a></li><li><a href="https://www.protractortest.org/#/api-overview">https://www.protractortest.org/#/api-overview</a></li><li><a href="https://www.npmjs.com/package/protractor-html-reporter-2">https://www.npmjs.com/package/protractor-html-reporter-2</a></li><li><a href="https://github.com/angular/angular-cli/issues/10289">https://github.com/angular/angular-cli/issues/10289</a></li><li><a href="https://www.hhutzler.de/blog/angular-testing-protractor/">https://www.hhutzler.de/blog/angular-testing-protractor/</a></li><li><a href="https://www.npmjs.com/package/fs-extra">https://www.npmjs.com/package/fs-extra</a></li><li><a href="https://medium.com/@praveendavidmathew/creating-html-reports-for-protractor-7d9830ebf428">https://medium.com/@praveendavidmathew/creating-html-reports-for-protractor-7d9830ebf428</a></li><li><a href="https://applandeo.com/blog/automated-testing-of-angular-application-using-protractor/">https://applandeo.com/blog/automated-testing-of-angular-application-using-protractor/</a></li></ol><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=784583a4a468" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>