<?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: Harshil Patel</title>
    <description>The latest articles on DEV Community by Harshil Patel (@harshil_patel).</description>
    <link>https://dev.to/harshil_patel</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%2F1648253%2F8489c1ca-009a-4a82-ae33-6f2ec28a3f5b.jpeg</url>
      <title>DEV Community: Harshil Patel</title>
      <link>https://dev.to/harshil_patel</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/harshil_patel"/>
    <language>en</language>
    <item>
      <title>Kubernetes on Windows: A Guide to Running Kubernetes Using Minikube and kubectl</title>
      <dc:creator>Harshil Patel</dc:creator>
      <pubDate>Fri, 20 Dec 2024 23:04:10 +0000</pubDate>
      <link>https://dev.to/harshil_patel/kubernetes-on-windows-a-guide-to-running-kubernetes-using-minikube-fe8</link>
      <guid>https://dev.to/harshil_patel/kubernetes-on-windows-a-guide-to-running-kubernetes-using-minikube-fe8</guid>
      <description>&lt;p&gt;There is a GitHub repository where I have added detailed notes on what Kubernetes is, along with notes on its components and architecture. You can check it out here: &lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/hpatel292-seneca" rel="noopener noreferrer"&gt;
        hpatel292-seneca
      &lt;/a&gt; / &lt;a href="https://github.com/hpatel292-seneca/Learning-Kubernetes" rel="noopener noreferrer"&gt;
        Learning-Kubernetes
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      This repo contains basic notes about kubernetes like its Architecture, components and how to use kubernetes using kubectl
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Learning-Kubernetes&lt;/h1&gt;
&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;What is Kubernetes?&lt;/h2&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Kubernetes is an open source Container Orchestration tool.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;What is Container Orchestration??&lt;/h3&gt;
&lt;/div&gt;
&lt;p&gt;Container Orchestration is a process of automatically managing and coordinating a large number containers. It manage task like&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Starting and Stopping containers&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Placing containers on right machine&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Scaling containers up and down based on usage&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Restart containers on failures&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Kubernetes helps you manage containerized application in different environment like on-permise, Cloud, Hybrid setup. It ensures you application runs effectively, remain scalable, and are resilient to failures, without you having to manage containers manually.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Why Kubernetes was developed??&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;Kubernetes was developed by Google and later open-sourced to help manage the complex challenges of deploying and running large-scale containerized applications. Before Kubernetes, Google used its internal system called Borg, which inspired Kubernetes.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Need for Container Orchestration tool:&lt;/h3&gt;

&lt;/div&gt;
&lt;p&gt;As Container adoption grew, so did the complexity for managing those containers. Here are few reasons why Kubernetes became…&lt;/p&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/hpatel292-seneca/Learning-Kubernetes" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;h3&gt;
  
  
  Installing Minikube
&lt;/h3&gt;

&lt;p&gt;To get started, you first need to install Minikube. Visit the official Minikube site: &lt;a href="https://minikube.sigs.k8s.io/docs/start/?arch=%2Fwindows%2Fx86-64%2Fstable%2F.exe+download" rel="noopener noreferrer"&gt;Minikube Start Documentation&lt;/a&gt;. Choose your operating system and follow the provided steps to install Minikube.&lt;/p&gt;

&lt;p&gt;For Windows users, you can use PowerShell to install Minikube by running the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;New-Item&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'c:\'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'minikube'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-ItemType&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Directory&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Force&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Invoke-WebRequest&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-OutFile&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'c:\minikube\minikube.exe'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Uri&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'https://github.com/kubernetes/minikube/releases/latest/download/minikube-windows-amd64.exe'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-UseBasicParsing&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will download and install Minikube to your system. Next, add Minikube to your system’s PATH environment variable by running the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$oldPath&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;]::&lt;/span&gt;&lt;span class="n"&gt;GetEnvironmentVariable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Path'&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="n"&gt;EnvironmentVariableTarget&lt;/span&gt;&lt;span class="p"&gt;]::&lt;/span&gt;&lt;span class="n"&gt;Machine&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$oldPath&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;';'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-notcontains&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'C:\minikube'&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="n"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;]::&lt;/span&gt;&lt;span class="n"&gt;SetEnvironmentVariable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Path'&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="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'{0};C:\minikube'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$oldPath&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="n"&gt;EnvironmentVariableTarget&lt;/span&gt;&lt;span class="p"&gt;]::&lt;/span&gt;&lt;span class="n"&gt;Machine&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This ensures that you can run Minikube commands from any terminal session.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting Up a Hypervisor
&lt;/h3&gt;

&lt;p&gt;Minikube requires a hypervisor to run. Depending on your operating system, you can choose from several options. For a detailed list of supported drivers, refer to the &lt;a href="https://minikube.sigs.k8s.io/docs/drivers/" rel="noopener noreferrer"&gt;Minikube Drivers Documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you already have Docker Desktop installed on your Windows system, you can use Docker as the Minikube driver. To start a Kubernetes cluster using the Docker driver, run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;minikube&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--driver&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2F4yryuymhooyu0kp86eje.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%2F4yryuymhooyu0kp86eje.png" alt="Minikube Start Example" width="800" height="265"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This command will also install &lt;code&gt;kubectl&lt;/code&gt; as a dependency. You can now use &lt;code&gt;kubectl&lt;/code&gt; to interact with your Kubernetes cluster, specifically the API server.&lt;/p&gt;

&lt;p&gt;Now as minikube is running let's check if it's actually running or not. To get all nodes in the cluster you can cun |&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl get nodes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fzueee5t7dc88rsal510o.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%2Fzueee5t7dc88rsal510o.png" alt="Image description" width="506" height="90"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Basic Operations with kubectl
&lt;/h3&gt;

&lt;p&gt;Now, as kubectl is installed and configured, now we can use it to interact with Kubernetes cluster.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;1. Check the Status of Nodes&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Command&lt;/strong&gt;: &lt;code&gt;kubectl get nodes&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What It Does&lt;/strong&gt;: Lists all nodes in the cluster with their status (e.g., Ready, NotReady).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Example Output&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  NAME       STATUS   ROLES    AGE   VERSION
  minikube   Ready    master   5m    v1.23.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Details&lt;/strong&gt;: Shows information like node name, roles, and Kubernetes version. For Minikube, there is typically one master node.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;2. List Pods&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Command&lt;/strong&gt;: &lt;code&gt;kubectl get pods&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What It Does&lt;/strong&gt;: Displays all running pods in the default namespace.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;3. List Services&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Command&lt;/strong&gt;: &lt;code&gt;kubectl get services&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What It Does&lt;/strong&gt;: Lists all Kubernetes services in the default namespace.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Example Output&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  NAME         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
  kubernetes   ClusterIP   10.96.0.1       &amp;lt;none&amp;gt;        443/TCP   5m
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Details&lt;/strong&gt;: Services provide networking between pods. The default service is Kubernetes' internal DNS service.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;4. Create a Deployment&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Command&lt;/strong&gt;: &lt;code&gt;kubectl create deployment &amp;lt;name&amp;gt; --image=&amp;lt;image-name&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What It Does&lt;/strong&gt;: Creates a deployment, which manages the pods and ReplicaSets. In Kubernetes we cannot interact with pods directly, instead we can use deployment and deployment act as abstraction layer on pods.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Example&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  kubectl create deployment nginx-deployment &lt;span class="nt"&gt;--image&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Details&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;Creates a deployment named &lt;code&gt;nginx-deployment&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Downloads the &lt;code&gt;nginx&lt;/code&gt; image from Docker Hub and creates a pod using it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verification&lt;/strong&gt;: Run &lt;code&gt;kubectl get deployments&lt;/code&gt; to confirm the deployment.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;5. View Deployment&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Command&lt;/strong&gt;: &lt;code&gt;kubectl get deployments&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What It Does&lt;/strong&gt;: Lists all deployments in the current namespace.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Example Output&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  NAME               READY   UP-TO-DATE   AVAILABLE   AGE
  nginx-deployment   1/1     1            1           5m
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Details&lt;/strong&gt;: Displays the deployment's readiness and availability status.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;6. View ReplicaSets&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Command&lt;/strong&gt;: &lt;code&gt;kubectl get replicasets&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What It Does&lt;/strong&gt;: Lists all ReplicaSets associated with the deployments.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Example Output&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  NAME                      DESIRED   CURRENT   READY   AGE
  nginx-deployment-6fd8c8   1         1         1       5m
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Details&lt;/strong&gt;: Shows the desired and actual number of pods for each ReplicaSet.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;7. Edit a Deployment&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Command&lt;/strong&gt;: &lt;code&gt;kubectl edit deployment &amp;lt;name&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What It Does&lt;/strong&gt;: Opens the deployment's configuration file in your default text editor.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Example&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  kubectl edit deployment nginx-deployment
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Details&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Opens the configuration file for the deployment.&lt;/li&gt;
&lt;li&gt;Edit fields like the container image. Example:
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt; &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
   &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
   &lt;span class="pi"&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;nginx&lt;/span&gt;
     &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx:1.16&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;Save and close the editor.&lt;/li&gt;
&lt;li&gt;Kubernetes automatically terminates old pods and creates new ones with the updated configuration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verification&lt;/strong&gt;: Run &lt;code&gt;kubectl get pods&lt;/code&gt; to see old pods terminating and new ones starting.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;8. Check Logs&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Command&lt;/strong&gt;: &lt;code&gt;kubectl logs &amp;lt;pod-name&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What It Does&lt;/strong&gt;: Fetches logs from a specific pod.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Example&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  kubectl logs nginx-deployment-6fd8c8
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Details&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;Logs provide output generated by the container application inside the pod.&lt;/li&gt;
&lt;li&gt;If no logs are available, it means the application hasn't logged anything yet.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;9. Describe a Pod&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Command&lt;/strong&gt;: &lt;code&gt;kubectl describe pod &amp;lt;pod-name&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What It Does&lt;/strong&gt;: Provides detailed information about a pod.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Example&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  kubectl describe pod nginx-deployment-6fd8c8
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Details&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;Shows detailed events like image pulling, container creation, and issues (if any).&lt;/li&gt;
&lt;li&gt;Useful for debugging pod startup problems.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;10. Access Pod Shell&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Command&lt;/strong&gt;: &lt;code&gt;kubectl exec -it &amp;lt;pod-name&amp;gt; -- &amp;lt;command&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What It Does&lt;/strong&gt;: Opens an interactive terminal session in a container inside the pod.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Example&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  kubectl &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; nginx-deployment-6fd8c8 &lt;span class="nt"&gt;--&lt;/span&gt; /bin/bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Details&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;You are now inside the container of the pod.&lt;/li&gt;
&lt;li&gt;Use this to debug or test the container environment.&lt;/li&gt;
&lt;li&gt;Exit the session by typing &lt;code&gt;exit&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;11. Delete a Deployment&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Command&lt;/strong&gt;: &lt;code&gt;kubectl delete deployment &amp;lt;name&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What It Does&lt;/strong&gt;: Deletes the deployment along with its associated pods and ReplicaSets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Example&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  kubectl delete deployment nginx-deployment
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Details&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;Automatically removes all pods and ReplicaSets under the deployment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verification&lt;/strong&gt;: Run &lt;code&gt;kubectl get pods&lt;/code&gt; and &lt;code&gt;kubectl get replicasets&lt;/code&gt; to confirm deletion.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;12. Apply Configuration File&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Command&lt;/strong&gt;: &lt;code&gt;kubectl apply -f &amp;lt;file.yaml&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What It Does&lt;/strong&gt;: Creates or updates resources defined in a configuration file.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Example&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; nginx-deployment.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Details&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The file can define multiple resources, like deployments, services, etc.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Example Configuration&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apps/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deployment&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&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;nginx-deployment&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;replicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;matchLabels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&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;nginx&lt;/span&gt;
        &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx&lt;/span&gt;
        &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;containerPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;



&lt;ul&gt;
&lt;li&gt;To update the deployment, modify the file (e.g., change replicas to &lt;code&gt;3&lt;/code&gt;) and reapply it:
&lt;/li&gt;
&lt;/ul&gt;

&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; nginx-deployment.yaml
&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;

&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>Setting Up a CI/CD Pipeline with Azure and Playwright for StyleMate</title>
      <dc:creator>Harshil Patel</dc:creator>
      <pubDate>Fri, 13 Dec 2024 01:00:51 +0000</pubDate>
      <link>https://dev.to/harshil_patel/setting-up-a-cicd-pipeline-with-azure-and-playwright-for-stylemate-843</link>
      <guid>https://dev.to/harshil_patel/setting-up-a-cicd-pipeline-with-azure-and-playwright-for-stylemate-843</guid>
      <description>&lt;p&gt;I have been working on a web app called &lt;a href="https://github.com/hpatel292-seneca/StyleMate" rel="noopener noreferrer"&gt;StyleMate&lt;/a&gt;. Initially, I started this project to practice my C# and .NET skills. Over time, I added more pages and features and began exploring advanced functionalities. Recently, I integrated &lt;strong&gt;GROQ LLM&lt;/strong&gt; to generate intelligent clothing suggestions and a &lt;strong&gt;weather API&lt;/strong&gt; to enhance recommendations based on current weather conditions. &lt;/p&gt;

&lt;p&gt;After incorporating these features, I decided to add a &lt;strong&gt;CI/CD pipeline&lt;/strong&gt; for automated deployments and integrate &lt;strong&gt;testing&lt;/strong&gt; into my project. Here’s how I set up the pipeline, handled challenges, and implemented a staging environment for robust testing.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Adding a Deployment Pipeline&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;To deploy StyleMate, I created an Azure Web App with a &lt;strong&gt;free App Service plan&lt;/strong&gt; and a &lt;strong&gt;SQL database&lt;/strong&gt;. The database costs around $5 per month using Azure credits. To streamline deployments, I configured the &lt;strong&gt;deployment center&lt;/strong&gt; in the Azure Web App.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Challenges with Default Pipeline&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Initially, the default pipeline created by the deployment center used &lt;strong&gt;subscription ID&lt;/strong&gt;, &lt;strong&gt;tenant ID&lt;/strong&gt;, and &lt;strong&gt;resource ID&lt;/strong&gt; for deployment. However, I wanted to deploy using the &lt;strong&gt;Publish Profile&lt;/strong&gt; method, which is simpler and works well for smaller projects. &lt;/p&gt;

&lt;p&gt;After some research, I found that I needed to enable &lt;strong&gt;SCM Basic Auth Publishing&lt;/strong&gt; under the Web App configuration settings. This configuration allows the pipeline to use the Publish Profile for deployments.&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%2F1zkvv23as1xmcuad9ymy.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%2F1zkvv23as1xmcuad9ymy.png" alt="SCM Basic Auth Publishing" width="800" height="353"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;Pipeline Code&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Here’s the pipeline I configured for building and deploying StyleMate:&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="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build and deploy .NET Core application to Web App StyleMate&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;workflow_run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;workflows&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Build and deploy ASP.Net Core app to Azure Web App - StyleMate-Staging&lt;/span&gt;
    &lt;span class="na"&gt;types&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;completed&lt;/span&gt;
&lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;AZURE_WEBAPP_NAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;StyleMate&lt;/span&gt;
  &lt;span class="na"&gt;AZURE_WEBAPP_PACKAGE_PATH&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.\published&lt;/span&gt;
  &lt;span class="na"&gt;CONFIGURATION&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Release&lt;/span&gt;
  &lt;span class="na"&gt;DOTNET_CORE_VERSION&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;8.0.x&lt;/span&gt;
  &lt;span class="na"&gt;WORKING_DIRECTORY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.event.workflow_run.conclusion == 'success' }}&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;windows-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
    &lt;span class="pi"&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;Setup .NET SDK&lt;/span&gt;
      &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-dotnet@v3&lt;/span&gt;
      &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;dotnet-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ env.DOTNET_CORE_VERSION }}&lt;/span&gt;
    &lt;span class="pi"&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;Restore&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dotnet restore "${{ env.WORKING_DIRECTORY }}"&lt;/span&gt;
    &lt;span class="pi"&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;Build&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dotnet build "${{ env.WORKING_DIRECTORY }}" --configuration ${{ env.CONFIGURATION }} --no-restore&lt;/span&gt;
    &lt;span class="pi"&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;Install Playwright Browsers&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npx playwright install&lt;/span&gt;
    &lt;span class="pi"&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;Test&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dotnet test "${{ env.WORKING_DIRECTORY }}" --no-build&lt;/span&gt;
    &lt;span class="pi"&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;Publish&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dotnet publish "${{ env.WORKING_DIRECTORY }}" --configuration ${{ env.CONFIGURATION }} --no-build --output "${{ env.AZURE_WEBAPP_PACKAGE_PATH }}"&lt;/span&gt;
    &lt;span class="pi"&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;Publish Artifacts&lt;/span&gt;
      &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/upload-artifact@v3&lt;/span&gt;
      &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&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;webapp&lt;/span&gt;
        &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ env.AZURE_WEBAPP_PACKAGE_PATH }}&lt;/span&gt;
  &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;windows-latest&lt;/span&gt;
    &lt;span class="na"&gt;needs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;build&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&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;Download artifact from build job&lt;/span&gt;
      &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/download-artifact@v3&lt;/span&gt;
      &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&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;webapp&lt;/span&gt;
        &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ env.AZURE_WEBAPP_PACKAGE_PATH }}&lt;/span&gt;
    &lt;span class="pi"&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;Deploy to Azure WebApp&lt;/span&gt;
      &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;azure/webapps-deploy@v2&lt;/span&gt;
      &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;app-name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ env.AZURE_WEBAPP_NAME }}&lt;/span&gt;
        &lt;span class="na"&gt;publish-profile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.StyleMate_84FD }}&lt;/span&gt;
        &lt;span class="na"&gt;package&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ env.AZURE_WEBAPP_PACKAGE_PATH }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  &lt;strong&gt;Integrating Testing&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;I added testing to the pipeline to ensure the application works correctly before deploying to production. The pipeline includes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Unit Tests&lt;/strong&gt; using xUnit to validate core logic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;End-to-End (E2E) Tests&lt;/strong&gt; using Playwright to simulate real user interactions.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Challenge with E2E Testing&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Playwright tests run on an actual website. If a code change breaks functionality, deploying it directly to production can cause issues. Ideally, tests should run on a &lt;strong&gt;staging environment&lt;/strong&gt; to prevent this.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Initial Attempt: Deployment Slots&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Azure App Service &lt;strong&gt;deployment slots&lt;/strong&gt; would have been perfect for staging. However, the free App Service plan does not support deployment slots.&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%2Fli04zxbmi5xrj4lbibwm.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%2Fli04zxbmi5xrj4lbibwm.png" alt="Deployment Slot Unavailable" width="800" height="244"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Staging Environment Solution&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;As a workaround, I created another Azure Web App called &lt;code&gt;StyleMate-Staging&lt;/code&gt;. I then updated the pipeline to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Deploy the application to the &lt;strong&gt;staging web app (&lt;code&gt;StyleMate-Staging&lt;/code&gt;)&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Run Playwright tests on the staging environment.&lt;/li&gt;
&lt;li&gt;Deploy the application to the &lt;strong&gt;production web app (&lt;code&gt;StyleMate&lt;/code&gt;)&lt;/strong&gt; only if all tests pass.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Final Workflow&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Here’s how the pipeline works:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Build and Test&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The application is built and unit tests are executed.&lt;/li&gt;
&lt;li&gt;Artifacts are created and uploaded for deployment.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Staging Deployment&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The application is deployed to &lt;code&gt;StyleMate-Staging&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Playwright tests run on the staging site.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Production Deployment&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If all tests pass, the application is deployed to the production web app, &lt;code&gt;StyleMate&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Conclusion&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;By integrating a CI/CD pipeline and testing workflow, I ensured the reliability and quality of StyleMate. The staging environment allowed me to run Playwright E2E tests safely without impacting production users. While Azure's free App Service plan has limitations (e.g., no deployment slots), the workaround with a separate staging app worked seamlessly.&lt;/p&gt;

&lt;p&gt;This setup provides a robust foundation for deploying high-quality code and further enhancing StyleMate.&lt;/p&gt;

&lt;p&gt;If you’d like to check out the project, here’s the &lt;a href="https://github.com/hpatel292-seneca/StyleMate" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>My Journey Contributing to Node.js Core: The Final Chapter (3/3)</title>
      <dc:creator>Harshil Patel</dc:creator>
      <pubDate>Fri, 29 Nov 2024 04:06:49 +0000</pubDate>
      <link>https://dev.to/harshil_patel/my-journey-contributing-to-nodejs-core-the-final-chapter-33-14</link>
      <guid>https://dev.to/harshil_patel/my-journey-contributing-to-nodejs-core-the-final-chapter-33-14</guid>
      <description>&lt;p&gt;This is the final blog in my three-part series where I share my experience contributing to the Node.js core repository. In my previous blogs, I detailed how I identified an issue, tested it locally to confirm its existence, and opened a pull request (PR) with the necessary changes. You can read the first two blogs here:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://dev.to/harshil_patel/contributing-to-nodejs-improving-test-runner-coverage-reporting-kmg"&gt;Contributing to Node.js: Improving Test Runner Coverage Reporting (1/3)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/harshil_patel/contributing-to-nodejs-implementing-dynamic-colors-for-test-runner-diagnostics-23-4len6"&gt;Contributing to Node.js: Implementing Dynamic Colors for Test Runner Diagnostics (2/3)&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;


&lt;div class="ltag_github-liquid-tag"&gt;
  &lt;h1&gt;
    &lt;a href="https://github.com/nodejs/node/pull/55964" rel="noopener noreferrer"&gt;
      &lt;img class="github-logo" alt="GitHub logo" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg"&gt;
      &lt;span class="issue-title"&gt;
        test_runner: add level-based diagnostic handling for reporter
      &lt;/span&gt;
      &lt;span class="issue-number"&gt;#55964&lt;/span&gt;
    &lt;/a&gt;
  &lt;/h1&gt;
  &lt;div class="github-thread"&gt;
    &lt;div class="timeline-comment-header"&gt;
      &lt;a href="https://github.com/hpatel292-seneca" rel="noopener noreferrer"&gt;
        &lt;img class="github-liquid-tag-img" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Favatars.githubusercontent.com%2Fu%2F100322816%3Fv%3D4" alt="hpatel292-seneca avatar"&gt;
      &lt;/a&gt;
      &lt;div class="timeline-comment-header-text"&gt;
        &lt;strong&gt;
          &lt;a href="https://github.com/hpatel292-seneca" rel="noopener noreferrer"&gt;hpatel292-seneca&lt;/a&gt;
        &lt;/strong&gt; posted on &lt;a href="https://github.com/nodejs/node/pull/55964" rel="noopener noreferrer"&gt;&lt;time&gt;Nov 23, 2024&lt;/time&gt;&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag-github-body"&gt;
      &lt;p&gt;This fixes #55922&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Change summary&lt;/h2&gt;
&lt;span class="octicon octicon-link"&gt;&lt;/span&gt;
&lt;/div&gt;
&lt;p&gt;Updated the reporter.diagnostic to accept level parameter like this&lt;/p&gt;
&lt;div class="highlight highlight-source-js js-code-highlight"&gt;
&lt;pre&gt;  &lt;span class="pl-en"&gt;diagnostic&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s1"&gt;nesting&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-s1"&gt;loc&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-s1"&gt;message&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-s1"&gt;level&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-s"&gt;'info'&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;&lt;/span&gt; &lt;span class="pl-kos"&gt;{&lt;/span&gt;
    &lt;span class="pl-smi"&gt;this&lt;/span&gt;&lt;span class="pl-kos"&gt;[&lt;/span&gt;&lt;span class="pl-s1"&gt;kEmitMessage&lt;/span&gt;&lt;span class="pl-kos"&gt;]&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s"&gt;'test:diagnostic'&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-kos"&gt;{&lt;/span&gt;
      &lt;span class="pl-c1"&gt;__proto__&lt;/span&gt;: &lt;span class="pl-c1"&gt;null&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt;
      nesting&lt;span class="pl-kos"&gt;,&lt;/span&gt;
      message&lt;span class="pl-kos"&gt;,&lt;/span&gt;
      level&lt;span class="pl-kos"&gt;,&lt;/span&gt;
      ...&lt;span class="pl-s1"&gt;loc&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt;
    &lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
  &lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Then I updated &lt;code&gt;#handleEvent&lt;/code&gt; like this&lt;/p&gt;
&lt;div class="highlight highlight-source-js js-code-highlight"&gt;
&lt;pre&gt; #&lt;span class="pl-en"&gt;handleEvent&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-kos"&gt;{&lt;/span&gt; type&lt;span class="pl-kos"&gt;,&lt;/span&gt; data &lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt; &lt;span class="pl-kos"&gt;{&lt;/span&gt;
    &lt;span class="pl-k"&gt;switch&lt;/span&gt; &lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s1"&gt;type&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt; &lt;span class="pl-kos"&gt;{&lt;/span&gt;
      &lt;span class="pl-k"&gt;case&lt;/span&gt; &lt;span class="pl-s"&gt;'test:fail'&lt;/span&gt;:
        &lt;span class="pl-k"&gt;if&lt;/span&gt; &lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s1"&gt;data&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;details&lt;/span&gt;&lt;span class="pl-kos"&gt;?.&lt;/span&gt;&lt;span class="pl-c1"&gt;error&lt;/span&gt;&lt;span class="pl-kos"&gt;?.&lt;/span&gt;&lt;span class="pl-c1"&gt;failureType&lt;/span&gt; &lt;span class="pl-c1"&gt;!==&lt;/span&gt; &lt;span class="pl-s1"&gt;kSubtestsFailed&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt; &lt;span class="pl-kos"&gt;{&lt;/span&gt;
          &lt;span class="pl-v"&gt;ArrayPrototypePush&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-smi"&gt;this&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;#&lt;span class="pl-c1"&gt;failedTests&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-s1"&gt;data&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
        &lt;span class="pl-kos"&gt;}&lt;/span&gt;
        &lt;span class="pl-k"&gt;return&lt;/span&gt; &lt;span class="pl-smi"&gt;this&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;#&lt;span class="pl-en"&gt;handleTestReportEvent&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s1"&gt;type&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-s1"&gt;data&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
      &lt;span class="pl-k"&gt;case&lt;/span&gt; &lt;span class="pl-s"&gt;'test:pass'&lt;/span&gt;:
        &lt;span class="pl-k"&gt;return&lt;/span&gt; &lt;span class="pl-smi"&gt;this&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;#&lt;span class="pl-en"&gt;handleTestReportEvent&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s1"&gt;type&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-s1"&gt;data&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
      &lt;span class="pl-k"&gt;case&lt;/span&gt; &lt;span class="pl-s"&gt;'test:start'&lt;/span&gt;:
        &lt;span class="pl-v"&gt;ArrayPrototypeUnshift&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-smi"&gt;this&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;#&lt;span class="pl-c1"&gt;stack&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-kos"&gt;{&lt;/span&gt; &lt;span class="pl-c1"&gt;__proto__&lt;/span&gt;: &lt;span class="pl-c1"&gt;null&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; data&lt;span class="pl-kos"&gt;,&lt;/span&gt; type &lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
        &lt;span class="pl-k"&gt;break&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
      &lt;span class="pl-k"&gt;case&lt;/span&gt; &lt;span class="pl-s"&gt;'test:stderr'&lt;/span&gt;:
      &lt;span class="pl-k"&gt;case&lt;/span&gt; &lt;span class="pl-s"&gt;'test:stdout'&lt;/span&gt;:
        &lt;span class="pl-k"&gt;return&lt;/span&gt; &lt;span class="pl-s1"&gt;data&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;message&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
      &lt;span class="pl-k"&gt;case&lt;/span&gt; &lt;span class="pl-s"&gt;'test:diagnostic'&lt;/span&gt;:  &lt;span class="pl-c"&gt;// Here I added logic&lt;/span&gt;
        &lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-s1"&gt;diagnosticColor&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt;
          &lt;span class="pl-s1"&gt;reporterColorMap&lt;/span&gt;&lt;span class="pl-kos"&gt;[&lt;/span&gt;&lt;span class="pl-s1"&gt;data&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;level&lt;/span&gt;&lt;span class="pl-kos"&gt;]&lt;/span&gt; &lt;span class="pl-c1"&gt;||&lt;/span&gt; &lt;span class="pl-s1"&gt;reporterColorMap&lt;/span&gt;&lt;span class="pl-kos"&gt;[&lt;/span&gt;&lt;span class="pl-s"&gt;'test:diagnostic'&lt;/span&gt;&lt;span class="pl-kos"&gt;]&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
        &lt;span class="pl-k"&gt;return&lt;/span&gt; &lt;span class="pl-s"&gt;`&lt;span class="pl-s1"&gt;&lt;span class="pl-kos"&gt;${&lt;/span&gt;&lt;span class="pl-s1"&gt;diagnosticColor&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="pl-s1"&gt;&lt;span class="pl-kos"&gt;${&lt;/span&gt;&lt;span class="pl-en"&gt;indent&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s1"&gt;data&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;nesting&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="pl-s1"&gt;&lt;span class="pl-kos"&gt;${&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="pl-s"&gt;&lt;span class="pl-s1"&gt;          &lt;span class="pl-s1"&gt;reporterUnicodeSymbolMap&lt;/span&gt;&lt;span class="pl-kos"&gt;[&lt;/span&gt;&lt;span class="pl-s1"&gt;type&lt;/span&gt;&lt;span class="pl-kos"&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="pl-s"&gt;&lt;span class="pl-s1"&gt;        &lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="pl-s1"&gt;&lt;span class="pl-kos"&gt;${&lt;/span&gt;&lt;span class="pl-s1"&gt;data&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;message&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="pl-s1"&gt;&lt;span class="pl-kos"&gt;${&lt;/span&gt;&lt;span class="pl-s1"&gt;colors&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;white&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;/span&gt;\n`&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
      &lt;span class="pl-k"&gt;case&lt;/span&gt; &lt;span class="pl-s"&gt;'test:coverage'&lt;/span&gt;:
        &lt;span class="pl-k"&gt;return&lt;/span&gt; &lt;span class="pl-en"&gt;getCoverageReport&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;
          &lt;span class="pl-en"&gt;indent&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s1"&gt;data&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;nesting&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt;
          &lt;span class="pl-s1"&gt;data&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;summary&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt;
          &lt;span class="pl-s1"&gt;reporterUnicodeSymbolMap&lt;/span&gt;&lt;span class="pl-kos"&gt;[&lt;/span&gt;&lt;span class="pl-s"&gt;'test:coverage'&lt;/span&gt;&lt;span class="pl-kos"&gt;]&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt;
          &lt;span class="pl-s1"&gt;colors&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;blue&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt;
          &lt;span class="pl-c1"&gt;true&lt;/span&gt;
        &lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
    &lt;span class="pl-kos"&gt;}&lt;/span&gt;
  &lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;And I am Updated &lt;code&gt;reporterColorMap&lt;/code&gt; like this&lt;/p&gt;
&lt;div class="highlight highlight-source-js js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-s1"&gt;reporterColorMap&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-kos"&gt;{&lt;/span&gt;
  &lt;span class="pl-c1"&gt;__proto__&lt;/span&gt;: &lt;span class="pl-c1"&gt;null&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt;
  &lt;span class="pl-k"&gt;get&lt;/span&gt; &lt;span class="pl-s"&gt;'test:fail'&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt; &lt;span class="pl-kos"&gt;{&lt;/span&gt;
    &lt;span class="pl-k"&gt;return&lt;/span&gt; &lt;span class="pl-s1"&gt;colors&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;red&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
  &lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt;
  &lt;span class="pl-k"&gt;get&lt;/span&gt; &lt;span class="pl-s"&gt;'test:pass'&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt; &lt;span class="pl-kos"&gt;{&lt;/span&gt;
    &lt;span class="pl-k"&gt;return&lt;/span&gt; &lt;span class="pl-s1"&gt;colors&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;green&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
  &lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt;
  &lt;span class="pl-k"&gt;get&lt;/span&gt; &lt;span class="pl-s"&gt;'test:diagnostic'&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt; &lt;span class="pl-kos"&gt;{&lt;/span&gt;
    &lt;span class="pl-k"&gt;return&lt;/span&gt; &lt;span class="pl-s1"&gt;colors&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;blue&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
  &lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt;
  &lt;span class="pl-k"&gt;get&lt;/span&gt; &lt;span class="pl-en"&gt;info&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt; &lt;span class="pl-kos"&gt;{&lt;/span&gt; &lt;span class="pl-c"&gt;// Here I added logic&lt;/span&gt;
    &lt;span class="pl-k"&gt;return&lt;/span&gt; &lt;span class="pl-s1"&gt;colors&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;blue&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
  &lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt;
  &lt;span class="pl-k"&gt;get&lt;/span&gt; &lt;span class="pl-en"&gt;debug&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt; &lt;span class="pl-kos"&gt;{&lt;/span&gt; 
    &lt;span class="pl-k"&gt;return&lt;/span&gt; &lt;span class="pl-s1"&gt;colors&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;gray&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
  &lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt;
  &lt;span class="pl-k"&gt;get&lt;/span&gt; &lt;span class="pl-en"&gt;warn&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt; &lt;span class="pl-kos"&gt;{&lt;/span&gt; 
    &lt;span class="pl-k"&gt;return&lt;/span&gt; &lt;span class="pl-s1"&gt;colors&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;yellow&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
  &lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt;
  &lt;span class="pl-k"&gt;get&lt;/span&gt; &lt;span class="pl-en"&gt;error&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt; &lt;span class="pl-kos"&gt;{&lt;/span&gt; 
    &lt;span class="pl-k"&gt;return&lt;/span&gt; &lt;span class="pl-s1"&gt;colors&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;red&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
  &lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt;
&lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;and color already contain logic for this colors&lt;/p&gt;
&lt;p&gt;I also set the reporter.diagnostic call from test.js like this (level="Error")&lt;/p&gt;
&lt;div class="highlight highlight-source-js js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-k"&gt;if&lt;/span&gt; &lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s1"&gt;actual&lt;/span&gt; &lt;span class="pl-c1"&gt;&amp;lt;&lt;/span&gt; &lt;span class="pl-s1"&gt;threshold&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt; &lt;span class="pl-kos"&gt;{&lt;/span&gt;
            &lt;span class="pl-s1"&gt;harness&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;success&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-c1"&gt;false&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
            &lt;span class="pl-s1"&gt;process&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;exitCode&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-s1"&gt;kGenericUserError&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
            &lt;span class="pl-s1"&gt;reporter&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;diagnostic&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;
              &lt;span class="pl-s1"&gt;nesting&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt;
              &lt;span class="pl-s1"&gt;loc&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt;
              &lt;span class="pl-s"&gt;`Error: &lt;span class="pl-s1"&gt;&lt;span class="pl-kos"&gt;${&lt;/span&gt;&lt;span class="pl-v"&gt;NumberPrototypeToFixed&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="pl-s"&gt;&lt;span class="pl-s1"&gt;                &lt;span class="pl-s1"&gt;actual&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="pl-s"&gt;&lt;span class="pl-s1"&gt;                &lt;span class="pl-c1"&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="pl-s"&gt;&lt;span class="pl-s1"&gt;              &lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;/span&gt;% &lt;span class="pl-s1"&gt;&lt;span class="pl-kos"&gt;${&lt;/span&gt;&lt;span class="pl-s1"&gt;name&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;/span&gt; coverage does not meet threshold of &lt;span class="pl-s1"&gt;&lt;span class="pl-kos"&gt;${&lt;/span&gt;&lt;span class="pl-s1"&gt;threshold&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;/span&gt;%.`&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt;
              &lt;span class="pl-s"&gt;'error'&lt;/span&gt;  &lt;span class="pl-c"&gt;// Level is set to error for red color&lt;/span&gt;
            &lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
          &lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Here is Demo output:
&lt;a rel="noopener noreferrer" href="https://github.com/user-attachments/assets/6e5ed480-90a9-46b8-82f0-cfbe1025bab9"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fuser-attachments%2Fassets%2F6e5ed480-90a9-46b8-82f0-cfbe1025bab9" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

    &lt;/div&gt;
    &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/nodejs/node/pull/55964" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;After submitting the PR, I was excited to see it get approved almost immediately. However, it hasn't been merged yet, as Node.js requires two approvals for a PR to land.&lt;/p&gt;

&lt;p&gt;One interesting aspect of contributing to Node.js is the continuous integration (CI) process. The CI for Node.js takes around four hours to complete its run. Unfortunately, one of the CI checks for my PR failed. The failing check was &lt;a href="https://github.com/nodejs/node/actions/runs/11983534043/job/33427085217?pr=55964" rel="noopener noreferrer"&gt;&lt;code&gt;First commit message adheres to guidelines / lint-commit-message (pull_request)&lt;/code&gt;&lt;/a&gt;. As the name suggests, this check validates whether the commit message adheres to Node.js's specific guidelines.&lt;/p&gt;

&lt;p&gt;Upon reviewing the CI output, I discovered that Node.js requires commit messages to follow a specific format outlined in the &lt;a href="https://github.com/nodejs/node/blob/main/doc/contributing/pull-requests.md#commit-message-guidelines" rel="noopener noreferrer"&gt;&lt;code&gt;Contributing.md&lt;/code&gt;&lt;/a&gt; document. Here’s a snippet of the commit message requirements:&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%2Fulfufcm4svrznoz8k0n3.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%2Fulfufcm4svrznoz8k0n3.png" alt="Image description" width="800" height="197"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Although I had seen this requirement earlier and formatted my commit message accordingly, I made a small mistake—I forgot to include a blank line after the first line. I reached out to a maintainer to ask if I could rewrite the commit message and force push the change. After receiving approval, I pushed the corrected commit message.&lt;/p&gt;

&lt;p&gt;However, after the new commit, the CI ran again, and this time, the Lint CI failed. To debug further, I decided to run the linting process locally, where I encountered the following errors:&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%2Fwzhutv3xa3fk67bntqyt.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%2Fwzhutv3xa3fk67bntqyt.png" alt="Image description" width="800" height="443"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The lint errors I encountered were minor, and I was able to fix them quickly. After addressing these issues, I pushed the changes again, and this time all CI checks passed successfully for my PR.&lt;/p&gt;

&lt;p&gt;Subsequently, a maintainer requested a change to remove the &lt;code&gt;debug()&lt;/code&gt; from the log level. They mentioned that there are alternative ways to include debug output and provided guidance on how to proceed:&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%2F8umzlzagylvo3rqd1dfy.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%2F8umzlzagylvo3rqd1dfy.png" alt="Image description" width="800" height="394"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I made the requested changes and pushed the updated code.&lt;/p&gt;

&lt;p&gt;Later, I received another request from the maintainer to update the documentation and write a test case for the changes to ensure the PR could land. Fortunately, they provided clear directions on where to add the documentation and tests:&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%2F18m18jpbrpnnnn21i552.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%2F18m18jpbrpnnnn21i552.png" alt="Image description" width="800" height="101"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Updating the Documentation
&lt;/h3&gt;

&lt;p&gt;I started with the documentation updates and navigated to &lt;code&gt;doc/api/test.md&lt;/code&gt;, as suggested by the maintainer. The document was extensive, so I searched for keywords like "diagnostic" and found the relevant section for &lt;a href="https://github.com/nodejs/node/blob/main/doc/api/test.md#event-testdiagnostic" rel="noopener noreferrer"&gt;&lt;code&gt;Event: test:diagnostic&lt;/code&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%2Fqarh1pb8xoaioluew28l.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%2Fqarh1pb8xoaioluew28l.png" alt="Image description" width="800" height="210"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I wasn't sure if this was the correct place to update the documentation, so I messaged the maintainer in the PR thread to confirm my proposed changes. Here’s the exact message I sent:&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%2Ff5nn4tqpbpo75inc3jhd.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%2Ff5nn4tqpbpo75inc3jhd.png" alt="Image description" width="800" height="469"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The maintainer responded, confirming that this was the right place for the updates but requested that I remove mentions of color and the default values. After updating the documentation accordingly and getting approval, I pushed the changes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding Test Cases
&lt;/h3&gt;

&lt;p&gt;Next, I moved on to adding test cases, which turned out to be the most challenging part of this PR. The maintainer directed me to the &lt;code&gt;/test/parallel&lt;/code&gt; directory to locate the appropriate file for adding tests. However, I was surprised to find &lt;code&gt;3567&lt;/code&gt; test files in this directory:&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%2F8ls8l0nply14f5ftp5b5.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%2F8ls8l0nply14f5ftp5b5.png" alt="Image description" width="800" height="64"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The contributing guide mentioned that if you're unsure where to add a test, it can temporarily go in &lt;code&gt;/test/parallel&lt;/code&gt;. However, I noticed that many tests had not been moved from this directory, making it difficult to determine their purpose or scope:&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%2Fhkxro80hg8x1h886fzvz.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%2Fhkxro80hg8x1h886fzvz.png" alt="Image description" width="800" height="139"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I spent considerable time exploring various files and eventually found two tests related to the error threshold. While these tests verified that output was being printed to the stream, they did not cover colored output. I messaged the maintainer to ask for guidance on where to add tests for colored output and referenced these files:&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%2Fyw122mkxpdcrm6knoag2.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%2Fyw122mkxpdcrm6knoag2.png" alt="Image description" width="800" height="370"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The maintainer suggested adding the test in &lt;code&gt;test-runner-output.mjs&lt;/code&gt;, noting that it contained snapshot tests, some of which tested colored output:&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%2F30o1eo4epu3rvoah7r7w.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%2F30o1eo4epu3rvoah7r7w.png" alt="Image description" width="800" height="319"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I reviewed the &lt;code&gt;test-runner-output.mjs&lt;/code&gt; file but struggled to understand it. To clarify, I researched snapshot testing and found this explanation:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Snapshot tests&lt;/strong&gt; capture the rendered output of a component, function, or application at a specific point in time and store it in a "snapshot" file. Future test runs compare the output against the saved snapshot to ensure that it has not unexpectedly changed.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Using this understanding, I identified a test checking colors in the snapshot. After analyzing the snapshot file, I confirmed it included color information. I wrote a new test to verify that the ANSI escape sequence for red (&lt;code&gt;\u001b[31m&lt;/code&gt;) was present in the snapshot. While the test passed locally, it did not create a snapshot. &lt;/p&gt;

&lt;p&gt;I suspected I needed to pass a flag to enable snapshot creation. Using &lt;code&gt;tools/test.py --help&lt;/code&gt;, I discovered the &lt;code&gt;--snapshot&lt;/code&gt; flag and ran the test again. Despite the flag, no snapshot was created, nor did I receive any errors. Further investigation revealed that I could use a locally built Node.js binary to run the tests:&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%2Fhbncy0j1yxyzxv0arwsh.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%2Fhbncy0j1yxyzxv0arwsh.png" alt="Image description" width="800" height="123"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Using the locally built Node.js binary, I discovered the test was being skipped because Windows lacks pseudo-terminal support:&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%2Fbafvff2echj70y8j5d53.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%2Fbafvff2echj70y8j5d53.png" alt="Image description" width="800" height="100"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I messaged the maintainer, explaining my test implementation, references, and the issue I encountered:&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%2Fdg3fxfa7pldynn5nzs16.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%2Fdg3fxfa7pldynn5nzs16.png" alt="Image description" width="800" height="456"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The maintainer advised modifying the test to explicitly require pseudo-terminals, ensuring it wouldn't run on unsupported systems:&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%2Ft8pnn9zavbznpxo48kbq.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%2Ft8pnn9zavbznpxo48kbq.png" alt="Image description" width="800" height="225"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I implemented the test accordingly. Initially, I printed the output for debugging purposes, verifying that the error message appeared in red:&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%2F6ycjqk31bttgwg6j70ji.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%2F6ycjqk31bttgwg6j70ji.png" alt="Image description" width="800" height="516"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Afterward, I committed the final test and pushed the changes. The CI ran again, and all checks passed successfully. However, the maintainer requested another update to the documentation, asking me to remove a few additional details. I followed their instructions, made the changes, and committed them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Final Thoughts
&lt;/h3&gt;

&lt;p&gt;Contributing to Node.js has been a transformative experience. From navigating CI workflows to writing snapshot tests, every challenge taught me something new. The opportunity to collaborate with experienced developers and interact with maintainers was invaluable. This journey reinforced my willingness to learn and adapt, and I look forward to contributing more to open-source projects in the future.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Contributing to Node.js: Implementing Dynamic Colors for Test Runner Diagnostics (2/3)</title>
      <dc:creator>Harshil Patel</dc:creator>
      <pubDate>Wed, 27 Nov 2024 21:39:01 +0000</pubDate>
      <link>https://dev.to/harshil_patel/contributing-to-nodejs-implementing-dynamic-colors-for-test-runner-diagnostics-23-4len</link>
      <guid>https://dev.to/harshil_patel/contributing-to-nodejs-implementing-dynamic-colors-for-test-runner-diagnostics-23-4len</guid>
      <description>&lt;h3&gt;
  
  
  &lt;strong&gt;Introduction&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;This is the second blog in a series of three, where I am sharing my experience contributing to the Node.js core repository. You can read the previous blog here: &lt;a href="https://dev.to/harshil_patel/contributing-to-nodejs-improving-test-runner-coverage-reporting-kmg"&gt;Contributing to Node.js: Improving Test Runner Coverage Reporting&lt;/a&gt;. In the previous blog, I discussed how I discovered the issue and confirmed that it exists in Node.js.&lt;/p&gt;

&lt;p&gt;After that, I forked and cloned the Node.js repository and opened it in VS Code. One thing I’ve learned from working on many open-source repositories is that finding &lt;em&gt;where&lt;/em&gt; to add or modify code is just as important as knowing &lt;em&gt;what&lt;/em&gt; code to add. Fortunately, there was another pull request linked to the issue I was working on, which gave me some useful context.&lt;/p&gt;

&lt;p&gt;That pull request also addressed the issue I was working on, but it hardcoded the value for the color. My task, on the other hand, was to implement a &lt;code&gt;level&lt;/code&gt; parameter and update the event reader to handle the logic for adding colors dynamically. For example, my issue was to ensure that error messages for coverage would appear in red, but instead of hardcoding the color, the &lt;code&gt;level&lt;/code&gt; parameter would allow developers to set it appropriately based on the severity.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Tracing the Code&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;From the linked pull request, I knew where I could start. The first file to investigate was &lt;a href="https://github.com/nodejs/node/blob/24a8662359afb5ec90a0be95529f088f8e69ebc4/lib/internal/test_runner/test.js#L1089" rel="noopener noreferrer"&gt;&lt;code&gt;test.js&lt;/code&gt;&lt;/a&gt;, which prints the error message when the test coverage threshold isn't met.&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%2Fydz5yjku21qehgfmz0hs.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%2Fydz5yjku21qehgfmz0hs.png" alt="Code Snippet" width="800" height="134"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here, I noticed a call to &lt;code&gt;reporter.diagnostic()&lt;/code&gt;. To better understand what was happening, I backtracked to locate the definition of this function. After spending a good amount of time, I found the definition of &lt;a href="https://github.com/nodejs/node/blob/853b304db89410b3809049352f8211f6113fea6e/lib/internal/test_runner/tests_stream.js#L117" rel="noopener noreferrer"&gt;&lt;code&gt;diagnostic()&lt;/code&gt;&lt;/a&gt; in &lt;code&gt;tests_stream.js&lt;/code&gt;.&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="nf"&gt;diagnostic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;nesting&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;loc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;level&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;info&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;kEmitMessage&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;test:diagnostic&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="na"&gt;__proto__&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;nesting&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;level&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;loc&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;I added a &lt;code&gt;level&lt;/code&gt; parameter here, defaulting it to &lt;code&gt;'info'&lt;/code&gt;. This was the first step to make the error color configurable.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Adding Logic for Severity-Based Colors&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Next, I located the handler for &lt;code&gt;test:diagnostic&lt;/code&gt; in &lt;a href="https://github.com/nodejs/node/blob/853b304db89410b3809049352f8211f6113fea6e/lib/internal/test_runner/reporter/spec.js#L57" rel="noopener noreferrer"&gt;&lt;code&gt;spec.js&lt;/code&gt;&lt;/a&gt;. Here, I added logic to dynamically retrieve the color using &lt;code&gt;reporterColorMap&lt;/code&gt;, which maps severity levels (e.g., &lt;code&gt;info&lt;/code&gt;, &lt;code&gt;warn&lt;/code&gt;, &lt;code&gt;error&lt;/code&gt;) to specific colors.&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="err"&gt;#&lt;/span&gt;&lt;span class="nf"&gt;handleEvent&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;switch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;test:diagnostic&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;const&lt;/span&gt; &lt;span class="nx"&gt;diagnosticColor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;reporterColorMap&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;level&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;reporterColorMap&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;test:diagnostic&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;diagnosticColor&lt;/span&gt;&lt;span class="p"&gt;}${&lt;/span&gt;&lt;span class="nf"&gt;indent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nesting&lt;/span&gt;&lt;span class="p"&gt;)}${&lt;/span&gt;&lt;span class="nx"&gt;reporterUnicodeSymbolMap&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;]}${&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;}${&lt;/span&gt;&lt;span class="nx"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;white&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;\n`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&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;I then traced the &lt;code&gt;reporterColorMap&lt;/code&gt; definition in &lt;a href="https://github.com/nodejs/node/blob/853b304db89410b3809049352f8211f6113fea6e/lib/internal/test_runner/reporter/utils.js#L29" rel="noopener noreferrer"&gt;&lt;code&gt;utils.js&lt;/code&gt;&lt;/a&gt; and updated it to map severity levels to appropriate colors. Here’s the updated code:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;reporterColorMap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;__proto__&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;test:fail&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;red&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;test:pass&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;green&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;test:diagnostic&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;info&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;warn&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;yellow&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;error&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;red&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;With these changes, the &lt;code&gt;level&lt;/code&gt; parameter now dynamically controls the color of the diagnostic message, making it easier for developers to identify severity levels at a glance.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Building Node.js Locally&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Now that the changes were made, it was time to test my work. To do this, I needed to build Node.js locally. I referred to the &lt;a href="https://github.com/nodejs/node/blob/main/BUILDING.md" rel="noopener noreferrer"&gt;Building.md&lt;/a&gt; file for instructions. Before building, I had to install the prerequisites for my environment. The document mentioned three options for installing these prerequisites.&lt;/p&gt;

&lt;p&gt;Initially, I tried the second option, which involved using &lt;code&gt;WinGet&lt;/code&gt;. However, during installation, it prompted me to grant several permissions, one of which required uninstalling Visual Studio Code. Since I didn’t want to reinstall Visual Studio Code, I canceled the installation. I then reached out to the maintainer for advice, and they recommended the third option: building with Boxstarter.&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%2Fggbqbnvst15msqm3h7nh.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%2Fggbqbnvst15msqm3h7nh.png" alt="Maintainer's Recommendation" width="800" height="398"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Testing the Build&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;After preparing the prerequisites using Boxstarter, I started building Node.js locally. However, the build process ran for over 1 hour and 30 minutes without completing. This seemed unusual, so I asked the maintainer about it in the issue thread. They mentioned that the build process typically takes about 25 minutes.&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%2F392tyu4pk97btisvulur.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%2F392tyu4pk97btisvulur.png" alt="Maintainer's Response" width="800" height="303"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After reading that, I canceled the build and decided to try again the next day. On the next attempt, the build completed in just 10 minutes. With the locally built version of Node.js ready, I went back to the sample repository I had created earlier to test the issue of error printing in blue (as described in my first blog). This time, I ran the same test cases but used the locally built Node.js. And guess what? It worked! The error was successfully printed in red.&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%2Fuigpyisamx0wccjqlz95.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%2Fuigpyisamx0wccjqlz95.png" alt="Test Result" width="800" height="443"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Opening a Pull Request&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;After confirming that my changes worked, I committed them and opened a &lt;a href="https://github.com/nodejs/node/pull/55964" rel="noopener noreferrer"&gt;pull request&lt;/a&gt;. In the pull request, I described the changes I made and asked the maintainer for a review. Shortly after, I received a response from the maintainer, who mentioned that I needed to update the documentation and write test cases for the changes I had made.&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%2Fwx7kskfsed70e0ncrswm.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%2Fwx7kskfsed70e0ncrswm.png" alt="Maintainer's Feedback" width="800" height="205"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Conclusion&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;That’s it for this blog. In the next blog, I will share how I updated the documentation and added test cases to complete the pull request.&lt;br&gt;
Next Blog: &lt;a href="https://dev.to/harshil_patel/my-journey-contributing-to-nodejs-core-the-final-chapter-33-14"&gt;My Journey Contributing to Node.js Core: The Final Chapter (3/3)&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Contributing to Node.js: Improving Test Runner Coverage Reporting (1/3)</title>
      <dc:creator>Harshil Patel</dc:creator>
      <pubDate>Sun, 24 Nov 2024 05:03:26 +0000</pubDate>
      <link>https://dev.to/harshil_patel/contributing-to-nodejs-improving-test-runner-coverage-reporting-kmg</link>
      <guid>https://dev.to/harshil_patel/contributing-to-nodejs-improving-test-runner-coverage-reporting-kmg</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Hi! My name is Harshil. I am currently enrolled in the Open Source Development course at Seneca College. In this course, we are required to contribute to different open-source repositories every week. So far, I have contributed to more than 10 issues across 6+ open-source repositories. You can find the full list of my contributions here: &lt;a href="https://github.com/hpatel292-seneca/hpatel292-seneca/blob/main/README.md#-open-source-contributions" rel="noopener noreferrer"&gt;List&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;During my most recent contribution to &lt;a href="https://github.com/tarasglek/chatcraft.org/pull/730" rel="noopener noreferrer"&gt;Chatcraft&lt;/a&gt;, I realized that contributing to significant issues can be challenging, but true learning happens when you push yourself outside your comfort zone. For that pull request, I spent over 35 hours working on it, as I received numerous change requests from the maintainer. The most important lesson I learned is that big things take time and persistence. &lt;/p&gt;

&lt;p&gt;With this insight and my growing experience in open-source contributions, I started searching for a big issue to tackle next. My goal is to contribute to something that I will genuinely be proud of. &lt;/p&gt;

&lt;h2&gt;
  
  
  Finding the Right Issue
&lt;/h2&gt;

&lt;p&gt;I started exploring different repositories and their issues. Initially, I looked for issues in the repositories I had already contributed to, but I couldn’t find any suitable ones. Trust me, finding a good issue to work on is a challenging task. I also tried checking out various large repositories, such as React, VS Code, and .NET, but I still couldn’t find anything that matched my skills and interests.&lt;/p&gt;

&lt;p&gt;Finally, I discovered a good issue in the &lt;a href="https://github.com/nodejs/node/issues/55922" rel="noopener noreferrer"&gt;Node.js&lt;/a&gt; repository. Node.js is a cross-platform JavaScript runtime environment that executes code outside of a browser, built on Chrome's V8 engine. I have worked extensively with Node.js, having built many projects using it, but I never imagined that one day I would get the opportunity to contribute to its core development. I reached out to the maintainer to ask if I could work on the issue.&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%2Fnyckqxxauz9jynbtba4g.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%2Fnyckqxxauz9jynbtba4g.png" alt="Image description" width="800" height="283"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding the Issue
&lt;/h2&gt;

&lt;p&gt;After receiving the maintainer's response, I started looking into the &lt;a href="https://github.com/nodejs/node/issues/55922" rel="noopener noreferrer"&gt;issue&lt;/a&gt;. The title of the issue is &lt;em&gt;"Add a level parameter to test runner diagnostics."&lt;/em&gt; Initially, it was difficult to understand the issue based on the title and description, but there was a reference to a related pull request that provided more context. Upon reviewing the pull request, I discovered that the issue was related to the &lt;code&gt;test_runner&lt;/code&gt; module in Node.js.&lt;/p&gt;

&lt;p&gt;The Node.js &lt;code&gt;test_runner&lt;/code&gt; is a built-in module that enables developers to create, manage, and execute JavaScript tests. It supports various testing workflows with features like TAP output and subtests. You can learn more about the &lt;code&gt;test_runner&lt;/code&gt; &lt;a href="https://nodejs.org/api/test.html#test-runner" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The issue suggests adding a &lt;code&gt;level&lt;/code&gt; parameter (e.g., &lt;code&gt;debug&lt;/code&gt;, &lt;code&gt;info&lt;/code&gt;, &lt;code&gt;warn&lt;/code&gt;, &lt;code&gt;error&lt;/code&gt;) to the diagnostics functionality of the &lt;code&gt;test_runner&lt;/code&gt;. This enhancement would allow developers to apply color coding to the output based on the severity of messages, making it easier to differentiate between message types. The idea originates from a related issue where errors in test coverage thresholds were displayed in blue instead of red, leading to confusion during code reviews and debugging in CI pipelines. Here is the original &lt;a href="https://github.com/nodejs/help/issues/4504" rel="noopener noreferrer"&gt;issue&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For example, if I am developing a project and using Node.js for testing, I might write some test cases and require all my files to have 100% test coverage. Using the &lt;code&gt;test_runner&lt;/code&gt;, I could pass the flag &lt;code&gt;--test-coverage-lines=100&lt;/code&gt;, which compares the actual coverage to the expected value. If the actual coverage is less than expected, the test coverage report currently prints an error message in blue. This color is not noticeable, causing developers to mistakenly believe their code meets the coverage requirement. Moreover, if this flag is used in a CI pipeline, the pipeline could fail unexpectedly, creating further confusion. &lt;/p&gt;

&lt;h2&gt;
  
  
  Planning the Fix
&lt;/h2&gt;

&lt;p&gt;After that, I reviewed the files involved in this issue and posted a message in the issue thread outlining the changes I was planning to make.&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%2F2qfzouh9niuovy72jciv.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%2F2qfzouh9niuovy72jciv.png" alt="Proposed Changes" width="800" height="506"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The maintainer responded promptly, suggesting some modifications and encouraging me to start working on the issue and submit a pull request.&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%2Fdb6v819dz2iybq0nhj3j.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%2Fdb6v819dz2iybq0nhj3j.png" alt="Maintainer's Response" width="800" height="253"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;First, I wanted to try the &lt;code&gt;--test-coverage-lines=100&lt;/code&gt; flag locally. To do this, I created a sample JavaScript repository, added a sample file, and wrote a test case file to test that code. However, when I tried using the flag, I encountered 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.amazonaws.com%2Fuploads%2Farticles%2Fmyfnnchvx6ou9f0w4wav.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%2Fmyfnnchvx6ou9f0w4wav.png" alt="Error Message" width="797" height="77"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After reviewing the original issue, I noticed that they were using Node.js version 22.&lt;em&gt;. To match their setup, I installed Node.js v22.&lt;/em&gt; using Node Version Manager (NVM) and switched to that version.&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%2Fv4g6mv4nbrt80gxvhuaz.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%2Fv4g6mv4nbrt80gxvhuaz.png" alt="Switching Node Version" width="697" height="83"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Despite this, the flag still didn’t work. I tried various troubleshooting steps, but none of them resolved the issue. Eventually, I found a &lt;a href="https://betterstack.com/community/guides/testing/nodejs-test-runner/" rel="noopener noreferrer"&gt;blog&lt;/a&gt; that explained how to set up and run the Node.js test runner properly. By following the steps outlined in the blog, I successfully set up the test runner. &lt;/p&gt;

&lt;p&gt;Here is the result:&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%2Ftv30pgadjug2zgzuwzye.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%2Ftv30pgadjug2zgzuwzye.png" alt="Test Runner Result" width="777" height="577"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The error message was printed, but it appeared in blue, which confirmed the issue I needed to address: changing the color of the error message to red by adding level in diagnostics.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This contribution was a perfect example of how open-source development combines problem-solving, collaboration, and persistence. While I faced challenges setting up the environment and understanding the issue, the process deepened my understanding of Node.js internals and taught me how to navigate complex problems effectively.&lt;/p&gt;

&lt;p&gt;In my next blog, I will discuss the steps I took to address and fix this issue. Next blog can be found here: &lt;a href="https://dev.to/harshil_patel/contributing-to-nodejs-implementing-dynamic-colors-for-test-runner-diagnostics-23-4len"&gt;Contributing to Node.js: Implementing Dynamic Colors for Test Runner Diagnostics (2/3)&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Publishing resume-enhancer to PyPI</title>
      <dc:creator>Harshil Patel</dc:creator>
      <pubDate>Sun, 24 Nov 2024 02:05:29 +0000</pubDate>
      <link>https://dev.to/harshil_patel/publishing-resume-enhancer-to-pypi-372l</link>
      <guid>https://dev.to/harshil_patel/publishing-resume-enhancer-to-pypi-372l</guid>
      <description>&lt;p&gt;Hi my name is Harshil and if you have read my previous blogs then you know that I am working on a Command line tool called &lt;a href="https://github.com/hpatel292-seneca/ResumeEnhancer" rel="noopener noreferrer"&gt;resume-enhancer&lt;/a&gt;. ResumeEnhancer is a command-line tool that uses AI to optimize your resume for specific job descriptions. Input your current resume and job description, and it will suggest changes to highlight relevant skills, experience, and certifications without adding false information. It helps tailor your resume to increase your chances of getting hired. I was working on this tool for quite some time and &lt;a href="https://dev.to/harshil_patel/announcing-resume-enhancer-your-resume-perfectly-tailored-for-every-job-4e39"&gt;here&lt;/a&gt; is my first blog I wrote when I started working on this. &lt;/p&gt;

&lt;p&gt;I publish &lt;a href="https://pypi.org/project/resume-enhancer" rel="noopener noreferrer"&gt;resume-enhancer&lt;/a&gt; to PyPI. This blog is about my experience of publishing my CLI tool to PyPI.&lt;/p&gt;

&lt;p&gt;To begin with, I created a file called &lt;code&gt;pyproject.toml&lt;/code&gt; and it contain logic for building the resume-enhancer. Below is the example of my &lt;code&gt;pyproject.toml&lt;/code&gt; file for resume-enhancer and I will share some details about it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[build-system]&lt;/span&gt;
&lt;span class="py"&gt;requires&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"hatchling"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="py"&gt;build-backend&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"hatchling.build"&lt;/span&gt;

&lt;span class="nn"&gt;[project]&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"resume-enhancer"&lt;/span&gt;
&lt;span class="py"&gt;dynamic&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="py"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"A CLI tool for optimizing and tailoring resumes based on job descriptions using AI."&lt;/span&gt;
&lt;span class="py"&gt;readme&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"README.md"&lt;/span&gt;
&lt;span class="py"&gt;readme-content-type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"text/markdown"&lt;/span&gt;
&lt;span class="py"&gt;license&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="py"&gt;text&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"MIT"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="py"&gt;authors&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="err"&gt;{&lt;/span&gt; &lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Harshil Patel"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;email&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"26harshilpatel11@gmail.com"&lt;/span&gt; &lt;span class="err"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="py"&gt;homepage&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"https://github.com/hpatel292-seneca/ResumeEnhancer"&lt;/span&gt;
&lt;span class="py"&gt;requires-python&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="py"&gt;"&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;3.11&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="py"&gt;dependencies&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="py"&gt;"annotated-types=&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.7&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="s"&gt;",&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;    &lt;span class="py"&gt;"anyio=&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;4.5&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="s"&gt;",&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;    &lt;span class="py"&gt;"certifi=&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;2024.8&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="s"&gt;",&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;    &lt;span class="py"&gt;"charset-normalizer=&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;3.3&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="s"&gt;",&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;    &lt;span class="py"&gt;"colorama=&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.4&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="s"&gt;",&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;    &lt;span class="py"&gt;"distro=&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;1.9&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="s"&gt;",&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;    &lt;span class="py"&gt;"groq=&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.11&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="s"&gt;",&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;    &lt;span class="py"&gt;"h11=&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.14&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="s"&gt;",&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;    &lt;span class="py"&gt;"halo=&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;31&lt;/span&gt;&lt;span class="s"&gt;",&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;    &lt;span class="py"&gt;"httpcore=&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="s"&gt;",&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;    &lt;span class="py"&gt;"httpx=&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.27&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="s"&gt;",&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;    &lt;span class="py"&gt;"idna=&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;3.10&lt;/span&gt;&lt;span class="s"&gt;",&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;    &lt;span class="py"&gt;"jiter=&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="s"&gt;",&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;    &lt;span class="py"&gt;"log-symbols=&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="s"&gt;",&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;    &lt;span class="py"&gt;"lxml=&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;5.3&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="s"&gt;",&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;    &lt;span class="py"&gt;"openai=&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;1.46&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="s"&gt;",&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;    &lt;span class="py"&gt;"pydantic=&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;2.9&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="s"&gt;",&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;    &lt;span class="py"&gt;"pydantic_core=&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;2.23&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="s"&gt;",&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;    &lt;span class="s"&gt;"pypdf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="py"&gt;"python-docx=&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;1.1&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="s"&gt;",&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;    &lt;span class="py"&gt;"requests=&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;2.32&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="s"&gt;",&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;    &lt;span class="py"&gt;"six=&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;1.16&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="s"&gt;",&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;    &lt;span class="py"&gt;"sniffio=&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;1.3&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="s"&gt;",&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;    &lt;span class="py"&gt;"spinners=&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="s"&gt;",&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;    &lt;span class="py"&gt;"termcolor=&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;2.4&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="s"&gt;",&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;    &lt;span class="py"&gt;"tomli=&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;2.0&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="s"&gt;",&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;    &lt;span class="py"&gt;"tqdm=&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;4.66&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="s"&gt;",&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;    &lt;span class="py"&gt;"typing_extensions=&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;4.12&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="s"&gt;",&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;    &lt;span class="py"&gt;"urllib3=&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;2.2&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="nn"&gt;[project.optional-dependencies]&lt;/span&gt;
&lt;span class="py"&gt;dev&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="py"&gt;"black=&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;24.10&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="s"&gt;",&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;    &lt;span class="s"&gt;"flake8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="py"&gt;"pre-commit=&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;4.0&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="s"&gt;",&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;    &lt;span class="py"&gt;"pytest=&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;8.3&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="s"&gt;",&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;    &lt;span class="py"&gt;"pytest-xdist=&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;3.6&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="nn"&gt;[project.scripts]&lt;/span&gt;
&lt;span class="py"&gt;resume-enhancer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"app.resume_enhancer:main"&lt;/span&gt;

&lt;span class="nn"&gt;[tool.hatch.build.targets.wheel]&lt;/span&gt;
&lt;span class="py"&gt;packages&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"app"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c"&gt;# Dynamic versioning using Hatchling&lt;/span&gt;
&lt;span class="nn"&gt;[tool.hatch.version]&lt;/span&gt;
&lt;span class="py"&gt;path&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"app/_version.py"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  &lt;strong&gt;1. &lt;code&gt;[build-system]&lt;/code&gt;&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;This is where you tell Python what tools to use to build your project. I chose &lt;strong&gt;Hatchling&lt;/strong&gt; because it’s lightweight and gets the job done without unnecessary complexity. The &lt;code&gt;requires&lt;/code&gt; line lists Hatchling as a dependency, and &lt;code&gt;build-backend&lt;/code&gt; points to the module that actually handles the building process.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;2. &lt;code&gt;[project]&lt;/code&gt;&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;This is all about the “what” and “who” of your project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;name&lt;/code&gt;&lt;/strong&gt; is the name of the tool people will search for on PyPI. In my case, it’s &lt;code&gt;resume-enhancer&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;dynamic&lt;/code&gt;&lt;/strong&gt;: Instead of hardcoding the version number, I’m letting Hatchling handle it dynamically (more on that below).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;description&lt;/code&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;code&gt;readme&lt;/code&gt;&lt;/strong&gt;: These give users an overview of the tool and display nicely on the PyPI project page. The description is short and sweet, and the &lt;code&gt;readme&lt;/code&gt; points to my &lt;code&gt;README.md&lt;/code&gt; file, which explains everything in detail.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;license&lt;/code&gt;&lt;/strong&gt;: I went with MIT because it’s simple and permissive. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;authors&lt;/code&gt;&lt;/strong&gt;: This is where I get to take some credit—my name and email go here.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;homepage&lt;/code&gt;&lt;/strong&gt;: A link to the GitHub repo so users can check out the source code, file issues, or contribute.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;requires-python&lt;/code&gt;&lt;/strong&gt;: I set a minimum Python version of 3.11 because I wanted to use its newer features.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;dependencies&lt;/code&gt;&lt;/strong&gt;: This is the shopping list of all the packages my tool needs to work. It includes things like &lt;code&gt;openai&lt;/code&gt; (for the AI magic), &lt;code&gt;pydantic&lt;/code&gt; (for validating data), and &lt;code&gt;tqdm&lt;/code&gt; (for those cool progress bars).&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;3. &lt;code&gt;[project.optional-dependencies]&lt;/code&gt;&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;This is the “extras” section for developers. I added a &lt;code&gt;dev&lt;/code&gt; group here with tools like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;black&lt;/code&gt;&lt;/strong&gt;: Makes sure the code is formatted nicely.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;flake8&lt;/code&gt;&lt;/strong&gt;: Keeps the code lint-free.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;pytest&lt;/code&gt;&lt;/strong&gt;: Makes testing easier.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;pre-commit&lt;/code&gt;&lt;/strong&gt;: Runs checks automatically before you commit code to avoid silly mistakes.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;4. &lt;code&gt;[project.scripts]&lt;/code&gt;&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Here’s the cool part—this section makes the tool runnable from the command line. When someone installs &lt;code&gt;resume-enhancer&lt;/code&gt;, they can just type &lt;code&gt;resume-enhancer&lt;/code&gt; in the terminal to launch it. This works by pointing to the &lt;code&gt;main&lt;/code&gt; function inside my &lt;code&gt;app.resume_enhancer&lt;/code&gt; module.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;5. &lt;code&gt;[tool.hatch.build.targets.wheel]&lt;/code&gt;&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;This section tells Hatchling to include the &lt;code&gt;app&lt;/code&gt; directory when building the package. It’s a simple way to make sure only the important files are packaged when the tool is uploaded to PyPI.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;6. &lt;code&gt;[tool.hatch.version]&lt;/code&gt;&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;This part makes versioning easy. Instead of manually updating the version every time I make changes, Hatchling reads it directly from a file (&lt;code&gt;app/_version.py&lt;/code&gt;). It’s super handy, especially if you’re using automation tools for publishing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Publishing Resume Enhancer to PyPI
&lt;/h3&gt;

&lt;p&gt;Once my &lt;code&gt;pyproject.toml&lt;/code&gt; file was ready, the next step was to package and publish &lt;strong&gt;Resume Enhancer&lt;/strong&gt;. Since I’m working on Windows, I used Python commands to handle the build and upload process. Here’s how it works:&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 1: Install Required Tools&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Before you can publish, you need to make sure the necessary tools are installed. Run these commands to set everything up:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;py &lt;span class="nt"&gt;-m&lt;/span&gt; pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--upgrade&lt;/span&gt; build twine
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;build&lt;/code&gt;&lt;/strong&gt; is used to create the distributable package.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;twine&lt;/code&gt;&lt;/strong&gt; handles uploading the package to PyPI.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 2: Build the Package&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;To package the tool, I ran the following command in the project directory (the one containing &lt;code&gt;pyproject.toml&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;py &lt;span class="nt"&gt;-m&lt;/span&gt; build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates a &lt;code&gt;dist/&lt;/code&gt; directory containing two files:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A source distribution (&lt;code&gt;.tar.gz&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;A built distribution (&lt;code&gt;.whl&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These files are what you’ll upload to PyPI.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 3: Publish to PyPI&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Once the package was built, I published it to PyPI with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;py &lt;span class="nt"&gt;-m&lt;/span&gt; twine upload dist/&lt;span class="k"&gt;*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Twine will prompt you to enter your PyPI credentials. If you don’t have an account, you can &lt;a href="https://pypi.org/account/register/" rel="noopener noreferrer"&gt;create one here&lt;/a&gt;. Once you enter the credentials, Twine takes care of the rest, uploading your package to PyPI.&lt;/p&gt;

&lt;h3&gt;
  
  
  Changes Made for the Chosen Package Format and Registry
&lt;/h3&gt;

&lt;p&gt;To package &lt;strong&gt;Resume Enhancer&lt;/strong&gt; for PyPI, I didn’t need to make drastic changes to my codebase, but I did have to tweak a few things to meet the requirements of the chosen package format and registry. Here’s what I changed:&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;1. Added &lt;code&gt;app/_version.py&lt;/code&gt;&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;To dynamically manage the version of the tool, I created a file called &lt;code&gt;app/_version.py&lt;/code&gt; with the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Version file for Resume Enhancer Tool
&lt;/span&gt;&lt;span class="n"&gt;__version__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1.0.2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This file acts as the single source of truth for the version number, making it easier to update in one place whenever I release a new version.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;2. Configured the Version in the Code&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;In my config file, I imported the version from &lt;code&gt;app/_version.py&lt;/code&gt; to ensure consistency throughout the tool. Here’s an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;app._version&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;__version__&lt;/span&gt;

&lt;span class="c1"&gt;# App Config
&lt;/span&gt;&lt;span class="n"&gt;TOOL_NAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Resume Enhancer Tool&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;VERSION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;__version__&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By doing this, I can display the version wherever needed (e.g., in the CLI output) and ensure that it matches the version uploaded to PyPI.&lt;/p&gt;

&lt;h3&gt;
  
  
  User Testing Session: Insights and Improvements
&lt;/h3&gt;

&lt;p&gt;I had one of my friends who test the &lt;strong&gt;Resume Enhancer&lt;/strong&gt; tool. It was a great experience, as he easily navigated through the tool using the README as his guide and successfully ran it without encountering any issues. His feedback was incredibly valuable in ensuring that the tool and documentation were beginner-friendly and easy to use.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;What Worked Well&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Clear Documentation&lt;/strong&gt;: My friend found the README straightforward and was able to install the tool, understand the commands, and use it effectively.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Smooth Functionality&lt;/strong&gt;: The tool worked as expected, and he especially appreciated the examples in the &lt;code&gt;Usage&lt;/code&gt; section, which helped him quickly test the core functionality.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Suggestions for Improvement&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Although he had no trouble running the tool, he suggested adding:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;A Note on Installing Dependencies&lt;/strong&gt;: He pointed out that while &lt;code&gt;pip install resume-enhancer&lt;/code&gt; works, users might not realize they need to have &lt;code&gt;pip&lt;/code&gt; set up correctly on their system. A small section explaining how to install &lt;code&gt;pip&lt;/code&gt; and ensure Python is in the system PATH would help complete beginners.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  How to Install and Use Resume Enhancer
&lt;/h3&gt;

&lt;p&gt;Now that &lt;strong&gt;Resume Enhancer&lt;/strong&gt; is live on PyPI, installing and using the tool is quick and easy! Here's how users can get started:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Install the Tool&lt;/strong&gt;
Open your terminal or command prompt and run:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   pip &lt;span class="nb"&gt;install &lt;/span&gt;resume-enhancer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Verify Installation&lt;/strong&gt;
Check if the installation was successful by running:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   resume-enhancer &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Run the Tool&lt;/strong&gt;
To use the tool, provide the path to your resume and job description along with your API key:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   resume-enhancer &lt;span class="nt"&gt;--resume&lt;/span&gt; path_to_resume &lt;span class="nt"&gt;--description&lt;/span&gt; path_to_description &lt;span class="nt"&gt;--api_key&lt;/span&gt; groq_api_key
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Configuration File&lt;/strong&gt;
You can simplify command usage by creating a configuration file. Learn more about it &lt;a href="https://github.com/hpatel292-seneca/ResumeEnhancer?tab=readme-ov-file#configuration-file-usage" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For detailed documentation and examples, check out the &lt;a href="https://github.com/hpatel292-seneca/ResumeEnhancer" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt; and the &lt;a href="https://pypi.org/project/resume-enhancer/" rel="noopener noreferrer"&gt;PyPI project page&lt;/a&gt;.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>From Bug Fixes to Best Practices: My Open Source Contributions to ChatCraft and xUnit</title>
      <dc:creator>Harshil Patel</dc:creator>
      <pubDate>Wed, 20 Nov 2024 22:35:23 +0000</pubDate>
      <link>https://dev.to/harshil_patel/from-bug-fixes-to-best-practices-my-open-source-contributions-to-chatcraft-and-xunit-3kdc</link>
      <guid>https://dev.to/harshil_patel/from-bug-fixes-to-best-practices-my-open-source-contributions-to-chatcraft-and-xunit-3kdc</guid>
      <description>&lt;h2&gt;
  
  
  First PR
&lt;/h2&gt;

&lt;p&gt;This blog is about the two PRs I made this week. If you’ve read my last blog about my contribution to ChatCraft, where I worked on the Ask and Retry menus, you’ll know that I contributed a reusable component for those menus. While working on that issue, I discovered a bug.&lt;/p&gt;

&lt;p&gt;The bug occurred when you retried a response—sometimes the request failed on the LLM side, but no error message was displayed on ChatCraft when it happened. To address this, I opened an issue, included a video demonstrating the bug, and asked the maintainer if I could work on it.&lt;/p&gt;


&lt;div class="ltag_github-liquid-tag"&gt;
  &lt;h1&gt;
    &lt;a href="https://github.com/tarasglek/chatcraft.org/issues/716" rel="noopener noreferrer"&gt;
      &lt;img class="github-logo" alt="GitHub logo" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg"&gt;
      &lt;span class="issue-title"&gt;
        Sometime AI message retry is failing.
      &lt;/span&gt;
      &lt;span class="issue-number"&gt;#716&lt;/span&gt;
    &lt;/a&gt;
  &lt;/h1&gt;
  &lt;div class="github-thread"&gt;
    &lt;div class="timeline-comment-header"&gt;
      &lt;a href="https://github.com/hpatel292-seneca" rel="noopener noreferrer"&gt;
        &lt;img class="github-liquid-tag-img" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Favatars.githubusercontent.com%2Fu%2F100322816%3Fv%3D4" alt="hpatel292-seneca avatar"&gt;
      &lt;/a&gt;
      &lt;div class="timeline-comment-header-text"&gt;
        &lt;strong&gt;
          &lt;a href="https://github.com/hpatel292-seneca" rel="noopener noreferrer"&gt;hpatel292-seneca&lt;/a&gt;
        &lt;/strong&gt; posted on &lt;a href="https://github.com/tarasglek/chatcraft.org/issues/716" rel="noopener noreferrer"&gt;&lt;time&gt;Nov 09, 2024&lt;/time&gt;&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag-github-body"&gt;
      &lt;p&gt;&lt;a href="https://github.com/user-attachments/assets/769179fd-2428-4725-8ec9-3fe759ea887e" rel="noopener noreferrer"&gt;https://github.com/user-attachments/assets/769179fd-2428-4725-8ec9-3fe759ea887e&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Hi @humphd @tarasglek , I notice that some AI message retry is failing API call.&lt;/p&gt;

    &lt;/div&gt;
    &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/tarasglek/chatcraft.org/issues/716" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;After receiving the maintainer's approval to work on this, I started investigating the issue. My first task was to locate the function responsible for making the API call for the retry action. I began by examining the component that renders the retry button. Then, I followed the chain of parent components that passed the &lt;code&gt;RetryFunction&lt;/code&gt; to this component. Eventually, I found the function &lt;a href="https://github.com/tarasglek/chatcraft.org/blob/09dd743cfcb81126c6370041577be77164508539/src/components/Message/AiMessage.tsx#L98" rel="noopener noreferrer"&gt;&lt;code&gt;handleRetryClick&lt;/code&gt;&lt;/a&gt;, which contained the logic for handling retries. Interestingly, there was a &lt;code&gt;//TODO&lt;/code&gt; comment left by the original developer to add UI error handling in the catch block.&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%2Fcge63lcwcfz62ksq8q8r.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%2Fcge63lcwcfz62ksq8q8r.png" alt="Image description" width="533" height="145"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After identifying the location, my next task was to display the error message using the &lt;a href="https://github.com/tarasglek/chatcraft.org/blob/main/src/hooks/use-alert.tsx" rel="noopener noreferrer"&gt;&lt;code&gt;useAlert()&lt;/code&gt;&lt;/a&gt; hook, as requested by the maintainer. I implemented the change by adding the &lt;a href="https://github.com/tarasglek/chatcraft.org/blob/09dd743cfcb81126c6370041577be77164508539/src/hooks/use-alert.tsx#L47" rel="noopener noreferrer"&gt;&lt;code&gt;error()&lt;/code&gt;&lt;/a&gt; method like this:&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%2Fqw7qovs0947th3pdp5nv.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%2Fqw7qovs0947th3pdp5nv.png" alt="Image description" width="407" height="127"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I then created a PR for this change and requested the maintainer's review.&lt;/p&gt;


&lt;div class="ltag_github-liquid-tag"&gt;
  &lt;h1&gt;
    &lt;a href="https://github.com/tarasglek/chatcraft.org/pull/733" rel="noopener noreferrer"&gt;
      &lt;img class="github-logo" alt="GitHub logo" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg"&gt;
      &lt;span class="issue-title"&gt;
        added UI Error display for failed retry on message
      &lt;/span&gt;
      &lt;span class="issue-number"&gt;#733&lt;/span&gt;
    &lt;/a&gt;
  &lt;/h1&gt;
  &lt;div class="github-thread"&gt;
    &lt;div class="timeline-comment-header"&gt;
      &lt;a href="https://github.com/hpatel292-seneca" rel="noopener noreferrer"&gt;
        &lt;img class="github-liquid-tag-img" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Favatars.githubusercontent.com%2Fu%2F100322816%3Fv%3D4" alt="hpatel292-seneca avatar"&gt;
      &lt;/a&gt;
      &lt;div class="timeline-comment-header-text"&gt;
        &lt;strong&gt;
          &lt;a href="https://github.com/hpatel292-seneca" rel="noopener noreferrer"&gt;hpatel292-seneca&lt;/a&gt;
        &lt;/strong&gt; posted on &lt;a href="https://github.com/tarasglek/chatcraft.org/pull/733" rel="noopener noreferrer"&gt;&lt;time&gt;Nov 16, 2024&lt;/time&gt;&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag-github-body"&gt;
      &lt;p&gt;This Fixes #716&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Changes&lt;/h2&gt;
&lt;span class="octicon octicon-link"&gt;&lt;/span&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;chatcraft.org\src\components\Message\AiMessage.tsx  (&lt;a href="https://github.com/tarasglek/chatcraft.org/blob/83d180244708becc62ac61e4452b923a2412a33b/src/components/Message/AiMessage.tsx#L130" rel="noopener noreferrer"&gt;https://github.com/tarasglek/chatcraft.org/blob/83d180244708becc62ac61e4452b923a2412a33b/src/components/Message/AiMessage.tsx#L130&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Update the catch block for retry from this&lt;/p&gt;
&lt;div class="highlight highlight-source-python js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-en"&gt;catch&lt;/span&gt; (&lt;span class="pl-s1"&gt;err&lt;/span&gt;) {
        &lt;span class="pl-c1"&gt;//&lt;/span&gt; &lt;span class="pl-v"&gt;TODO&lt;/span&gt;: &lt;span class="pl-v"&gt;UI&lt;/span&gt; &lt;span class="pl-s1"&gt;error&lt;/span&gt; &lt;span class="pl-s1"&gt;handling&lt;/span&gt;
        &lt;span class="pl-s1"&gt;console&lt;/span&gt;.&lt;span class="pl-en"&gt;warn&lt;/span&gt;(&lt;span class="pl-s"&gt;"Unable to retry message"&lt;/span&gt;, { &lt;span class="pl-s1"&gt;model&lt;/span&gt;, &lt;span class="pl-s1"&gt;err&lt;/span&gt; });
      }&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;to this&lt;/p&gt;
&lt;div class="highlight highlight-source-python js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-en"&gt;catch&lt;/span&gt; (&lt;span class="pl-s1"&gt;err&lt;/span&gt;: &lt;span class="pl-s1"&gt;any&lt;/span&gt;) {
        &lt;span class="pl-en"&gt;error&lt;/span&gt;({
          &lt;span class="pl-s1"&gt;title&lt;/span&gt;: &lt;span class="pl-s"&gt;`Response Error`&lt;/span&gt;,
          &lt;span class="pl-s1"&gt;message&lt;/span&gt;: &lt;span class="pl-s1"&gt;err&lt;/span&gt;.&lt;span class="pl-s1"&gt;message&lt;/span&gt;,
        });
        &lt;span class="pl-s1"&gt;console&lt;/span&gt;.&lt;span class="pl-en"&gt;warn&lt;/span&gt;(&lt;span class="pl-s"&gt;"Unable to retry message"&lt;/span&gt;, { &lt;span class="pl-s1"&gt;model&lt;/span&gt;, &lt;span class="pl-s1"&gt;err&lt;/span&gt; });
      }&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Here is the demo:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/user-attachments/assets/371ebd0d-dbb1-4236-a38f-110eedee883e" rel="noopener noreferrer"&gt;https://github.com/user-attachments/assets/371ebd0d-dbb1-4236-a38f-110eedee883e&lt;/a&gt;&lt;/p&gt;

    &lt;/div&gt;
    &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/tarasglek/chatcraft.org/pull/733" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;The maintainer requested a small update to change the error message from &lt;code&gt;Response Error&lt;/code&gt; to &lt;code&gt;Retry Error&lt;/code&gt;. After making this change, my PR was merged.&lt;/p&gt;

&lt;h3&gt;
  
  
  Second PR: Improving Documentation for Member References in xUnit Attributes
&lt;/h3&gt;

&lt;p&gt;Recently, I have been learning xUnit for writing unit test cases for one of my projects. While exploring the xUnit GitHub repository, I noticed it is an open-source project. This sparked my interest, and I started looking for issues to contribute to. I found a relatively simple issue:&lt;br&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_github-liquid-tag"&gt;
  &lt;h1&gt;
    &lt;a href="https://github.com/xunit/xunit/issues/2991" rel="noopener noreferrer"&gt;
      &lt;img class="github-logo" alt="GitHub logo" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg"&gt;
      &lt;span class="issue-title"&gt;
        Add an analyzer which recommends nameof usage
      &lt;/span&gt;
      &lt;span class="issue-number"&gt;#2991&lt;/span&gt;
    &lt;/a&gt;
  &lt;/h1&gt;
  &lt;div class="github-thread"&gt;
    &lt;div class="timeline-comment-header"&gt;
      &lt;a href="https://github.com/bradwilson" rel="noopener noreferrer"&gt;
        &lt;img class="github-liquid-tag-img" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Favatars.githubusercontent.com%2Fu%2F16944%3Fv%3D4" alt="bradwilson avatar"&gt;
      &lt;/a&gt;
      &lt;div class="timeline-comment-header-text"&gt;
        &lt;strong&gt;
          &lt;a href="https://github.com/bradwilson" rel="noopener noreferrer"&gt;bradwilson&lt;/a&gt;
        &lt;/strong&gt; posted on &lt;a href="https://github.com/xunit/xunit/issues/2991" rel="noopener noreferrer"&gt;&lt;time&gt;Aug 01, 2024&lt;/time&gt;&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag-github-body"&gt;
      &lt;p&gt;Whenever we use &lt;code&gt;string&lt;/code&gt; properties on attributes intended to point at a member, we should encourage the use of &lt;code&gt;nameof&lt;/code&gt; rather than hard-coded strings.&lt;/p&gt;
&lt;p&gt;This includes, at least (please look and see if there are others):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;IFactAttribute.SkipUnless&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;IFactAttribute.SkipWhen&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;MemberDataAttribute&lt;/code&gt; constructor&lt;/li&gt;
&lt;/ul&gt;

    &lt;/div&gt;
    &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/xunit/xunit/issues/2991" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;This issue aimed to improve the clarity and reliability of attributes that reference class members. The goal was to encourage developers to use safer practices, such as the &lt;code&gt;nameof&lt;/code&gt; operator, when referencing members in attributes like &lt;code&gt;MemberData&lt;/code&gt;, &lt;code&gt;SkipWhen&lt;/code&gt;, and &lt;code&gt;SkipUnless&lt;/code&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  The Problem
&lt;/h3&gt;

&lt;p&gt;When working with attributes like &lt;code&gt;MemberDataAttribute&lt;/code&gt;, developers often rely on hard-coded strings to reference class members. For example:&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="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;MemberData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"SomeMemberName"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This approach has several drawbacks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Refactoring Risks&lt;/strong&gt;: If the member name changes, the string reference doesn’t update, leading to runtime errors.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Readability&lt;/strong&gt;: Hard-coded strings are less clear than using &lt;code&gt;nameof&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compile-Time Safety&lt;/strong&gt;: Strings aren’t validated at compile time, making it easier to introduce typos or errors.&lt;/li&gt;
&lt;/ul&gt;


&lt;h3&gt;
  
  
  The Solution
&lt;/h3&gt;

&lt;p&gt;To address this, I proposed an improvement to the XML documentation for these attributes. The updated documentation explicitly recommends using the &lt;code&gt;nameof&lt;/code&gt; operator to reference members, ensuring compile-time safety and better readability.&lt;/p&gt;
&lt;h4&gt;
  
  
  Example Documentation Updates
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;For &lt;code&gt;MemberDataAttribute&lt;/code&gt;:&lt;/strong&gt;&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="c1"&gt;/// &amp;lt;param name="memberName"&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;/// The name of the public static member on the test class that will provide the test data. &lt;/span&gt;
&lt;span class="c1"&gt;/// It is recommended to use the &amp;lt;c&amp;gt;nameof&amp;lt;/c&amp;gt; operator to ensure compile-time safety, &lt;/span&gt;
&lt;span class="c1"&gt;/// e.g., &amp;lt;c&amp;gt;nameof(SomeMemberName)&amp;lt;/c&amp;gt;.&lt;/span&gt;
&lt;span class="c1"&gt;/// &amp;lt;/param&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;For &lt;code&gt;SkipWhen&lt;/code&gt;:&lt;/strong&gt;&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="c1"&gt;/// &amp;lt;remarks&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;/// To avoid issues during refactoring, it is recommended to use the &amp;lt;c&amp;gt;nameof&amp;lt;/c&amp;gt; operator&lt;/span&gt;
&lt;span class="c1"&gt;/// to reference the property, e.g., &amp;lt;c&amp;gt;SkipWhen = nameof(IsTestSkipped)&amp;lt;/c&amp;gt;.&lt;/span&gt;
&lt;span class="c1"&gt;/// &amp;lt;/remarks&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;For &lt;code&gt;SkipUnless&lt;/code&gt;:&lt;/strong&gt;&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="c1"&gt;/// &amp;lt;remarks&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;/// The name of the public static member on the test class that will provide the test data.&lt;/span&gt;
&lt;span class="c1"&gt;/// It is recommended to use the &amp;lt;c&amp;gt;nameof&amp;lt;/c&amp;gt; operator to ensure compile-time safety, &lt;/span&gt;
&lt;span class="c1"&gt;/// e.g., &amp;lt;c&amp;gt;nameof(SomeMemberName)&amp;lt;/c&amp;gt;.&lt;/span&gt;
&lt;span class="c1"&gt;/// &amp;lt;/remarks&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Impact
&lt;/h3&gt;

&lt;p&gt;These changes improve the developer experience by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Encouraging &lt;strong&gt;compile-time safety&lt;/strong&gt;, reducing runtime errors.&lt;/li&gt;
&lt;li&gt;Enhancing &lt;strong&gt;readability&lt;/strong&gt; and &lt;strong&gt;maintainability&lt;/strong&gt; of code.&lt;/li&gt;
&lt;li&gt;Supporting better practices for &lt;strong&gt;refactoring&lt;/strong&gt;, especially in large projects.&lt;/li&gt;
&lt;/ul&gt;


&lt;h3&gt;
  
  
  My PR
&lt;/h3&gt;

&lt;p&gt;After implementing the changes, I submitted a PR and requested the maintainer’s review. You can find the PR here:&lt;br&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_github-liquid-tag"&gt;
  &lt;h1&gt;
    &lt;a href="https://github.com/xunit/xunit/pull/3062" rel="noopener noreferrer"&gt;
      &lt;img class="github-logo" alt="GitHub logo" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg"&gt;
      &lt;span class="issue-title"&gt;
        added recommendation to use nameof
      &lt;/span&gt;
      &lt;span class="issue-number"&gt;#3062&lt;/span&gt;
    &lt;/a&gt;
  &lt;/h1&gt;
  &lt;div class="github-thread"&gt;
    &lt;div class="timeline-comment-header"&gt;
      &lt;a href="https://github.com/hpatel292-seneca" rel="noopener noreferrer"&gt;
        &lt;img class="github-liquid-tag-img" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Favatars.githubusercontent.com%2Fu%2F100322816%3Fv%3D4" alt="hpatel292-seneca avatar"&gt;
      &lt;/a&gt;
      &lt;div class="timeline-comment-header-text"&gt;
        &lt;strong&gt;
          &lt;a href="https://github.com/hpatel292-seneca" rel="noopener noreferrer"&gt;hpatel292-seneca&lt;/a&gt;
        &lt;/strong&gt; posted on &lt;a href="https://github.com/xunit/xunit/pull/3062" rel="noopener noreferrer"&gt;&lt;time&gt;Nov 18, 2024&lt;/time&gt;&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag-github-body"&gt;
      &lt;p&gt;This fixes #2991&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Change History&lt;/h3&gt;
&lt;span class="octicon octicon-link"&gt;&lt;/span&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;MemberDataAttribute&lt;/strong&gt;: Updated XML documentation for the &lt;code&gt;memberName&lt;/code&gt; parameter to recommend using the &lt;code&gt;nameof&lt;/code&gt; operator for compile-time safety.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SkipWhen&lt;/strong&gt;: Updated XML documentation to suggest the use of &lt;code&gt;nameof&lt;/code&gt; for referencing public static properties to ensure safer and more maintainable code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SkipUnless&lt;/strong&gt;: Added a recommendation in the XML documentation to use the &lt;code&gt;nameof&lt;/code&gt; operator when referencing public static properties, promoting best practices.&lt;/li&gt;
&lt;/ul&gt;

    &lt;/div&gt;
    &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/xunit/xunit/pull/3062" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;






&lt;h3&gt;
  
  
  Reflections
&lt;/h3&gt;

&lt;p&gt;This PR reinforced the importance of clear and detailed documentation in guiding developers toward best practices. While the change may seem small, it can significantly impact code quality and developer productivity.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Stepping Out of My Comfort Zone: Refactoring Menus in ChatCraft</title>
      <dc:creator>Harshil Patel</dc:creator>
      <pubDate>Wed, 20 Nov 2024 17:10:37 +0000</pubDate>
      <link>https://dev.to/harshil_patel/stepping-out-of-my-comfort-zone-refactoring-menus-in-chatcraft-4ea4</link>
      <guid>https://dev.to/harshil_patel/stepping-out-of-my-comfort-zone-refactoring-menus-in-chatcraft-4ea4</guid>
      <description>&lt;p&gt;Hi! My name is &lt;strong&gt;Harshil&lt;/strong&gt;, and I am currently enrolled in an &lt;strong&gt;Open Source Development course&lt;/strong&gt; at Seneca College. I had heard &lt;strong&gt;great reviews&lt;/strong&gt; about this course, which is why I decided to enroll. During the lectures, I remember my professor repeatedly saying to &lt;strong&gt;"go out of your comfort zone"&lt;/strong&gt; while contributing to open-source repositories. At the time, I didn’t give much thought to this advice.&lt;/p&gt;

&lt;p&gt;Last month, I contributed to &lt;strong&gt;four open-source repositories&lt;/strong&gt;, and I just realized that I did those contributions while staying in my &lt;strong&gt;comfort zone&lt;/strong&gt;. I was working with &lt;strong&gt;technologies I was familiar with&lt;/strong&gt;, tackling &lt;strong&gt;issues I knew how to resolve&lt;/strong&gt;, and handling tasks that weren’t particularly challenging for me. While these contributions were valuable, I now understand that I wasn’t learning much by staying in my comfort zone.&lt;/p&gt;

&lt;p&gt;This blog is about an &lt;strong&gt;issue&lt;/strong&gt; I contributed to that pushed me out of my comfort zone. It was during this experience that I realized &lt;strong&gt;learning happens when you push yourself beyond what feels comfortable&lt;/strong&gt;. Growth truly begins when you step outside your comfort zone.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Issue&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;This blog is about my contribution to &lt;strong&gt;ChatCraft&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/tarasglek" rel="noopener noreferrer"&gt;
        tarasglek
      &lt;/a&gt; / &lt;a href="https://github.com/tarasglek/chatcraft.org" rel="noopener noreferrer"&gt;
        chatcraft.org
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Developer-oriented ChatGPT clone
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;ChatCraft.org&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;Welcome to &lt;a href="https://chatcraft.org" rel="nofollow noopener noreferrer"&gt;ChatCraft.org&lt;/a&gt;, your open-source web companion for coding with Large Language Models (LLMs). Designed with developers in mind, ChatCraft transforms the way you interact with GPT models, making it effortless to read, write, debug, and enhance your code.&lt;/p&gt;
&lt;p&gt;Whether you're exploring new designs or learning about the latest technologies, ChatCraft is your go-to platform. With a user interface inspired by GitHub, and editable Markdown everywhere, you'll feel right at home from the get-go.&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/tarasglek/chatcraft.orgdocs/chatcraft-example.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Ftarasglek%2Fchatcraft.orgdocs%2Fchatcraft-example.png" alt="ChatCraft UI Example"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Features&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;We think ChatCraft is the best platform for learning, experimenting, and getting creative with code. Here's a few of the reasons why we think you'll agree:&lt;/p&gt;
&lt;p&gt;🛠️ &lt;strong&gt;You're in Control&lt;/strong&gt;: Customize all aspects of a chat. Use your own System Prompts, edit, delete, and retry AI messages with models from competing vendors in the same chat.&lt;/p&gt;
&lt;p&gt;🌍 &lt;strong&gt;Multiple AI Providers&lt;/strong&gt;: ChatCraft supports both OpenAI and OpenRouter, giving you access to a…&lt;/p&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/tarasglek/chatcraft.org" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;The task for this assignment was to tackle &lt;strong&gt;bigger issues&lt;/strong&gt; than the ones I worked on last month. While looking for issues, I found an interesting one in ChatCraft:&lt;br&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_github-liquid-tag"&gt;
  &lt;h1&gt;
    &lt;a href="https://github.com/tarasglek/chatcraft.org/issues/643" rel="noopener noreferrer"&gt;
      &lt;img class="github-logo" alt="GitHub logo" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg"&gt;
      &lt;span class="issue-title"&gt;
        Re-use features from Ask menu in Retry sub-menu
      &lt;/span&gt;
      &lt;span class="issue-number"&gt;#643&lt;/span&gt;
    &lt;/a&gt;
  &lt;/h1&gt;
  &lt;div class="github-thread"&gt;
    &lt;div class="timeline-comment-header"&gt;
      &lt;a href="https://github.com/humphd" rel="noopener noreferrer"&gt;
        &lt;img class="github-liquid-tag-img" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Favatars.githubusercontent.com%2Fu%2F427398%3Fv%3D4" alt="humphd avatar"&gt;
      &lt;/a&gt;
      &lt;div class="timeline-comment-header-text"&gt;
        &lt;strong&gt;
          &lt;a href="https://github.com/humphd" rel="noopener noreferrer"&gt;humphd&lt;/a&gt;
        &lt;/strong&gt; posted on &lt;a href="https://github.com/tarasglek/chatcraft.org/issues/643" rel="noopener noreferrer"&gt;&lt;time&gt;May 31, 2024&lt;/time&gt;&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag-github-body"&gt;
      &lt;p&gt;In #642 &lt;a class="mentioned-user" href="https://dev.to/menghif"&gt;@menghif&lt;/a&gt; did some expert work to make the Ask menu more responsive and easier to use.  We have a similar need in the Retry menu for AI Message responses.  It would be interesting to see if we can re-use the same UX there, such that you can switch providers/models when retrying.&lt;/p&gt;

    &lt;/div&gt;
    &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/tarasglek/chatcraft.org/issues/643" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;
  

&lt;p&gt;The issue, &lt;strong&gt;"Re-use features from Ask menu in Retry sub-menu,"&lt;/strong&gt; focuses on &lt;strong&gt;improving the Retry menu&lt;/strong&gt; for AI responses by reusing the better &lt;strong&gt;design and functionality from the Ask menu&lt;/strong&gt;. Essentially, the task was to &lt;strong&gt;develop a reusable component&lt;/strong&gt; for the menu that could be rendered in different places.&lt;/p&gt;

&lt;p&gt;Since I had contributed to ChatCraft before, I already had a &lt;strong&gt;local clone&lt;/strong&gt; of the repository. However, when I tried pulling the latest changes, I realized my local repository wasn’t up-to-date with the original repository. At that point, I needed to &lt;strong&gt;set up a remote upstream&lt;/strong&gt;. After setting it up, I successfully pulled the latest changes and got started.&lt;/p&gt;


&lt;h2&gt;
  
  
  &lt;strong&gt;Contribution Process&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;I started by &lt;strong&gt;identifying the files&lt;/strong&gt; I needed to change. Once I found them, I took time to &lt;strong&gt;understand the context&lt;/strong&gt; and figured out the &lt;strong&gt;logic&lt;/strong&gt; behind the menu functionality, which needed to be extracted into a &lt;strong&gt;reusable component&lt;/strong&gt;. Essentially, there were two places—the &lt;strong&gt;Ask menu&lt;/strong&gt; and the &lt;strong&gt;Retry menu&lt;/strong&gt;—that rendered menu components, and the maintainer wanted to reuse the same logic in both places.&lt;/p&gt;

&lt;p&gt;I wasn’t very familiar with the &lt;strong&gt;menu component from &lt;code&gt;@chakra-ui/react&lt;/code&gt;&lt;/strong&gt;, so I researched it and found that its structure looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;Menu&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;MenuButton&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;MenuList&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;MenuGroup&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;MenuItem&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Initially, I thought about making &lt;strong&gt;&lt;code&gt;MenuList&lt;/code&gt; reusable&lt;/strong&gt; while keeping &lt;code&gt;Menu&lt;/code&gt; and &lt;code&gt;MenuButton&lt;/code&gt; at their original locations since the &lt;strong&gt;Ask menu&lt;/strong&gt; and &lt;strong&gt;Retry menu&lt;/strong&gt; had some differences in functionality, like &lt;strong&gt;hover effects and click handling&lt;/strong&gt;. However, this approach didn’t work because the &lt;strong&gt;Ask menu&lt;/strong&gt; used components from &lt;strong&gt;&lt;code&gt;@chakra-ui/react&lt;/code&gt;&lt;/strong&gt;, while the &lt;strong&gt;Retry menu&lt;/strong&gt; used components from &lt;strong&gt;&lt;code&gt;@szhsin/react-menu&lt;/code&gt;&lt;/strong&gt;. This difference in libraries meant I had to make the &lt;strong&gt;entire menu reusable&lt;/strong&gt; instead of just parts of it. I mentioned this in the issue's comments:&lt;br&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_github-liquid-tag"&gt;
  &lt;h1&gt;
    &lt;a href="https://github.com/tarasglek/chatcraft.org/issues/643#issuecomment-2466273803" rel="noopener noreferrer"&gt;
      &lt;img class="github-logo" alt="GitHub logo" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg"&gt;
      &lt;span class="issue-title"&gt;
        Comment for
      &lt;/span&gt;
      &lt;span class="issue-number"&gt;#643&lt;/span&gt;
    &lt;/a&gt;
  &lt;/h1&gt;
  &lt;div class="github-thread"&gt;
    &lt;div class="timeline-comment-header"&gt;
      &lt;a href="https://github.com/hpatel292-seneca" rel="noopener noreferrer"&gt;
        &lt;img class="github-liquid-tag-img" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Favatars.githubusercontent.com%2Fu%2F100322816%3Fu%3Db1afadbeb52d76e9bb5e09e41516d61fccac4e17%26v%3D4" alt="hpatel292-seneca avatar"&gt;
      &lt;/a&gt;
      &lt;div class="timeline-comment-header-text"&gt;
        &lt;strong&gt;
          &lt;a href="https://github.com/hpatel292-seneca" rel="noopener noreferrer"&gt;hpatel292-seneca&lt;/a&gt;
        &lt;/strong&gt; commented on &lt;a href="https://github.com/tarasglek/chatcraft.org/issues/643#issuecomment-2466273803" rel="noopener noreferrer"&gt;&lt;time&gt;Nov 09, 2024&lt;/time&gt;&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag-github-body"&gt;
      &lt;p&gt;Here's the corrected and improved version:&lt;/p&gt;

&lt;p&gt;Hi @humphd, I tried to separate out the menu logic into a separate component, and it worked. Here is the demo:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/user-attachments/assets/985dbb7e-4f2e-4e3f-a873-7f569741f0d2" rel="noopener noreferrer"&gt;Demo Link&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I just need to confirm one thing. I used a conditional in the component to render the &lt;code&gt;MenuButton&lt;/code&gt; like this:&lt;/p&gt;
&lt;div class="highlight highlight-source-js js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-kos"&gt;{&lt;/span&gt;&lt;span class="pl-s1"&gt;menuButtonLabel&lt;/span&gt; &lt;span class="pl-c1"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
      &lt;span class="pl-v"&gt;React&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;isValidElement&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s1"&gt;menuButtonLabel&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt; &lt;span class="pl-c1"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
      &lt;span class="pl-s1"&gt;menuButtonLabel&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;type&lt;/span&gt; &lt;span class="pl-c1"&gt;===&lt;/span&gt; &lt;span class="pl-v"&gt;SubMenu&lt;/span&gt; ? &lt;span class="pl-kos"&gt;(&lt;/span&gt;
        &lt;span class="pl-c1"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-ent"&gt;MenuButton&lt;/span&gt;
          &lt;span class="pl-c1"&gt;as&lt;/span&gt;&lt;span class="pl-c1"&gt;=&lt;/span&gt;&lt;span class="pl-kos"&gt;{&lt;/span&gt;&lt;span class="pl-v"&gt;Box&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt;
          &lt;span class="pl-c1"&gt;fontSize&lt;/span&gt;&lt;span class="pl-c1"&gt;=&lt;/span&gt;&lt;span class="pl-s"&gt;"1rem"&lt;/span&gt;
          &lt;span class="pl-c1"&gt;fontWeight&lt;/span&gt;&lt;span class="pl-c1"&gt;=&lt;/span&gt;&lt;span class="pl-s"&gt;"normal"&lt;/span&gt;
          &lt;span class="pl-c1"&gt;cursor&lt;/span&gt;&lt;span class="pl-c1"&gt;=&lt;/span&gt;&lt;span class="pl-s"&gt;"pointer"&lt;/span&gt;
          &lt;span class="pl-c1"&gt;color&lt;/span&gt;&lt;span class="pl-c1"&gt;=&lt;/span&gt;&lt;span class="pl-s"&gt;"inherit"&lt;/span&gt;
          &lt;span class="pl-c1"&gt;padding&lt;/span&gt;&lt;span class="pl-c1"&gt;=&lt;/span&gt;&lt;span class="pl-s"&gt;"0"&lt;/span&gt;
          &lt;span class="pl-c1"&gt;background&lt;/span&gt;&lt;span class="pl-c1"&gt;=&lt;/span&gt;&lt;span class="pl-s"&gt;"none"&lt;/span&gt;
          &lt;span class="pl-c1"&gt;border&lt;/span&gt;&lt;span class="pl-c1"&gt;=&lt;/span&gt;&lt;span class="pl-s"&gt;"none"&lt;/span&gt;
          &lt;span class="pl-c1"&gt;aria-label&lt;/span&gt;&lt;span class="pl-c1"&gt;=&lt;/span&gt;&lt;span class="pl-kos"&gt;{&lt;/span&gt;&lt;span class="pl-s1"&gt;label&lt;/span&gt; &lt;span class="pl-c1"&gt;||&lt;/span&gt; &lt;span class="pl-s"&gt;"Choose Model"&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt;
          &lt;span class="pl-c1"&gt;title&lt;/span&gt;&lt;span class="pl-c1"&gt;=&lt;/span&gt;&lt;span class="pl-kos"&gt;{&lt;/span&gt;&lt;span class="pl-s1"&gt;label&lt;/span&gt; &lt;span class="pl-c1"&gt;||&lt;/span&gt; &lt;span class="pl-s"&gt;"Choose Model"&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt;
          &lt;span class="pl-c1"&gt;onClick&lt;/span&gt;&lt;span class="pl-c1"&gt;=&lt;/span&gt;&lt;span class="pl-kos"&gt;{&lt;/span&gt;&lt;span class="pl-s1"&gt;openOnHover&lt;/span&gt; ? &lt;span class="pl-c1"&gt;undefined&lt;/span&gt; : &lt;span class="pl-s1"&gt;onOpen&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt; &lt;span class="pl-c"&gt;// Open on click if not openOnHover&lt;/span&gt;
          &lt;span class="pl-c1"&gt;onMouseEnter&lt;/span&gt;&lt;span class="pl-c1"&gt;=&lt;/span&gt;&lt;span class="pl-kos"&gt;{&lt;/span&gt;&lt;span class="pl-s1"&gt;openOnHover&lt;/span&gt; ? &lt;span class="pl-s1"&gt;onOpen&lt;/span&gt; : &lt;span class="pl-c1"&gt;undefined&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt; &lt;span class="pl-c"&gt;// Open on hover if openOnHover&lt;/span&gt;
          &lt;span class="pl-c1"&gt;onMouseLeave&lt;/span&gt;&lt;span class="pl-c1"&gt;=&lt;/span&gt;&lt;span class="pl-kos"&gt;{&lt;/span&gt;&lt;span class="pl-s1"&gt;openOnHover&lt;/span&gt; ? &lt;span class="pl-s1"&gt;onClose&lt;/span&gt; : &lt;span class="pl-c1"&gt;undefined&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt; &lt;span class="pl-c"&gt;// Close on hover out if openOnHover&lt;/span&gt;
        &lt;span class="pl-c1"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="pl-kos"&gt;{&lt;/span&gt;&lt;span class="pl-s1"&gt;menuButtonLabel&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt;
        &lt;span class="pl-c1"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-c1"&gt;/&lt;/span&gt;&lt;span class="pl-ent"&gt;MenuButton&lt;/span&gt;&lt;span class="pl-c1"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="pl-kos"&gt;)&lt;/span&gt; : &lt;span class="pl-kos"&gt;(&lt;/span&gt;
        &lt;span class="pl-c1"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-ent"&gt;MenuButton&lt;/span&gt;
          &lt;span class="pl-c1"&gt;as&lt;/span&gt;&lt;span class="pl-c1"&gt;=&lt;/span&gt;&lt;span class="pl-kos"&gt;{&lt;/span&gt;&lt;span class="pl-v"&gt;IconButton&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt;
          &lt;span class="pl-c1"&gt;size&lt;/span&gt;&lt;span class="pl-c1"&gt;=&lt;/span&gt;&lt;span class="pl-s"&gt;"sm"&lt;/span&gt;
          &lt;span class="pl-c1"&gt;fontSize&lt;/span&gt;&lt;span class="pl-c1"&gt;=&lt;/span&gt;&lt;span class="pl-s"&gt;"1.25rem"&lt;/span&gt;
          &lt;span class="pl-c1"&gt;aria-label&lt;/span&gt;&lt;span class="pl-c1"&gt;=&lt;/span&gt;&lt;span class="pl-kos"&gt;{&lt;/span&gt;&lt;span class="pl-s1"&gt;label&lt;/span&gt; &lt;span class="pl-c1"&gt;||&lt;/span&gt; &lt;span class="pl-s"&gt;"Choose Model"&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt;
          &lt;span class="pl-c1"&gt;title&lt;/span&gt;&lt;span class="pl-c1"&gt;=&lt;/span&gt;&lt;span class="pl-kos"&gt;{&lt;/span&gt;&lt;span class="pl-s1"&gt;label&lt;/span&gt; &lt;span class="pl-c1"&gt;||&lt;/span&gt; &lt;span class="pl-s"&gt;"Choose Model"&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt;
          &lt;span class="pl-c1"&gt;icon&lt;/span&gt;&lt;span class="pl-c1"&gt;=&lt;/span&gt;&lt;span class="pl-kos"&gt;{&lt;/span&gt;&lt;span class="pl-c1"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-ent"&gt;TbChevronUp&lt;/span&gt; &lt;span class="pl-c1"&gt;/&lt;/span&gt;&lt;span class="pl-c1"&gt;&amp;gt;&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt;
          &lt;span class="pl-c1"&gt;onClick&lt;/span&gt;&lt;span class="pl-c1"&gt;=&lt;/span&gt;&lt;span class="pl-kos"&gt;{&lt;/span&gt;&lt;span class="pl-s1"&gt;openOnHover&lt;/span&gt; ? &lt;span class="pl-c1"&gt;undefined&lt;/span&gt; : &lt;span class="pl-s1"&gt;onOpen&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt; &lt;span class="pl-c"&gt;// Open on click if not openOnHover&lt;/span&gt;
          &lt;span class="pl-c1"&gt;onMouseEnter&lt;/span&gt;&lt;span class="pl-c1"&gt;=&lt;/span&gt;&lt;span class="pl-kos"&gt;{&lt;/span&gt;&lt;span class="pl-s1"&gt;openOnHover&lt;/span&gt; ? &lt;span class="pl-s1"&gt;onOpen&lt;/span&gt; : &lt;span class="pl-c1"&gt;undefined&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt; &lt;span class="pl-c"&gt;// Open on hover if openOnHover&lt;/span&gt;
          &lt;span class="pl-c1"&gt;onMouseLeave&lt;/span&gt;&lt;span class="pl-c1"&gt;=&lt;/span&gt;&lt;span class="pl-kos"&gt;{&lt;/span&gt;&lt;span class="pl-s1"&gt;openOnHover&lt;/span&gt; ? &lt;span class="pl-s1"&gt;onClose&lt;/span&gt; : &lt;span class="pl-c1"&gt;undefined&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt; &lt;span class="pl-c"&gt;// Close on hover out if openOnHover&lt;/span&gt;
          &lt;span class="pl-c1"&gt;borderLeftRadius&lt;/span&gt;&lt;span class="pl-c1"&gt;=&lt;/span&gt;&lt;span class="pl-s"&gt;"0"&lt;/span&gt;
        &lt;span class="pl-c1"&gt;/&lt;/span&gt;&lt;span class="pl-c1"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;The reason for this approach is that both use cases require &lt;code&gt;MenuButton&lt;/code&gt; to function differently. Initially, I tried to keep this logic separate and only shared the &lt;code&gt;menuItem&lt;/code&gt; logic, but it didn’t work. This is because the two places where this component needs to render use different menu libraries: one uses &lt;code&gt;@chakra-ui/react&lt;/code&gt; components, and the other uses &lt;code&gt;@szhsin/react-menu&lt;/code&gt;. That’s why I had to make the entire &lt;code&gt;Menu&lt;/code&gt; component reusable.&lt;/p&gt;
&lt;p&gt;What do you think?&lt;/p&gt;

    &lt;/div&gt;
    &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/tarasglek/chatcraft.org/issues/643#issuecomment-2466273803" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;



&lt;p&gt;To handle the differences, I used an &lt;strong&gt;ad-hoc approach&lt;/strong&gt; for rendering &lt;code&gt;MenuButton&lt;/code&gt;. The &lt;strong&gt;Ask menu&lt;/strong&gt; required a &lt;strong&gt;click effect&lt;/strong&gt; to open, while the &lt;strong&gt;Retry menu&lt;/strong&gt; used a &lt;strong&gt;hover effect&lt;/strong&gt;. With this solution in place, everything was working as expected, so I created a &lt;strong&gt;pull request (PR)&lt;/strong&gt;:&lt;br&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_github-liquid-tag"&gt;
  &lt;h1&gt;
    &lt;a href="https://github.com/tarasglek/chatcraft.org/pull/729" rel="noopener noreferrer"&gt;
      &lt;img class="github-logo" alt="GitHub logo" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg"&gt;
      &lt;span class="issue-title"&gt;
        Added ModelProviderMenu component and reused it in PromptsendButton and MessageBase
      &lt;/span&gt;
      &lt;span class="issue-number"&gt;#729&lt;/span&gt;
    &lt;/a&gt;
  &lt;/h1&gt;
  &lt;div class="github-thread"&gt;
    &lt;div class="timeline-comment-header"&gt;
      &lt;a href="https://github.com/hpatel292-seneca" rel="noopener noreferrer"&gt;
        &lt;img class="github-liquid-tag-img" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Favatars.githubusercontent.com%2Fu%2F100322816%3Fv%3D4" alt="hpatel292-seneca avatar"&gt;
      &lt;/a&gt;
      &lt;div class="timeline-comment-header-text"&gt;
        &lt;strong&gt;
          &lt;a href="https://github.com/hpatel292-seneca" rel="noopener noreferrer"&gt;hpatel292-seneca&lt;/a&gt;
        &lt;/strong&gt; posted on &lt;a href="https://github.com/tarasglek/chatcraft.org/pull/729" rel="noopener noreferrer"&gt;&lt;time&gt;Nov 15, 2024&lt;/time&gt;&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag-github-body"&gt;
      &lt;p&gt;&lt;strong&gt;This fixes #643.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Added a reusable component, &lt;code&gt;ModelProviderMenu&lt;/code&gt;, and replaced the logic in &lt;code&gt;PromptSendButton&lt;/code&gt; and &lt;code&gt;MessageBase&lt;/code&gt; to utilize this component.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Overview of Changes:&lt;/h3&gt;
&lt;span class="octicon octicon-link"&gt;&lt;/span&gt;
&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Added a new component:&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;ModelProviderMenu&lt;/code&gt; contains the logic for displaying models and providers, allowing the user to change models and providers directly from it.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Updated &lt;code&gt;PromptSendButton&lt;/code&gt;:&lt;/strong&gt;&lt;br&gt;
Replaced the existing menu logic in &lt;code&gt;PromptSendButton&lt;/code&gt; with the &lt;code&gt;ModelProviderMenu&lt;/code&gt; component. This menu appears next to the send button.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Updated &lt;code&gt;MessageBase&lt;/code&gt;:&lt;/strong&gt;&lt;br&gt;
Refactored the retry logic in &lt;code&gt;MessageBase&lt;/code&gt; to use the &lt;code&gt;ModelProviderMenu&lt;/code&gt; component, ensuring consistency and reusability.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Demo&lt;/h3&gt;
&lt;span class="octicon octicon-link"&gt;&lt;/span&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a href="https://github.com/user-attachments/assets/b55bf561-08c3-4d74-8958-1f7b3c6ac559" rel="noopener noreferrer"&gt;https://github.com/user-attachments/assets/b55bf561-08c3-4d74-8958-1f7b3c6ac559&lt;/a&gt;&lt;/p&gt;

    &lt;/div&gt;
    &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/tarasglek/chatcraft.org/pull/729" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Interestingly, the owner added me as a &lt;strong&gt;developer for the repository&lt;/strong&gt; and asked me to send another PR from the &lt;strong&gt;original repository’s branch&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%2Fmk0bjycjnlnlgkty3lwj.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%2Fmk0bjycjnlnlgkty3lwj.png" alt="Image description" width="800" height="140"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I accepted the &lt;strong&gt;developer request&lt;/strong&gt; and cloned the original repository. Initially, I was confused about how to transfer my changes from my &lt;strong&gt;forked repository’s branch&lt;/strong&gt; to the &lt;strong&gt;original repository’s branch&lt;/strong&gt;. Then I remembered I could &lt;strong&gt;set up my forked repository as a remote&lt;/strong&gt;, fetch the branches, and merge the changes. I did that successfully and made another &lt;a href="https://github.com/tarasglek/chatcraft.org/pull/730" rel="noopener noreferrer"&gt;PR&lt;/a&gt; from the original repository branch.&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;Change Requests&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;After making the PR, the maintainer started reviewing my changes and requested several updates—&lt;strong&gt;eight change requests in total&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_github-liquid-tag"&gt;
  &lt;h1&gt;
    &lt;a href="https://github.com/tarasglek/chatcraft.org/pull/730" rel="noopener noreferrer"&gt;
      &lt;img class="github-logo" alt="GitHub logo" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg"&gt;
      &lt;span class="issue-title"&gt;
        Added ModelProviderMenu component and reused it in PromptsendButton and MessageBase
      &lt;/span&gt;
      &lt;span class="issue-number"&gt;#730&lt;/span&gt;
    &lt;/a&gt;
  &lt;/h1&gt;
  &lt;div class="github-thread"&gt;
    &lt;div class="timeline-comment-header"&gt;
      &lt;a href="https://github.com/hpatel292-seneca" rel="noopener noreferrer"&gt;
        &lt;img class="github-liquid-tag-img" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Favatars.githubusercontent.com%2Fu%2F100322816%3Fv%3D4" alt="hpatel292-seneca avatar"&gt;
      &lt;/a&gt;
      &lt;div class="timeline-comment-header-text"&gt;
        &lt;strong&gt;
          &lt;a href="https://github.com/hpatel292-seneca" rel="noopener noreferrer"&gt;hpatel292-seneca&lt;/a&gt;
        &lt;/strong&gt; posted on &lt;a href="https://github.com/tarasglek/chatcraft.org/pull/730" rel="noopener noreferrer"&gt;&lt;time&gt;Nov 15, 2024&lt;/time&gt;&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag-github-body"&gt;
      &lt;p&gt;This is Requested from  #729&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;/div&amp;gt;
&amp;lt;div class="gh-btn-container"&amp;gt;&amp;lt;a class="gh-btn" href="https://github.com/tarasglek/chatcraft.org/pull/730"&amp;gt;View on GitHub&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;
  

&lt;p&gt;It turned out that my &lt;strong&gt;ad-hoc approach&lt;/strong&gt; was making the new component &lt;strong&gt;less reusable&lt;/strong&gt; and introduced &lt;strong&gt;bugs&lt;/strong&gt;. The maintainer suggested that the new component should use &lt;strong&gt;&lt;code&gt;@szhsin/react-menu&lt;/code&gt;&lt;/strong&gt; instead of &lt;strong&gt;&lt;code&gt;@chakra-ui/react&lt;/code&gt;&lt;/strong&gt;, and asked me to revisit the previous approach of &lt;strong&gt;extracting &lt;code&gt;MenuList&lt;/code&gt;&lt;/strong&gt;.  &lt;/p&gt;

&lt;p&gt;I started by making the required changes and created a &lt;strong&gt;new component&lt;/strong&gt; called &lt;strong&gt;&lt;code&gt;ModelSelectionMenuList&lt;/code&gt;&lt;/strong&gt;, extracting the &lt;code&gt;MenuList&lt;/code&gt; into this component. It worked as expected for the &lt;strong&gt;Retry menu&lt;/strong&gt;, as its parent component was already using &lt;strong&gt;&lt;code&gt;@szhsin/react-menu&lt;/code&gt;&lt;/strong&gt;. However, it &lt;strong&gt;failed for the Ask menu&lt;/strong&gt;, which was still using &lt;strong&gt;&lt;code&gt;@chakra-ui/react&lt;/code&gt;&lt;/strong&gt; components.  &lt;/p&gt;

&lt;p&gt;I reached out to the maintainer again, and they advised &lt;strong&gt;refactoring the Ask menu&lt;/strong&gt; to also use &lt;strong&gt;&lt;code&gt;@szhsin/react-menu&lt;/code&gt;&lt;/strong&gt; components. Following their guidance, I successfully refactored the Ask menu to align with the new structure. This made the component &lt;strong&gt;consistent and reusable&lt;/strong&gt; across both menus.&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;Mobile Screen Refactoring&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;While refactoring the &lt;strong&gt;Ask menu&lt;/strong&gt;, I discovered that it was rendered &lt;strong&gt;differently for mobile and laptop screens&lt;/strong&gt;. To address this, I updated my new component to handle both variations by using the same &lt;strong&gt;template logic&lt;/strong&gt; to render different menus based on &lt;strong&gt;screen size&lt;/strong&gt;. I also ensured that the &lt;strong&gt;Ask menu for mobile screens&lt;/strong&gt; was refactored appropriately.&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;Final Thoughts&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;In total, I spent around &lt;strong&gt;two full days&lt;/strong&gt; completing the PR for this issue. During the process, I had moments of &lt;strong&gt;doubt&lt;/strong&gt;, feeling like I might not be able to finish it. However, I &lt;strong&gt;persevered&lt;/strong&gt; and completed the task, gaining &lt;strong&gt;valuable knowledge&lt;/strong&gt; in the process.&lt;/p&gt;

&lt;p&gt;The most important lesson I learned from this issue—and from the &lt;strong&gt;Open Source Development course&lt;/strong&gt;—is to &lt;strong&gt;challenge myself&lt;/strong&gt; and &lt;strong&gt;step out of my comfort zone&lt;/strong&gt;. Growth truly happens when we push our boundaries, and this experience reinforced that belief.&lt;/p&gt;
&lt;/div&gt;

</description>
    </item>
    <item>
      <title>Automating Testing: My Journey with GitHub Actions, CI Pipelines, and Test Automation</title>
      <dc:creator>Harshil Patel</dc:creator>
      <pubDate>Fri, 15 Nov 2024 22:13:45 +0000</pubDate>
      <link>https://dev.to/harshil_patel/automating-testing-my-journey-with-github-actions-ci-pipelines-and-test-automation-1fon</link>
      <guid>https://dev.to/harshil_patel/automating-testing-my-journey-with-github-actions-ci-pipelines-and-test-automation-1fon</guid>
      <description>&lt;p&gt;This week, I was tasked with automating testing, linting, and error checking for my &lt;a href="https://github.com/hpatel292-seneca/ResumeEnhancer/tree/main" rel="noopener noreferrer"&gt;ResumeEnhancer&lt;/a&gt; project using a GitHub Actions &lt;a href="https://en.wikipedia.org/wiki/Continuous_integration" rel="noopener noreferrer"&gt;CI&lt;/a&gt; workflow. Continuous Integration (CI) automatically tests new code as soon as it's added, helping to catch issues early before they affect the entire project. This ensures the code remains stable, speeds up development, and facilitates smoother collaboration among developers.&lt;/p&gt;

&lt;p&gt;CI pipelines are not new to me, as I’ve developed CI/CD pipelines for previous projects. However, last month, while contributing four pull requests to open-source projects, I realized the importance of pipelines in maintaining clean and consistent code across repositories.&lt;/p&gt;

&lt;p&gt;In this blog, I’ll share my experience setting up a CI pipeline. I chose &lt;a href="https://github.com/features/actions" rel="noopener noreferrer"&gt;GitHub Actions&lt;/a&gt; because I’m already using GitHub for source control. GitHub Actions spin up a machine to execute tasks when code is pushed or a pull request is created. To implement CI with GitHub Actions, you define a series of steps to ensure your code meets the required standards.&lt;/p&gt;

&lt;p&gt;For my &lt;strong&gt;ResumeEnhancer&lt;/strong&gt; project, I am using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://pypi.org/project/black/" rel="noopener noreferrer"&gt;Black&lt;/a&gt; for code formatting,&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://flake8.pycqa.org/en/latest/index.html" rel="noopener noreferrer"&gt;Flake8&lt;/a&gt; for linting,&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.pytest.org/en/stable/getting-started.html" rel="noopener noreferrer"&gt;Pytest&lt;/a&gt; for testing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here’s how my CI pipeline looks:&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="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ResumeEnhancer CI Pipeline - Test, Lint, and Format&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;main"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;main"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;read&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;strategy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;matrix&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;python-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3.11"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3.12"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;# Check out the repository code&lt;/span&gt;
      &lt;span class="pi"&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;Checkout code&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v3&lt;/span&gt;

      &lt;span class="c1"&gt;# Set up Python&lt;/span&gt;
      &lt;span class="pi"&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;Set up Python ${{ matrix.python-version }}&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-python@v2&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;python-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ matrix.python-version }}&lt;/span&gt;

      &lt;span class="c1"&gt;# Install dependencies&lt;/span&gt;
      &lt;span class="pi"&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;Install dependencies&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;python -m pip install --upgrade pip&lt;/span&gt;
          &lt;span class="s"&gt;pip install -r requirements.txt&lt;/span&gt;

      &lt;span class="c1"&gt;# Make format.sh and lint.sh executable&lt;/span&gt;
      &lt;span class="pi"&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;Make scripts executable&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;chmod +x ./format.sh ./lint.sh&lt;/span&gt;

      &lt;span class="c1"&gt;# Run formatting&lt;/span&gt;
      &lt;span class="pi"&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;Run format.sh&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./format.sh&lt;/span&gt;
        &lt;span class="na"&gt;continue-on-error&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;

      &lt;span class="c1"&gt;# Run linting&lt;/span&gt;
      &lt;span class="pi"&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;Run lint.sh&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./lint.sh&lt;/span&gt;
        &lt;span class="na"&gt;continue-on-error&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;

      &lt;span class="c1"&gt;# Run tests&lt;/span&gt;
      &lt;span class="pi"&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;Run Tests&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;pytest&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Initially, I included Python versions &lt;code&gt;["3.10", "3.11", "3.12"]&lt;/code&gt;, but my &lt;a href="https://github.com/hpatel292-seneca/ResumeEnhancer/actions/runs/11785771611/job/32827688995" rel="noopener noreferrer"&gt;CI run&lt;/a&gt; failed. After investigating, I discovered that the &lt;code&gt;tomllib&lt;/code&gt; module, which I’m using, is built into Python starting from version 3.11. As a result, I had to remove Python 3.10 from the list.&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%2F6v8hr6i6s4nzvnuy60if.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%2F6v8hr6i6s4nzvnuy60if.png" alt="Image description" width="800" height="417"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This CI pipeline for the &lt;strong&gt;ResumeEnhancer&lt;/strong&gt; project automatically runs whenever code is pushed to the &lt;code&gt;main&lt;/code&gt; branch or a pull request is created. It uses an Ubuntu environment and tests the code on Python versions 3.11 and 3.12. The pipeline performs several steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Checks out the repository code.&lt;/li&gt;
&lt;li&gt;Sets up the required Python version.&lt;/li&gt;
&lt;li&gt;Installs project dependencies.&lt;/li&gt;
&lt;li&gt;Ensures that the formatting (&lt;a href="https://github.com/hpatel292-seneca/ResumeEnhancer/blob/main/format.sh" rel="noopener noreferrer"&gt;format.sh&lt;/a&gt;) and linting (&lt;a href="https://github.com/hpatel292-seneca/ResumeEnhancer/blob/main/lint.sh" rel="noopener noreferrer"&gt;lint.sh&lt;/a&gt;) scripts are executable and error-free.&lt;/li&gt;
&lt;li&gt;Runs automated tests using &lt;code&gt;pytest&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here’s the result after successfully setting up the CI pipeline:&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%2Fasp4kh6ya6sg11081flm.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%2Fasp4kh6ya6sg11081flm.png" alt="Image description" width="800" height="378"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While I’ve worked with CI before, setting it up for my project reminded me of how valuable it is. It’s reassuring to know that every time I push code or open a pull request, the pipeline automatically checks formatting, linting, and tests—saving me from manual effort. It’s like having a safety net that ensures the code remains clean and consistent, which is especially helpful when contributing to open-source projects or collaborating with others. CI reduces stress and makes development more efficient.&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing Out a Classmate’s CI Workflow
&lt;/h3&gt;

&lt;p&gt;To test CI workflows further, I collaborated with my friend Non. I added a test case to his repository, &lt;a href="https://github.com/fadingNA/chat-completion-api" rel="noopener noreferrer"&gt;chat-completion-api&lt;/a&gt;, to evaluate his CI pipeline, and he did the same for mine. His project also uses Python, but instead of &lt;code&gt;pytest&lt;/code&gt;, it uses &lt;a href="https://docs.python.org/3/library/unittest.html" rel="noopener noreferrer"&gt;unittest&lt;/a&gt; for testing. &lt;/p&gt;

&lt;p&gt;The main difference between &lt;code&gt;unittest&lt;/code&gt; and &lt;code&gt;pytest&lt;/code&gt; is that &lt;code&gt;unittest&lt;/code&gt; is a built-in testing framework with a structured approach, while &lt;code&gt;pytest&lt;/code&gt; offers more flexibility and features, making testing simpler and more powerful. Since I’m familiar with &lt;code&gt;pytest&lt;/code&gt;, adapting to &lt;code&gt;unittest&lt;/code&gt; was straightforward. I wrote two test cases for a function in &lt;code&gt;utils.py&lt;/code&gt;, and here’s the &lt;a href="https://github.com/fadingNA/chat-completion-api/pull/22" rel="noopener noreferrer"&gt;PR&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Overall, I enjoyed working with CI. It simplifies development and ensures high-quality, consistent code.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Automated Testing Made Simple: Setting Up and Exploring pytest</title>
      <dc:creator>Harshil Patel</dc:creator>
      <pubDate>Wed, 06 Nov 2024 22:43:50 +0000</pubDate>
      <link>https://dev.to/harshil_patel/automated-testing-made-simple-setting-up-and-exploring-pytest-13bc</link>
      <guid>https://dev.to/harshil_patel/automated-testing-made-simple-setting-up-and-exploring-pytest-13bc</guid>
      <description>&lt;p&gt;This blog is about integrating a testing framework into my &lt;a href="https://github.com/hpatel292-seneca/ResumeEnhancer" rel="noopener noreferrer"&gt;ResumeEnhancer&lt;/a&gt; CLI tool. As my work on ResumeEnhancer has progressed, I realized that adding test cases would be essential to maintain code quality as the project grows. I considered a few options like &lt;code&gt;pytest&lt;/code&gt;, &lt;code&gt;nose&lt;/code&gt;, and &lt;code&gt;unittest&lt;/code&gt; but chose &lt;a href="https://docs.pytest.org/en/stable/getting-started.html" rel="noopener noreferrer"&gt;pytest&lt;/a&gt; since it’s widely used and a great choice for learning a popular testing framework.&lt;/p&gt;

&lt;p&gt;Setting up &lt;code&gt;pytest&lt;/code&gt; is straightforward—just run &lt;code&gt;pip install pytest&lt;/code&gt;, and you’re ready to go.&lt;/p&gt;

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

&lt;p&gt;I began by researching how to write and run test cases with &lt;code&gt;pytest&lt;/code&gt;. I created a &lt;code&gt;tests&lt;/code&gt; folder and added my first test file called &lt;code&gt;test_utils.py&lt;/code&gt;. I decided to follow the &lt;code&gt;test_filename.py&lt;/code&gt; naming convention and wrote my initial test case for a utility function called &lt;code&gt;read_txt_file()&lt;/code&gt;. This function reads text from a &lt;code&gt;.txt&lt;/code&gt; file, as the name suggests. I explored three different ways to test this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Mock the &lt;code&gt;open&lt;/code&gt; function&lt;/strong&gt; – This approach pretends to open a file without actually creating one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Create a temporary text file&lt;/strong&gt; – Here, I’d create a small &lt;code&gt;.txt&lt;/code&gt; file in the test specifically for this function.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use a &lt;code&gt;test.txt&lt;/code&gt; file in the project folder&lt;/strong&gt; – I’d store a test file in the main folder to use as needed.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Finally, I decided to mock the &lt;code&gt;open&lt;/code&gt; function and proceeded to write the test cases.&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%2Fprrihz6r05m5m8kfdsos.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%2Fprrihz6r05m5m8kfdsos.png" alt="Image description" width="800" height="557"&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%2F0gwlzi12f59e72vvtznt.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%2F0gwlzi12f59e72vvtznt.png" alt="Image description" width="800" height="134"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, I explored ways to run specific test cases. I found that you can run a particular test case with &lt;code&gt;pytest path_to_test_file::class_name::test_name&lt;/code&gt;. If you haven’t used classes, you can omit the class name, like this:&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%2Fs00dvfbme31d9vwfzasf.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%2Fs00dvfbme31d9vwfzasf.png" alt="Image description" width="800" height="139"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;pytest&lt;/code&gt; also has a Watch Mode, which reruns only failing tests automatically whenever the code changes. To use it, add the &lt;code&gt;--looponfail&lt;/code&gt; option:&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%2F8wjjcm0cpziv8xhkkgzw.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%2F8wjjcm0cpziv8xhkkgzw.png" alt="Image description" width="800" height="168"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Mocking API Responses
&lt;/h2&gt;

&lt;p&gt;Next, I wanted to add test cases for the &lt;code&gt;get_response()&lt;/code&gt; method. This function takes a resume and job description as inputs, along with settings to customize the response. It uses an AI model to review the resume and suggests ways to make it a better fit for the job description. The feedback focuses on highlighting important skills, experiences, and keywords that could be added or emphasized to improve alignment with the job requirements.&lt;/p&gt;

&lt;p&gt;Since &lt;code&gt;get_response()&lt;/code&gt; uses the Groq API, I needed to mock the API call for testing. Here’s how I initially tried it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;responses&lt;/span&gt;

&lt;span class="n"&gt;sample_llm_response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;choices&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;delta&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Mocked response content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}}]}&lt;/span&gt;
&lt;span class="n"&gt;responses&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;responses&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;POST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://api.groq.com/openai/v1/chat/completions&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# Mock this URL
&lt;/span&gt;    &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;sample_llm_response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;200&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;However, when I ran the test, it failed with an error saying the &lt;code&gt;API_KEY&lt;/code&gt; was invalid. &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%2F18vd2u80hctc3favvb7c.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%2F18vd2u80hctc3favvb7c.png" alt="Image description" width="800" height="75"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After some investigation, I realized that the code initializes the Groq client first with &lt;code&gt;client = Groq(api_key)&lt;/code&gt;, and then makes the API call. Mocking this constructor call directly didn’t work, so I decided to mock the entire Groq client like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@mock.patch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;resume_enhancer.Groq&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Mock Groq client initialization
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_get_response_valid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mock_groq&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Mock the client instance and its chat.completions.create method
&lt;/span&gt;    &lt;span class="n"&gt;mock_client_instance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Mock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;mock_groq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;return_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mock_client_instance&lt;/span&gt;  &lt;span class="c1"&gt;# Groq() returns the mock client instance
&lt;/span&gt;    &lt;span class="n"&gt;mock_client_instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;completions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;return_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;  &lt;span class="c1"&gt;# Mock response
&lt;/span&gt;        &lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Mock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Mock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;delta&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Mock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Mocked response content&lt;/span&gt;&lt;span class="sh"&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="c1"&gt;# Capture stdout to check the printed output
&lt;/span&gt;    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;patch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sys.stdout&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;new_callable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;StringIO&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;mock_stdout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;get_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;resume&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Sample Resume&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Sample Job Description&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;test_api_key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mock_stdout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getvalue&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="c1"&gt;# Assert that the expected content is printed in the output
&lt;/span&gt;    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Mocked response content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I wrote 11 test cases to check different scenarios for &lt;code&gt;get_response()&lt;/code&gt;, and all the tests passed. However, I found myself repeating the Groq mocking code in each test, which made things messy. I looked for ways to avoid this repetition and found I could use a class-based setup to share common mocking logic among all tests. Here’s the improved setup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Test_get_response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;setup_method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# Mock the Groq client and its completions method
&lt;/span&gt;        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;patcher&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;patch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;resume_enhancer.Groq&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mock_groq&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;patcher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# Start the patch
&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mock_client_instance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Mock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mock_groq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;return_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mock_client_instance&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mock_client_instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;completions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;return_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Mock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Mock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;delta&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Mock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Mocked response content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))],&lt;/span&gt;
                &lt;span class="n"&gt;x_groq&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Mock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="n"&gt;usage&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Mock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                        &lt;span class="n"&gt;completion_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="n"&gt;prompt_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="n"&gt;total_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="n"&gt;completion_time&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.35&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="n"&gt;prompt_time&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="n"&gt;queue_time&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="n"&gt;total_time&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.6&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="p"&gt;]&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;teardown_method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;patcher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# Stop all patches after each test
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This setup removed the repetitive mock code, and everything worked smoothly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I learned a lot about &lt;code&gt;pytest&lt;/code&gt;, including how to mock API calls, function calls, and even third-party classes like Groq. Testing has shown me how useful it is for keeping the code reliable as I add features.&lt;/p&gt;

&lt;p&gt;Before this, I had written test cases in JavaScript using Jest, but this was my first experience mocking both functions and an API. Previously, I only did basic tests without much setup for dependencies, so learning to mock an entire API client in &lt;code&gt;pytest&lt;/code&gt; was new for me. This process has helped me understand the value of mocking for isolating parts of the code in testing.&lt;/p&gt;

&lt;p&gt;Now, I see why testing is so valuable, and I’ll make it a habit in future projects. It catches issues early, makes the code easier to update, and builds confidence that everything works as expected. This experience has shown me that putting effort into testing upfront is well worth it for creating better, more reliable projects.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Automating Code Quality: A Guide to Setting Up Formatters, Linters, and Pre-Commit Hooks in Python Projects</title>
      <dc:creator>Harshil Patel</dc:creator>
      <pubDate>Fri, 01 Nov 2024 18:42:22 +0000</pubDate>
      <link>https://dev.to/harshil_patel/automating-code-quality-a-guide-to-setting-up-formatters-linters-and-pre-commit-hooks-in-python-projects-331o</link>
      <guid>https://dev.to/harshil_patel/automating-code-quality-a-guide-to-setting-up-formatters-linters-and-pre-commit-hooks-in-python-projects-331o</guid>
      <description>&lt;h1&gt;
  
  
  Enhancing Code Quality with Static Analysis Tools in Resume Enhancer
&lt;/h1&gt;

&lt;p&gt;In this blog, I’ll share my experience of adding &lt;strong&gt;Static Analysis&lt;/strong&gt; tools to my &lt;a href="https://github.com/hpatel292-seneca/ResumeEnhancer" rel="noopener noreferrer"&gt;Resume Enhancer&lt;/a&gt; project. Static analysis tools work on source code itself, helping to maintain quality by automatically fixing formatting issues, spotting potential code flaws, and alerting us to common errors.&lt;/p&gt;

&lt;h2&gt;
  
  
  Source Code Formatter
&lt;/h2&gt;

&lt;p&gt;For code formatting, I chose &lt;a href="https://pypi.org/project/black/" rel="noopener noreferrer"&gt;Black&lt;/a&gt;. Black has become my go-to formatter because it removes the need to worry about minor formatting decisions. By using Black, I give up some control over these small details, but in return, I get a fast, consistent, and reliable tool that keeps my code clean and eliminates pycodestyle warnings. This frees up time and mental energy for more important tasks.&lt;/p&gt;

&lt;p&gt;To install Black, I used:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;black
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After exploring the documentation, I ran Black manually on my main code file, &lt;code&gt;resumeEnhancer.py&lt;/code&gt;, and found that most of my code was already formatted. Here’s what it looked like:&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%2Fewi9pawt4p6aftsqofd6.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%2Fewi9pawt4p6aftsqofd6.png" alt="Image description" width="800" height="356"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, I created a &lt;code&gt;format.sh&lt;/code&gt; script to make it easy to run Black on all coding files in one step. Here’s the result of executing the script:&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%2Ffkxe2bkacyr44rubfs1o.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%2Ffkxe2bkacyr44rubfs1o.png" alt="Image description" width="800" height="140"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Linter
&lt;/h2&gt;

&lt;p&gt;Linters help us catch common and subtle mistakes, which can be easy to overlook. To spot these issues, I used &lt;a href="https://flake8.pycqa.org/en/latest/index.html" rel="noopener noreferrer"&gt;Flake8&lt;/a&gt; to lint my source code. After installing Flake8 with &lt;code&gt;pip install flake8&lt;/code&gt;, I ran it on my main code file and, to my surprise, discovered many errors:&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%2Fixsbo65wwejlyf1af6i0.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%2Fixsbo65wwejlyf1af6i0.png" alt="Image description" width="800" height="422"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I added Flake8 configurations in a &lt;code&gt;.flake8&lt;/code&gt; file and created a &lt;code&gt;lint.sh&lt;/code&gt; script to streamline the linting process. After fixing all the errors that Flake8 identified, I realized how essential linting is—it catches tiny errors that could otherwise lead to larger issues later on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Editor/IDE Integration
&lt;/h2&gt;

&lt;p&gt;Using static analysis tools at build time is great for automating code quality checks, but integrating them directly into my editor made an even bigger impact. With Black and Flake8 integrated into my editor, I could catch and fix issues as I coded—no waiting until I was finished. A formatter that tidies code on save and a linter that flags issues in real-time helped keep my code clean and consistent.&lt;/p&gt;

&lt;p&gt;To integrate Black and Flake8, I installed the necessary extensions in VSCode. Initially, I installed an extension called &lt;code&gt;black&lt;/code&gt; instead of &lt;code&gt;black formatter&lt;/code&gt;, which caused formatting issues. After troubleshooting, I discovered the correct extension, and everything worked smoothly.&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%2Fmkqhgsdwo1nyv5gdly1n.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%2Fmkqhgsdwo1nyv5gdly1n.png" alt="Image description" width="324" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Pre-Commit Hook
&lt;/h2&gt;

&lt;p&gt;Adding a &lt;strong&gt;Git pre-commit hook&lt;/strong&gt; was a powerful way to ensure my code remained consistent and clean before each commit. A pre-commit hook automatically runs the formatter on any files being committed, saving us from having to remember to do it manually.&lt;/p&gt;

&lt;p&gt;Pre-commit hooks are especially useful when working with others because they apply formatting uniformly across all commits, making the project’s codebase more readable. Setting one up was straightforward. I used &lt;code&gt;pre-commit&lt;/code&gt; to configure a hook that runs Black on any changed files before committing.&lt;/p&gt;

&lt;p&gt;After configuring the pre-commit hook, I tested it, and it worked perfectly:&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%2F4fte4v39afqwq8av8wni.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%2F4fte4v39afqwq8av8wni.png" alt="Image description" width="800" height="504"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;This lab taught me the value of automating code quality checks. Setting up tools like Black and Flake8 wasn’t just about following rules—it made my coding process smoother and more efficient. I realized how much time and effort is saved when formatting and linting happen automatically, allowing me to catch issues early on. This experience showed me the power of automation and consistent coding standards in collaborative projects.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Hacktoberfest 2024: My Journey and Key Learnings</title>
      <dc:creator>Harshil Patel</dc:creator>
      <pubDate>Sun, 27 Oct 2024 23:00:12 +0000</pubDate>
      <link>https://dev.to/harshil_patel/hacktoberfest-2024-my-journey-and-key-learnings-5h4k</link>
      <guid>https://dev.to/harshil_patel/hacktoberfest-2024-my-journey-and-key-learnings-5h4k</guid>
      <description>&lt;h2&gt;
  
  
  Final Recap Blog Post: My Hacktoberfest 2024 Journey
&lt;/h2&gt;

&lt;p&gt;October has been a month full of learning and challenges! Hacktoberfest 2024 was my first time contributing to open-source, and it was a great experience. In this blog, I’ll reflect on everything I did this month, how I improved, and what I learned from it all.&lt;/p&gt;

&lt;h3&gt;
  
  
  Summary of Contributions
&lt;/h3&gt;

&lt;p&gt;I started this month by contributing to four different projects as part of Hacktoberfest. Each one came with its own set of challenges, but in the end, I was able to complete all four PRs successfully.&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%2F100ypzw7enwxtwjvea5s.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%2F100ypzw7enwxtwjvea5s.png" alt="Hacktoberfest" width="619" height="189"&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%2Fxdvrxk0mf9r625879yel.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%2Fxdvrxk0mf9r625879yel.png" alt="Hacktoberfest" width="800" height="639"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;My first &lt;a href="https://github.com/tarasglek/chatcraft.org/pull/690" rel="noopener noreferrer"&gt;PR&lt;/a&gt; was for &lt;a href="https://github.com/tarasglek/chatcraft.org" rel="noopener noreferrer"&gt;Chatcraft&lt;/a&gt;, where I worked on adding new logos for different LLM models. This task taught me how important it is to communicate with maintainers and be open to feedback. Even small changes, like updating logos, can make a big impact.&lt;/p&gt;

&lt;p&gt;The second &lt;a href="https://github.com/Canner/WrenAI/pull/745" rel="noopener noreferrer"&gt;PR&lt;/a&gt; I did was for &lt;a href="https://github.com/Canner/WrenAI" rel="noopener noreferrer"&gt;WrenAI&lt;/a&gt;, and this one was the most challenging. I had to deal with multiple services running on Docker, install dependencies, and even run into OpenAI token errors. It was frustrating at times, but I learned so much about how big projects work and how to debug them.&lt;/p&gt;

&lt;p&gt;For my third &lt;a href="https://github.com/zero-to-mastery/ZTM-Quest/pull/153" rel="noopener noreferrer"&gt;PR&lt;/a&gt;, I contributed to &lt;a href="https://github.com/zero-to-mastery/ZTM-Quest" rel="noopener noreferrer"&gt;ZTM-Quest&lt;/a&gt;, a fun game project where I adjusted the player's speed based on their energy level. This PR was exciting because it combined coding with something I enjoy—games. It also taught me about game mechanics and handling dynamic properties.&lt;/p&gt;

&lt;p&gt;My fourth &lt;a href="https://github.com/planetarium/libplanet/pull/3972" rel="noopener noreferrer"&gt;PR&lt;/a&gt; was for &lt;a href="https://github.com/planetarium/libplanet" rel="noopener noreferrer"&gt;Libplanet&lt;/a&gt;, and I wanted to switch things up by working with C#. I had experience with C# from my ASP.NET projects, so it felt like the right move. I worked on removing redundant code from a utility class used for unit testing. This task helped me brush up on unit testing and gave me a chance to work with a decentralized .NET library.&lt;/p&gt;

&lt;h3&gt;
  
  
  How I Improved
&lt;/h3&gt;

&lt;p&gt;Throughout Hacktoberfest, I’ve definitely grown as a developer. At the beginning of the month, I felt a bit lost when navigating large repos, but by the end, I was much more confident. I’ve gotten better at source control, debugging, managing dependencies, and reading through codebases. My problem-solving skills have also improved since I had to tackle a lot of unexpected issues, especially with WrenAI.&lt;/p&gt;

&lt;p&gt;Patience and persistence were key lessons for me. Not everything went smoothly, and some tasks took longer than expected. But I learned how to stay calm and work through the problems one step at a time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lessons Learned
&lt;/h3&gt;

&lt;p&gt;One of the biggest things I learned during Hacktoberfest was that open-source isn’t just about coding. It’s about teamwork, following guidelines, and understanding the community’s goals. The feedback I got from maintainers was super helpful, and it taught me how to write cleaner, more efficient code.&lt;/p&gt;

&lt;p&gt;I also learned not to be afraid of contributing to bigger projects, even if they seem overwhelming. Some of my best learning moments came from working on WrenAI and Libplanet, which pushed me to step out of my comfort zone.&lt;/p&gt;

&lt;h3&gt;
  
  
  What’s Next
&lt;/h3&gt;

&lt;p&gt;Even though Hacktoberfest 2024 is over, I’m excited to keep contributing to open-source. This month has shown me how rewarding it can be to be part of these projects, and I want to keep going. I’m looking forward to exploring new repos and continuing to grow as a developer.&lt;/p&gt;

&lt;p&gt;To wrap it up, Hacktoberfest 2024 was an amazing experience. I’ve learned a lot, made valuable contributions, and grown both in my skills and confidence. I’m excited to see where this open-source journey takes me next!&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
