<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: abigail armijo</title>
    <description>The latest articles on DEV Community by abigail armijo (@abigail_armijo).</description>
    <link>https://dev.to/abigail_armijo</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1660045%2F6da2aec6-a527-4dfa-91fe-a62541d8344a.jpg</url>
      <title>DEV Community: abigail armijo</title>
      <link>https://dev.to/abigail_armijo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/abigail_armijo"/>
    <language>en</language>
    <item>
      <title>How I Set Up Claude Code as My Testing Toolkit: Issue Fixes, PR Reviews, and Skills for Test Case Generation</title>
      <dc:creator>abigail armijo</dc:creator>
      <pubDate>Mon, 13 Jul 2026 15:52:12 +0000</pubDate>
      <link>https://dev.to/abigail_armijo/how-i-set-up-claude-code-as-my-testing-toolkit-issue-fixes-pr-reviews-and-skills-for-test-case-5db</link>
      <guid>https://dev.to/abigail_armijo/how-i-set-up-claude-code-as-my-testing-toolkit-issue-fixes-pr-reviews-and-skills-for-test-case-5db</guid>
      <description>&lt;p&gt;I believe AI will be another service like the internet or a cell phone, and it's important to use it correctly by adding the right context, being aware of token usage, and following your own process. &lt;/p&gt;

&lt;p&gt;For this reason some months ago I finished different courses about how to use Claude:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A course with Ivan Davidov and a small contribution from Debbie O'Brien, on setting up agents with Playwright.&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://anthropic.skilljar.com/" rel="noopener noreferrer"&gt;anthropic Claude&lt;/a&gt; courses &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I checked the &lt;a href="https://github.com/addyosmani/agent-skills" rel="noopener noreferrer"&gt;Addy Osmani Agent skills repo&lt;/a&gt; and checked his courses on linkedin.&lt;/p&gt;

&lt;p&gt;And I am taking the Mosh Hamedani course &lt;a href="https://codewithmosh.com/p/claude-code" rel="noopener noreferrer"&gt;Claude Code for Professional Developers&lt;/a&gt; and finished other claude skills course.&lt;/p&gt;

&lt;p&gt;Also, in one of the jobs, I used skills developed by other QAs. I initially struggled with complex queries and generating API automation test cases due to the complexity of the user stories. But after some feedback from the agents and the user stories were clearer and with more context, like including the legacy stored procedure or checking the PR code, I got better results using the skills with GitHub copilot. &lt;/p&gt;

&lt;p&gt;It's better to create your own agents with your rules and process. You need a framework with concrete coding rules and conventions, for your test cases. &lt;/p&gt;

&lt;p&gt;For example, for test cases, I prefer critical user journeys with detailed steps and assertions in bullet points, rather than 10 tests that test a small part of the real user flow.&lt;/p&gt;

&lt;p&gt;For automation frameworks, I like to follow these rules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create components such as grid, combo, and calendar instead of helpers with that functions.&lt;/li&gt;
&lt;li&gt;The Page Object class only contains the elements with the components and general functions.&lt;/li&gt;
&lt;li&gt;On spec file I access the elements of the component like loginPage.loginButton.click() instead of create a LoginClick on the Page class. &lt;/li&gt;
&lt;li&gt;For the selectors I prefer getByRole because I think it is better for accessibility, and the user sees buttons and text instead of complex xPaths or data-test-ids.&lt;/li&gt;
&lt;li&gt;Add assertions that I can reuse in several tests on the page object or in the component.&lt;/li&gt;
&lt;li&gt;Wrap all actions into steps so the Playwright report can be read and understood by anyone on the team. &lt;/li&gt;
&lt;li&gt;Generate any data with FakerJS&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How to setup Claude
&lt;/h2&gt;

&lt;p&gt;Once you have the structure of your framework, it is important to generate CLAUDE.md using the /init command. This command will analyze the structure of your repo and create a file with the main structure and commands to run and execute the tests of your project, and each time you request something, Claude will read this file. You need to check the generated file and ensure that includes all the info.&lt;/p&gt;

&lt;p&gt;With Claude, you can define &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Hooks&lt;/strong&gt;: that will be executed before any action or after any action. For example, you can run ESLint after the file is saved.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Skills&lt;/strong&gt;: to reuse the same instructions, for example, to create manual test cases with some format. Before you can create commands but now are integrated into skills.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Create a hook to prevent access to .env file
&lt;/h3&gt;

&lt;p&gt;The .env file, usually contains all the passwords. For security, it's better that the agent can't access it. The course &lt;a href="https://anthropic.skilljar.com/claude-code-in-action" rel="noopener noreferrer"&gt;Claude Code in Action&lt;/a&gt; includes an example that restricts access with JavaScript, but for practice I also created a hook to prevent access with a Python script.&lt;/p&gt;

&lt;p&gt;To create the hook:&lt;/p&gt;

&lt;p&gt;1) Create the scripts with the rule or command that you want to execute. I created a hooks folder with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;read_hook.js:&lt;/strong&gt; This JS script checks if the file is .env and restricts access, but allows access to .env.example because this is the example for others; qa can create the .env file.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;bash_env_check.py:&lt;/strong&gt; This is a Python file with the same restriction, and I decided to check the same restriction with Python, but now to restrict reading the .env file with commands.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;2) Add the hook in the .claude/settings.json file.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add a hooks key &lt;/li&gt;
&lt;li&gt;Add the event that triggers the hook. For example, the PreToolUse event is executed before each tool execution and can prevent the tool from executing any action. &lt;/li&gt;
&lt;li&gt;Add the matcher; in this case, it will be "Read|Edit" to prevent Claude from reading or editing the .env file&lt;/li&gt;
&lt;li&gt;Add the hooks key as:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;command&lt;/span&gt;
&lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;node&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;./hooks/read_hook.js"&lt;/span&gt;
&lt;span class="na"&gt;statusMessage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Checking&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;file&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;access..."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"permissions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"allow"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"mcp__playwright"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bash(playwright-cli*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bash(npx tsc --noEmit*)"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"deny"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"PreToolUse"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"matcher"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Read|Edit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; 
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"node ./hooks/read_hook.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"statusMessage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Checking file access..."&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"PostToolUse"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you request access you will see an error&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2iev19wkt4leoym7iu1u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2iev19wkt4leoym7iu1u.png" alt="Access to .env file is restricted" width="800" height="260"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a skill to create the manual test case from an Azure DevOps User story
&lt;/h2&gt;

&lt;p&gt;I created the user story and a Figma design. I added only one screenshot without any interaction.&lt;/p&gt;

&lt;p&gt;But usually Figma designs are like this, which include the web and mobile versions also other Figma designs include interaction&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.figma.com/design/wgVhACpf8EbHwMUEh77E0c/Practical-UI---Design-system-preview?node-id=3432-65411&amp;amp;t=IpN9YNiP2AMiTpSy-1" rel="noopener noreferrer"&gt;https://www.figma.com/design/wgVhACpf8EbHwMUEh77E0c/Practical-UI---Design-system-preview?node-id=3432-65411&amp;amp;t=IpN9YNiP2AMiTpSy-1&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I am not a fan of BDD, so I didn't follow the BDD syntax for the user story. I've worked with and without BDD. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Description:&lt;/strong&gt;&lt;br&gt;
The admin user needs&amp;nbsp;to register the different servers used to connect to the ERP to see some reports from the Firebird database.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Server Fields&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Id&lt;/strong&gt;&amp;nbsp;- Server ID (numeric auto-increment)&lt;br&gt;
&lt;strong&gt;Key&lt;/strong&gt;&amp;nbsp;– Server key (unique identifier)&lt;br&gt;
&lt;strong&gt;Name&lt;/strong&gt;&amp;nbsp;– Server name&lt;br&gt;
&lt;strong&gt;URL&lt;/strong&gt;&amp;nbsp;– URL to connect to the ERP API&lt;br&gt;
&lt;strong&gt;Active&lt;/strong&gt;&amp;nbsp;– Whether the server is enabled&lt;/p&gt;

&lt;p&gt;Scope:&amp;nbsp;view the registered servers, search by name or key, add, edit, delete, and mark a server active/inactive.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Design&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://www.figma.com/proto/rY4DuulnpEMQ9vGwzkbVK9/Practical-UI-design-system?node-id=9221-1831&amp;amp;p=f&amp;amp;t=sRD8XZAWmyZJB4nW-1&amp;amp;scaling=min-zoom&amp;amp;content-scaling=fixed&amp;amp;page-id=9220%3A79786" rel="noopener noreferrer"&gt;https://www.figma.com/proto/rY4DuulnpEMQ9vGwzkbVK9/Practical-UI-design-system?node-id=9221-1831&amp;amp;p=f&amp;amp;t=sRD8XZAWmyZJB4nW-1&amp;amp;scaling=min-zoom&amp;amp;content-scaling=fixed&amp;amp;page-id=9220%3A79786&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the attached file, you can find translations for English, Japanese, Spanish, and German.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Acceptance Criteria&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Servers grid&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Admin can see a list of registered servers showing&amp;nbsp;Key,&amp;nbsp;Name,&amp;nbsp;URL, and&amp;nbsp;Active&amp;nbsp;status.&lt;/li&gt;
&lt;li&gt;Admin can search the list; results filter as text is typed. The list supports pagination with page sizes of 10, 20, 50, and 100 rows.&lt;/li&gt;
&lt;li&gt;The list can be exported to&amp;nbsp;Excel&amp;nbsp;and&amp;nbsp;PDF.&lt;/li&gt;
&lt;li&gt;When there are no servers, the list shows a "There are no servers registered yet. Click \"Add New\" to register the first one." message.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Add / Edit&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An admin can add a new server, edit an existing one, or delete it.&lt;/li&gt;
&lt;li&gt;Key, Name, and URL are all&amp;nbsp;required; Save is disabled while the form is invalid.
&lt;strong&gt;Key:&lt;/strong&gt;&amp;nbsp;number between 1 and 999999.
&lt;strong&gt;Name:&lt;/strong&gt;&amp;nbsp;text, maximum 150 characters.
&lt;strong&gt;URL:&lt;/strong&gt;&amp;nbsp;must start with http:// or https://, maximum 200 characters.
-Key&amp;nbsp;must be unique; uniqueness is validated&amp;nbsp;server-side. Saving a duplicate Key returns a server error message, and the record is not saved.
-A new server defaults to Active.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Delete / Active state&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deleting a server&amp;nbsp;requires confirmation; canceling the confirmation leaves the server unchanged.&lt;/li&gt;
&lt;li&gt;An admin can mark a server as active or inactive; inactive servers remain visible in the list with Active = No.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Navigation&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clicking a server name opens that server's companies (another user story)
&lt;strong&gt;Permissions&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Only authenticated admin users can access the servers screen and its actions.&lt;/li&gt;
&lt;li&gt;I attached a CSV file with the translations to the different languages.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While I was testing, I found some gaps in my user story and Figma design, like the missing confirmation before deleting a server and the missing empty state. So I requested Claude to add a comment. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fneje4xk9x87z6g1g2alc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fneje4xk9x87z6g1g2alc.png" alt="Claude comment" width="800" height="466"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's important to review the user stories and create test cases at the start of the sprint to prevent gaps before the developer starts coding so the developer can generate unit tests and know the test cases can prevent bugs.&lt;/p&gt;

&lt;p&gt;I prefer creating small skills for specific tasks rather than a general skill. &lt;/p&gt;

&lt;p&gt;For manual test cases I created these skills:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;read-testcase:&lt;/strong&gt; Read Azure DevOps test case by ID&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;manual-test:&lt;/strong&gt; To create the UI and API test cases, read the user story, ask for any gaps in the user story, and include a preview of the test before creating.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;First you need to add the Azure DevOps MCP on Claude with an Entra Id account. And add your Azure DevOps Project&lt;/p&gt;

&lt;p&gt;I created an entra Id user &lt;a href="mailto:mcp_azure@abi1521hotmail.onmicrosoft.com"&gt;mcp_azure@abi1521hotmail.onmicrosoft.com&lt;/a&gt;, because you need to access with your Entra ID account instead of your personal email.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude mcp add azure-devops &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--&lt;/span&gt; npx &lt;span class="nt"&gt;-y&lt;/span&gt; @azure-devops/mcp your_organization &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--authentication&lt;/span&gt; interactive &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--tenant&lt;/span&gt; your_tenant_id
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  How to create a skill
&lt;/h3&gt;

&lt;p&gt;1) Inside the .claude folder, create a skills folder&lt;br&gt;
2) Add a folder with the name of the skill, e.g., read-testcase&lt;br&gt;
3) Add a SKILL.md file&lt;br&gt;
4) Add a references folder (optional) if you want to include a file with some references; for example, for the manual test skill, I created one file for API tests and another for E2E tests&lt;br&gt;
5) Add a scripts folder (optional): Executable actions for a specific task.&lt;/p&gt;

&lt;p&gt;The structure of the SKILLmd file is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;name (required):&lt;/strong&gt; the name of the skill should be the same as the folder&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;description (required):&lt;/strong&gt; Claude will read the skill's description to use automatically. The description should describe what the skill does and when it should be used. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;allowed-tools&amp;nbsp;(optional):&lt;/strong&gt; To restrict the tools Claude can use when the skill is active &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I usually add some description about the role of the skill for example I want to create update the test cases for a user story or for playwright code, sometimes the code already exists but the manual tests are outdated.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;manual-tests&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Create and update manual Test Cases in Azure DevOps from a user story — API, E2E/UI, and performance types and wire them into a Test Plan/Suite. Use to write, generate, revise, or refresh manual test cases, e.g. "create test cases for story 123", "turn this Playwright spec into ADO test cases", "update test case 456 steps", "create test cases from this Figma design".&lt;/span&gt;
&lt;span class="na"&gt;allowed-tools&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;&amp;gt;-&lt;/span&gt;
  &lt;span class="s"&gt;Read,&lt;/span&gt;
  &lt;span class="s"&gt;mcp__azure-devops__core_list_projects,&lt;/span&gt;
  &lt;span class="s"&gt;mcp__azure-devops__wit_get_work_item,&lt;/span&gt;
  &lt;span class="s"&gt;mcp__azure-devops__wit_update_work_item,&lt;/span&gt;
  &lt;span class="s"&gt;mcp__azure-devops__testplan_list_test_plans,&lt;/span&gt;
  &lt;span class="s"&gt;mcp__azure-devops__testplan_create_test_plan,&lt;/span&gt;
  &lt;span class="s"&gt;mcp__azure-devops__testplan_list_test_suites,&lt;/span&gt;
  &lt;span class="s"&gt;mcp__azure-devops__testplan_create_test_suite,&lt;/span&gt;
  &lt;span class="s"&gt;mcp__azure-devops__testplan_create_test_case,&lt;/span&gt;
  &lt;span class="s"&gt;mcp__azure-devops__testplan_add_test_cases_to_suite,&lt;/span&gt;
  &lt;span class="s"&gt;mcp__azure-devops__testplan_update_test_case_steps,&lt;/span&gt;
  &lt;span class="s"&gt;mcp__claude-in-chrome__tabs_context_mcp,&lt;/span&gt;
  &lt;span class="s"&gt;mcp__claude-in-chrome__tabs_create_mcp,&lt;/span&gt;
  &lt;span class="s"&gt;mcp__claude-in-chrome__navigate,&lt;/span&gt;
  &lt;span class="s"&gt;mcp__claude-in-chrome__computer,&lt;/span&gt;
  &lt;span class="s"&gt;mcp__claude-in-chrome__read_page&lt;/span&gt;
&lt;span class="s"&gt;---&lt;/span&gt;

&lt;span class="na"&gt;Act as a manual tester&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;turn a user story (or an automated test / feature description) into well-structured **manual** Test Case work items in Azure DevOps, and keep existing ones current when the story changes. Test cases come in three flavours — API, E2E/UI, and Performance — each with its own step template (see `references/`).&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the steps that I usually follow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Provide some error handling like confirm the Azure DevOps is connected. &lt;/li&gt;
&lt;li&gt;Include concrete code on the same step or as reference file. &lt;/li&gt;
&lt;li&gt;Minimize verbosity I prefer skills with less than 300 lines. &lt;/li&gt;
&lt;li&gt;Include a checklist to verify that follow all the rules that I setup.&lt;/li&gt;
&lt;li&gt;I usually request first the planned changes or the planned test cases &lt;/li&gt;
&lt;li&gt;Ask any doubt instead of guessing.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Step 1: Verify the MCP is available and Test Plan Id&lt;/span&gt;

&lt;span class="s"&gt;Before anything else, confirm the Azure DevOps MCP is connected (e.g. attempt a small read like `mcp__azure-devops__testplan_list_test_plans`). If it is not available, stop and show&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;

&lt;span class="s"&gt;Azure DevOps MCP is not set up. Configure it in .mcp.json (set your org) and set&lt;/span&gt;
&lt;span class="s"&gt;AZURE_DEVOPS_TOKEN in your shell, then restart Claude Code and try again.&lt;/span&gt;

&lt;span class="na"&gt;**Determine the target project** — never hardcode it&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;use the project the user named; otherwise propose the `.mcp.json` default (`--project` arg) and confirm before writing; if neither is clear, list projects with `mcp__azure-devops__core_list_projects` and ask.&lt;/span&gt;

&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="s"&gt;testplan_*` and `wit_*` tools require an explicit `project` arg on every call, so resolve it once up front and reuse it. If a single request spans projects (e.g. a story in one project, test cases destined for another), confirm each.&lt;/span&gt;

&lt;span class="err"&gt;*&lt;/span&gt;&lt;span class="nv"&gt;*Determine&lt;/span&gt; &lt;span class="s"&gt;the destination Plan/Suite** — ask the user whether cases go into an **existing** Plan/Suite or a **new** one, if they haven't said&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="err"&gt;*&lt;/span&gt;&lt;span class="nv"&gt;*Existing&lt;/span&gt;&lt;span class="err"&gt;**&lt;/span&gt; &lt;span class="s"&gt;— `mcp__azure-devops__testplan_list_test_plans` (project) to find the named plan; `mcp__azure-devops__testplan_list_test_suites` (project, planId) to find the named suite, or use the root suite (`parentSuite` empty/itself) if they don't want a sub-suite.&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="err"&gt;*&lt;/span&gt;&lt;span class="nv"&gt;*New&lt;/span&gt;&lt;span class="err"&gt;**&lt;/span&gt; &lt;span class="s"&gt;— create the plan with `mcp__azure-devops__testplan_create_test_plan` (project, name, iteration — default to the project root iteration unless they name a sprint), then the suite with `mcp__azure-devops__testplan_create_test_suite` (project, planId, `parentSuiteId` = root suite id, name). Confirm both names before creating.&lt;/span&gt;

&lt;span class="s"&gt;Capture `planId` and `suiteId` — needed later when the cases are created and added to the suite.&lt;/span&gt;

&lt;span class="err"&gt;*&lt;/span&gt;&lt;span class="nv"&gt;*Find&lt;/span&gt; &lt;span class="s"&gt;the test case (update mode)** — if the user gave a test case ID, use it directly; if they gave a story ID and asked to refresh its cases, fetch the story with `mcp__azure-devops__wit_get_work_item` (`expand&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;all"&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="s"&gt;) and follow its `Microsoft.VSTS.Common.TestedBy` links to the linked test cases.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Checklist Example
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Checklist&lt;/span&gt;

Run through this silently before presenting the draft/diff for approval, and once more before any write call — it's a final gate, not documentation.
&lt;span class="p"&gt;
-&lt;/span&gt; [ ] Mode (create vs. update) chosen; for updates, current steps read fresh before drafting changes
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Test type chosen and the matching &lt;span class="sb"&gt;`references/*.md`&lt;/span&gt; template followed
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Steps derived from real source (story fields / actual spec file / stated facts) — zero invented endpoints, fields, or thresholds
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Figma link in the story spotted and the design opened and inspected before drafting (E2E create mode); design-quoted text marked High confidence
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Open doubts (unspecified texts, behaviors, edge cases) batched and asked before drafting; assumptions only after the user deferred or couldn't answer
&lt;span class="p"&gt;-&lt;/span&gt; [ ] No extra &lt;span class="sb"&gt;`|`&lt;/span&gt; inside any action/expected-result text — &lt;span class="sb"&gt;`|`&lt;/span&gt; is the tool-input delimiter (Step 5), not part of the preview's &lt;span class="sb"&gt;`→`&lt;/span&gt; format
&lt;span class="p"&gt;-&lt;/span&gt; [ ] No assertion-only "Observe/Inspect/Verify" steps; multi-assertion expected results written as bullets (steps XML via &lt;span class="sb"&gt;`wit_update_work_item`&lt;/span&gt;)
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Journeys are positive-scenario only, negative scenarios split into their own (parameterized) cases; cleanup postcondition present when the case creates data
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Destination plan + suite resolved (plan/suite created on request when none exists) — create mode
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Draft (create) or before→after diff (update) presented and user approved before any write
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I created other skills to create the automated playwright tests some of the skills are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;read-testcase:&lt;/strong&gt; To read the test case on Azure DevOps&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;check-auth:&lt;/strong&gt; Check if the file that stores the token is created on the .auth folder. This is created on the global setup to allow Claude check the website.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;page-snapshot&lt;/strong&gt;: To get the current html to the website to get the locators the file instead of guessing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;playwright-component:&lt;/strong&gt; To create or update any new component for example if the website to test includes a DatePicker can create a DatePicker component&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;playwright-page:&lt;/strong&gt; To create the page(s) required for the test.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;playwright-automation:&lt;/strong&gt; To create the test case with playwright.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;playwright-cli:&lt;/strong&gt; This is the official playwright skill that explains how to automate the browser.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;update-packages:&lt;/strong&gt; To update the npm packages to the latest version and check any breaking changes. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'm still working on that but you can see the repo with the skills: &lt;a href="https://github.com/apis3445/TestingDojo" rel="noopener noreferrer"&gt;https://github.com/apis3445/TestingDojo&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Create claude actions .yml files to tag claude on issues or review your PR.
&lt;/h3&gt;

&lt;p&gt;You can include a .yml file to allow Claude to tag your GitHub issues or to review your PR automatically.&lt;/p&gt;

&lt;p&gt;You need to &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Give Claude access to your GitHub Repo&lt;/li&gt;
&lt;li&gt;Upload a .yml file with the action that you want Claude to complete. You can see the different yml files on their &lt;a href="https://github.com/anthropics/claude-code-action/tree/main/examples" rel="noopener noreferrer"&gt;Claude Code action&lt;/a&gt; repo. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;One disadvantage I encountered is that when I was trying to use the .yml to review the PR, if you change the .yml for the code review, you will get an error that the file needs to be the same as the main branch, but you can add a comment &lt;a class="mentioned-user" href="https://dev.to/claude"&gt;@claude&lt;/a&gt; review to get the PR review comment.&lt;/p&gt;

&lt;p&gt;The PR review is part of your Claude usage. In my experience at one of my jobs, some suggestions weren't good. The review suggested changing code that will be fixed in other user stories, or that flagged code as incorrect due to a lack of context, even though the context was in the PR description. You can reply with suggestions, for example: &lt;a class="mentioned-user" href="https://dev.to/claude"&gt;@claude&lt;/a&gt;, I fixed it, and Claude will check again. &lt;/p&gt;

&lt;p&gt;But I prefer &lt;a href="https://www.qodo.ai" rel="noopener noreferrer"&gt;Qodo&lt;/a&gt;, which is focused on PR review. You can see both PR reviews here:  &lt;a href="https://github.com/apis3445/TestingDojo/pull/15" rel="noopener noreferrer"&gt;https://github.com/apis3445/TestingDojo/pull/15&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now the disadvantage that I found:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;LLM context memory is limited and needs to load all the info every time because if you restore one session, there is a limit and it needs to compress the context. &lt;/li&gt;
&lt;li&gt;The Claude Skilljar free courses are outdated; for example, one course explains commands, but commands were integrated into skills, or the PR review changed.&lt;/li&gt;
&lt;li&gt;LLMs don't say 'I don't know' — they guess and generate code like the correct solution, and if it's wrong, they try the same idea instead of asking. Sometimes I get better results after doing manual research. &lt;/li&gt;
&lt;li&gt;And with large files at least Copilot deleted all the code trying to add one code change, so with larges files around 2,300 lines struggle to update content.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can use the mcp context7 to get up-to-date docs for AI agents and for example for angular, google added claude skills but there is still a gap to LLM don't write deprecated code. &lt;/p&gt;

&lt;p&gt;Create skill or automate your flows require continuous feedback so it's important that improve the skills and be aware of the token usage.&lt;/p&gt;

&lt;p&gt;Thanks for reading so don't hesitate to ask questions or share your feedback. If this helped you, feel free to share it with the community.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>testing</category>
      <category>claude</category>
    </item>
    <item>
      <title>Practice Real-World Testing Scenarios for QA: Challenge 2 - Data Grid Validation and CRUD Operations</title>
      <dc:creator>abigail armijo</dc:creator>
      <pubDate>Sat, 11 Jul 2026 14:42:12 +0000</pubDate>
      <link>https://dev.to/abigail_armijo/practice-real-world-testing-scenarios-for-qa-challenge-2-data-grid-validation-and-crud-operations-50oc</link>
      <guid>https://dev.to/abigail_armijo/practice-real-world-testing-scenarios-for-qa-challenge-2-data-grid-validation-and-crud-operations-50oc</guid>
      <description>&lt;p&gt;Hi! The second challenge is ready — if you missed it, start with Challenge 1: Login with Different Users to get familiar with the site and set up your environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Challenge 2: Create test scenarios from User story.
&lt;/h2&gt;

&lt;p&gt;This time you will create the testing scenarios following the structure of a user story. &lt;/p&gt;

&lt;p&gt;You can find some tricky gaps on the user story and I will give the gaps when I publish my solution.&lt;/p&gt;

&lt;p&gt;I only created one small Figma design but usually are more complete with the desktop and mobile version. You can check this Practical Design Example. &lt;/p&gt;

&lt;p&gt;Create the manual test, API Tests and E2E tests. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Title:&lt;/strong&gt; [UI - API] Register Servers&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Description:&lt;/strong&gt;&lt;br&gt;
The admin user needs&amp;nbsp;to register the different servers used to connect to the ERP to see some reports from the Firebird database.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Server Fields&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Id&lt;/strong&gt;&amp;nbsp;- Server ID (numeric auto-increment)&lt;br&gt;
&lt;strong&gt;Key&lt;/strong&gt;&amp;nbsp;– Server key (unique identifier)&lt;br&gt;
&lt;strong&gt;Name&lt;/strong&gt;&amp;nbsp;– Server name&lt;br&gt;
&lt;strong&gt;URL&lt;/strong&gt;&amp;nbsp;– URL to connect to the ERP API&lt;br&gt;
&lt;strong&gt;Active&lt;/strong&gt;&amp;nbsp;– Whether the server is enabled&lt;/p&gt;

&lt;p&gt;Scope:&amp;nbsp;view the registered servers, search by name or key, add, edit, delete, and mark a server active/inactive.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Design&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://www.figma.com/proto/rY4DuulnpEMQ9vGwzkbVK9/Practical-UI-design-system?node-id=9221-1831&amp;amp;p=f&amp;amp;t=sRD8XZAWmyZJB4nW-1&amp;amp;scaling=min-zoom&amp;amp;content-scaling=fixed&amp;amp;page-id=9220%3A79786" rel="noopener noreferrer"&gt;https://www.figma.com/proto/rY4DuulnpEMQ9vGwzkbVK9/Practical-UI-design-system?node-id=9221-1831&amp;amp;p=f&amp;amp;t=sRD8XZAWmyZJB4nW-1&amp;amp;scaling=min-zoom&amp;amp;content-scaling=fixed&amp;amp;page-id=9220%3A79786&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the attached file, you can find translations for English, Japanese, Spanish, and German.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Acceptance Criteria&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Servers grid&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Admin can see a list of registered servers showing&amp;nbsp;Key,&amp;nbsp;Name,&amp;nbsp;URL, and&amp;nbsp;Active&amp;nbsp;status.&lt;/li&gt;
&lt;li&gt;Admin can search the list; results filter as text is typed. The list supports pagination with page sizes of 10, 20, 50, and 100 rows.&lt;/li&gt;
&lt;li&gt;The list can be exported to&amp;nbsp;Excel&amp;nbsp;and&amp;nbsp;PDF.&lt;/li&gt;
&lt;li&gt;When there are no servers, the list shows a message that explains that there is no data registered.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Add / Edit&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An admin can add a new server, edit an existing one, or delete it.&lt;/li&gt;
&lt;li&gt;Key, Name, and URL are all&amp;nbsp;required; Save is disabled while the form is invalid.
&lt;strong&gt;Key:&lt;/strong&gt;&amp;nbsp;number between 1 and 999999.
&lt;strong&gt;Name:&lt;/strong&gt;&amp;nbsp;text, maximum 150 characters.
&lt;strong&gt;URL:&lt;/strong&gt;&amp;nbsp;must start with http:// or https://, maximum 200 characters.
-Key&amp;nbsp;must be unique; uniqueness is validated&amp;nbsp;server-side. Saving a duplicate Key returns a server error message, and the record is not saved.
-A new server defaults to Active.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Delete / Active state&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can delete a server only if no company is assigned to it.&lt;/li&gt;
&lt;li&gt;An admin can mark a server as active or inactive; inactive servers remain visible in the list with Active = No.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Navigation&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clicking a server name opens that server's companies (that will be in user story #451)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Permissions&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Only authenticated admin users can access the servers screen and its actions.&lt;/li&gt;
&lt;li&gt;I attached a CSV file with the translations to the different languages.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;CSV file&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;Key&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;English&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;Spanish&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;German&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;Japanese&lt;/span&gt;
&lt;span class="nt"&gt;title&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;Servers&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;Servidores&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;Server&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;サーバー&lt;/span&gt;
&lt;span class="nt"&gt;newServer&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;New&lt;/span&gt; &lt;span class="nt"&gt;Server&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;Nuevo&lt;/span&gt; &lt;span class="nt"&gt;Servidor&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;Neuer&lt;/span&gt; &lt;span class="nt"&gt;Server&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;新しいサーバー&lt;/span&gt;
&lt;span class="nt"&gt;key&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;Key&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;Clave&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;Serverschlüssel&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;サーバーキー&lt;/span&gt;
&lt;span class="nt"&gt;keyHint&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;Key&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;1&lt;/span&gt; &lt;span class="nt"&gt;to&lt;/span&gt; &lt;span class="err"&gt;999&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="err"&gt;999&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;&lt;span class="nt"&gt;Clave&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;1&lt;/span&gt; &lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="err"&gt;999&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="err"&gt;999&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;&lt;span class="nt"&gt;Schlüssel&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;1&lt;/span&gt; &lt;span class="nt"&gt;bis&lt;/span&gt; &lt;span class="err"&gt;999&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="err"&gt;999&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;&lt;span class="nt"&gt;キー&lt;/span&gt;&lt;span class="err"&gt;（1〜999&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="err"&gt;999）&lt;/span&gt;
&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;Name&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;Nombre&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;Servername&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;サーバー名&lt;/span&gt;
&lt;span class="nt"&gt;nameHint&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;Descriptive&lt;/span&gt; &lt;span class="nt"&gt;name&lt;/span&gt; &lt;span class="nt"&gt;for&lt;/span&gt; &lt;span class="nt"&gt;the&lt;/span&gt; &lt;span class="nt"&gt;server&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;Nombre&lt;/span&gt; &lt;span class="nt"&gt;descriptivo&lt;/span&gt; &lt;span class="nt"&gt;para&lt;/span&gt; &lt;span class="nt"&gt;el&lt;/span&gt; &lt;span class="nt"&gt;servidor&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;Beschreibender&lt;/span&gt; &lt;span class="nt"&gt;Name&lt;/span&gt; &lt;span class="nt"&gt;für&lt;/span&gt; &lt;span class="nt"&gt;den&lt;/span&gt; &lt;span class="nt"&gt;Server&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;サーバーの記述的な名前&lt;/span&gt;
&lt;span class="nt"&gt;url&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;URL&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;URL&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;Server-URL&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;サーバーURL&lt;/span&gt;
&lt;span class="nt"&gt;urlHint&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;Full&lt;/span&gt; &lt;span class="nt"&gt;URL&lt;/span&gt; &lt;span class="nt"&gt;including&lt;/span&gt; &lt;span class="nt"&gt;protocol&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;e&lt;/span&gt;&lt;span class="nc"&gt;.g&lt;/span&gt;&lt;span class="o"&gt;.,&lt;/span&gt; &lt;span class="nt"&gt;https&lt;/span&gt;&lt;span class="o"&gt;://&lt;/span&gt;&lt;span class="nt"&gt;server&lt;/span&gt;&lt;span class="nc"&gt;.example.com&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;&lt;span class="nt"&gt;URL&lt;/span&gt; &lt;span class="nt"&gt;completa&lt;/span&gt; &lt;span class="nt"&gt;incluyendo&lt;/span&gt; &lt;span class="nt"&gt;protocolo&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;ej&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;https&lt;/span&gt;&lt;span class="o"&gt;://&lt;/span&gt;&lt;span class="nt"&gt;servidor&lt;/span&gt;&lt;span class="nc"&gt;.ejemplo.com&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;&lt;span class="nt"&gt;Vollständige&lt;/span&gt; &lt;span class="nt"&gt;URL&lt;/span&gt; &lt;span class="nt"&gt;einschließlich&lt;/span&gt; &lt;span class="nt"&gt;Protokoll&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;z&lt;/span&gt;&lt;span class="nc"&gt;.B&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;https&lt;/span&gt;&lt;span class="o"&gt;://&lt;/span&gt;&lt;span class="nt"&gt;server&lt;/span&gt;&lt;span class="nc"&gt;.beispiel.com&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;&lt;span class="nt"&gt;プロトコルを含む完全なURL&lt;/span&gt;&lt;span class="err"&gt;（&lt;/span&gt;&lt;span class="nt"&gt;例&lt;/span&gt;&lt;span class="err"&gt;：&lt;/span&gt;&lt;span class="nt"&gt;https&lt;/span&gt;&lt;span class="o"&gt;://&lt;/span&gt;&lt;span class="nt"&gt;server&lt;/span&gt;&lt;span class="nc"&gt;.example.com&lt;/span&gt;&lt;span class="err"&gt;）&lt;/span&gt;
&lt;span class="nt"&gt;active&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;Active&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;Activo&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;Aktiv&lt;/span&gt; &lt;span class="nt"&gt;Status&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;アクティブステータス&lt;/span&gt;
&lt;span class="nt"&gt;activeHint&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;Enable&lt;/span&gt; &lt;span class="nt"&gt;this&lt;/span&gt; &lt;span class="nt"&gt;server&lt;/span&gt; &lt;span class="nt"&gt;to&lt;/span&gt; &lt;span class="nt"&gt;allow&lt;/span&gt; &lt;span class="nt"&gt;access&lt;/span&gt; &lt;span class="nt"&gt;to&lt;/span&gt; &lt;span class="nt"&gt;database&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;Habilitar&lt;/span&gt; &lt;span class="nt"&gt;este&lt;/span&gt; &lt;span class="nt"&gt;servidor&lt;/span&gt; &lt;span class="nt"&gt;para&lt;/span&gt; &lt;span class="nt"&gt;permitir&lt;/span&gt; &lt;span class="nt"&gt;acceso&lt;/span&gt; &lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="nt"&gt;base&lt;/span&gt; &lt;span class="nt"&gt;de&lt;/span&gt; &lt;span class="nt"&gt;datos&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;Diesen&lt;/span&gt; &lt;span class="nt"&gt;Server&lt;/span&gt; &lt;span class="nt"&gt;aktivieren&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;um&lt;/span&gt; &lt;span class="nt"&gt;Datenbankzugriff&lt;/span&gt; &lt;span class="nt"&gt;zu&lt;/span&gt; &lt;span class="nt"&gt;ermöglichen&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nt"&gt;このサーバーを有効にしてデータベースアクセスを許可する&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Tips
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The grid is your source of truth for viewing, sorting, filtering, and handling empty states — and every value it shows should match what the API actually returns and/or the description of the user story.&lt;/li&gt;
&lt;li&gt;Creating and editing records, on the other hand, happens on a separate form page, backed by the Data API, so you'll get to practice writing pre-conditions and post-conditions around real API calls, and generating random test data with Faker instead of hardcoding it.&lt;/li&gt;
&lt;li&gt;As always, every challenge in this series also covers testing across different languages — the site supports multiple locales by default, so keep that in mind as you validate your scenarios.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Website: &lt;a href="https://abi-testing-dojo-demo.azurewebsites.net/" rel="noopener noreferrer"&gt;https://abi-testing-dojo-demo.azurewebsites.net/&lt;/a&gt;&lt;br&gt;
Security API (Scalar): &lt;a href="https://abi-testing-dojo-auth-demo.azurewebsites.net" rel="noopener noreferrer"&gt;https://abi-testing-dojo-auth-demo.azurewebsites.net&lt;/a&gt;&lt;br&gt;
Data API (Swagger): &lt;a href="https://abi-testing-dojo-ar-demo.azurewebsites.net" rel="noopener noreferrer"&gt;https://abi-testing-dojo-ar-demo.azurewebsites.net&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you complete this challenge, share your repository and feel free to ask any doubt about this challenge. &lt;/p&gt;

</description>
      <category>api</category>
      <category>devchallenge</category>
      <category>testing</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Reviving a 2010 Project When the Code Is Broken: A 15-Year Tech Time Capsule with Svelte, Supabase, Vercel</title>
      <dc:creator>abigail armijo</dc:creator>
      <pubDate>Sat, 06 Jun 2026 19:06:31 +0000</pubDate>
      <link>https://dev.to/abigail_armijo/reviving-a-2010-project-when-the-code-is-broken-a-15-year-tech-time-capsule-with-svelte-supabase-39a4</link>
      <guid>https://dev.to/abigail_armijo/reviving-a-2010-project-when-the-code-is-broken-a-15-year-tech-time-capsule-with-svelte-supabase-39a4</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/github-2026-05-21"&gt;GitHub Finish-Up-A-Thon Challenge&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;I created Zinemash, a web site to create a short comic in easy steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Choose your predefined comic template&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7ikbbzj0n7xs2oqi6ytz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7ikbbzj0n7xs2oqi6ytz.png" alt="Choose your template" width="800" height="728"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Drop in your images&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw0ilqj6r55jjg8l84wc6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw0ilqj6r55jjg8l84wc6.png" alt="Drop your images" width="800" height="566"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add speech bubbles&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkmn9f0q3mila2anmswtj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkmn9f0q3mila2anmswtj.png" alt="Add speech bubbles" width="800" height="566"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Everything autosaves, and when you're done you can publish the comic to a public section&lt;/p&gt;

&lt;p&gt;Tech stack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Svelte: Front end&lt;/li&gt;
&lt;li&gt;Vercel: Deploys&lt;/li&gt;
&lt;li&gt;Supabase: Auth, image storage and save&lt;/li&gt;
&lt;li&gt;Resend: To send the email to the users for login&lt;/li&gt;
&lt;li&gt;Gemini: All imagen were generated with gemini&lt;/li&gt;
&lt;li&gt;Opensilver: To convert the old silverlight project &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Name Breakdown&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Zine" (from fanzine): Represents the independent, creative, and artistic culture of self-publishing multi-page booklets and magazines.&lt;/li&gt;
&lt;li&gt;"Mash" (from mashup): Signals the core drag-and-drop experience. Users can mash together layout templates, speech bubbles, text, and their own uploaded images.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Try and create your comic: &lt;a href="https://zinemash.vercel.app" rel="noopener noreferrer"&gt;zinemash&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Github: &lt;a href="https://github.com/apis3445/webcomic" rel="noopener noreferrer"&gt;https://github.com/apis3445/webcomic&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;See the demo and the old presentation (in Spanish)&lt;/p&gt;


&lt;div&gt;
  &lt;iframe src="https://loom.com/embed/399686bc9fea4c0c8f073d700ea3be7b"&gt;
  &lt;/iframe&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  The Comeback Story
&lt;/h2&gt;

&lt;p&gt;One of my favorite hobbies is drawing, so over the years I've built several apps related to it. While studying, I developed an app to add predefined figures and text in a 2D &amp;amp; 3D application for my Computer Graphics course. During my internship, I created a shared chalkboard using Macromedia Flash Communication Server for online classes.&lt;/p&gt;

&lt;p&gt;In 2010, I joined a Microsoft competition. I had just bought an HP 2-in-1 tablet laptop to keep drawing, but a manufacturing defect that wasn't covered by the warranty left it unusable after only two years. First place was a laptop. I didn't win the laptop, but I created the app.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmr135d8821u9shiyjkxd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmr135d8821u9shiyjkxd.png" alt="Winners of the competition" width="800" height="585"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I submitted a project titled "Silverlight Web Comic." My goals were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Learn Silverlight. I hadn't used it before (I'd only used Macromedia Flash).&lt;/li&gt;
&lt;li&gt;Create a webpage to help anyone build simple comics.&lt;/li&gt;
&lt;li&gt;Make it an open source project.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I used my previous experience to build a comic generator: you could drag and drop images and add speech bubbles from a defined set. I built it as a proof of concept, and this was a preview. Remember, this was 2010, so I created a very colorful app :D&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SourceForge: &lt;a href="https://sourceforge.net/projects/comicajax/" rel="noopener noreferrer"&gt;https://sourceforge.net/projects/comicajax/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GitHub (OpenSilver version): &lt;a href="https://github.com/apis3445/openSilverWebComic" rel="noopener noreferrer"&gt;https://github.com/apis3445/openSilverWebComic&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Live demo: &lt;a href="https://ashy-tree-02247c010.7.azurestaticapps.net" rel="noopener noreferrer"&gt;Silverlight Web Comic&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then I changed to a more demanding job, and the project stayed unfinished.&lt;/p&gt;

&lt;p&gt;When I saw this challenge, I remembered it. But getting the code back wasn't easy. I had uploaded it to CodePlex and CodeProject (both gone now) and another website whose name I've forgotten. After searching, I found it on SourceForge, but the zip file was damaged, and I could only extract some of the files.&lt;/p&gt;

&lt;p&gt;The next challenge: I have a Mac M1, and Silverlight is discontinued. After more searching, I found OpenSilver, which runs Silverlight in modern browsers. With Copilot's help, I converted the project from Silverlight to OpenSilver and decompiled the code for the missing files. Seeing my 2010 project run again gave me the screenshots I never had, since the original demo video lived on CodePlex, which no longer exists.&lt;/p&gt;

&lt;p&gt;OpenSilver brought the old project back to life, but to truly finish it the way I imagined in 2010, I decided to rebuild it from scratch with Svelte, Supabase, and Vercel.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Experience with GitHub Copilot
&lt;/h2&gt;

&lt;p&gt;I have limitations because I only have the free Copilot subscription, since I have Copilot Pro at my job and I couldn't bought one month so I had to use carefully.&lt;/p&gt;

&lt;p&gt;I used Copilot to create a plan for my project, and ask if was feasible to implement using Svelte and Supabase and some suggestions for the canvas and images, bubbles. Copilot defined a detailed plan.&lt;/p&gt;

&lt;p&gt;I asked for help converting to OpenSilver, suggested how to help, and asked for help with the decompile option for the missing files and to fix some of the decompilation. I once decompiled a hard drive when it stopped working without a backup at my second job (before Git existed). After a few days, the hard drive worked again. Decompile is not easy, but Copilot helps me to decompile and with the autocomplete helped to translate to English the code and remove all the extra code added by decompiler.&lt;/p&gt;

&lt;p&gt;Also I requested Copilot on Microsoft Power Point to translate my old presentation to English and modernize.&lt;/p&gt;

&lt;p&gt;I used another AI tools like lovable to help me with the design of the UI, qodo for PR reviews. &lt;/p&gt;

&lt;p&gt;I tried Copilot cli to help me with the integration to supabase, Vercel and how to deploy.  When I finished the implementation I created the images of the soccer comic with Gemini and I requested one friend to help me with some testing and I fixed some of the errors that my friend shared with me. &lt;/p&gt;

&lt;p&gt;I have been in QA for the last 6 years now, but I still enjoy coding and I learned a lot in a short time. &lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>githubchallenge</category>
    </item>
    <item>
      <title>What I Learned Exploring AI-Generated 3D: A Hands-On Tour of Meshy, Tripo, and Three.js</title>
      <dc:creator>abigail armijo</dc:creator>
      <pubDate>Tue, 26 May 2026 04:02:44 +0000</pubDate>
      <link>https://dev.to/abigail_armijo/what-i-learned-exploring-ai-generated-3d-a-hands-on-tour-of-meshy-tripo-and-threejs-5cic</link>
      <guid>https://dev.to/abigail_armijo/what-i-learned-exploring-ai-generated-3d-a-hands-on-tour-of-meshy-tripo-and-threejs-5cic</guid>
      <description>&lt;p&gt;Hi! I will share my experience building a POC to generate 2D and 3D images for a friend's project.&lt;/p&gt;

&lt;p&gt;One friend asked me to attend a meeting at his house to define the user story for a project he is planning. This was my first in-person meeting since COVID. &lt;/p&gt;

&lt;p&gt;The general idea is that, during the registration process:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The user answers some predefined questions and generates a 2D Dragon image with a predefined branding style. &lt;/li&gt;
&lt;li&gt;The image is generated with an AI image API creation (could be nano banana, GPT, or something else)&lt;/li&gt;
&lt;li&gt;Generate a 3D version of your dragon with Tripo AI and get the GLB file needed for Three.js&lt;/li&gt;
&lt;li&gt;Add some predefined animations on Threejs to the dragon.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The initial step was researching the available APIs and their pricing. &lt;/p&gt;

&lt;h2&gt;
  
  
  Image generation
&lt;/h2&gt;

&lt;p&gt;First, I checked and tried to generate images. For images, you have different options. If you want to use them for free and compare the same prompt across different AI models, you can use this to compare them: &lt;a href="https://arena.ai/image/side-by-side" rel="noopener noreferrer"&gt;https://arena.ai/image/side-by-side&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;I experimented with my logo, which was created some years ago. To improve the style, I want to remove the background, but the images added white and gray boxes instead of the actual transparent PNG or move the incorrect puzzle piece.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2uulr0mc1ygwqov054s3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2uulr0mc1ygwqov054s3.png" alt="Logo with bad transparent png" width="799" height="430"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fke0z4ns5959c214st9mz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fke0z4ns5959c214st9mz.png" alt="Logo with incorrect moving of the puzzle pieces" width="799" height="430"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can add text to generate your image, or upload an image. These are my experiments with Gemini &lt;/p&gt;

&lt;p&gt;From one of my photos I requested:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Remove the background &lt;/li&gt;
&lt;li&gt;Change the color of my jacket to red&lt;/li&gt;
&lt;li&gt;Add a smile because I am always serious in photos&lt;/li&gt;
&lt;li&gt;Add a soccer stadium background&lt;/li&gt;
&lt;li&gt;Change the image like a Panini World Cup sticker. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgyalxa42fgawxfjz3l46.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgyalxa42fgawxfjz3l46.jpeg" alt="Photo as panini sticker" width="800" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I also created a banner for my LinkedIn after some experiments trying to add the logo and all in place, without overlapping text or the image with the profile picture, and with the logo in the correct position. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftpm5rknrx13enbdlj5uf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftpm5rknrx13enbdlj5uf.png" alt="Linkedn banner" width="800" height="200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Another interesting experiment was adding an infographic to explain accessibility testing. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frszz1h1ve80hg8o8ppye.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frszz1h1ve80hg8o8ppye.png" alt="Accessibility infographic" width="800" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Gemini also includes some songs and videos, here is the link from my LinkedIn &lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://www.linkedin.com/posts/abigailarmijo_continue-my-experiments-with-images-i-explore-ugcPost-7456404876936466433-xxOe/?utm_source=share&amp;amp;amp%3Butm_medium=member_desktop&amp;amp;amp%3Brcm=ACoAAAf8LngBpB0IE2QWhBYGkg0bmh-5EacIaUU" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdms.licdn.com%2Fplaylist%2Fvid%2Fv2%2FD5605AQHI9qdQPa5iuQ%2Fthumbnail-with-play-button-overlay-high%2FB56Z3p4u5EJ4C0-%2F0%2F1777745459771%3Fe%3D2147483647%26v%3Dbeta%26t%3DdxQ56eBNrb49WawcroeUrEUtfspVfokBcF2-5_gVOh8" height="720" class="m-0" width="720"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://www.linkedin.com/posts/abigailarmijo_continue-my-experiments-with-images-i-explore-ugcPost-7456404876936466433-xxOe/?utm_source=share&amp;amp;amp%3Butm_medium=member_desktop&amp;amp;amp%3Brcm=ACoAAAf8LngBpB0IE2QWhBYGkg0bmh-5EacIaUU" rel="noopener noreferrer" class="c-link"&gt;
            Continue my experiments with images, I explore now the music option. I requested to the previous image, change to use the Mexican soccer jersey, and requested some music related to the soccer world… | Abigail Armijo Hernández  🇺🇦🇮🇱🇵🇸🕊
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            Continue my experiments with images, I explore now the music option. I requested to the previous image, change to use the Mexican soccer jersey, and requested some music related to the soccer world cup

          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstatic.licdn.com%2Faero-v1%2Fsc%2Fh%2Fal2o9zrvru7aqj8e1x2rzsrca" width="64" height="64"&gt;
          linkedin.com
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;So sometimes, although you describe the A pose as returning to the T pose and it overlaps the image, some tips add context, for example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add context/role: For example, you are a graphic designer with experience in designing new characters. &lt;/li&gt;
&lt;li&gt;Small and specific tasks: Don't try to modify an image with all instructions at the same time. For my panini, it was on steps: remove background, change jacket color, add some smile, change background to World Cup, and add panini. Also, I shared the current image of the last Mexican jersey.&lt;/li&gt;
&lt;li&gt;Add some quality criteria and a checklist &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I got better results with GPT-Image-2&lt;/p&gt;

&lt;h2&gt;
  
  
  3D Images
&lt;/h2&gt;

&lt;p&gt;I asked Claude to suggest some options for the 3D images. Another alternative to Tripo is Meshy.&lt;/p&gt;

&lt;p&gt;Also, it's important to understand the key concepts to achieve better results with 3D images and animations. We're easier with a dragon than a person.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A-pose&lt;/strong&gt;:  is the standard initial posture in which most characters are modeled: standing, facing forward, with the arms extended downward forming roughly a 45° angle with the torso (hence the "A" shape). This position is not random: it makes it easier to place the internal skeleton later. It prevents the mesh from deforming oddly in delicate areas, such as the shoulders or armpits, as the character starts to move.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mesh&lt;/strong&gt;: is the visible surface of the model, a kind of "digital skin" made up of thousands of small polygons (usually triangles or squares) joined together by edges and vertices. The more polygons it has, the more detailed and smooth the character will look, but it will also be heavier to process.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rigging&lt;/strong&gt;  consists of placing a virtual skeleton made of bones and joints inside the model. Although the viewer will never see it, this skeleton is what allows the character to be animated: when a bone is moved (for example, the arm bone), the mesh surrounding it deforms in response to that movement, just as human skin follows the real bone.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fix4uljrzvtfe6ckhof4p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fix4uljrzvtfe6ckhof4p.png" alt="3D Key concepts" width="800" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Both APIs are very similar in price: around 30/40 tokens per image, whether from text or image. The image is textureless, and you can call another API to generate the texture or rigging. &lt;/p&gt;

&lt;p&gt;For animations, both include animations, but are more for people. I couldn't find a flying animation, but with Claude, I can create one after a few attempts.&lt;/p&gt;

&lt;p&gt;You can download the models and export to Mixamo and other software like Blender or Unity to use in your games. &lt;/p&gt;

&lt;p&gt;You can also send the model to 3D printing services. &lt;/p&gt;

&lt;h3&gt;
  
  
  Tripo
&lt;/h3&gt;

&lt;p&gt;Payments for API and Web designers use different credits. I tested with me as a model and dancing, but you need pose A, or else your jacket looks like one piece. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F893v7fsinec3ockz4sg9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F893v7fsinec3ockz4sg9.png" alt="Dancing" width="799" height="430"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I tried one dragon and a girl, and the animations were bad, but as a tester, I was curious.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp8l3mtlctb92flcis5qq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp8l3mtlctb92flcis5qq.png" alt="Dragon and girl" width="799" height="430"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you set an A pose, you get better results, but my logo wasn't added correctly after using the auto rig.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fszhymanfohs6jm9xr96b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fszhymanfohs6jm9xr96b.png" alt="Ninja kick animation" width="799" height="430"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I tried to export and import to Mixamo, I got an error while exporting. And with the other model, my arms were wrong.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fizaxl5odvqfp81won5uo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fizaxl5odvqfp81won5uo.png" alt="Import image from Tripo to Mixamo" width="800" height="476"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For some images, the 3D model is incorrect, and most of the auto textures are metallic.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnro83s10e31ug8duq0wf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnro83s10e31ug8duq0wf.png" alt="2D Image" width="799" height="430"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fykh4cu3dsnxkbm392v9i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fykh4cu3dsnxkbm392v9i.png" alt="3D model" width="799" height="430"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To get some images of only my face, I forgot to mention that I need jeans, and some images generated were without pants.  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjuicf9e2wsfdsikpan6b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjuicf9e2wsfdsikpan6b.png" alt=" " width="799" height="430"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Also, sometimes it wasn't following the A pose and generated a T pose or multiple images inside the image.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0cwo38un1bla8x7ydy8i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0cwo38un1bla8x7ydy8i.png" alt="Imge from image" width="799" height="430"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The support is not user-friendly, and they can have a solution in 7 business days. You need to add your email and model ID. I requested more clarification about the issue with the image, but the answer was that only one person is required, and that the A pose is mandatory.&lt;/p&gt;

&lt;p&gt;This was my best result with some animations is better on laptops than mobile:&lt;br&gt;
&lt;a href="https://studio.tripo3d.ai/3d-model/c449581f-5da7-4bf9-8be0-1d1c2b6f3e6b?invite_code=CTeDxH" rel="noopener noreferrer"&gt;https://studio.tripo3d.ai/3d-model/c449581f-5da7-4bf9-8be0-1d1c2b6f3e6b?invite_code=CTeDxH&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Meshy
&lt;/h2&gt;

&lt;p&gt;It's easier, and I think the rig option is better because you can set the parts needed for rigging. The credits are shared between the API and the web designer.&lt;/p&gt;

&lt;p&gt;Here I tried me as some QA image&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgdnx1uc7igr04bkyrklb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgdnx1uc7igr04bkyrklb.png" alt="QA image" width="799" height="430"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A car, but the change in the color of the car to matte instead of metallic wasn't clear.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa0c8ezqy6bf8xac35co2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa0c8ezqy6bf8xac35co2.png" alt="Sports car" width="799" height="430"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I saw the manual rig I added myself as a Lego, but I couldn't remove the knees. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffo5rsmgyeqynzlh82r4d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffo5rsmgyeqynzlh82r4d.png" alt="Rig steps" width="799" height="430"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Lego animation&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmkgjredgvq3ek0lb1zde.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmkgjredgvq3ek0lb1zde.png" alt="Lego animation" width="799" height="430"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The import from Meshy to Mixamo went smoothly, with no errors.&lt;/p&gt;

&lt;p&gt;I requested support to remove the knees for lego animations and was added to the list of features if it becomes more popular, with at least more friendly and faster answers without my username or model ID and I got the answer faster.&lt;/p&gt;

&lt;p&gt;This is one example with meshy: &lt;br&gt;
&lt;a href="https://www.meshy.ai/s/EQNVGv" rel="noopener noreferrer"&gt;https://www.meshy.ai/s/EQNVGv&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Mixamo
&lt;/h2&gt;

&lt;p&gt;This is an Adobe product that lets you upload your models and add predefined animations. I like it because the animations are better than the AI.&lt;/p&gt;

&lt;p&gt;This is the import auto rigger.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmzjyvul112vfea6s2jzm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmzjyvul112vfea6s2jzm.png" alt="Mixamo Auto-rigger" width="800" height="441"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The animation is better because the model has a cape and was animated correctly.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.loom.com/share/45017412fe594bbeb50960c17d4d0aa9" rel="noopener noreferrer"&gt;Ninja dancing video&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Claude POC demo
&lt;/h2&gt;

&lt;p&gt;I created an experiment to build a Svelte app that generates images in 2D and 3D for the dragons and the ninja, related to my &lt;a href="https://abi-testing-dojo-demo.azurewebsites.net/" rel="noopener noreferrer"&gt;Abi's testing dojo&lt;/a&gt;, my website to practice testing. &lt;/p&gt;

&lt;p&gt;I got some errors with the animations maybe threejs animations are not very common. I got some errors from the API and for meshy I need to prepare the image for the rigging because if you don't limit the image you end with a image with the Rigging API max limit. That is on their API documentation with API parameter you limit that faces.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;When using input_task_id, models with more than 300,000 faces are not supported for rigging. Please use the Remesh API to reduce the face count before rigging.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The solution was add should_remesh: true and target_polycount: 150000 directly on the API request &lt;/p&gt;

&lt;p&gt;&lt;code&gt;const res = await fetch(&lt;/code&gt;${MESHY_BASE}/openapi/v1/image-to-3d&lt;code&gt;, {&lt;br&gt;
    method: 'POST',&lt;br&gt;
    headers: headers(),&lt;br&gt;
    body: JSON.stringify({&lt;br&gt;
      image_url: imageUrl,&lt;br&gt;
      model_type: 'standard',&lt;br&gt;
      should_texture: true,&lt;br&gt;
      should_remesh: true,&lt;br&gt;
      target_polycount: 150000&lt;br&gt;
    })&lt;br&gt;
  })&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9oxbwp499lqsw9qwqwhb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9oxbwp499lqsw9qwqwhb.png" alt="Custom animation created by Claude" width="800" height="410"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can try for a limited time because I only bought few credits: &lt;a href="https://ninja-generator-910246220092.us-central1.run.app/" rel="noopener noreferrer"&gt;https://ninja-generator-910246220092.us-central1.run.app/&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;One thing I noticed is that most of the examples of 2D and 3D images were of white people. And some ninjas have the cap with holes. There may be some bias about that.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>testing</category>
      <category>programming</category>
      <category>nanobanana</category>
    </item>
    <item>
      <title>Solution to Challenge 1 - Login with Different Users</title>
      <dc:creator>abigail armijo</dc:creator>
      <pubDate>Sat, 16 May 2026 14:54:14 +0000</pubDate>
      <link>https://dev.to/abigail_armijo/practice-real-world-testing-scenarios-for-qa-solution-to-challenge-1-login-with-different-users-27di</link>
      <guid>https://dev.to/abigail_armijo/practice-real-world-testing-scenarios-for-qa-solution-to-challenge-1-login-with-different-users-27di</guid>
      <description>&lt;p&gt;Challenge #1 is done — here's my solution to &lt;a href="https://dev.to/abigail_armijo/practice-real-world-testing-scenarios-for-qa-challenge-1-login-with-different-users-2m64"&gt;Practice Real-World Testing Scenarios for QA: Challenge #1 - Login with Different Users&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I created the Abi's testing dojo, a website with some of the challenges that I had to handle localization:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;A global trade web app&lt;/strong&gt; where that I have to test to ensure it was correctly translated into Portuguese. I got an Excel with the translation in English and Portuguese.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An e-commerce site for different countries&lt;/strong&gt; TThe DOM elements differed across countries, so instead of adding a lot of if/else or switch cases, I refactored using CSS locators in a JSON file for each country. So the test steps were the same, and with a parameter, you can test across different countries.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So the first challenge is to test login with different languages and varying levels of access; the user role determines the menu displayed. &lt;/p&gt;

&lt;p&gt;I prefer creating API tests with Postman because it is easier and focused on APIs, but I have also worked on two projects with RestSharp. I think RestAssured for Java is a very popular option, so I included the example with RestAssured for .Net&lt;/p&gt;

&lt;h2&gt;
  
  
  API Testing
&lt;/h2&gt;

&lt;p&gt;I tested these scenarios:&lt;/p&gt;

&lt;h3&gt;
  
  
  Login
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Login with the admin user, and the language will return the access token and the same language&lt;/li&gt;
&lt;li&gt;Login with a normal user will return the access token&lt;/li&gt;
&lt;li&gt;Login with an invalid user returns 401&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Menu
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Login with the admin user returns the admin menu.&lt;/li&gt;
&lt;li&gt;Login with a normal user will return the menu for normal users.&lt;/li&gt;
&lt;li&gt;Menu without user returns status code 401&lt;/li&gt;
&lt;li&gt;Menu access with a user with access to a menu link returns HasAccess true&lt;/li&gt;
&lt;li&gt;Menu access with a user without access to a menu link returns HasAccess false&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Usually, the dates should be in UTC, so on login, I checked that the expiration date was within 24 hours, and I added some time as bandwidth.&lt;/p&gt;

&lt;h3&gt;
  
  
  Postman
&lt;/h3&gt;

&lt;p&gt;I usually create a folder to test different scenarios for a feature, using this naming convention: API_Scenario_Expected_Result. Example: &lt;strong&gt;Login_withAdminUser_ReturnsAdminToken&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frcxhcj2zl16wrphw04m1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frcxhcj2zl16wrphw04m1.png" alt="Postman Folders" width="800" height="738"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I always add utility function that contains common validations across all tests. There is also a &lt;a href="https://learning.postman.com/docs/tests-and-scripts/write-scripts/packages/package-library" rel="noopener noreferrer"&gt;package library option&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faidq5bws8qim7zn5vbny.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faidq5bws8qim7zn5vbny.png" alt="Postman Global Scripts" width="800" height="567"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In one project, the JSON returned by the API changed significantly, but with the global function, I only needed to update the function, and the error message was different for each API.&lt;/p&gt;

&lt;p&gt;On each API request, you can use the global functions as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// 1. Test that the status code is 200&lt;/span&gt;
&lt;span class="nx"&gt;utils&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;StatusOk&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can execute your tests on Postman with different options:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;** Postman scheduled option** You can, for example, schedule daily tests and receive an email if they fail. I used this option in one project.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdjdbe7sf6lffe9jn45e7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdjdbe7sf6lffe9jn45e7.png" alt="Postman schedule options" width="800" height="1040"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Export&lt;/strong&gt; collection and environment to a Git repo with Newman.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Postman Cli&lt;/strong&gt; with the collection and environment IDs. I used this with one project with Bamboo as CI/CD.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can also create a CSV file so I can run the same test with different test data. For example, the login will be the same; you only need to change the default language, and each language and menu should be in a CSV file with the same name as your environment variables.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;You can install the &lt;a href="https://marketplace.visualstudio.com/items?itemName=MaciejMaciejewski.postman-report" rel="noopener noreferrer"&gt;Postman Report&lt;/a&gt; on Azure Devops to get the results on your Azure DevOps CI/CD&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j66v9x1nxkezakauzkli.png" rel="noopener noreferrer"&gt;Postman Newman report on Azure DevOps&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  .NET
&lt;/h3&gt;

&lt;p&gt;For .NET, I decided to try a new testing framework&amp;nbsp;&lt;a href="https://tunit.dev" rel="noopener noreferrer"&gt;TUnit&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;For security, you will never store passwords in code; instead, store them as secrets on your computer or in Azure Key Vault.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;If you are using Azure DevOps and you have Visual Studio, you can &lt;a href="https://learn.microsoft.com/en-us/azure/devops/test/associate-automated-test-with-test-case?view=azure-devops" rel="noopener noreferrer"&gt;associate your API tests with the manual Test Case&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  RestSharp
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://restsharp.dev" rel="noopener noreferrer"&gt;RestSharp&lt;/a&gt; is a popular package for making HTTP Requests, although it's not focused on testing like RestAssured. I've worked with RestSharp in 2 different projects.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;I also created a TestBase with global functions that include the Auth Bearer token in all requests that require the user token.&lt;/p&gt;

&lt;p&gt;I usually add each file with the API to test, for example, login + Test.cs (e.g., LoginTest.cs), which extends BasePage.&lt;/p&gt;

&lt;p&gt;I created a wrapper to call the API. If new versions of RestSharp introduce a breaking change, you only need to update the ApiClient class.&lt;/p&gt;

&lt;h4&gt;
  
  
  RestAssured
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://github.com/basdijkstra/rest-assured-net/wiki/Usage-Guide" rel="noopener noreferrer"&gt;RestAssured&lt;/a&gt; iis very popular for Java API testing and uses the &lt;strong&gt;Given/When/Then&lt;/strong&gt; syntax.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt; &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;Given&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ContentType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"application/json"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;loginRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;When&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;AuthUrl&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/api/Users/login"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Then&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;StatusCode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;And&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"$.AccessToken"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Is&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Not&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Is&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Null&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()))&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;And&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"$.TokenExpiration"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Is&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Not&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Is&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Null&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()))&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DeserializeTo&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;LoginResponse&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  UI Testing
&lt;/h2&gt;

&lt;p&gt;For UI testing, you have different options. I've worked with&amp;nbsp;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.selenium.dev" rel="noopener noreferrer"&gt;Selenium&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;&lt;a href="https://nightwatchjs.org" rel="noopener noreferrer"&gt;Nightwatch.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://testcafe.io" rel="noopener noreferrer"&gt;TestCafe&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.cypress.io" rel="noopener noreferrer"&gt;Cypress&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://playwright.dev" rel="noopener noreferrer"&gt;Playwright&lt;/a&gt; &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I tried some AI options that can generate code for manual tests. I added the link to the article. I tested with the first version, so I guess it now includes more options&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.qase.io" rel="noopener noreferrer"&gt;Qase&lt;/a&gt;&lt;/strong&gt; &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.blinq.io" rel="noopener noreferrer"&gt;BlinqIO&lt;/a&gt;&lt;/strong&gt;: &lt;a href="https://abigailarmijo.substack.com/p/boost-your-testing-efficiency-with" rel="noopener noreferrer"&gt;Boost your testing efficiency with Blinqio: AI Test Engineer)&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.testmuai.com" rel="noopener noreferrer"&gt;KaneAI&lt;/a&gt;&lt;/strong&gt;: &lt;a href="https://abigailarmijo.substack.com/p/meet-kaneai-the-new-ai-testing-assistant?utm_source=publication-search" rel="noopener noreferrer"&gt;Meet KaneAI: The New AI Testing Assistant&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://qabunch.com" rel="noopener noreferrer"&gt;QAautoMATER&lt;/a&gt;&lt;/strong&gt;: &lt;a href="https://abigailarmijo.substack.com/p/qaautomater-the-ai-codeless-tool" rel="noopener noreferrer"&gt;QAautoMATER: The AI codeless tool for Web, API, and Mobile testing&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To create tests, I usually follow the Page Object Model (POM) and components to add common functionality, for example, to grids, date pickers, and combos. With this combo, I've worked on UI migration from Telerik webforms to Component One, so I only need to change the Grid from Telerik to Component One, instead of changing some helper or more code.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;I explained in a video &lt;a href="https://youtu.be/0n1F_eMkUqE?si=k1ca3v5-00w5OKcv" rel="noopener noreferrer"&gt;How to design a web automation testing framework&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For the different languages, I created a data folder with the text for each language.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvtnhj3pre1jzerwkv137.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvtnhj3pre1jzerwkv137.png" alt="Data folder with the translations" width="800" height="535"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I created a fixture to set the current language from the playwright projects.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Each project runs the full test suite once in that language.&lt;/span&gt;
&lt;span class="c1"&gt;// The project name is matched by the locale fixture in fixtures/index.ts to load the correct data/xx-XX.json file.&lt;/span&gt;
&lt;span class="c1"&gt;// To add a new language: add a project here, add the mapping in fixtures/index.ts, and create data/xx-XX.json.&lt;/span&gt;
  &lt;span class="nx"&gt;projects&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;English&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;use&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;devices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Desktop Chrome&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Spanish&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;use&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;devices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Desktop Chrome&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;German&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;use&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;devices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Desktop Chrome&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Japanese&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;use&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;devices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Desktop Chrome&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fixture&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Fixtures are shared test dependencies that Playwright injects into every test automatically.&lt;/span&gt;
&lt;span class="c1"&gt;// Instead of repeating setup code in each test, you declare it once here and use it as a parameter.&lt;/span&gt;
&lt;span class="c1"&gt;// This file exports a custom `test` object that replaces the default one from @playwright/test.&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;base&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@playwright/test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Maps the Playwright project name (defined in playwright.config.ts) to a BCP-47 locale code.&lt;/span&gt;
&lt;span class="c1"&gt;// The project name is used as the human-readable label in the HTML report.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;localeMap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;English&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en-US&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;Spanish&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;es-MX&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;German&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;   &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;de-DE&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;Japanese&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ja-JP&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Fixtures&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;locale&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;extend&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Fixtures&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;locale&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({},&lt;/span&gt; &lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;testInfo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;localeMap&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;testInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;project&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en-US&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;expect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@playwright/test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the website, I used &lt;a href="https://jsverse.gitbook.io/transloco" rel="noopener noreferrer"&gt;Transloco&lt;/a&gt; which uses a similar approach to JSON.&lt;/p&gt;

&lt;p&gt;I follow the same security best practices for using the .env file with user and password information, and I also created API helpers, for example, to read Excel, PDF, or API files.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;Playwright includes a global setup where you can store the session for different users and reuse it for different tests.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;globalSetup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mkdir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;authFolder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;recursive&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;chromium&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newPage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;BASE_URL&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;loginApi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;LoginApi&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;userLogin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Login&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;Company&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;COMPANY&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;UserName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NORMAL_USER&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;Password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NORMAL_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;KeepSession&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;Code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;storeToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;loginApi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userLogin&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;saveAuthState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;normal-user.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And to use, you only need to add the state&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Valid access to the page&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;storageState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.auth/normal-user.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I add tags in general by feature to execute tests by tag. You can also create tags like regression, security, smoke.&lt;/p&gt;

&lt;p&gt;With an additional npm package, you can add a tag with the Test Case ID from Azure DevOps, and each time you execute the Azure DevOps pipeline, it will mark the test failed, passed, or skipped on Azure DevOps.&lt;/p&gt;

&lt;p&gt;I published the results on GitHub Pages. I used this approach with one project, but they wanted to store the last week in separate folders, and on that project, I connected it to Allure Report.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;I also included the screencast and actions annotations to include the description of the steps on the video&lt;/p&gt;

&lt;p&gt;You can check my solution code &lt;a href="https://github.com/apis3445/TestingDojo" rel="noopener noreferrer"&gt;TestingDojo Code&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Thanks for following along with my take on Challenge #1! Testing is all about continuous learning, so don't hesitate to ask questions or share your feedback below. If this helped you in any way, feel free to share it with the community.&lt;/p&gt;

</description>
      <category>testing</category>
      <category>automation</category>
      <category>playwright</category>
      <category>api</category>
    </item>
    <item>
      <title>Practice Real-World Testing Scenarios for QA: Challenge 1 - Login with Different Users</title>
      <dc:creator>abigail armijo</dc:creator>
      <pubDate>Wed, 25 Mar 2026 03:30:25 +0000</pubDate>
      <link>https://dev.to/abigail_armijo/practice-real-world-testing-scenarios-for-qa-challenge-1-login-with-different-users-2m64</link>
      <guid>https://dev.to/abigail_armijo/practice-real-world-testing-scenarios-for-qa-challenge-1-login-with-different-users-2m64</guid>
      <description>&lt;h2&gt;
  
  
  Welcome to Abi's testing dojo challenges.
&lt;/h2&gt;

&lt;p&gt;I&amp;nbsp;am thrilled to finally launch this series of testing challenges designed to help you learn and practice testing with real-world scenarios.&lt;/p&gt;

&lt;p&gt;As QA professionals, we know that nothing replaces the experience of tackling real scenarios on actual applications. That's why I've built a website to serve as your training ground for API Testing, E2E Automation, performance testing, accessibility testing, and more. It contains a dashboard, grids, different users, and languages.&lt;/p&gt;

&lt;p&gt;I'll be publishing a new QA Challenge. These aren't just "dummy" or standalone exercises. They are designed to simulate the complex workflows that you face when you are working as QA.&lt;/p&gt;

&lt;p&gt;To keep the website available to everyone, it automatically resets all the info. It redeploys the basic data every Sunday at 00:00 UTC. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Website&lt;/strong&gt;:&amp;nbsp;&lt;a href="https://abi-testing-dojo-demo.azurewebsites.net/" rel="noopener noreferrer"&gt;https://abi-testing-dojo-demo.azurewebsites.net/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security API (Scalar)&lt;/strong&gt;:&amp;nbsp;&lt;a href="https://abi-testing-dojo-auth-demo.azurewebsites.net" rel="noopener noreferrer"&gt;https://abi-testing-dojo-auth-demo.azurewebsites.net&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data API (Swagger)&lt;/strong&gt;: &lt;a href="https://abi-testing-dojo-ar-demo.azurewebsites.net" rel="noopener noreferrer"&gt;https://abi-testing-dojo-ar-demo.azurewebsites.net&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The website is an accounts receivable dashboard for different companies. One server can have one or more companies, and each company can have different users with different levels of access.&lt;/p&gt;

&lt;p&gt;An admin user can register new servers, companies, and users. &lt;/p&gt;

&lt;p&gt;A standard user can only see the dashboard, reports, and config parameters for the dashboard.&lt;/p&gt;

&lt;h2&gt;
  
  
  Challenge 1: Log in with different users
&lt;/h2&gt;

&lt;p&gt;A login is about more than just a username and password. To test professional applications, you need to understand some main concepts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Authentication&lt;/strong&gt;:&amp;nbsp;The system verifies&amp;nbsp;who you are&amp;nbsp;by checking your credentials and issuing an AccessToken.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Authorization&lt;/strong&gt;:&amp;nbsp;Once you are logged in, the system checks&amp;nbsp;what you are allowed to do.&amp;nbsp;and which companies you can access based on your role.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Internationalization (i18n)&lt;/strong&gt;: Testing that the application supports multiple languages (the language switcher at the top of the page).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Localization (l10n)&lt;/strong&gt;: Testing that the application is correctly translated for a specific region (switching to Spanish or Japanese).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this challenge, we will automate both using an&amp;nbsp;Admin&amp;nbsp;account and a&amp;nbsp;Standard User&amp;nbsp;account.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Scenarios to test
&lt;/h3&gt;

&lt;p&gt;You need to verify the login functionality for two different user with different roles:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Admin (John Doe)&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;User&lt;/strong&gt;:&amp;nbsp;jdoe&amp;nbsp;&lt;br&gt;
&lt;strong&gt;Password&lt;/strong&gt;:&amp;nbsp;Admin+123&lt;br&gt;
&lt;strong&gt;Companies&lt;/strong&gt;:&amp;nbsp;Northwind,&amp;nbsp;Contoso, or&amp;nbsp;Adventure Works&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Standard User (Jane Doe)&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;User&lt;/strong&gt;:&amp;nbsp;janedoe &lt;br&gt;
&lt;strong&gt;Password&lt;/strong&gt;:&amp;nbsp;User+123&lt;br&gt;
&lt;strong&gt;Company&lt;/strong&gt;:&amp;nbsp;Northwind&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  The Dojo Challenge
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;API Challenge&lt;/strong&gt;: &lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;Perform a&amp;nbsp;POST&amp;nbsp;request to the login endpoint. Verify you receive a&amp;nbsp;200 OK&amp;nbsp;and a valid&amp;nbsp;AccessToken.&lt;/li&gt;
&lt;li&gt;Perform a post with invalid user and password.&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;E2E Challenge&lt;/strong&gt;: &lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;Automate the UI flow (Company, User, Pass), click Login, and verify the redirection to the dashboard. The menu is different for an admin and a standard user.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Pro Tips for this Challenge
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Role-Based Access&lt;/strong&gt;: Users shouldn't be able to log into a company without access.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wait for the Dashboard&lt;/strong&gt;: The dashboard loads data from multiple APIs. Ensure your E2E test waits for the charts to appear before declaring victory!&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Save that Token&lt;/strong&gt;: You’ll need the&amp;nbsp;AccessToken&amp;nbsp;for future challenges. Keep it handy!&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Internalization&lt;/strong&gt;: Log in by selecting a different language. You can use the Google option to translate to your preferred language&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can use environment variables for the user and password because for security is better don't store passwords in the code&lt;/p&gt;

&lt;h2&gt;
  
  
  My Solution
&lt;/h2&gt;

&lt;p&gt;I’ll be sharing my own implementation for this challenge on &lt;a href="https://github.com/apis3445/TestingDojo" rel="noopener noreferrer"&gt;https://github.com/apis3445/TestingDojo&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Postman (API Collections)&lt;/li&gt;
&lt;li&gt;C# / .NET (API Automation)&lt;/li&gt;
&lt;li&gt;Playwright (E2E Masterclass)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you complete this challenge, share your repository or a screenshot of your passing tests and tag it!&lt;/p&gt;

</description>
      <category>testing</category>
      <category>automation</category>
      <category>playwright</category>
      <category>api</category>
    </item>
    <item>
      <title>My AI-Powered Portfolio: Built with Antigravity, Svelte &amp; Cloud Run</title>
      <dc:creator>abigail armijo</dc:creator>
      <pubDate>Thu, 29 Jan 2026 03:05:52 +0000</pubDate>
      <link>https://dev.to/abigail_armijo/my-ai-powered-portfolio-built-with-antigravity-svelte-cloud-run-369k</link>
      <guid>https://dev.to/abigail_armijo/my-ai-powered-portfolio-built-with-antigravity-svelte-cloud-run-369k</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw242mqstm715cwfh6ke2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw242mqstm715cwfh6ke2.png" alt=" " width="800" height="323"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/new-year-new-you-google-ai-2025-12-31"&gt;New Year, New You Portfolio Challenge Presented by Google AI&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  About Me
&lt;/h2&gt;

&lt;p&gt;I'm a software developer who also enjoys automation testing.&lt;/p&gt;

&lt;p&gt;I'm currently working as a QA Engineer, and I enjoy creating automation frameworks from scratch tailored to the specific needs of the project.&lt;/p&gt;

&lt;p&gt;I published some public examples, videos, and articles to share my knowledge with the community. &lt;/p&gt;

&lt;p&gt;My project for this year is to share a website to practice testing with real-world scenarios that I created as a POC for a client. I redesigned the website with Antigravity and included translations to English and German.&lt;/p&gt;

&lt;p&gt;I created this portfolio to showcase my best projects, and I enjoyed the challenge of building it entirely with AI.&lt;/p&gt;

&lt;h2&gt;
  
  
  Portfolio
&lt;/h2&gt;


&lt;div class="ltag__cloud-run"&gt;
  &lt;iframe height="600px" src="https://portfolio-592756863001.northamerica-south1.run.app"&gt;
  &lt;/iframe&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;p&gt;I created my portfolio with Antigravity. I used the planning option to create the portfolio and shared links to my LinkedIn, GitHub, GitBook, and Substack to Antigravity help me to create my about and the sections for my porftoflio. Although the initial plan suggested was to use React, I changed the plan to Svelte instead of React.&lt;/p&gt;

&lt;p&gt;I added my logo on .png and asked Antigravity to add as the favicon.icon. I requested to download my profile picture from Substack (or any of the links I shared), to add to the portfolio.&lt;/p&gt;

&lt;p&gt;Antigravity helped me a lot with the UI, I requested some changes to make it more accessible and user-friendly. It also helped me with the deployment, including setting up environment variables for the Gemini API key for the AI feature.&lt;/p&gt;

&lt;p&gt;The AI feature uses the Gemini Flash model (free tier), so the answers are limited and could fail for the limits of free version. &lt;/p&gt;

&lt;p&gt;Antigravity help me with the prompt and change the design to use json files on each sections to include the sections as context for the GoogleGenerativeAI. &lt;/p&gt;

&lt;p&gt;For my testing practice website, Antigravity helped me redesign the site, fix bugs, add generate English and German translations, and validate some ideas for the challenges that I will post in the next weeks, for example to include japanese.&lt;/p&gt;

&lt;p&gt;The concept originated from a previous role, where I proposed a modern alternative to traditional PDF CVs for sharing consultant bios with clients. This newly created template is fully reusable—you simply need to update the JSON files to customize the content for each section&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'm Most Proud Of
&lt;/h2&gt;

&lt;p&gt;I am proud that I created this website in just a few hours; checking the planning and making adjustments was very helpful. &lt;/p&gt;

&lt;p&gt;I love learning new things, so I enjoyed creating a new website only with Antigravity, as well as deploying to Google Cloud, which was new to me. And I learned the Google API for the AI feature.&lt;/p&gt;

&lt;p&gt;I liked the redesign that Antigravity did for my Angular website, "Abi's Testing Dojo." This helped me to release the new version of the website 2 weeks ago.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Thanks to the Google AI team for this challenge, the opportunity to learn new things, and for reviewing my portfolio.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>googleaichallenge</category>
      <category>portfolio</category>
      <category>gemini</category>
    </item>
  </channel>
</rss>
