<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>decaster.dev</title><description>Vincent De Caster, software engineer in Texas. Writing on building software and leading engineering teams.</description><link>https://decaster.dev/</link><language>en-us</language><item><title>How to kill a port that is in use</title><link>https://decaster.dev/how-to-kill-port-in-use/</link><guid isPermaLink="true">https://decaster.dev/how-to-kill-port-in-use/</guid><description>Find out how to handle EADDRINUSE in less than 30 sec</description><pubDate>Sat, 20 Apr 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;We&apos;ve all seen the dreaded &lt;code&gt;EADDRINUSE&lt;/code&gt; error. No worries though, this is super easy to fix.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
 code: &apos;EADDRINUSE&apos;,
 errno: -48,
 syscall: &apos;listen&apos;,
 address: &apos;::&apos;,
 port: 8080
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are a few ways to fix this:&lt;/p&gt;
&lt;h2&gt;tl;dr&lt;/h2&gt;
&lt;p&gt;‘npx kill-port’ is my goto, it is fast, easy to remember and does what you want it to on both all operating systems.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npx kill-port 8080
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But let’s go over all the other available options in case kill port does not work for you.&lt;/p&gt;
&lt;h2&gt;Unix-based systems (Linux, macOS)&lt;/h2&gt;
&lt;h3&gt;lsof and kill&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;lsof -ti:8080 | xargs kill
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;fuser&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;fuser -k 8080/tcp
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;netstat and kill&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;sudo netstat -ltnp | grep &apos;:8080&apos; | awk &apos;{print $7}&apos; | sed &apos;s/\/.\*//&apos; | xargs kill
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Windows&lt;/h2&gt;
&lt;h3&gt;netstat and taskkill&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;netstat -aon | findstr :8080
taskkill /F /PID &amp;lt;PID&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Using PowerShell&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;Get-Process -Id (Get-NetTCPConnection -LocalPort 8080).OwningProcess | Stop-Process -Force
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Cross-platform tools&lt;/h2&gt;
&lt;h3&gt;npx kill-port:&lt;/h3&gt;
&lt;p&gt;As mentioned before, this is a straightforward method that works across different operating systems.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npx kill-port 8080
&lt;/code&gt;&lt;/pre&gt;
</content:encoded><category>node</category><category>cli</category><category>terminal</category></item><item><title>Using Plausible Analytics in Gatsby.js or Astro.js</title><link>https://decaster.dev/using-plausible-analytics-in-gatsby-js-or-astro-js/</link><guid isPermaLink="true">https://decaster.dev/using-plausible-analytics-in-gatsby-js-or-astro-js/</guid><description>Plausible is a privacy-first alternative to Google Analytics that can be set up in less than 5 min</description><pubDate>Fri, 02 Jun 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;What is Plausible Analytics?&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;Plausible is intuitive, lightweight and open source web analytics. No cookies and fully compliant with GDPR, CCPA and PECR.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;It is an open source project dedicated to making web analytics more privacy-friendly.&lt;/p&gt;
&lt;p&gt;Some of the core features that makes it more privacy-first over Google Analytics are&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;No cookies&lt;/li&gt;
&lt;li&gt;No tracking and collecting of personal data&lt;/li&gt;
&lt;li&gt;Minimized data collection &amp;amp; secure storage&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Other
than these benefits it is also just super easy to set up (unless you
want to use a proxy, that requires a little more work).&lt;/p&gt;
&lt;p&gt;Their
pricing is also fair, ranging from $9/mo for 10k pageviews to $169/mo
for 10m pageviews. Though they do have the option to self-host, you can
find the guide to self-host here: https://plausible.io/docs/self-hosting&lt;/p&gt;
&lt;p&gt;In
a nutshell, if you want to move away from Google Analytics and/or want
more privacy oriented tracking, Plausible definitely is a good choice!&lt;/p&gt;
&lt;h2&gt;Alternatives to Plausible&lt;/h2&gt;
&lt;p&gt;Plausible isn’t the only privacy-first analytics service, here are some other ones:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://usefathom.com/&quot;&gt;Fathom&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.simpleanalytics.com/&quot;&gt;Simple analytics&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://matomo.org/&quot;&gt;Matomo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Of
these three, I have used Fathom in the past, while Fathoms design is
amazing and it was a breeze to set up, they lack one major thing in my
opinion. And that is the option to self host or use a proxy. But I’ll
talk a little more about that later.&lt;/p&gt;
&lt;h2&gt;How to Set Up Plausible Analytics&lt;/h2&gt;
&lt;p&gt;To set up Plausible analytics all frameworks will use the same script&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;script
  defer
  data-domain=&quot;yourdomain.com&quot;
  src=&quot;https://plausible.io/js/script.js&quot;
&amp;gt;&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The only difference really is where to put them.&lt;/p&gt;
&lt;h3&gt;Gatsby.js&lt;/h3&gt;
&lt;p&gt;Add the script to your &lt;strong&gt;html.tsx&lt;/strong&gt; file&lt;/p&gt;
&lt;h3&gt;Astro&lt;/h3&gt;
&lt;p&gt;I like to work with a &lt;strong&gt;BaseLayout.astro&lt;/strong&gt; that wraps around pages. This file contains all SEO properties etc. We will add the script here&lt;/p&gt;
&lt;h2&gt;Working with Adblockers&lt;/h2&gt;
&lt;p&gt;To
be honest, this is something I only noticed a few weeks ago, when a
user uses an adblocker, chances are that your analytics are blocked as
well. Why do they block them? Well some blocklist maintainers have taken
a stance against these forms of analytics too.&lt;/p&gt;
&lt;p&gt;Luckily with Plausible there is a simple workaround for this: using a proxy.&lt;/p&gt;
&lt;p&gt;I’ll cover how to use a proxy with &lt;a href=&quot;https://vercel.com/&quot;&gt;Vercel&lt;/a&gt; and &lt;a href=&quot;https://www.netlify.com/&quot;&gt;Netlify&lt;/a&gt; in this post.&lt;/p&gt;
&lt;h3&gt;Vercel&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Create a &lt;strong&gt;Vercel.json&lt;/strong&gt; file at the root of your repo (same for a monorepo)&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;rewrites&quot;: [
    {
      &quot;source&quot;: &quot;/js/script.js&quot;,
      &quot;destination&quot;: &quot;https://plausible.io/js/script.js&quot;
    },
    {
      &quot;source&quot;: &quot;/api/event&quot;,
      &quot;destination&quot;: &quot;https://plausible.io/api/event&quot;
    }
  ]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;update the script tag to use &lt;strong&gt;src=&quot;/js/script.js&quot; data-api=&quot;/api/event&quot;&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Netlify&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Create an &lt;strong&gt;_redirects&lt;/strong&gt; file in the root of your project (in a monorepo this will be your apps/app folder)&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;/js/script.js https://plausible.io/js/script.outbound-links.js 200
/api/event https://plausible.io/api/event 200
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;Copy this _redirects file to the dist folder after the Netlify build is
done, one of the ways you could do this is by updating your build
command to&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;astro build &amp;amp;&amp;amp; cp _redirects dist/_redirects
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;Update your script tag to use src=&quot;/js/script.js&quot;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Setting up Plausible analytics is easy and straightforward, it gives you all the tools you need to get privacy-first tracking.&lt;/p&gt;
&lt;p&gt;Hit me up on Twitter at &lt;a href=&quot;https://twitter.com/vdecaster&quot;&gt;@vdecaster&lt;/a&gt; if you have any questions!&lt;/p&gt;
</content:encoded><category>analytics</category><category>astro</category><category>gatsby</category></item><item><title>Using GitHub Actions vs Jenkins</title><link>https://decaster.dev/github-actions-vs-jenkins/</link><guid isPermaLink="true">https://decaster.dev/github-actions-vs-jenkins/</guid><description>Learn what the benefits of Github Actions are over something like Jenkins</description><pubDate>Thu, 22 Sep 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Recently I had to go through the process
setting up a new CI/CD pipeline at work, and because the current
workflow in our monorepo heavily depends on GitHub actions, I decided to
gather some of the benefits of using &lt;a href=&quot;https://docs.github.com/en/actions/learn-github-actions/understanding-github-actions&quot;&gt;GitHub Actions (GHA)&lt;/a&gt; over Jenkins.&lt;/p&gt;
&lt;p&gt;Let me know on &lt;a href=&quot;https://twitter.com/vdecaster&quot;&gt;X&lt;/a&gt; if you would like to see more in depth practical examples of using GitHub Actions in a large monorepo.&lt;/p&gt;
&lt;p&gt;Without further ado, enjoy my brief document on GitHub Actions, hopefully this helps you out.&lt;/p&gt;
&lt;h2&gt;Seamless Integration &amp;amp; Ease of Use&lt;/h2&gt;
&lt;p&gt;GHA
seamlessly integrates with the GitHub ecosystem, offering unparalleled
ease of use. Conversely, Jenkins, an open-source powerhouse, demands
separate infrastructure setup and maintenance. The installation,
configuration, and management of Jenkins are complex tasks, requiring
significant time and effort. Additionally, Jenkins&apos; knowledge tends to
be siloed due to its complexity, in contrast to GHA&apos;s user-friendly
maintenance and setup. GHA is built directly into GitHub, negating the
need for separate infrastructure.&lt;/p&gt;
&lt;h2&gt;Self hosted runners&lt;/h2&gt;
&lt;p&gt;Both GitHub Actions and Jenkins provide options for self-hosted runners, but one distinct advantage of &lt;a href=&quot;https://docs.github.com/en/actions/hosting-your-own-runners/managing-self-hosted-runners/about-self-hosted-runners&quot;&gt;GHA self-hosted runners&lt;/a&gt; is that we can easily &lt;a href=&quot;https://docs.github.com/en/actions/hosting-your-own-runners/managing-self-hosted-runners/using-self-hosted-runners-in-a-workflow#using-default-labels-to-route-jobs&quot;&gt;pick and choose when to use a self-hosted runner&lt;/a&gt; with a single line. This allows easy access to internally hosted services and sensitive data when a workflow run needs it.&lt;/p&gt;
&lt;h2&gt;Developer Productivity and Workflow Flexibility&lt;/h2&gt;
&lt;p&gt;GHA provides flexibility by allowing actions to trigger on a &lt;a href=&quot;https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows&quot;&gt;variety of events&lt;/a&gt;,
such as push or pull request updates. This allows teams to customize
their workflows according to their unique needs. For example, a &apos;push&apos;
event may trigger a full CI/CD pipeline, whereas a &apos;pull request&apos; update
could trigger unit tests. Jenkins also supports event-driven triggers
but lacks the direct integration with GitHub repositories to provide the
same level of granular control and responsiveness.&lt;/p&gt;
&lt;h2&gt;Contextual Understanding&lt;/h2&gt;
&lt;p&gt;Another advantage of GHA is that it operates within the &lt;a href=&quot;https://docs.github.com/en/actions/learn-github-actions/contexts&quot;&gt;GitHub context&lt;/a&gt;. This provides extensive metadata about the triggering event and the GitHub environment. Developers can get &lt;a href=&quot;https://docs.github.com/en/webhooks-and-events/webhooks/webhook-events-and-payloads#pull_request&quot;&gt;information about the PR&lt;/a&gt; it was triggered from, labels, &lt;a href=&quot;https://github.com/actions/checkout#fetch-all-history-for-all-tags-and-branches&quot;&gt;full commit history&lt;/a&gt;
and more. This empowers developers to create detailed, conditional
workflows. Jenkins, despite being robust and extensible, lacks this
innate contextual understanding and cannot provide the same level of
fine-tuned control out of the box.&lt;/p&gt;
&lt;h2&gt;Security&lt;/h2&gt;
&lt;p&gt;GHA offers a robust security model, with &lt;a href=&quot;https://docs.github.com/en/actions/security-guides/encrypted-secrets&quot;&gt;secrets&lt;/a&gt; stored encrypted and available to workflows running in the same repository.&lt;/p&gt;
&lt;h2&gt;Scripting language flexibility&lt;/h2&gt;
&lt;p&gt;GHA
supports a variety of scripting languages out of the box, this gives
developers the freedom to write their automation scripts in the language
they are most comfortable with.&lt;/p&gt;
&lt;h2&gt;Efficient Caching&lt;/h2&gt;
&lt;p&gt;GHA
provides advanced caching capabilities, which can significantly speed
up builds. This is particularly beneficial for mono-repos where you
might need to build multiple packages per PR.&lt;/p&gt;
&lt;h2&gt;Fetch Depth Control&lt;/h2&gt;
&lt;p&gt;One
major advantage of using GHA is that developers can control the fetch
depth of the git checkout operation on a per-job basis. This means that
if you don’t require the entire git history, a shallow clone can be used
to speed up the process. This level of control is harder to achieve in
Jenkins&lt;/p&gt;
&lt;h2&gt;Configuration as Code&lt;/h2&gt;
&lt;p&gt;With GHA, all
the configuration details are stored within the repository rather than
in a GUI, this promotes better version control, audit trails and
collaboration.&lt;/p&gt;
&lt;h2&gt;Collaboration&lt;/h2&gt;
&lt;p&gt;Jenkins&apos;
complexity often limits the knowledge circle to a few engineers, making
it challenging for other developers to step in and make modifications.
In contrast, GHA&apos;s simplicity promotes wider developer participation in
setting up and maintaining a pipeline.&lt;/p&gt;
</content:encoded><category>ci-cd</category><category>github-actions</category><category>devops</category></item><item><title>How to change gatbsy favicon</title><link>https://decaster.dev/how-to-change-gatsby-favicon/</link><guid isPermaLink="true">https://decaster.dev/how-to-change-gatsby-favicon/</guid><description>Learn how to change the Gatsby favicon </description><pubDate>Thu, 22 Sep 2022 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Initial problem&lt;/h2&gt;
&lt;p&gt;You&apos;d think that I would remember to change that after looking at the gatsby logo on my tab. Well actually I didn&apos;t see any favicon on the tab during any of the development 🤔&lt;/p&gt;
&lt;p&gt;So went to check out what went wrong and here are my findings:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;by default there should be a &lt;code&gt;favicon.ico&lt;/code&gt; in the &lt;code&gt;/public/static&lt;/code&gt; folder. This was not the case. More on that in the next point&lt;/li&gt;
&lt;li&gt;I found a couple of posts mentioning that if you have the &lt;code&gt;gatsby-plugin-sharp&lt;/code&gt; plugin installed (I had that one by default) then you should be seeing an icon in &lt;code&gt;/src/images&lt;/code&gt;. That was not the case, nor was this plugin set up for that.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Okay so we have a good clue, now let&apos;s update that plugin&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; {
    resolve: &quot;gatsby-plugin-sharp&quot;,
    options: {
      icon: &quot;./src/images/icon.png&quot;,
    },
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Alas, no luck. This did not fix my problem. Time to dig somewhat deeper. After looking into it a little more, I found that the documentation on &lt;a href=&quot;https://www.gatsbyjs.com/plugins/gatsby-plugin-sharp/&quot;&gt;gatsby-plugin-sharp&lt;/a&gt; didn&apos;t actually have anything about &lt;code&gt;icon&lt;/code&gt;. That led me to believe this was outdated.&lt;/p&gt;
&lt;h2&gt;Solution&lt;/h2&gt;
&lt;p&gt;After looking a little more, I found a plugin called &lt;a href=&quot;https://www.gatsbyjs.com/plugins/gatsby-plugin-manifest/&quot;&gt;gatsby-plugin-manifest&lt;/a&gt;. Finally, an answer to my problem&lt;/p&gt;
&lt;h3&gt;How to use&lt;/h3&gt;
&lt;p&gt;first off, let&apos;s install it:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npm install gatsby-plugin-manifest --save
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now add the plugin and required manifest settings in &lt;code&gt;gatsby-config.js&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;module.exports = {
  plugins: [
    {
      resolve: `gatsby-plugin-manifest`,
      options: {
        name: `The Uncommon Byte`,
        short_name: `Uncommon Byte`,
        start_url: `/`,
        background_color: `#FFF`,
        theme_color: `#FBEDE0`,
        display: `standalone`,
      },
    },
  ],
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Cool 😎 now that we have all the formalities out of the way, let&apos;s dive into adding our favicon!&lt;/p&gt;
&lt;p&gt;I usually use something like &lt;a href=&quot;https://favicon.io/&quot;&gt;favicon.io&lt;/a&gt; to generate all my icons from one image. The fun thing with the manifest plugin is that you only need these 3 items that are generated.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;android-chrome-192x192.png&lt;/li&gt;
&lt;li&gt;android-chrome-512x512.png&lt;/li&gt;
&lt;li&gt;favicon-32x32.png ( I renamed this to favicon.png)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now let&apos;s add these in our manifest plugin. Inside the &lt;code&gt;options&lt;/code&gt; object, add the following code&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;icon: `src/images/favicon.png`,
icons: [
  {
    src: `/favicons/android-chrome-192x192.png`,
    sizes: `192x192`,
    type: `image/png`,
  },
  {
    src: `/favicons/android-chrome-512x512.png`,
    sizes: `512x512`,
    type: `image/png`,
  },
],
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once you added this, run &lt;code&gt;npm run develop&lt;/code&gt; again and you should be seeing your icon 🎉&lt;/p&gt;
&lt;h3&gt;Legacy&lt;/h3&gt;
&lt;p&gt;traditionally you would have to add this to your head&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;link rel=&quot;apple-touch-icon&quot; sizes=&quot;180x180&quot; href=&quot;/apple-touch-icon.png&quot; /&amp;gt;
&amp;lt;link rel=&quot;icon&quot; type=&quot;image/png&quot; sizes=&quot;32x32&quot; href=&quot;/favicon-32x32.png&quot; /&amp;gt;
&amp;lt;link rel=&quot;icon&quot; type=&quot;image/png&quot; sizes=&quot;16x16&quot; href=&quot;/favicon-16x16.png&quot; /&amp;gt;
&amp;lt;link rel=&quot;manifest&quot; href=&quot;/site.webmanifest&quot; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;No worries though, the plugin takes care of everything for you. In iOS 11.3 apple even added support for the web app manifest specification.&lt;/p&gt;
&lt;p&gt;If you do not want these legacy icons to be generated you can always disable them by setting the &lt;code&gt;legacy&lt;/code&gt; option to &lt;code&gt;false&lt;/code&gt; in the plugin options.&lt;/p&gt;
&lt;h2&gt;TL;DR&lt;/h2&gt;
&lt;p&gt;Alright, time to wrap this up, we&apos;ve basically learned that favicons might not be enabled by default with gatsby (probably also depends on how you initialized, with a template etc). And that the &lt;a href=&quot;https://www.gatsbyjs.com/plugins/gatsby-plugin-manifest/&quot;&gt;gatsby-plugin-manifest&lt;/a&gt; is a must have for all your manifest needs (including favicon!)&lt;/p&gt;
</content:encoded><category>gatsby</category><category>favicon</category></item><item><title>How to find a specific object key in jest</title><link>https://decaster.dev/how-to-find-a-specific-object-key-in-jest/</link><guid isPermaLink="true">https://decaster.dev/how-to-find-a-specific-object-key-in-jest/</guid><description>We’ve all been there, writing a jest test and having that one case where we want to have a very specific key in an object.</description><pubDate>Sat, 03 Sep 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;We’ve all been there, writing a jest test and having that one case where we want to have a very specific key in an object.&lt;/p&gt;
&lt;p&gt;our first attempt might be&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;it(&quot;should redirect to correct url if id is incorrect&quot;, () =&amp;gt; {
  const mockFn = jest.fn()

  mockRouter.query = {
    id: &quot;id2&quot;,
  }
  mockRouter.push = mockFn

  render(&amp;lt;Component&amp;gt;I am visible&amp;lt;/Component&amp;gt;)

  expect(mockFn).toHaveBeenCalledWith({
    query: { id: &quot;id1&quot; },
  })
})

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will throw us an error like this&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Error: expect(jest.fn()) .toHaveBeenCalledWith(...expected)

&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Solution&lt;/h2&gt;
&lt;p&gt;Since Jest 18 we are able to use objectContaining . This makes it super easy to check for 1 or more specific keys:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;it(&quot;should redirect to correct url if id is incorrect&quot;, () =&amp;gt; {
  const mockFn = jest.fn()

  mockRouter.query = {
    id: &quot;id2&quot;,
  }
  mockRouter.push = mockFn

  render(&amp;lt;Component&amp;gt;I am visible&amp;lt;/Component&amp;gt;)

  expect(mockFn).toHaveBeenCalledWith(
    expect.objectContaining({
      query: { id: &quot;id1&quot; },
    })
  )
})

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;this test will pass.&lt;/p&gt;
&lt;h2&gt;Alternative solutions&lt;/h2&gt;
&lt;p&gt;in the case that you only have a few keys you could also use expect.anything()&lt;/p&gt;
&lt;p&gt;this would be used like so:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;expect(mockFn).toHaveBeenCalledWith({
   query: { id: &quot;id1&quot; },
   key: expect.anything(),
   key2: expect.anything()
})

&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;there are 2 options to find a specific key in objects. objectContaining and expect.anything()&lt;/p&gt;
</content:encoded><category>jest</category><category>testing</category><category>javascript</category></item></channel></rss>