<?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>Forem: Doan Trong Nam</title>
    <description>The latest articles on Forem by Doan Trong Nam (@doantrongnam).</description>
    <link>https://forem.com/doantrongnam</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%2F1316554%2Fdb28baa3-db07-4b6c-88f8-1ad5d03ff9f2.jpeg</url>
      <title>Forem: Doan Trong Nam</title>
      <link>https://forem.com/doantrongnam</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/doantrongnam"/>
    <language>en</language>
    <item>
      <title>A Developer's Guide to Unit Testing Nuxt 3 Server Routes</title>
      <dc:creator>Doan Trong Nam</dc:creator>
      <pubDate>Thu, 03 Jul 2025 07:46:04 +0000</pubDate>
      <link>https://forem.com/doantrongnam/a-developers-guide-to-unit-testing-nuxt-3-server-routes-4f55</link>
      <guid>https://forem.com/doantrongnam/a-developers-guide-to-unit-testing-nuxt-3-server-routes-4f55</guid>
      <description>&lt;p&gt;Testing is a critical part of building robust and reliable applications. While Nuxt 3 makes creating server routes incredibly simple, setting up a proper testing environment for them can seem a bit daunting. Fear not! With the power of &lt;code&gt;vitest&lt;/code&gt; and &lt;code&gt;@nuxt/test-utils&lt;/code&gt;, you can create a clean, efficient, and powerful testing suite for your server-side logic.&lt;/p&gt;

&lt;p&gt;This guide will walk you through setting up unit tests for your Nuxt 3 server routes, from initial configuration to mocking dependencies and writing comprehensive tests.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Configuring Vitest for Your Nuxt Environment
&lt;/h2&gt;

&lt;p&gt;The first step is to ensure &lt;code&gt;vitest&lt;/code&gt; knows how to run your tests within a Nuxt context. This is crucial for auto-imports and other Nuxt-specific features to work correctly in your test files.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;@nuxt/test-utils&lt;/code&gt; package provides a handy &lt;code&gt;defineVitestConfig&lt;/code&gt; function that simplifies this process. Here’s what a typical &lt;code&gt;vitest.config.ts&lt;/code&gt; looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// vitest.config.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;defineVitestConfig&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@nuxt/test-utils/config&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;defineVitestConfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Enable the Nuxt environment.&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;nuxt&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;environmentOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;nuxt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// You can specify a DOM environment for components testing if needed.&lt;/span&gt;
        &lt;span class="na"&gt;domEnvironment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;happy-dom&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By setting &lt;code&gt;environment: 'nuxt'&lt;/code&gt;, you're telling Vitest to use a custom environment that boots up a Nuxt instance, making all its magic available to your tests.&lt;/p&gt;

&lt;p&gt;Read more at &lt;a href="https://nuxt.com/docs/getting-started/testing#using-a-nuxt-runtime-environment" rel="noopener noreferrer"&gt;Official Nuxt Testing Doc&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Creating a Global Test Setup File
&lt;/h2&gt;

&lt;p&gt;To avoid repetitive mocking in every test file, it's best to create a global setup file. Vitest can be configured to run this file before your test suite starts. A common location for this is &lt;code&gt;test/setup.ts&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This file is the perfect place to mock global utilities, especially the &lt;code&gt;h3&lt;/code&gt; functions that power Nuxt's server routes (&lt;code&gt;defineEventHandler&lt;/code&gt;, &lt;code&gt;readBody&lt;/code&gt;, etc.).&lt;/p&gt;

&lt;p&gt;Here’s how you can create a reusable utility to mock &lt;code&gt;h3&lt;/code&gt; functions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// test/setup.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;H3Event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;EventHandlerRequest&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;h3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;vi&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vitest&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;H3Event&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;EventHandlerRequest&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;useH3TestUtils&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;h3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;vi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hoisted&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;defineEventHandler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;vi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="na"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Handler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;readBody&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;vi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;H3Event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_requestBody&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_requestBody&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&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;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_requestBody&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;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_requestBody&lt;/span&gt; &lt;span class="o"&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;getRouterParams&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;vi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="na"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;H3Event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;{}),&lt;/span&gt;
    &lt;span class="na"&gt;getQuery&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;vi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="na"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;H3Event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="o"&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;// Stub the global functions to support auto-imports in your tests&lt;/span&gt;
  &lt;span class="nx"&gt;vi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stubGlobal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;defineEventHandler&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;h3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;defineEventHandler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;vi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stubGlobal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;readBody&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;h3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;readBody&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;vi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stubGlobal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;getRouterParams&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;h3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getRouterParams&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;vi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stubGlobal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;getQuery&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;h3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getQuery&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;h3&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key Concepts:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;vi.hoisted()&lt;/code&gt;: This lifts the mock to the top of the module, ensuring that any subsequent imports of &lt;code&gt;h3&lt;/code&gt; receive our mocked version.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;vi.stubGlobal()&lt;/code&gt;: Since Nuxt relies on auto-imports, these functions are effectively global. This function replaces the global instance with our mock, making it available seamlessly in our test files.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  3. Mocking the H3Event Object
&lt;/h2&gt;

&lt;p&gt;Your server handlers receive an &lt;code&gt;event&lt;/code&gt; object that contains all the request details (body, params, query, headers). To test your handlers effectively, you need a way to create mock versions of this event.&lt;/p&gt;

&lt;p&gt;Let's create a helper function for this in &lt;code&gt;test/mocks/h3-event.ts&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// test/mocks/h3-event.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;H3Event&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;h3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;merge&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lodash&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;createMockH3Event&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;partialEvent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Partial&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;H3Event&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;H3Event&lt;/span&gt; &lt;span class="o"&gt;=&amp;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;event&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;node&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;req&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&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;content-type&lt;/span&gt;&lt;span class="dl"&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;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;partialEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
      &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;partialEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="o"&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;// Our mock readBody function will look for this property&lt;/span&gt;
    &lt;span class="na"&gt;_requestBody&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;partialEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;H3Event&lt;/span&gt;

  &lt;span class="c1"&gt;// Deeply merge the partial event to allow for overrides&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;partialEvent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;H3Event&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This powerful helper lets you easily simulate any request scenario. Need to test a &lt;code&gt;POST&lt;/code&gt; request with a specific body? Or a &lt;code&gt;GET&lt;/code&gt; request with query parameters? This function has you covered.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Writing Your First Server Route Test
&lt;/h2&gt;

&lt;p&gt;With the setup complete, you're ready to write a test. Let's use an example API route that calls an external service. We'll look at &lt;code&gt;test/server/api/test.post.test.ts&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// test/server/api/test.post.test.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;vi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;beforeEach&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vitest&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;mockNuxtImport&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@nuxt/test-utils/runtime&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="c1"&gt;// Import our test utilities&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useH3TestUtils&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;~/test/setup&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createMockH3Event&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;~/test/mocks/h3-event&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;defineEventHandler&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useH3TestUtils&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST /api/test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// 3. Dynamically import the handler *after* mocks are set up&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;~/server/api/test.post&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;is registered as an event handler&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;defineEventHandler&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toHaveBeenCalled&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;return success response for valid request&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;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;event&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createMockH3Event&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Simulate a valid request body&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;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mocked data&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  5. Mocking Built-in Composables
&lt;/h2&gt;

&lt;p&gt;In Nuxt 3, many server-side functionalities are encapsulated in composables. For example, &lt;code&gt;useRuntimeConfig&lt;/code&gt; is commonly used to access environment variables or configuration settings.&lt;br&gt;
To mock these composables, you can use &lt;code&gt;mockNuxtImport&lt;/code&gt; from &lt;code&gt;@nuxt/test-utils/runtime&lt;/code&gt;. This allows you to provide a mock implementation that your handler can use during tests.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// test/server/api/test.post.test.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;vi&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vitest&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;mockNuxtImport&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@nuxt/test-utils/runtime&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useRuntimeConfigMock&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;vi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hoisted&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;useRuntimeConfigMock&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;vi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;gemini&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;apiKey&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-api-key&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;})),&lt;/span&gt;
&lt;span class="p"&gt;}))&lt;/span&gt;
&lt;span class="nf"&gt;mockNuxtImport&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;useRuntimeConfig&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;useRuntimeConfigMock&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  6. Mocking Custom Functions
&lt;/h2&gt;

&lt;p&gt;If your server route relies on custom functions (like fetching data from an external API), you can mock these as well. This allows you to control the responses and test how your handler behaves under different scenarios.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// test/server/api/test.post.test.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;vi&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vitest&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="c1"&gt;// Mock custom functions&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;formatDate&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;vi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hoisted&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;formatDate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;vi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="p"&gt;}))&lt;/span&gt;

&lt;span class="nx"&gt;vi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;~/server/utils/datetime&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;formatDate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}))&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST /api/test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;formats date correctly&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;formatDate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mockReturnValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2023-10-01&lt;/span&gt;&lt;span class="dl"&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;event&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createMockH3Event&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2023-10-01T00:00:00Z&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;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;formattedDate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2023-10-01&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;formatDate&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toHaveBeenCalledWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2023-10-01T00:00:00Z&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Breakdown of the Test File:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Mock Composables&lt;/strong&gt;: Use &lt;code&gt;mockNuxtImport&lt;/code&gt; from &lt;code&gt;@nuxt/test-utils/runtime&lt;/code&gt; to provide a mock implementation for server-side composables like &lt;code&gt;useRuntimeConfig&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Initialize Utilities&lt;/strong&gt;: Call the test utility functions (&lt;code&gt;useH3TestUtils&lt;/code&gt;, etc.) at the top level to set up the global mocks.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Dynamic Import&lt;/strong&gt;: Import your API handler &lt;em&gt;inside&lt;/em&gt; the &lt;code&gt;describe&lt;/code&gt; block. This is critical to ensure that all your mocks are in place before the handler module is loaded.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Simulate Requests&lt;/strong&gt;: Use &lt;code&gt;createMockH3Event&lt;/code&gt; to craft the specific request context for each test case.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Assert&lt;/strong&gt;: Make assertions against the handler's return value and check if your mocked functions were called with the expected arguments.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Follow-up Steps
&lt;/h2&gt;

&lt;p&gt;In next article, we will explore how to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mock third-party libraries and services.&lt;/li&gt;
&lt;li&gt;Mock database connection&lt;/li&gt;
&lt;/ul&gt;




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

&lt;p&gt;By combining a global setup file, a flexible event factory, and targeted mocking, you can build a comprehensive and maintainable test suite for your Nuxt 3 server routes. This setup not only isolates your handlers for true unit testing but also provides a clear and repeatable pattern for testing all your server-side logic.&lt;/p&gt;

&lt;p&gt;Happy testing!&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://nuxt.com/docs/getting-started/testing" rel="noopener noreferrer"&gt;Nuxt 3 Testing Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/nuxt/test-utils/issues/531#issuecomment-2451021363" rel="noopener noreferrer"&gt;Github comment&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;&lt;a href="https://github.com/1337NamNori/nuxt-test-demo" rel="noopener noreferrer"&gt;Github Repository&lt;/a&gt;&lt;/p&gt;

</description>
      <category>nuxt</category>
      <category>vue</category>
      <category>testing</category>
      <category>vitest</category>
    </item>
    <item>
      <title>Wrapping PrimeVue Components: Tips for a Better Developer Experience</title>
      <dc:creator>Doan Trong Nam</dc:creator>
      <pubDate>Mon, 24 Mar 2025 04:19:13 +0000</pubDate>
      <link>https://forem.com/doantrongnam/wrapping-primevue-components-tips-for-a-better-developer-experience-2lja</link>
      <guid>https://forem.com/doantrongnam/wrapping-primevue-components-tips-for-a-better-developer-experience-2lja</guid>
      <description>&lt;p&gt;PrimeVue is a fantastic UI library for Vue.js developers, offering a rich set of components that are both customizable and easy to integrate. However, as your application grows, you might find yourself repeating the same configurations—like base styles, default props, or custom logic—across multiple instances of PrimeVue components. This is where wrapping PrimeVue components into your own custom components becomes a game-changer. In this blog post, I’ll walk you through why wrapping is beneficial, a common issue you might face with VSCode’s prop autocompletion, and how to fix it for a smoother development experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Wrap PrimeVue Components?
&lt;/h2&gt;

&lt;p&gt;When you use PrimeVue components directly in your templates, they work great out of the box. But in a real-world application, you often need more control and consistency. Here are a few reasons why wrapping PrimeVue components into your own custom components makes sense:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Centralized Base Styles
PrimeVue components come with their own default styles, but your app might require a consistent look and feel that aligns with your design system. By wrapping a component (e.g., &lt;code&gt;p-button&lt;/code&gt; from PrimeVue), you can apply custom classes or styles globally without repeating them in every template.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- Before wrapping --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;p-button&lt;/span&gt; &lt;span class="na"&gt;label=&lt;/span&gt;&lt;span class="s"&gt;"Click Me"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"my-custom-button"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- After wrapping --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;app-button&lt;/span&gt; &lt;span class="na"&gt;label=&lt;/span&gt;&lt;span class="s"&gt;"Click Me"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- ~/components/app/button.vue --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p-button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"my-custom-button"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Default Props for Consistency
If your app uses a PrimeVue component with certain props repeatedly (e.g., &lt;code&gt;size="small"&lt;/code&gt; or &lt;code&gt;severity="success"&lt;/code&gt; for a &lt;code&gt;p-button&lt;/code&gt;), you can set these as defaults in your wrapped component. This reduces boilerplate code and ensures consistency.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- ~/components/app/button.vue with default props --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p-button&lt;/span&gt; &lt;span class="na"&gt;rounded&lt;/span&gt; &lt;span class="na"&gt;size=&lt;/span&gt;&lt;span class="s"&gt;"large"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Custom Logic or Behavior
Wrapping allows you to add custom logic—like event handlers, computed properties, or validation—specific to your app’s needs, all while keeping the original PrimeVue component intact.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By wrapping PrimeVue components, you create reusable building blocks tailored to your project, making your codebase cleaner and more maintainable.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem: VSCode Stops Suggesting Props
&lt;/h2&gt;

&lt;p&gt;While wrapping is great, it introduces a small hiccup: VSCode’s autocompletion for props might stop working. When you use a PrimeVue component directly (e.g., &lt;code&gt;&amp;lt;p-button&amp;gt;&lt;/code&gt;), tools like Vue - Official Extension (Vue’s language support extension for VSCode) recognize it and suggest all available props (&lt;code&gt;label&lt;/code&gt;, &lt;code&gt;icon&lt;/code&gt;, &lt;code&gt;loading&lt;/code&gt;, etc.). But once you wrap it in &lt;code&gt;&amp;lt;app-button&amp;gt;&lt;/code&gt;, VSCode no longer knows what props the underlying Button component accepts because &lt;code&gt;app-button&lt;/code&gt; is just a custom component to the editor.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&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%2Fg3wxi4p48gp0v9hau42p.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%2Fg3wxi4p48gp0v9hau42p.png" alt="Editor not suggest label prop" width="800" height="190"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Typing label here won’t trigger prop suggestions, and you might need to manually refer to PrimeVue’s documentation. This slows down development and can be frustrating, especially for larger teams or complex components.&lt;/p&gt;

&lt;p&gt;The Solution: Add Prop Definitions&lt;br&gt;
To restore VSCode’s prop autocompletion, you need to tell Vue - Official Extension what props your wrapped component accepts. This can be done by defining the props in your custom component and leveraging TypeScript or Vue’s defineComponent for better tooling support. Here’s how:&lt;/p&gt;

&lt;p&gt;Step 1: Define Props in Your Wrapped Component&lt;br&gt;
Update app/button.vue to explicitly declare the props it accepts, matching the props of the underlying PrimeVue Button component.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- ~/components/app/button.vue --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p-button&lt;/span&gt; &lt;span class="na"&gt;v-bind=&lt;/span&gt;&lt;span class="s"&gt;"props"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"ts"&lt;/span&gt; &lt;span class="na"&gt;setup&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;import&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;ButtonProps&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;primevue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;AppButtonProps&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="cm"&gt;/* @vue-ignore */&lt;/span&gt; &lt;span class="nx"&gt;ButtonProps&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;props&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;defineProps&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;AppButtonProps&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step 2: Add slot if needed&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- ~/components/app/button.vue --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p-button&lt;/span&gt; &lt;span class="na"&gt;v-bind=&lt;/span&gt;&lt;span class="s"&gt;"props"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;template&lt;/span&gt; &lt;span class="na"&gt;#icon&lt;/span&gt; &lt;span class="na"&gt;v-if=&lt;/span&gt;&lt;span class="s"&gt;"$slots.icon"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;slot&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"icon"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;slot&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/p-button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"ts"&lt;/span&gt; &lt;span class="na"&gt;setup&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;import&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;ButtonProps&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;primevue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;AppButtonProps&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="cm"&gt;/* @vue-ignore */&lt;/span&gt; &lt;span class="nx"&gt;ButtonProps&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;props&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;defineProps&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;AppButtonProps&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step 3: Add default prop if needed by &lt;code&gt;withDefaults()&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- ~/components/app/button.vue --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p-button&lt;/span&gt; &lt;span class="na"&gt;v-bind=&lt;/span&gt;&lt;span class="s"&gt;"props"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;template&lt;/span&gt; &lt;span class="na"&gt;#icon&lt;/span&gt; &lt;span class="na"&gt;v-if=&lt;/span&gt;&lt;span class="s"&gt;"$slots.icon"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;slot&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"icon"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;slot&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/p-button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"ts"&lt;/span&gt; &lt;span class="na"&gt;setup&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;import&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;ButtonProps&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;primevue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;AppButtonProps&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="cm"&gt;/* @vue-ignore */&lt;/span&gt; &lt;span class="nx"&gt;ButtonProps&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;rounded&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;boolean&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;props&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;withDefaults&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;defineProps&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;AppButtonProps&lt;/span&gt;&lt;span class="o"&gt;&amp;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;rounded&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step 4: Verify Autocompletion&lt;br&gt;
With Vue - Official Extension installed in VSCode (ensure you have the Vue Official extension), try using  in a template:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;app-button&lt;/span&gt; &lt;span class="na"&gt;label=&lt;/span&gt;&lt;span class="s"&gt;"Click Me"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, when you type &lt;code&gt;&amp;lt;app-button&lt;/code&gt; , VSCode will suggest &lt;code&gt;label&lt;/code&gt;, &lt;code&gt;icon&lt;/code&gt;, &lt;code&gt;loading&lt;/code&gt;, ... just like it would for the original &lt;code&gt;&amp;lt;p-button&amp;gt;&lt;/code&gt; component.&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%2Fsa0abf8pleb4yng7johd.gif" 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%2Fsa0abf8pleb4yng7johd.gif" alt="Image description" width="760" height="255"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;Wrapping PrimeVue components is a powerful technique to enforce consistency, reduce repetition, and tailor components to your app’s needs. While it might break VSCode’s prop autocompletion initially, defining props explicitly (with or without TypeScript) brings it back and keeps your workflow smooth. Next time you’re building with PrimeVue, give wrapping a try—it’s a small investment that pays off in the long run!&lt;/p&gt;

&lt;p&gt;What’s your favorite PrimeVue component to customize? Let me know in the comments!&lt;/p&gt;

&lt;p&gt;This blog post is concise, practical, and follows your requested structure. You can tweak the examples or tone to match your style before posting it on Dev.to. Let me know if you’d like any adjustments!&lt;/p&gt;

&lt;p&gt;Example source code: &lt;a href="https://github.com/1337NamNori/primevue-wrap-tutorial" rel="noopener noreferrer"&gt;https://github.com/1337NamNori/primevue-wrap-tutorial&lt;/a&gt;&lt;/p&gt;

</description>
      <category>nuxt</category>
      <category>vue</category>
      <category>primevue</category>
      <category>programming</category>
    </item>
    <item>
      <title>How to Create a Duration-Based Progress Bar with PrimeVue ProgressBar</title>
      <dc:creator>Doan Trong Nam</dc:creator>
      <pubDate>Tue, 11 Feb 2025 07:11:29 +0000</pubDate>
      <link>https://forem.com/doantrongnam/how-to-create-a-duration-based-progress-bar-with-primevue-progressbar-2akk</link>
      <guid>https://forem.com/doantrongnam/how-to-create-a-duration-based-progress-bar-with-primevue-progressbar-2akk</guid>
      <description>&lt;p&gt;I used to struggle with creating a progress bar using PrimeVue's ProgressBar component that smoothly runs from 0% to 100% over a specified duration. However, after reading the PrimeVue documentation, I couldn't find any built-in option to achieve this. So, a bit of trickery is needed here.&lt;/p&gt;

&lt;p&gt;First, let's check out the documentation for this component: &lt;a href="https://primevue.org/progressbar/" rel="noopener noreferrer"&gt;PrimeVue ProgressBar&lt;/a&gt;&lt;br&gt;
We can see that we need to pass a prop called &lt;code&gt;:value&lt;/code&gt;, which is a number between 0 and 100 representing the progress percentage.&lt;/p&gt;

&lt;p&gt;Next, let's take a look at the &lt;a href="https://primevue.org/progressbar/#dynamic" rel="noopener noreferrer"&gt;Dynamnic&lt;/a&gt;&lt;br&gt;
section of the documentation.&lt;/p&gt;

&lt;p&gt;Here, we can see that whenever the value of the progress bar changes, it takes some time for the old value to transition to the new one. So, how long does this transition take?&lt;/p&gt;

&lt;p&gt;Let's inspect this element:&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%2F0ilvaum0a4foplvuobhx.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%2F0ilvaum0a4foplvuobhx.png" alt="Inspect ProgressBar's css" width="800" height="295"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Notice that there is a CSS property &lt;code&gt;transition: width 1s ease-in-out;&lt;/code&gt;. This means it takes 1 second for the progress bar to update its value. In other words, when changing the value from 0% to 100%, it takes 1 second to complete. So, the default duration for this component is 1 second.&lt;/p&gt;

&lt;p&gt;To customize this duration, we simply need to modify the &lt;code&gt;transition-duration&lt;/code&gt; property of the &lt;code&gt;progress-bar-value&lt;/code&gt; element inside the ProgressBar component. We can achieve this using PrimeVue's passthrough feature:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;ProgressBar&lt;/span&gt; &lt;span class="na"&gt;:value=&lt;/span&gt;&lt;span class="s"&gt;"value"&lt;/span&gt; &lt;span class="na"&gt;:pt:value:style=&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;{ 'transition-duration': '3s' }" /&amp;gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, simply update the value to see the effect.&lt;/p&gt;

&lt;h3&gt;
  
  
  Final Result:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;// ~/components/app/progress-bar.vue
&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p-progress-bar&lt;/span&gt;
    &lt;span class="na"&gt;v-bind=&lt;/span&gt;&lt;span class="s"&gt;"props"&lt;/span&gt;
    &lt;span class="na"&gt;:pt:value:style=&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;{ 'transition-duration': props.duration }"
  /&amp;gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt; &lt;span class="na"&gt;setup&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"ts"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;import&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;ProgressBarProps&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;primevue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// define prop type to get suggestion in VSCode&lt;/span&gt;
&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;AppProgressBarProps&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="cm"&gt;/* @vue-ignore */&lt;/span&gt; &lt;span class="nx"&gt;ProgressBarProps&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;string&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;props&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;withDefaults&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;defineProps&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;AppProgressBarProps&lt;/span&gt;&lt;span class="o"&gt;&amp;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;duration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;3s&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="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;StackBlitz: &lt;a href="https://stackblitz.com/edit/nuxt-starter-cdnmfmvv?file=components%2Fapp%2Fprogress-bar.vue" rel="noopener noreferrer"&gt;https://stackblitz.com/edit/nuxt-starter-cdnmfmvv?file=components%2Fapp%2Fprogress-bar.vue&lt;/a&gt;&lt;/p&gt;

</description>
      <category>vue</category>
      <category>primevue</category>
      <category>nuxt</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Is the ellipsis in your Japanese font centered in the line? Here is the solution.</title>
      <dc:creator>Doan Trong Nam</dc:creator>
      <pubDate>Tue, 18 Jun 2024 03:17:32 +0000</pubDate>
      <link>https://forem.com/doantrongnam/is-the-ellipsis-in-your-japanese-font-centered-in-the-line-here-is-the-solution-3pjl</link>
      <guid>https://forem.com/doantrongnam/is-the-ellipsis-in-your-japanese-font-centered-in-the-line-here-is-the-solution-3pjl</guid>
      <description>&lt;p&gt;Hey fellow software developers, have you ever worked with Japanese fonts (or other special fonts) and encountered the issue where the ellipsis in line breaks is centered? For example, as shown below:&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%2Fxffniso2ka8o7er44wsl.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%2Fxffniso2ka8o7er44wsl.png" alt="Image description" width="800" height="211"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://jsfiddle.net/doantrongnam/n170wx8L/1/" rel="noopener noreferrer"&gt;Code&lt;/a&gt;&lt;br&gt;
Here are 2 solutions for you:&lt;/p&gt;
&lt;h2&gt;
  
  
  1. Cut the string and add 3 dots.
&lt;/h2&gt;

&lt;p&gt;The interesting thing is that only the browser's ellipsis has this issue, while typing three dots manually doesn't cause any problems. Therefore, we can cut the string at a fixed number of characters and then add three dots. However, I don't recommend this approach. Its advantage is that it will definitely work. But the disadvantage is that the length of each character varies, so if we cut at the same number of characters, the items will have different lengths. This makes our webpage look unattractive. Moreover, if the technical requirement is to cut at a certain number of lines, it is difficult to determine the number of characters to cut to fit the number of lines. Not to mention other issues such as different devices, different screen widths, etc.&lt;/p&gt;
&lt;h2&gt;
  
  
  2. Replace the font of the ellipsis.
&lt;/h2&gt;

&lt;p&gt;Here is the solution&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="k"&gt;@font-face&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Font name, you can name it whatever you want&lt;/span&gt;
  &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'ellipsis-font'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// get the font locally, you can replace it with any font you want &lt;/span&gt;
  &lt;span class="nl"&gt;src&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;local&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Times New Roman'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// only override the ellipsis, here is the unicode for the ellipsis&lt;/span&gt;
  &lt;span class="na"&gt;unicode-range&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;U&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="m"&gt;2026&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.your-text-class&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'ellipsis-font'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="n"&gt;insert&lt;/span&gt; &lt;span class="n"&gt;your&lt;/span&gt; &lt;span class="nb"&gt;default&lt;/span&gt; &lt;span class="n"&gt;font-family&lt;/span&gt; &lt;span class="n"&gt;here&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;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%2Fvr9ambkjf7vzz9s44fjs.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%2Fvr9ambkjf7vzz9s44fjs.png" alt="Image description" width="800" height="211"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://jsfiddle.net/doantrongnam/8trbhdLp/4/" rel="noopener noreferrer"&gt;Code&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The advantage of this method is that the length is cut very flexibly, and you can combine it with -webkit-line-clamp to customize the number of lines. The disadvantage is that if the user's local machine doesn't have the font you used, the ellipsis will still appear centered in the line.&lt;/p&gt;

</description>
      <category>ellipsis</category>
      <category>css</category>
      <category>scss</category>
      <category>webdev</category>
    </item>
    <item>
      <title>As a Developer, Sometimes You Should Play the Role of a Tester</title>
      <dc:creator>Doan Trong Nam</dc:creator>
      <pubDate>Wed, 10 Apr 2024 04:36:29 +0000</pubDate>
      <link>https://forem.com/doantrongnam/as-a-developer-sometimes-you-should-play-the-role-of-a-tester-247h</link>
      <guid>https://forem.com/doantrongnam/as-a-developer-sometimes-you-should-play-the-role-of-a-tester-247h</guid>
      <description>&lt;p&gt;What is the equivalent of a Software Tester, Quality Analyst, or Quality Assurance Engineer, etc in your country? In Vietnam, we often shorten it to "tester".&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why do I say that developers should also play the role of a tester?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;"Playing the role of a tester" here doesn't just mean testing. Because obviously, after coding, testing is a must. But "playing the role" here means you have to approach the problem with the mindset of a tester.&lt;/p&gt;

&lt;p&gt;Recently, in the project team I am participating in, we have added a new step to the developer's workflow. We have to write test cases (which was already required before), record videos or take screenshots to prove that we have tested, and the tester will review the test cases before we submit pull requests for the leader to review. Some people may find it redundant and time-consuming, especially when coding is already tiring. However, I believe it is necessary.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Firstly, for the project, it helps to ensure better output quality.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Secondly, for individual developers, I find myself equipped with more skills and mindsets to develop myself.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That's when I realized the importance of playing the role of a tester. So how does a developer — someone always striving to build — embrace the mindset of a tester — someone always looking to break things?&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Drop the mindset of "Users wouldn't do that bullsh*t anyway."
&lt;/h2&gt;

&lt;p&gt;Most developers tend to have the mindset of "happy case first." This means that we always start by building from the successful scenarios of a feature, and only then consider exceptions such as validation, third-party errors, etc. Testers, on the other hand, always think from the perspective of error cases first. Whether it's entering negative numbers into a money input, entering 31/2 as a date, or even the most ridiculous scenarios that we might consider foolish.&lt;br&gt;
Most testers in my country are women. And I often joke that because women can come up with anything, that's why they work as testers =)))&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%2Flra69m2s3891drlif31g.jpg" 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%2Flra69m2s3891drlif31g.jpg" alt="Contrast" width="640" height="400"&gt;&lt;/a&gt;&lt;br&gt;
Due to this contrast, sometimes we overlook slightly daunting cases. Just imagine if you approached the problem like a tester, you could cover more edge cases and prevent them from turning into bigger issues. Moreover, it would also avoid unnecessary arguments between the two sides and preserve the harmony 😝&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Test all related parts, even if you're 100% sure you haven't touched the previous code
&lt;/h2&gt;

&lt;p&gt;Yeah, sometimes what you think is certain turns out to be uncertain. With the human factor involved, achieving 100% certainty becomes even more challenging. Who knows if you didn't accidentally modify a utility or export a function to be used elsewhere? Who knows if you checked which screens call the API you've modified?&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%2Fwqjm2qj9nlz1bs69zafy.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwqjm2qj9nlz1bs69zafy.jpeg" alt="War" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sometimes, you don't even trust yourself, so don't wonder why testers don't trust you =)) I've witnessed a colleague arguing with a tester for half an hour just because he was asked to test related parts. He insisted that he didn't change anything in that logic, so there was no need to test it. And he said if there was a bug in that part, it was because of the previous coder, not him. He was also right. But if he had known from the beginning that he needed to test related parts, it would have saved a lot of time arguing. And even if he found out that the bug wasn't caused by him, what's the harm in that??? It still improves the project.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. If possible, write test cases.
&lt;/h2&gt;

&lt;p&gt;If you have time, write test cases in Google Sheets or Excel like testers usually do. Self-testing is an indispensable process in software development. Imagine every time you fix a bug reported by the testing team, you need to self-test a complete business flow corresponding to the scope of your task. If you have written careful test cases beforehand, self-testing after each fix will be faster.&lt;br&gt;
A good test case file should include: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ID or sequence number&lt;/li&gt;
&lt;li&gt;Description&lt;/li&gt;
&lt;li&gt;Pre-condition&lt;/li&gt;
&lt;li&gt;Procedure&lt;/li&gt;
&lt;li&gt;Expected outcome&lt;/li&gt;
&lt;li&gt;Test result&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  4. Take some time to learn about security.
&lt;/h2&gt;

&lt;p&gt;As a developer, you don't need to have all the skills of a security expert. However, you need to grasp some basic techniques to protect your website.&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%2Foh9hfx9s7tkrchvw5tlj.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foh9hfx9s7tkrchvw5tlj.jpeg" alt="Peace" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sometimes testers are just doing their job. Just like us, they also contribute to making the final product as perfect as possible. But sometimes we think they are finding faults in us. No no, there's no personal attack here. The conflict between developers and testers is a perennial topic. If each side knows how to put themselves in the other's shoes and set aside their egos, then unnecessary conflicts can be avoided. Peace, I'm out.&lt;/p&gt;

</description>
      <category>testing</category>
      <category>webdev</category>
      <category>programming</category>
      <category>career</category>
    </item>
    <item>
      <title>Forget the default Terminal! Warp is here now.</title>
      <dc:creator>Doan Trong Nam</dc:creator>
      <pubDate>Fri, 22 Mar 2024 09:29:41 +0000</pubDate>
      <link>https://forem.com/doantrongnam/forget-the-default-terminal-warp-is-here-now-2ac6</link>
      <guid>https://forem.com/doantrongnam/forget-the-default-terminal-warp-is-here-now-2ac6</guid>
      <description>&lt;p&gt;If you're tired of customizing your terminal, then Warp is here to save you. It's a terminal with integrated utilities, themes, and especially AI. Yes, an AI-integrated terminal has emerged. &lt;br&gt;
Debuting in 2023, Warp impressed with its default interface being so beautiful without any customization needed. If you've ever had to install a theme for zsh by editing text files, you'll understand how annoying it can be. Warp allows you to change themes via the UI seamlessly.&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%2Fjw1we1bnj02xf2ov5iga.gif" 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%2Fjw1we1bnj02xf2ov5iga.gif" alt="Change theme" width="600" height="452"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Furthermore, have you ever had to Google how to install auto-complete and auto-suggest plugins for zsh? I know because I've been there too. But now, everything is available immediately when you install Warp. NO-ADDITIONAL-STEPS-REQUIRED. You can use the shortcut &lt;code&gt;Ctrl + R&lt;/code&gt; to open the command search popup.&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%2Fi3a37atkfo0c0dms9qgv.gif" 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%2Fi3a37atkfo0c0dms9qgv.gif" alt="Search command" width="600" height="452"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Furthermore, as I mentioned earlier, Warp integrates AI. You just need to type &lt;code&gt;#&lt;/code&gt; into the terminal, and Warp will activate AI. The Free package of Warp allows you to send 20 AI requests per day.&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%2Frzqg53b9ix68l812dbk1.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%2Frzqg53b9ix68l812dbk1.png" alt="WARP AI" width="800" height="157"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Personally, I find this to be a useful feature for me. Having to open Google or ChatGPT while working in the Terminal to find commands is quite an unpleasant developer experience and disrupts my flow. &lt;/p&gt;

&lt;p&gt;Warp is currently available only for MacOS and Linux. The developer has not announced when it will be released for Windows. Stay tuned to read the article about Warp on Windows as soon as it's released.&lt;/p&gt;

</description>
      <category>terminal</category>
      <category>ai</category>
      <category>productivity</category>
      <category>programming</category>
    </item>
    <item>
      <title>Do you know that 0.1 + 0.2 is not equal to 0.3?</title>
      <dc:creator>Doan Trong Nam</dc:creator>
      <pubDate>Wed, 20 Mar 2024 02:28:05 +0000</pubDate>
      <link>https://forem.com/doantrongnam/do-you-know-01-02-is-not-03-5e2o</link>
      <guid>https://forem.com/doantrongnam/do-you-know-01-02-is-not-03-5e2o</guid>
      <description>&lt;p&gt;Yes, this is a well-known issue in JavaScript (and many other programming languages) that use binary floating-point numbers. The problem arises from the fact that some numbers cannot be represented exactly in binary floating point, which leads to small rounding errors.&lt;br&gt;
Here's an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// Outputs: 0.30000000000000004&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Floating-point numbers are a way of representing real numbers in computer systems. They are used to approximate real numbers, but they can't always do it perfectly due to the way they are stored in memory.&lt;/p&gt;

&lt;p&gt;In many programming languages, including JavaScript, floating-point numbers are represented according to the IEEE 754 standard. This standard represents numbers in binary, which means they are expressed as a sum of powers of 2.&lt;/p&gt;

&lt;p&gt;While some numbers can be represented exactly in this system (like 0.5, which is 2^-1), others cannot. For example, the decimal number 0.1 cannot be represented exactly as a finite binary fraction. In binary, 1/10 is an infinitely repeating fraction.&lt;/p&gt;

&lt;p&gt;When you perform arithmetic operations with these approximated numbers, the small errors can accumulate, leading to unexpected results. For example, in JavaScript, the expression 0.1 + 0.2 doesn't exactly equal 0.3.&lt;/p&gt;

&lt;p&gt;This is not a flaw in the programming languages themselves, but a consequence of using binary floating-point numbers. To deal with this, programmers often use special libraries for high-precision arithmetic, or use techniques to compare floating-point numbers with a certain tolerance, rather than expecting them to be exactly equal.&lt;/p&gt;

&lt;p&gt;Yes, there are libraries in JavaScript that can help with precision issues related to floating point arithmetic. One of them is &lt;code&gt;decimal.js&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here's an example of how you can use it:&lt;/p&gt;

&lt;p&gt;First, install the library:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install decimal.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, use it in your 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;Decimal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;decimal.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Decimal&lt;/span&gt;&lt;span class="p"&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="nf"&gt;plus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Decimal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;  &lt;span class="c1"&gt;// Outputs: 0.3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This library allows for much more precise arithmetic than JavaScript's native Number type. Try &lt;a href="https://codesandbox.io/s/decimaljs-playground-m8knp" rel="noopener noreferrer"&gt;Playground&lt;/a&gt;&lt;/p&gt;

</description>
      <category>programming</category>
      <category>javascript</category>
      <category>beginners</category>
      <category>tip</category>
    </item>
    <item>
      <title>Powerful Date tricks in JavaScript that don't require any external libraries</title>
      <dc:creator>Doan Trong Nam</dc:creator>
      <pubDate>Tue, 19 Mar 2024 08:02:22 +0000</pubDate>
      <link>https://forem.com/doantrongnam/powerful-date-tricks-in-javascript-that-dont-require-any-external-libraries-3cm8</link>
      <guid>https://forem.com/doantrongnam/powerful-date-tricks-in-javascript-that-dont-require-any-external-libraries-3cm8</guid>
      <description>&lt;p&gt;Imagine you have a pet project, and its feature requires a few date-time calculation functions. However, it's too small, and you don't want to install additional dependencies. Or maybe your client doesn't want to use libraries due to security concerns or performance optimization. Or simply, you don't want to decide between moment.js, dayjs, or date-fns. Then this article is for you. Why not try plain JavaScript?&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Get the First and Last Day of the Month:
&lt;/h2&gt;

&lt;p&gt;You can get the first and last day of the month using &lt;code&gt;Date&lt;/code&gt; methods.&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;getFirstDayOfMonth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getFullYear&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getMonth&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="mi"&gt;1&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;getLastDayOfMonth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getFullYear&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getMonth&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. Calculate the Difference Between Two Dates:
&lt;/h2&gt;

&lt;p&gt;You can calculate the difference between two dates using subtraction. This will give you the difference in milliseconds, which you can convert to other units as needed.&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;date1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2024-03-11&lt;/span&gt;&lt;span class="dl"&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;date2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2024-01-01&lt;/span&gt;&lt;span class="dl"&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;differenceInMilliseconds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;date1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;date2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Formatting Dates:
&lt;/h2&gt;

&lt;p&gt;You can format dates in various ways using &lt;code&gt;Date&lt;/code&gt; methods.&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;date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&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;formattedDate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getDate&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getMonth&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getFullYear&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. Add/Subtract Days from a Date:
&lt;/h2&gt;

&lt;p&gt;You can add or subtract days from a date by manipulating the milliseconds.&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;date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&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;numberOfDaysToAdd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setDate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getDate&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;numberOfDaysToAdd&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  5. Check if a Year is a Leap Year:
&lt;/h2&gt;

&lt;p&gt;You can check if a year is a leap year by checking if it's divisible by 4, unless it's divisible by 100 but not 400.&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;function&lt;/span&gt; &lt;span class="nf"&gt;isLeapYear&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;year&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="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;year&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;year&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;year&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  6. Get Current Unix Timestamp:
&lt;/h2&gt;

&lt;p&gt;You can get the current Unix timestamp by using &lt;code&gt;Date.now()&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These are just a few examples of what you can do with dates in JavaScript without any external libraries. JavaScript's built-in Date object provides a lot of functionality for handling dates and times.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>programming</category>
      <category>date</category>
    </item>
    <item>
      <title>What I've Learned About Git from Senior Colleagues (Part 2 - thoughful commit)</title>
      <dc:creator>Doan Trong Nam</dc:creator>
      <pubDate>Fri, 15 Mar 2024 09:34:21 +0000</pubDate>
      <link>https://forem.com/doantrongnam/what-ive-learned-about-git-from-senior-colleagues-part-2-thoughful-commit-2j65</link>
      <guid>https://forem.com/doantrongnam/what-ive-learned-about-git-from-senior-colleagues-part-2-thoughful-commit-2j65</guid>
      <description>&lt;p&gt;Welcome back to part 2 of the series 'What I've Learned About Git from Senior Colleagues.' In this installment, I want to discuss the importance of writing good commit messages. 'But can't I just write anything?' Well, technically, you can commit with any message :) . But have you ever seen a commit history that looks like this?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;72ae8b3 Fixed bug
7f48a12 Refactor code
35b9f78 Update file
a9c0e17 Implement new feature
de97c5f Fixed a minor typo &lt;span class="k"&gt;in &lt;/span&gt;the README.md file, added descriptive comments to improve code readability &lt;span class="k"&gt;in &lt;/span&gt;main.py, and optimized database query performance by implementing caching
b3fd247 Completed task &lt;span class="c"&gt;#123&lt;/span&gt;
8a71e6f Fixed bug reported by QA team
f2a6b41 Merge branch &lt;span class="s1"&gt;'feature-branch'&lt;/span&gt; into &lt;span class="s1"&gt;'main'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;OK, 'fix bug' fixes which bug exactly? 'Refactor code' refactors which specific code section? 'Update file' updates which file? 'Implement new feature' is about what feature exactly? And do I really need to know every detail when you write 2983982374234 characters in a commit message?&lt;br&gt;
If it's a feature you developed, you might still remember it when you look back a few weeks later. But a few months later? Who knows. Not to mention, others might need to find your commit too. That's why writing meaningful commits is essential. I was fortunate that the first project I joined enforced strict rules for all commits. However, the purpose of this article is not to force you to follow my rules. Because each project may have different commit guidelines. But even in projects with no specific rules, you still need to write meaningful commit messages.&lt;br&gt;
At the end of this article, I'll share some rules from the projects I've been involved in. But for now, I'll highlight the core principles of writing meaningful commit messages.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The rules of a great Git commit message
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1.1. Don't make the subject too long
&lt;/h3&gt;

&lt;p&gt;Each project will have a specific limit for the commit subject, which could be 50, 70 characters, ... But even if the project doesn't specify, I think you should limit your commit subject to under 70 characters.&lt;br&gt;
Look at this. Do you want your project to look 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%2F8f0ncvkhxclhggfhobyv.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%2F8f0ncvkhxclhggfhobyv.png" alt="GitHub UI truncate commit message" width="800" height="58"&gt;&lt;/a&gt;&lt;br&gt;
The GitHub UI will truncate your message at 72 characters. Therefore, you need to summarize your code changes as briefly as possible. If your commit handles many changes and you cannot summarize them, you can try splitting that one commit into many smaller commits.&lt;/p&gt;

&lt;h3&gt;
  
  
  1.2. Capitalize the first letter of the subject.
&lt;/h3&gt;

&lt;p&gt;Try:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Update: logic authorize at post page&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;del&gt;update: logic authorize at post page&lt;/del&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  1.3. Don't end your commit subject with a period.
&lt;/h3&gt;

&lt;p&gt;The subject is just a title, and titles don't need periods.&lt;br&gt;
Try:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Update: logic authorize at post page&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;del&gt;Update: logic authorize at post page.&lt;/del&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are the core principles to write good commit messages. Some places recommend writing a commit body as well, but for me, that's optional. You can include it in the description of the pull request.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Some rules from the projects I've been involved in.
&lt;/h2&gt;

&lt;p&gt;I was fortunate that the first time I joined a real project, it enforced strict rules for commit messages. These could be suggestions for you on how to set rules for your own project.&lt;/p&gt;

&lt;h3&gt;
  
  
  2.1. [Action]: [FE/BE] / [Subject]
&lt;/h3&gt;

&lt;p&gt;Action could be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add:&lt;/li&gt;
&lt;li&gt;Update:&lt;/li&gt;
&lt;li&gt;Delete:&lt;/li&gt;
&lt;li&gt;Fix:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Add: BE / Add API to get post&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Update: FE / Modify button's style on login form&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2.2. [emoji] [Subject]
&lt;/h3&gt;

&lt;p&gt;You can see emoji list &lt;a href="https://gist.github.com/rxaviers/7360908" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;br&gt;
Some examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;:+1: Update API to get notifications&lt;/code&gt; -&amp;gt; GitHub UI will show &lt;code&gt;👍 Update API to get notifications&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;:up: Upgrade version DB&lt;/code&gt; -&amp;gt; GitHub UI will show &lt;code&gt;🆙 Upgrade version DB&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  3. Using AI to generate commit messages.
&lt;/h2&gt;

&lt;p&gt;VS Code has integrated the feature of using AI to generate commit messages based on your code changes. For example, if you have GitHub Copilot, you can go to the Version Control tab, click on the Copilot icon to see 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%2F4d86a55wfn5tdsg3z9y4.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%2F4d86a55wfn5tdsg3z9y4.png" alt="Generate commit message with GitHub Copilot" width="800" height="297"&gt;&lt;/a&gt;&lt;br&gt;
Additionally, you can use other AI services such as ChatGPT 3.5, ChatGPT 4.0, etc., via the GitLens extension. I will have another article that provides more detailed instructions. Stay tuned for more updates and follow along for the latest insights.&lt;/p&gt;

&lt;p&gt;In summary, writing meaningful commit messages is essential for effective version control. By following key principles like keeping messages concise and capitalizing the first letter, developers can enhance collaboration and project comprehension. Mastering this skill leads to clearer histories, smoother workflows, and improved professionalism. Happy coding!&lt;/p&gt;

&lt;p&gt;Reference:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/doantrongnam/what-ive-learned-about-git-from-senior-colleagues-part-1-git-stash-18e3"&gt;What I've Learned About Git from Senior Colleagues (Part 1 - git stash)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cbea.ms/git-commit/" rel="noopener noreferrer"&gt;How to Write a Git Commit Message&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>git</category>
      <category>commit</category>
      <category>github</category>
    </item>
    <item>
      <title>What I've Learned About Git from Senior Colleagues (Part 1 - git stash)</title>
      <dc:creator>Doan Trong Nam</dc:creator>
      <pubDate>Tue, 12 Mar 2024 06:40:38 +0000</pubDate>
      <link>https://forem.com/doantrongnam/what-ive-learned-about-git-from-senior-colleagues-part-1-git-stash-18e3</link>
      <guid>https://forem.com/doantrongnam/what-ive-learned-about-git-from-senior-colleagues-part-1-git-stash-18e3</guid>
      <description>&lt;h2&gt;
  
  
  Welcome to the Git Essentials Series!
&lt;/h2&gt;

&lt;p&gt;If you're an intern or fresher eager to bridge the gap between Git theory and real-world application, you're in the right place. In this series, I'll be sharing firsthand stories and insights gleaned from my journey, learning from seasoned colleagues since the beginning of my career. From navigating complex merge conflicts to mastering the art of efficient collaboration, each post will offer practical wisdom and valuable lessons learned along the way. Get ready to embark on this Git adventure with me, as we explore the depths of version control and unlock the secrets to seamless project management. Stay tuned for the first installment, where we'll get acquainted with &lt;code&gt;git stash&lt;/code&gt;.&lt;/p&gt;

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

&lt;h1&gt;
  
  
  1. My Story
&lt;/h1&gt;

&lt;p&gt;When I first started learning Git, I found it easy to apply basic commands like git add, git commit, git pull, git push, and so on. Perhaps you did too. I thought that knowing just these commands would be enough for working professionally, but reality proved to be more challenging. Once, my mentor wanted me to fix some comments on my pull request for feature A that I had submitted earlier. However, I was working on the branch for feature B. I couldn't commit these messy code changes. That's when he enlightened me about git stash.&lt;br&gt;
Let's get started! Remember that the examples in this blog use &lt;code&gt;git version 2.39.3&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;
  
  
  2. What is git stash? And how to use?
&lt;/h1&gt;

&lt;p&gt;When you run the command &lt;code&gt;git stash --help&lt;/code&gt;, the git documentation says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Use git stash when you want to record the current state of the working directory and the index, but want to go back to a clean working directory. The command saves your local modifications away and reverts the working directory to match the HEAD commit.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Still a bit hard to understand, huh? Uh huh? So, imagine your code changes are looking 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%2Fl40j53vvtnp8ei6lfc1v.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%2Fl40j53vvtnp8ei6lfc1v.png" alt="Before git stash save" width="798" height="580"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And after running git stash in the terminal, it looks 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%2Fgimpws3qfy37knii9y3e.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%2Fgimpws3qfy37knii9y3e.png" alt="After git stash save" width="798" height="580"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;WTF??? Did your code just disappear??? No! It's been saved in the stash, and your code has reverted to a clean state as if you haven't made any changes. So now, where can you find that piece of code you just had? Try running &lt;code&gt;git stash list&lt;/code&gt; in the terminal.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# output&lt;/span&gt;
stash@&lt;span class="o"&gt;{&lt;/span&gt;0&lt;span class="o"&gt;}&lt;/span&gt;: WIP on main: 8a278dc :+1: Refactor, add shortcut, README.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above output, &lt;code&gt;stash@{0}&lt;/code&gt;, where &lt;code&gt;0&lt;/code&gt; is the index of the stash. The closer the stash's time to the current time, the smaller the index. &lt;code&gt;WIP on main: 8a278dc :+1: Refactor, add shortcut, README.md&lt;/code&gt; is the message of the stash created by default, following the branch name and the message of the most recent commit.&lt;/p&gt;

&lt;p&gt;You can customize the stash message using the following syntax:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git stash save &lt;span class="o"&gt;{&lt;/span&gt;custom message&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="c"&gt;# Example&lt;/span&gt;
git stash save &lt;span class="s1"&gt;'Stash with message'&lt;/span&gt;
&lt;span class="c"&gt;# Then the stash list will look like...&lt;/span&gt;
stash@&lt;span class="o"&gt;{&lt;/span&gt;0&lt;span class="o"&gt;}&lt;/span&gt;: On main: Stash with message
stash@&lt;span class="o"&gt;{&lt;/span&gt;1&lt;span class="o"&gt;}&lt;/span&gt;: WIP on main: 8a278dc :+1: Refactor, add shortcut, README.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Yeah, now I can stash my messy code changes on the feature B branch, then switch back to fixing feature A, and later return to feature B. Now, if I want to retrieve the stashed code changes, what should I do?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git stash apply
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now your code has been restored&lt;br&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%2F5rplimnosmzco6qiiz56.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%2F5rplimnosmzco6qiiz56.png" alt="After git stash apply" width="798" height="580"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can pass the parameter as the index of the stash to specify the stashed code portion you want to restore.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git stash apply 1
&lt;span class="c"&gt;# or&lt;/span&gt;
git stash apply 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There's a note when you save a stash that untracked files won't be stashed. To stash them, you have two options: one is to add them to staged changes before stashing, and the other is to run &lt;code&gt;git stash save --include-untracked&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;By now, you've acquired a comprehensive understanding of the basic concepts and functionalities of git stash, equipping you to tackle a wide array of practical scenarios with confidence. As we transition to Chapter 3: "Some Other Useful Commands" rest assured that you've solidified your foundation in utilizing git stash effectively.&lt;/p&gt;

&lt;h1&gt;
  
  
  3. Some Other Useful Commands
&lt;/h1&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;git stash pop&lt;/code&gt;: This command is similar to git stash apply but also removes the most recent stash from the stash list after applying it.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;git stash drop&lt;/code&gt;: Use this command to remove a specific stash from the stash list.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;git stash clear&lt;/code&gt;: This command removes all stashes from the stash list, providing a clean slate.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you want to learn more, use the git stash --help command to see additional information. Always remember that help is the sacred command that helps you learn everything.&lt;/p&gt;

&lt;p&gt;In conclusion, mastering git stash is crucial for efficient version control management. With the basics covered, stay tuned for upcoming series where we'll delve into topics like git rebase, git merge, writing effective commit messages, and resolving conflicts seamlessly. These skills will further enhance your Git proficiency and streamline your development workflow. Keep exploring and happy coding!&lt;/p&gt;

&lt;p&gt;Reference: &lt;a href="https://git-scm.com/doc" rel="noopener noreferrer"&gt;Git Documentation&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>git</category>
      <category>productivity</category>
      <category>tip</category>
    </item>
    <item>
      <title>How I create my first Chrome extension with Github Copilot</title>
      <dc:creator>Doan Trong Nam</dc:creator>
      <pubDate>Wed, 06 Mar 2024 09:33:08 +0000</pubDate>
      <link>https://forem.com/doantrongnam/how-i-wrote-my-first-chrome-extension-with-github-copilot-bc7</link>
      <guid>https://forem.com/doantrongnam/how-i-wrote-my-first-chrome-extension-with-github-copilot-bc7</guid>
      <description>&lt;p&gt;My story is that I'm learning Japanese on a website called Dungmori.com, and I study new vocabulary on the Quizlet website. I encountered difficulties in copying vocabulary from Dungmori to Quizlet because I had to copy each word one by one. Previously, I used to copy sets of vocabulary created by other Quizlet users, then manually checked for any missing words compared to Dungmori and added them myself. This method consumed a lot of my time. Later, I discovered that Quizlet allows importing multiple words with various formatting options. For example, two words separated by a comma, tab, space, or two cards separated by line breaks \n, \r\n, ...&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%2Fi6h313z2dstadc80s5vs.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%2Fi6h313z2dstadc80s5vs.png" alt="Quizlet Set import UI" width="800" height="555"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Meanwhile, on Dungmori, they present new words using a standard &lt;code&gt;&amp;lt;table&amp;gt;&lt;/code&gt; tag, which allows me to easily use JavaScript to transform the text within this table into a text string for importing into Quizlet.&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%2F52x6na6gcus2wkgrq1zp.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%2F52x6na6gcus2wkgrq1zp.png" alt="Dungmori Vocab table" width="800" height="555"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But do I have to search for the code, open DevTools, and paste the code into the console tab every time I want to fetch vocabulary from this table? This question led me to consider creating an extension to install on the browser. However, I've never written an extension before, I only know HTML, CSS, and JavaScript. Perhaps GitHub Copilot can help me with that, right?&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%2Fxuwg8rbwtrffmzyd95d4.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxuwg8rbwtrffmzyd95d4.jpeg" alt="So f**king tired developer" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So, I began asking Copilot how to create an extension from scratch. The answer was that I needed to create a file called &lt;code&gt;manifest.json&lt;/code&gt; to store the important configurations of the extension, a file named &lt;code&gt;popup.html&lt;/code&gt; to design the interface for the extension, another file called &lt;code&gt;popup.js&lt;/code&gt; to write the logic, and a file named &lt;code&gt;content.js&lt;/code&gt; to interact with the DOM elements of the website. Copilot also guided me on how to import a code folder into the browser by enabling Developer mode in the extension settings, then selecting the code folder and reloading the browser.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;manifest.json&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"manifest_version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"My First Chrome Extension"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"A simple Chrome extension"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"browser_action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"default_icon"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"icon.png"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"default_popup"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"popup.html"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"permissions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"activeTab"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"content_scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"matches"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;all_urls&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"js"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"content.js"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- popup.html --&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Hello, this is my first Chrome extension!&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"popup.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I started writing the logic into the &lt;code&gt;content.js&lt;/code&gt; file to convert the text in the table into a string that can be imported into Quizlet and then copied to the clipboard. I won't delve much into this part because it involves basic DOM operations. Afterward, I ran a test, and voila, the logic ran as soon as the web page loaded. However, I wanted to trigger it by clicking a button in the extension popup. That way, it would require more steps to use, but it would be cooler &lt;code&gt;¯_(ツ)_/¯&lt;/code&gt;. Why not?&lt;br&gt;
Then, I turned to Copilot again and received the answer that this configuration is in the &lt;code&gt;manifest.json&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"content_scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"matches"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;all_urls&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"js"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"content.js"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The solution? Delete that section of the config, and manually trigger it using the &lt;code&gt;chrome.scripting.executeScript()&lt;/code&gt; method&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tabs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;active&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;currentWindow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; 
    &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tabs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scripting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;executeScript&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;tabId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;tabs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="na"&gt;files&lt;/span&gt;&lt;span class="p"&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;content.js&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="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Injected the content script.&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;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&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;Yeah, and now the &lt;code&gt;content.js&lt;/code&gt; file can be triggered when I click the button in the extension popup, but the copy to clipboard function isn't working as before. Once again, the almighty Copilot came to my rescue, providing the answer that:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The error "Document is not focused" occurs because the &lt;code&gt;document of&lt;/code&gt; the background page or popup loses focus when you switch to the content page, and the Clipboard API requires the document to be focused to write to the clipboard.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And the solution is that I need to send the text that needs to be copied from the &lt;code&gt;content.js&lt;/code&gt; file to the &lt;code&gt;popup.js&lt;/code&gt; file using a method that I'm not sure about, but the idea seems similar to a pub/sub mechanism.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// content.js&lt;/span&gt;
&lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendMessage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;copy&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;normalizedRows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)});&lt;/span&gt;

&lt;span class="c1"&gt;// background.js or popup.js&lt;/span&gt;
&lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onMessage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sendResponse&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;copy&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="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clipboard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Text copied to clipboard&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;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Could not copy text: &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;BOOOOM! THE RESULT IS HERE!&lt;br&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%2Fkvch2uz462q1kyvbqata.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%2Fkvch2uz462q1kyvbqata.png" alt="Result" width="630" height="602"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, I can copy vocabulary from Dungmori into the clipboard with just one click. Thanks to Copilot, it took me less than an hour to create this extension. Imagine if I had to search Google to do this; I would have to open a billion tabs every time an issue arises. But with Copilot, I just stay put in my VS Code window. Previously, I tried with ChatGPT and Gemini, but the output didn't come close to 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%2Fujd10ozp2r0qbf857vv6.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fujd10ozp2r0qbf857vv6.jpeg" alt="100000000000000 tabs" width="800" height="800"&gt;&lt;/a&gt;&lt;br&gt;
Above is not some high-tech tech sharing article; it's just my simple story and fun experience with Copilot. Also, thanks to Dall-E for helping me create illustrations, and ChatGPT for making my English translations sound less like a robot. Peace, I'm out.&lt;/p&gt;

&lt;p&gt;GitLab Repository: &lt;a href="https://gitlab.com/1337NamNori/copy-vocab-extension" rel="noopener noreferrer"&gt;https://gitlab.com/1337NamNori/copy-vocab-extension&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>extensions</category>
      <category>githubcopilot</category>
    </item>
  </channel>
</rss>
