<?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.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>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. &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. &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 &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. &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. &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 &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. &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. &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. &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. &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 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;: &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;: &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;: The system verifies who you are by checking your credentials and issuing an AccessToken.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Authorization&lt;/strong&gt;: Once you are logged in, the system checks what you are allowed to do. 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 Admin account and a Standard User 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;: jdoe &lt;br&gt;
&lt;strong&gt;Password&lt;/strong&gt;: Admin+123&lt;br&gt;
&lt;strong&gt;Companies&lt;/strong&gt;: Northwind, Contoso, or 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;: janedoe &lt;br&gt;
&lt;strong&gt;Password&lt;/strong&gt;: User+123&lt;br&gt;
&lt;strong&gt;Company&lt;/strong&gt;: 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 POST request to the login endpoint. Verify you receive a 200 OK and a valid 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 AccessToken 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>
