<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Jen C.</title>
    <description>The latest articles on DEV Community by Jen C. (@jenchen).</description>
    <link>https://dev.to/jenchen</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%2F1095199%2Ff14eb943-e036-49ea-b10c-87b5a191351b.JPG</url>
      <title>DEV Community: Jen C.</title>
      <link>https://dev.to/jenchen</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jenchen"/>
    <language>en</language>
    <item>
      <title>Security - Solving the "Content Security Policy (CSP) Header Not Set": style-src</title>
      <dc:creator>Jen C.</dc:creator>
      <pubDate>Thu, 19 Jun 2025 07:38:22 +0000</pubDate>
      <link>https://dev.to/jenchen/security-solving-the-content-security-policy-csp-header-not-set-style-src-42m6</link>
      <guid>https://dev.to/jenchen/security-solving-the-content-security-policy-csp-header-not-set-style-src-42m6</guid>
      <description>&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy/default-src" rel="noopener noreferrer"&gt;Content-Security-Policy: default-src directive&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy/style-src" rel="noopener noreferrer"&gt;Content-Security-Policy: style-src directive&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/style" rel="noopener noreferrer"&gt;HTMLElement: style property&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;Inspect the codebase for all current usages, and based on the findings, either adjust the value of &lt;code&gt;style-src&lt;/code&gt; accordingly or update the codebase to a more secure implementation, then update the value of &lt;code&gt;style-src&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Style usage in the project
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Inline style property
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...

&amp;lt;div
    className={background.className}
    style={{
        backgroundImage: `url(${background.url})`,
    }}
/&amp;gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Unsafe inline styles
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...

return (
    &amp;lt;&amp;gt;
      &amp;lt;Head /&amp;gt;
      {isiOS &amp;amp;&amp;amp; (
        &amp;lt;NextHead&amp;gt;
          &amp;lt;style&amp;gt;
            {`
              html,
              body {
                height: 100vh;
              }
            `}
          &amp;lt;/style&amp;gt;
        &amp;lt;/NextHead&amp;gt;
      )}

    ...

    &amp;lt;/&amp;gt; 
...

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Styles that are applied in JavaScript by setting the &lt;code&gt;style&lt;/code&gt; attribute directly or by using &lt;code&gt;cssText&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;document.querySelector("div").setAttribute("style", "display:none;");
document.querySelector("div").style.cssText = "display:none;";
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;NOTE: our project does not have this&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Style properties that are set directly on an element’s &lt;code&gt;style&lt;/code&gt; property.
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const Drawer = ({ open, onClick }) =&amp;gt; {
  useEffect(() =&amp;gt; {
    document.body.style.position = open ? 'fixed' : 'static';
    document.body.style.width = open ? '100%' : 'auto';
    document.body.style.overflow = open ? 'hidden' : 'auto';
  }, [open]);

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step-by-step guide
&lt;/h2&gt;

&lt;p&gt;Set the Content Security Policy (CSP) header to include:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;style-src 'self' 'nonce-${nonce}';
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Dynamically generate a &lt;code&gt;nonce&lt;/code&gt; and set it in the &lt;code&gt;x-nonce&lt;/code&gt; response header. For example, in Next.js middleware:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...

export function middleware(request) {
  const nonce = Buffer.from(crypto.randomUUID()).toString('base64');
  const cspHeader = `
    default-src 'self';
    style-src 'self' 'nonce-${nonce}';
`;
  // Replace newline characters and spaces
  const contentSecurityPolicyHeaderValue = cspHeader
    .replace(/\s{2,}/g, ' ')
    .trim();

  const requestHeaders = new Headers(request.headers);
  requestHeaders.set('x-nonce', nonce);
  requestHeaders.set(
    'Content-Security-Policy',
    contentSecurityPolicyHeaderValue
  );

  const response = NextResponse.next({
    request: {
      headers: requestHeaders,
    },
  });
  response.headers.set(
    'Content-Security-Policy',
    contentSecurityPolicyHeaderValue
  );

  return response;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the code that uses unsafe inline styles, retrieve the nonce value from the &lt;code&gt;x-nonce&lt;/code&gt; request header and set it as the nonce attribute on the &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; element.&lt;/p&gt;

&lt;p&gt;For example, the unsafe inline styles:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...

return (
    &amp;lt;&amp;gt;
      &amp;lt;Head /&amp;gt;
      {isiOS &amp;amp;&amp;amp; (
        &amp;lt;NextHead&amp;gt;
          &amp;lt;style nonce={nonce}&amp;gt;
            {`
              html,
              body {
                height: 100vh;
              }
            `}
          &amp;lt;/style&amp;gt;
        &amp;lt;/NextHead&amp;gt;
      )}

    ...

    &amp;lt;/&amp;gt; 
...

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

&lt;/div&gt;



&lt;p&gt;However, according to &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy#nonce-nonce_value" rel="noopener noreferrer"&gt;'nonce-'&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If a directive contains a nonce and unsafe-inline, then the browser ignores unsafe-inline.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Since our project heavily relies on inline style properties, we need to refactor the code to use class names in order to resolve this error.&lt;/p&gt;

&lt;p&gt;However, many of these inline styles depend on JavaScript variables at runtime, making it impractical to convert them to class names. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; &amp;lt;div
      data-testid='detail-meta'
      className='detail-meta'
      style={{
        minHeight: isArtist ? '14vw' : 'auto',
      }}&amp;gt;

      ...

  &amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Therefore, we set the &lt;code&gt;style-src&lt;/code&gt; directive to &lt;code&gt;'self' 'unsafe-inline'&lt;/code&gt; instead of using a &lt;code&gt;nonce&lt;/code&gt; to resolve this issue.&lt;/p&gt;

&lt;h2&gt;
  
  
  Further thoughts
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What happens if we remove &lt;code&gt;default-src&lt;/code&gt; and do not add any fetch directives?
&lt;/h3&gt;

&lt;p&gt;For example, if we remove &lt;code&gt;default-src&lt;/code&gt; along with &lt;code&gt;style-src&lt;/code&gt;, &lt;code&gt;style-src-elem&lt;/code&gt;, and &lt;code&gt;style-src-attr&lt;/code&gt; from the &lt;code&gt;Content-Security-Policy&lt;/code&gt; headers.&lt;/p&gt;

&lt;p&gt;Removing &lt;code&gt;default-src&lt;/code&gt; and using wildcards for other directives fails to resolve the Content Security Policy (CSP) Header Not Set error and the complex &lt;code&gt;style-src&lt;/code&gt; issues. In fact, it causes additional problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CSP: Failure to Define Directive with No Fallback&lt;/li&gt;
&lt;li&gt;CSP: Wildcard Directive&lt;/li&gt;
&lt;li&gt;CSP: style-src unsafe-inline&lt;/li&gt;
&lt;li&gt;Content Security Policy (CSP) Header Not Set&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvx7kee5dkbg84xwfva1b.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%2Fvx7kee5dkbg84xwfva1b.png" alt="Image description" width="800" height="141"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  What happens when the directive values &lt;code&gt;'unsafe-inline'&lt;/code&gt; and &lt;code&gt;'nonce-${nonce}'&lt;/code&gt; are set together?
&lt;/h3&gt;

&lt;p&gt;Get the error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Refused to apply inline style because it violates the following Content Security Policy directive: "style-src 'self' 'unsafe-inline' 'nonce-MjNjZTAzNDEtYWMxMC00OGViLTg5NDYtZjMxOTg0ODg2MjVm'". Note that 'unsafe-inline' is ignored if either a hash or nonce value is present in the source list.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As shown in this image:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqro6g9sh0v2jpwnla8cz.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%2Fqro6g9sh0v2jpwnla8cz.png" alt="Image description" width="800" height="68"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To resolve this issue, remove either &lt;code&gt;'unsafe-inline'&lt;/code&gt; or &lt;code&gt;'nonce-${nonce}'&lt;/code&gt; from the directive.&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>webdev</category>
      <category>security</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Security - Solving the "Content Security Policy (CSP) Header Not Set": frame-ancestors directive and frame-src directive</title>
      <dc:creator>Jen C.</dc:creator>
      <pubDate>Tue, 17 Jun 2025 03:55:51 +0000</pubDate>
      <link>https://dev.to/jenchen/security-solving-the-content-security-policy-csp-header-not-set-frame-ancestors-directive-fhf</link>
      <guid>https://dev.to/jenchen/security-solving-the-content-security-policy-csp-header-not-set-frame-ancestors-directive-fhf</guid>
      <description>&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy/frame-ancestors" rel="noopener noreferrer"&gt;Content-Security-Policy: frame-ancestors directive&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy/frame-src" rel="noopener noreferrer"&gt;Content-Security-Policy: frame-src directive&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;Since we don't yet know which related sites might embed our page, we don't need to include the &lt;code&gt;frame-ancestors&lt;/code&gt; directive in the Content Security Policy (CSP) headers at this time.&lt;/p&gt;

&lt;p&gt;Regarding the &lt;code&gt;frame-src&lt;/code&gt; directive, our project contains an &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt; element that loads a resource from &lt;code&gt;https://my.auth.first.my.com&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Additionally, we load the script &lt;code&gt;${MY_AUTH_URL}/sso/sso.js&lt;/code&gt; from &lt;code&gt;https://my.auth.first.my.com&lt;/code&gt;. This script creates an &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt; element that loads resources from &lt;code&gt;https://my.auth.second.my.com&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;script nonce={nonce} async src={`${MY_AUTH_URL}/sso/sso.js`} /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step-by-Step Guide
&lt;/h2&gt;

&lt;p&gt;Although we only know the domain value of &lt;code&gt;MY_AUTH_URL&lt;/code&gt;, we don’t have the exact domain (&lt;code&gt;https://my.auth.second.my.com&lt;/code&gt;) that is dynamically loaded by the script &lt;code&gt;${MY_AUTH_URL}/sso/sso.js&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;However, based on the project requirements, we can assume that the domain used within the script will follow the pattern &lt;code&gt;https://my.auth.XXX.my.com&lt;/code&gt;. To accommodate this, we can configure the &lt;code&gt;frame-src&lt;/code&gt; directive to allow all subdomains under &lt;code&gt;.my.com&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;frame-src https://*.my.com;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>nextjs</category>
      <category>webdev</category>
      <category>security</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Security - Solving the "Content Security Policy (CSP) Header Not Set" in Next.js: script-src and connect-src</title>
      <dc:creator>Jen C.</dc:creator>
      <pubDate>Mon, 16 Jun 2025 07:24:55 +0000</pubDate>
      <link>https://dev.to/jenchen/security-solving-the-content-security-policy-csp-header-not-set-in-nextjs-script-src-and-37ie</link>
      <guid>https://dev.to/jenchen/security-solving-the-content-security-policy-csp-header-not-set-in-nextjs-script-src-and-37ie</guid>
      <description>&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.npmjs.com/package/@next/react-refresh-utils" rel="noopener noreferrer"&gt;@next/react-refresh-utils&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.netlify.com/blog/2020/12/03/what-is-react-fast-refresh/" rel="noopener noreferrer"&gt;What is React Fast Refresh?&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy/script-src" rel="noopener noreferrer"&gt;Content-Security-Policy: script-src directive&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy/connect-src" rel="noopener noreferrer"&gt;Content-Security-Policy: connect-src directive&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;Referencing the previous post, we have a basic setup for the Content Security Policy (CSP) headers with the &lt;code&gt;script-src&lt;/code&gt; directive in the src/middleware.js file of the project.&lt;/p&gt;


&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/jenchen/security-solving-the-content-security-policy-csp-header-not-set-in-nextjs-2314" class="crayons-story__hidden-navigation-link"&gt;Security - Solving the "Content Security Policy (CSP) Header Not Set" in Next.js&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/jenchen" class="crayons-avatar  crayons-avatar--l  "&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%2Fuser%2Fprofile_image%2F1095199%2Ff14eb943-e036-49ea-b10c-87b5a191351b.JPG" alt="jenchen profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/jenchen" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Jen C.
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Jen C.
                
              
              &lt;div id="story-author-preview-content-2536767" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/jenchen" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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%2Fuser%2Fprofile_image%2F1095199%2Ff14eb943-e036-49ea-b10c-87b5a191351b.JPG" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Jen C.&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/jenchen/security-solving-the-content-security-policy-csp-header-not-set-in-nextjs-2314" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;May 28 '25&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/jenchen/security-solving-the-content-security-policy-csp-header-not-set-in-nextjs-2314" id="article-link-2536767"&gt;
          Security - Solving the "Content Security Policy (CSP) Header Not Set" in Next.js
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/nextjs"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;nextjs&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/webdev"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;webdev&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/security"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;security&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/javascript"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;javascript&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/jenchen/security-solving-the-content-security-policy-csp-header-not-set-in-nextjs-2314" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;1&lt;span class="hidden s:inline"&gt; reaction&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/jenchen/security-solving-the-content-security-policy-csp-header-not-set-in-nextjs-2314#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              &lt;span class="hidden s:inline"&gt;Add Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            3 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


&lt;h2&gt;
  
  
  Errors and solutions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Uncaught EvalError: Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive: "script-src 'self' 'nonce-xxx'
&lt;/h3&gt;

&lt;p&gt;Error message:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Uncaught EvalError: Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive: "script-src 'self' 'nonce-YjQ3MGE2NDEtYTE2ZS00ODU5LTgzYjEtY2I0ODQ0OTQ3YWJi' https://my.domain.com 'strict-dynamic'".

    at ./node_modules/next/dist/compiled/@next/react-refresh-utils/dist/runtime.js (react-refresh.js:30:26)

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

&lt;/div&gt;



&lt;h4&gt;
  
  
  Root cause
&lt;/h4&gt;

&lt;p&gt;Because the module used for React Refresh during development contains code that uses &lt;code&gt;eval()&lt;/code&gt;, the current CSP &lt;code&gt;script-src&lt;/code&gt; directive is not sufficient, as it does not include the &lt;code&gt;'unsafe-eval'&lt;/code&gt; source expression. As shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;script-src 'self' 'nonce-${nonce}' ${authUrl} 'strict-dynamic';
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Solution
&lt;/h4&gt;

&lt;p&gt;For security reasons, we should not include &lt;code&gt;'unsafe-eval'&lt;/code&gt; in the production environment. Therefore, we should check the current environment and add &lt;code&gt;'unsafe-eval'&lt;/code&gt; only when it is not a production environment.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const authUrl = `${myAuthURL.protocol}//${myAuthURL.host}`;

const isProd = process.env.NODE_ENV === 'production';

export function middleware(request) {
  const nonce = Buffer.from(crypto.randomUUID()).toString('base64');
  const cspHeader = `
    default-src 'self';
    script-src 'self' ${isProd ? '' : "'unsafe-eval'"} 'nonce-${nonce}' ${authUrl} 'strict-dynamic';
`;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Refused to connect to &lt;code&gt;'&amp;lt;URL&amp;gt;'&lt;/code&gt; because it violates the following Content Security Policy directive: "default-src 'self'". Note that 'connect-src' was not explicitly set, so 'default-src' is used as a fallback
&lt;/h3&gt;

&lt;p&gt;Error message:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Refused to connect to '&amp;lt;URL&amp;gt;' because it violates the following Content Security Policy directive: "default-src 'self'". Note that 'connect-src' was not explicitly set, so 'default-src' is used as a fallback.

Refused to connect to 'https://sr-client-cfg.amplitude.com/config?api_key=8750e93cf3cf432b04d4644133f3aa5c&amp;amp;config_keys=analyticsSDK&amp;amp;session_id=1750047771451' because it violates the following Content Security Policy directive: "default-src 'self'". Note that 'connect-src' was not explicitly set, so 'default-src' is used as a fallback.

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

&lt;/div&gt;



&lt;h4&gt;
  
  
  Root cause
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;'connect-src'&lt;/code&gt; directive is not set.&lt;/p&gt;

&lt;h4&gt;
  
  
  Solution
&lt;/h4&gt;

&lt;p&gt;Set the &lt;code&gt;'connect-src'&lt;/code&gt; directive in the Content Security Policy (CSP) headers, and start with a basic value:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;connect-src 'self';
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, inspect the project codebase and monitor error messages in Chrome DevTools to update the &lt;code&gt;connect-src&lt;/code&gt; values accordingly. For example, if the error message is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Refused to connect to '&amp;lt;URL&amp;gt;' because it violates the following Content Security Policy directive: "connect-src 'self'".

Refused to connect to 'https://api2.amplitude.com/2/httpapi' because it violates the following Content Security Policy directive: "connect-src 'self'".

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

&lt;/div&gt;



&lt;p&gt;As shown in the image:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy4v0aehe64xoc4tcy6fa.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%2Fy4v0aehe64xoc4tcy6fa.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We should add the URL &lt;code&gt;https://api2.amplitude.com/2/httpapi&lt;/code&gt; to the &lt;code&gt;connect-src&lt;/code&gt; directive:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;connect-src 'self' https://api2.amplitude.com/2/httpapi;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Additionally, for URLs with dynamic query parameters (e.g. &lt;code&gt;https://sr-client-cfg.amplitude.com/config?api_key=...&amp;amp;session_id=...&lt;/code&gt;), extract only the protocol and host, then add it to &lt;code&gt;connect-src&lt;/code&gt;. The updated directive would look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;connect-src 'self' https://api2.amplitude.com/2/httpapi https://sr-client-cfg.amplitude.com;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;According to &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy/connect-src" rel="noopener noreferrer"&gt;Content-Security-Policy: connect-src directive&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The following APIs are controlled by this directive:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;ping&lt;/code&gt; attribute in &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; elements&lt;/li&gt;
&lt;li&gt;&lt;code&gt;fetch()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;fetchLater()&lt;/code&gt; Experimental&lt;/li&gt;
&lt;li&gt;&lt;code&gt;XMLHttpRequest&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;WebSocket&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;EventSource&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Navigator.sendBeacon()&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;To ensure compliance, review the codebase for any usage of these APIs that make external network requests. If any requests are made to URLs not currently listed in the &lt;code&gt;connect-src&lt;/code&gt; directive, update the CSP header to include those origins accordingly.&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>webdev</category>
      <category>security</category>
      <category>javascript</category>
    </item>
    <item>
      <title>JavaScript package manager - How to upgrade a package and its indirect dependencies using Yarn 1.x</title>
      <dc:creator>Jen C.</dc:creator>
      <pubDate>Thu, 12 Jun 2025 09:41:25 +0000</pubDate>
      <link>https://dev.to/jenchen/javascript-package-manager-how-to-upgrade-a-package-and-its-indirect-dependencies-using-yarn-1x-31c4</link>
      <guid>https://dev.to/jenchen/javascript-package-manager-how-to-upgrade-a-package-and-its-indirect-dependencies-using-yarn-1x-31c4</guid>
      <description>&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://trivy.dev/latest/" rel="noopener noreferrer"&gt;Trivy&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://npm.github.io/how-npm-works-docs/index.html" rel="noopener noreferrer"&gt;How npm Works&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://classic.yarnpkg.com/lang/en/docs/managing-dependencies/?utm_source=chatgpt.com" rel="noopener noreferrer"&gt;Managing dependencies&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://classic.yarnpkg.com/lang/en/docs/cli/upgrade/" rel="noopener noreferrer"&gt;yarn upgrade&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://classic.yarnpkg.com/lang/en/docs/cli/why/" rel="noopener noreferrer"&gt;yarn why&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://classic.yarnpkg.com/en/docs/selective-version-resolutions" rel="noopener noreferrer"&gt;Selective dependency resolutions&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://josephkrump.com/posts/upgrade-module-sub-dependencies-with-yarn?utm_source=chatgpt.com" rel="noopener noreferrer"&gt;How to update module subdependencies with yarn&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.warp.dev/terminus/yarn-upgrade-package" rel="noopener noreferrer"&gt;Upgrade Yarn Package(s)&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Backgournd
&lt;/h2&gt;

&lt;p&gt;A &lt;strong&gt;HIGH&lt;/strong&gt; severity vulnerability was found in the &lt;code&gt;axios&lt;/code&gt; package during a security scan using Trivy, 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%2Fuys2ulmw7ix17lryvijm.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%2Fuys2ulmw7ix17lryvijm.png" alt="Image description" width="800" height="72"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We ran &lt;code&gt;yarn why axios&lt;/code&gt; to determine why &lt;code&gt;axios&lt;/code&gt; is installed and which packages depend on it. The output revealed that &lt;code&gt;axios&lt;/code&gt; is a dependency of &lt;code&gt;Package A&lt;/code&gt;, which is listed under the &lt;code&gt;dependencies&lt;/code&gt; section of the current project's package.json. This means &lt;code&gt;axios&lt;/code&gt; is indirectly included via &lt;code&gt;Package A&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To address the vulnerability in &lt;code&gt;axios&lt;/code&gt;, we need to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Upgrade &lt;code&gt;axios&lt;/code&gt; in &lt;code&gt;Package A&lt;/code&gt;, since it directly includes the vulnerable version.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Upgrade &lt;code&gt;Package A&lt;/code&gt; in the current project to the version that includes the patched &lt;code&gt;axios&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Verify that both &lt;code&gt;Package A&lt;/code&gt; and any direct use of &lt;code&gt;axios&lt;/code&gt; in the project (if applicable) now rely on the secure, updated version.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This ensures the vulnerability is fully resolved throughout the dependency tree.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step-by-step guide
&lt;/h2&gt;

&lt;h3&gt;
  
  
  In the project A
&lt;/h3&gt;

&lt;p&gt;Upgrade &lt;code&gt;axios&lt;/code&gt; in Project A, then release a new version of Project A that includes the updated &lt;code&gt;axios&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  In the current project
&lt;/h3&gt;

&lt;h4&gt;
  
  
  1. Upgrade to the latest version of &lt;code&gt;Package A&lt;/code&gt; by running:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn upgrade A --latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  2. Delete the &lt;code&gt;/node_modules&lt;/code&gt; folder to ensure a clean install
&lt;/h4&gt;

&lt;h4&gt;
  
  
  3. Reinstall all dependencies by running:
&lt;/h4&gt;



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

&lt;/div&gt;



&lt;h4&gt;
  
  
  4. Verify that both &lt;code&gt;Package A&lt;/code&gt; and &lt;code&gt;axios&lt;/code&gt; have been upgraded in the &lt;code&gt;yarn.lock&lt;/code&gt; file
&lt;/h4&gt;

&lt;h4&gt;
  
  
  5. Run unit tests to ensure functionality is correct:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"scripts": {

    ...

    "test": "jest",

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

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn test
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  6. Build the project to ensure there are no build-time errors:
&lt;/h4&gt;

&lt;p&gt;In package.json&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"scripts": {

    ...

    "build": "next build",

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

&lt;/div&gt;



&lt;p&gt;Run the command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make sure both commands complete successfully without any errors.&lt;/p&gt;

&lt;h4&gt;
  
  
  7. Run Trivy to see if the security issue has resolved
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;trivy fs &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>javascript</category>
      <category>frontend</category>
      <category>webdev</category>
      <category>security</category>
    </item>
    <item>
      <title>JavaScript package manager - How to fix Cannot find module 'cheerio' error with Enzyme in Yarn 1 projects</title>
      <dc:creator>Jen C.</dc:creator>
      <pubDate>Thu, 12 Jun 2025 03:41:36 +0000</pubDate>
      <link>https://dev.to/jenchen/javascript-package-manager-how-to-fix-cannot-find-module-cheerio-error-with-enzyme-in-yarn-1-4i3h</link>
      <guid>https://dev.to/jenchen/javascript-package-manager-how-to-fix-cannot-find-module-cheerio-error-with-enzyme-in-yarn-1-4i3h</guid>
      <description>&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://classic.yarnpkg.com/en/" rel="noopener noreferrer"&gt;Yarn 1&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://classic.yarnpkg.com/lang/en/docs/selective-version-resolutions/" rel="noopener noreferrer"&gt;Selective dependency resolutions&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://jestjs.io/" rel="noopener noreferrer"&gt;Jest&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://enzymejs.github.io/enzyme/" rel="noopener noreferrer"&gt;Enzyme&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://babeljs.io/" rel="noopener noreferrer"&gt;Babel&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;This guide addresses a common error encountered when running Jest tests with Enzyme in projects managed by Yarn 1. The error involves missing the Cheerio module due to version incompatibilities.&lt;/p&gt;

&lt;p&gt;When running tests using the command &lt;code&gt;yarn test&lt;/code&gt;, the following error is encountered:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Test suite failed to run

    Cannot find module 'cheerio/lib/utils' from 'node_modules/enzyme/build/Utils.js'

    Require stack:
      node_modules/enzyme/build/Utils.js
      node_modules/enzyme/build/ReactWrapper.js
      node_modules/enzyme/build/index.js
      jest/setup.js

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Project setup
&lt;/h3&gt;

&lt;h4&gt;
  
  
  package.json
&lt;/h4&gt;

&lt;p&gt;scripts&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; "scripts": {
    ...

    "test": "jest",

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

&lt;/div&gt;



&lt;p&gt;Jest configuration&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...

"jest": {
    "testEnvironment": "jsdom",
    "setupFiles": [
      "&amp;lt;rootDir&amp;gt;/jest/setupFiles.js"
    ],
    "setupFilesAfterEnv": [
      "&amp;lt;rootDir&amp;gt;/jest/setup.js"
    ],

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

&lt;/div&gt;



&lt;p&gt;devDependencies&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...

"devDependencies": {
    "@babel/core": "^7.26.10",
    "@testing-library/jest-dom": "^6.6.3",
    "@testing-library/react": "^14.0.0",
    "@wojtekmaj/enzyme-adapter-react-17": "^0.6.3",
    "babel-jest": "^27.1.0",
    "enzyme": "^3.11.0",
    "jest": "^27.1.0",

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

&lt;/div&gt;



&lt;h4&gt;
  
  
  Babel configuration
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;module.exports = function (api) {
  api.cache(true);

  return {
    presets: ['next/babel'],
  };
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Root cause
&lt;/h2&gt;

&lt;p&gt;Enzyme internally depends on Cheerio, but only supports up to &lt;code&gt;1.0.0-rc.3&lt;/code&gt;. If a newer version of Cheerio (such as &lt;code&gt;1.1.0&lt;/code&gt;) is installed, Enzyme will fail to locate expected modules, resulting in the error.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/cheeriojs/cheerio/issues/3987" rel="noopener noreferrer"&gt;Cheerio 1.0.0 is incompatible with enzyme 3.11.0. #3987&lt;br&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/enzymejs/enzyme/issues/2606" rel="noopener noreferrer"&gt;Cheerio Update to 1.0.0 is breaking Enzyme 3.11.0 for Node &amp;lt; 18.17.0 #2606&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/enzymejs/enzyme/issues/2558" rel="noopener noreferrer"&gt;Cheerio 1.0.0-rc.11 no longer support deep imports #2558&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Solution
&lt;/h2&gt;

&lt;p&gt;Based on the links in the previous section, the latest Cheerio version supported by Enzyme is &lt;code&gt;1.0.0-rc.3&lt;/code&gt;. Therefore, add a &lt;code&gt;resolutions&lt;/code&gt; field to force Enzyme to use Cheerio version &lt;code&gt;1.0.0-rc.3&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;package.json&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...

 "resolutions": {
    "enzyme/cheerio": "1.0.0-rc.3"
  }

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

&lt;/div&gt;



&lt;p&gt;Next, we observe the Cheerio version in the yarn.lock file before and after adding the &lt;code&gt;resolutions&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Before the change, Enzyme’s dependency on &lt;code&gt;cheerio@^1.0.0-rc.3&lt;/code&gt; is resolved to version &lt;code&gt;1.1.0&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...

cheerio@^1.0.0-rc.3:
  version "1.1.0"
  resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.1.0.tgz#87b9bec6dd3696e405ea79da7d2749d8308b0953"
  integrity sha512-+0hMx9eYhJvWbgpKV9hN7jg0JcwydpopZE4hgi+KvQtByZXPp04NiCWU0LzcAbP63abZckIHkTQaXVF52mX3xQ==
  dependencies:
    cheerio-select "^2.1.0"
    dom-serializer "^2.0.0"
    domhandler "^5.0.3"
    domutils "^3.2.2"
    encoding-sniffer "^0.2.0"
    htmlparser2 "^10.0.0"
    parse5 "^7.3.0"
    parse5-htmlparser2-tree-adapter "^7.1.0"
    parse5-parser-stream "^7.1.2"
    undici "^7.10.0"
    whatwg-mimetype "^4.0.0"

...

enzyme@^3.11.0:
  version "3.11.0"
  resolved "https://registry.yarnpkg.com/enzyme/-/enzyme-3.11.0.tgz#71d680c580fe9349f6f5ac6c775bc3e6b7a79c28"
  integrity sha512-Dw8/Gs4vRjxY6/6i9wU0V+utmQO9kvh9XLnz3LIudviOnVYDEe2ec+0k+NQoMamn1VrjKgCUOWj5jG/5M5M0Qw==
  dependencies:
    array.prototype.flat "^1.2.3"
    cheerio "^1.0.0-rc.3"

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

&lt;/div&gt;



&lt;p&gt;After the change, Enzyme’s dependency on &lt;code&gt;cheerio@^1.0.0-rc.3&lt;/code&gt; is resolved to version &lt;code&gt;1.0.0-rc.3&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...

cheerio@1.0.0-rc.3, cheerio@^1.0.0-rc.3:
  version "1.0.0-rc.3"
  resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.3.tgz#094636d425b2e9c0f4eb91a46c05630c9a1a8bf6"
  integrity sha512-0td5ijfUPuubwLUu0OBoe98gZj8C/AA+RW3v67GPlGOrvxWjZmBXiBCRU+I8VEiNyJzjth40POfHiz2RB3gImA==
  dependencies:
    css-select "~1.2.0"
    dom-serializer "~0.1.1"
    entities "~1.1.1"
    htmlparser2 "^3.9.1"
    lodash "^4.15.0"
    parse5 "^3.0.1"

...

enzyme@^3.11.0:
  version "3.11.0"
  resolved "https://registry.yarnpkg.com/enzyme/-/enzyme-3.11.0.tgz#71d680c580fe9349f6f5ac6c775bc3e6b7a79c28"
  integrity sha512-Dw8/Gs4vRjxY6/6i9wU0V+utmQO9kvh9XLnz3LIudviOnVYDEe2ec+0k+NQoMamn1VrjKgCUOWj5jG/5M5M0Qw==
  dependencies:
    array.prototype.flat "^1.2.3"
    cheerio "^1.0.0-rc.3"

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Should I Add Cheerio to devDependencies in package.json?
&lt;/h3&gt;

&lt;p&gt;No, we do not need to add Cheerio manually to the &lt;code&gt;devDependencies&lt;/code&gt;. When we install Enzyme, its direct dependencies, including Cheerio, are installed automatically. we can confirm this by running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn info v1.22.22
warning package.json: No license field
{
  'array.prototype.flat': '^1.2.3',
  cheerio: '^1.0.0-rc.3',
  'enzyme-shallow-equal': '^1.0.1',
  'function.prototype.name': '^1.1.2',
  has: '^1.0.3',
  'html-element-map': '^1.2.0',
  'is-boolean-object': '^1.0.1',
  'is-callable': '^1.1.5',
  'is-number-object': '^1.0.4',
  'is-regex': '^1.0.5',
  'is-string': '^1.0.5',
  'is-subset': '^0.1.1',
  'lodash.escape': '^4.0.1',
  'lodash.isequal': '^4.5.0',
  'object-inspect': '^1.7.0',
  'object-is': '^1.0.2',
  'object.assign': '^4.1.0',
  'object.entries': '^1.1.1',
  'object.values': '^1.1.1',
  raf: '^3.4.1',
  'rst-selector-parser': '^2.2.3',
  'string.prototype.trim': '^1.2.1'
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Add the resolutions field forcing Cheerio to &lt;code&gt;1.0.0-rc.3&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Remove node_modules folder and clear yarn cache&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;yarn install&lt;/code&gt; to update dependencies&lt;/li&gt;
&lt;li&gt;Verify with yarn.lock that the correct version of Cheerio is installed&lt;/li&gt;
&lt;li&gt;Run tests with &lt;code&gt;yarn test&lt;/code&gt; again&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>javascript</category>
      <category>react</category>
      <category>testing</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Security - Solving the "Content Security Policy (CSP) Header Not Set" in Next.js</title>
      <dc:creator>Jen C.</dc:creator>
      <pubDate>Wed, 28 May 2025 10:43:57 +0000</pubDate>
      <link>https://dev.to/jenchen/security-solving-the-content-security-policy-csp-header-not-set-in-nextjs-2314</link>
      <guid>https://dev.to/jenchen/security-solving-the-content-security-policy-csp-header-not-set-in-nextjs-2314</guid>
      <description>&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.zaproxy.org/" rel="noopener noreferrer"&gt;Zed Attack Proxy (ZAP)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://nextjs.org/docs/app/guides/content-security-policy" rel="noopener noreferrer"&gt;How to set a Content Security Policy (CSP) for your Next.js application&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://nextjs.org/docs/app/building-your-application/routing/middleware" rel="noopener noreferrer"&gt;Middleware&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/nonce" rel="noopener noreferrer"&gt;nonce&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/nonce" rel="noopener noreferrer"&gt;HTMLElement: nonce property&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://web.dev/articles/strict-csp" rel="noopener noreferrer"&gt;Mitigate cross-site scripting (XSS) with a strict Content Security Policy (CSP)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy#strict-dynamic" rel="noopener noreferrer"&gt;'strict-dynamic'&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://centralcsp.com/articles/how-to-setup-nonce-with-nextjs" rel="noopener noreferrer"&gt;How to setup nonce with NextJS&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://nextjs.org/docs/app/building-your-application/routing/middleware#matcher" rel="noopener noreferrer"&gt;Matcher&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;Address the "Content Security Policy (CSP) Header Not Set" issue using ZAP.&lt;/p&gt;

&lt;p&gt;Install ZAP and run an automated scan. Below is an example of the generated report:&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%2F4uh8kle3rq92i22ixnzy.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%2F4uh8kle3rq92i22ixnzy.png" alt="Image description" width="800" height="479"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding a nonce with Middleware
&lt;/h2&gt;

&lt;p&gt;Refer to &lt;a href="https://nextjs.org/docs/pages/guides/content-security-policy#adding-a-nonce-with-middleware" rel="noopener noreferrer"&gt;Next.js official document&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;middleware.js&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { NextResponse } from 'next/server'

export function middleware(request) {
  const nonce = Buffer.from(crypto.randomUUID()).toString('base64')
  const cspHeader = `
    default-src 'self';
    script-src 'self' 'nonce-${nonce}' 'strict-dynamic';
    style-src 'self' 'nonce-${nonce}';
    img-src 'self' blob: data:;
    font-src 'self';
    object-src 'none';
    base-uri 'self';
    form-action 'self';
    frame-ancestors 'none';
    upgrade-insecure-requests;
`
  // Replace newline characters and spaces
  const contentSecurityPolicyHeaderValue = cspHeader
    .replace(/\s{2,}/g, ' ')
    .trim()

  const requestHeaders = new Headers(request.headers)
  requestHeaders.set('x-nonce', nonce)
  requestHeaders.set(
    'Content-Security-Policy',
    contentSecurityPolicyHeaderValue
  )

  const response = NextResponse.next({
    request: {
      headers: requestHeaders,
    },
  })
  response.headers.set(
    'Content-Security-Policy',
    contentSecurityPolicyHeaderValue
  )

  return response
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can add a &lt;code&gt;matcher&lt;/code&gt; if needed in &lt;strong&gt;middleware.js&lt;/strong&gt;. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...

export const config = {
  matcher: [
    /*
     * Match all request paths except for the ones starting with:
     * - api (API routes)
     * - _next/static (static files)
     * - _next/image (image optimization files)
     * - favicon.ico (favicon file)
     */
    {
      source: '/((?!api|_next/static|_next/image|favicon.ico).*)',
      missing: [
        { type: 'header', key: 'next-router-prefetch' },
        { type: 'header', key: 'purpose', value: 'prefetch' },
      ],
    },
  ],
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Reading the nonce and adding it to script elements
&lt;/h3&gt;

&lt;p&gt;For example, when using the Next.js Pages Router, you can extract the &lt;code&gt;nonce&lt;/code&gt; from the request headers in the &lt;strong&gt;_document.js&lt;/strong&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...

static async getInitialProps(context) {
    const props = await super.getInitialProps(context);

    const nonce = context.req.headers['x-nonce'];

    return { ...props, nonce };
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the &lt;code&gt;render&lt;/code&gt; function, add the &lt;code&gt;nonce&lt;/code&gt; attribute to script elements such as &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;Script&amp;gt;&lt;/code&gt;, and &lt;code&gt;&amp;lt;NextScript&amp;gt;&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...

  render() {
    const { nonce } = this.props;

    return (
      &amp;lt;Html lang='en'&amp;gt;
        &amp;lt;Head&amp;gt;
          &amp;lt;meta charSet='utf-8' /&amp;gt;

          &amp;lt;Script
            nonce={nonce}
            type='application/ld+json'

            ...
          /&amp;gt;

          ...

        &amp;lt;/Head&amp;gt;
        &amp;lt;body&amp;gt;
          &amp;lt;Main /&amp;gt;
          &amp;lt;script nonce={nonce} defer src='/static/icons/svgxuse.js' /&amp;gt;
          &amp;lt;NextScript nonce={nonce} /&amp;gt;
        &amp;lt;/body&amp;gt;
      &amp;lt;/Html&amp;gt;
    );
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;NOTE: According to &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/nonce#accessing_nonces_and_nonce_hiding" rel="noopener noreferrer"&gt;Accessing nonces and nonce hiding&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;For security reasons, the nonce content attribute is hidden (an empty string will be returned).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Errors and solutions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Refused to load the script &lt;code&gt;'&amp;lt;URL&amp;gt;'&lt;/code&gt; because it violates the following Content Security Policy directive
&lt;/h3&gt;

&lt;p&gt;Error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Refused to load the script '&amp;lt;URL&amp;gt;' because it violates the following Content Security Policy directive: "script-src 'self' 'nonce-Mjk5MDMxNTctMzU3Mi00ZTk0LWI5MzQtYmZjZWM5ZWQ1ZWJh' &amp;lt;URL&amp;gt; 'strict-dynamic'". Note that 'strict-dynamic' is present, so host-based allowlisting is disabled. Note that 'script-src-elem' was not explicitly set, so 'script-src' is used as a fallback.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As shown in the image:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhqirajj3qac1c5z6excy.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%2Fhqirajj3qac1c5z6excy.png" alt="Image description" width="800" height="672"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Root cause
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;script-src 'self' 'nonce-${nonce}' 'strict-dynamic';
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the browser supports &lt;code&gt;'strict-dynamic'&lt;/code&gt;, &lt;code&gt;'self'&lt;/code&gt; is ignored. Therefore, the error "Refused to load the script" can occur even when running on localhost.&lt;/p&gt;

&lt;h4&gt;
  
  
  Solution
&lt;/h4&gt;

&lt;p&gt;Since it's not possible to determine if a browser supports &lt;code&gt;'strict-dynamic'&lt;/code&gt; in Next.js middleware, a quick and safe solution is to avoid ignoring matching prefetch requests (from &lt;code&gt;next/link&lt;/code&gt;) and static assets. &lt;/p&gt;

&lt;p&gt;To do this, remove the entire &lt;code&gt;config&lt;/code&gt; (as mentioned above) from &lt;strong&gt;middleware.js&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Missing &lt;code&gt;nonce&lt;/code&gt; attribute in scrips elements
&lt;/h3&gt;

&lt;p&gt;For example&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;script src="/_next/static/chunks/webpack.js" defer=""&amp;gt;&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As shown in the image:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsq38i0uzplvgzn1yhbbw.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%2Fsq38i0uzplvgzn1yhbbw.png" alt="Image description" width="800" height="165"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Root cause
&lt;/h4&gt;

&lt;p&gt;Missing &lt;code&gt;nonce&lt;/code&gt; in &lt;code&gt;&amp;lt;Head&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Solution
&lt;/h4&gt;

&lt;p&gt;In &lt;strong&gt;_document.js&lt;/strong&gt;, add &lt;code&gt;nonce&lt;/code&gt; attribute to &lt;code&gt;&amp;lt;Head&amp;gt;&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...

return (
      &amp;lt;Html lang='en'&amp;gt;
        &amp;lt;Head nonce={nonce}&amp;gt;
          &amp;lt;meta charSet='utf-8' /&amp;gt;

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

&lt;/div&gt;



</description>
      <category>nextjs</category>
      <category>webdev</category>
      <category>security</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Jest - How to verify that an element does not exist in the rendered output</title>
      <dc:creator>Jen C.</dc:creator>
      <pubDate>Wed, 21 May 2025 08:56:32 +0000</pubDate>
      <link>https://dev.to/jenchen/jest-how-to-verify-that-an-element-does-not-exist-in-the-rendered-output-2mf3</link>
      <guid>https://dev.to/jenchen/jest-how-to-verify-that-an-element-does-not-exist-in-the-rendered-output-2mf3</guid>
      <description>&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://testing-library.com/docs/queries/about/#types-of-queries" rel="noopener noreferrer"&gt;Types of Queries&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://jestjs.io/docs/expect#not" rel="noopener noreferrer"&gt;.not&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://testing-library.com/docs/guide-disappearance/#nottobeinthedocument" rel="noopener noreferrer"&gt;not.toBeInTheDocument&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://jestjs.io/docs/expect#tobenull" rel="noopener noreferrer"&gt;.toBeNull()&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://testing-library.com/docs/guide-disappearance/#waiting-for-disappearance" rel="noopener noreferrer"&gt;waitForElementToBeRemoved&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://testing-library.com/docs/queries/bytestid/" rel="noopener noreferrer"&gt;ByTestId&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://jestjs.io/docs/expect#tothrowerror" rel="noopener noreferrer"&gt;.toThrow(error?)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://testing-library.com/docs/dom-testing-library/api-async/#waitfor" rel="noopener noreferrer"&gt;waitFor&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector" rel="noopener noreferrer"&gt;Document: querySelector() method&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;.not.toBeInTheDocument()&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;For example, according to the &lt;a href="https://testing-library.com/docs/queries/about/#types-of-queries" rel="noopener noreferrer"&gt;Types of Queries&lt;/a&gt; documentation,&lt;code&gt;queryBy...&lt;/code&gt; returns &lt;code&gt;null&lt;/code&gt; if no matching elements are found. Therefore, we can use &lt;code&gt;.not.toBeInTheDocument()&lt;/code&gt; to assert that the element with the test ID &lt;code&gt;user-profile-icon&lt;/code&gt; is not rendered in the DOM.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;it('does not render the icon element when the "icon" prop is missing', () =&amp;gt; {
  expect(screen.queryByTestId('user-profile-icon')).not.toBeInTheDocument();
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;code&gt;toBeNull()&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The above example can be rewritten as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;it('does not render the icon element when the "icon" prop is missing', () =&amp;gt; {
  expect(screen.queryByTestId('user-profile-icon')).toBeNull();
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  When a query method throws an error if no elements are found
&lt;/h2&gt;

&lt;p&gt;Query methods &lt;code&gt;getBy...&lt;/code&gt;, &lt;code&gt;getAllBy...&lt;/code&gt;, &lt;code&gt;findBy...&lt;/code&gt;, and &lt;code&gt;findAllBy...&lt;/code&gt; will throw an error when they cannot find elements. So we can use &lt;code&gt;.toThrow()&lt;/code&gt; to verify that certain elements are not rendered.&lt;/p&gt;

&lt;p&gt;For example, to verify that an element with the text 'Not Present' does not exist in the render output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;beforeEach(() =&amp;gt; {
  render(&amp;lt;MyComponent /&amp;gt;);
});

...

it('query methods throw when elements are not found', () =&amp;gt; {
  expect(() =&amp;gt; screen.getByText('Not Present')).toThrow();
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In some cases, we may want to verify that an assertion fails when an element is not in the DOM. This can be done using &lt;code&gt;.toThrow()&lt;/code&gt;, though it's less common and typically not recommended for general absence checks.&lt;/p&gt;

&lt;p&gt;For example, we can use &lt;code&gt;querySelector()&lt;/code&gt; to select an element by its CSS class and assert that calling &lt;code&gt;.toBeInTheDocument()&lt;/code&gt; on a non-existent element (i.e., null) throws an error. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;describe('&amp;lt;Nav /&amp;gt; of &amp;lt;Header /&amp;gt;', () =&amp;gt; {
  let renderResult = null;

  beforeEach(() =&amp;gt; {
    renderResult = render(&amp;lt;Nav {...props} /&amp;gt;);
  });

  it('should hide sub menu', async () =&amp;gt; {
    expect(() =&amp;gt;
      expect(
        renderResult.container.querySelector('.main-header-sub-menu')
      ).toBeInTheDocument()
    ).toThrow();
  });
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  When the Testing Library may not update the DOM immediately
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Asynchronous State Updates&lt;/li&gt;
&lt;li&gt;Debounced or Throttled Updates&lt;/li&gt;
&lt;li&gt;State Updates After Re-render&lt;/li&gt;
&lt;li&gt;Transitions and Animations&lt;/li&gt;
&lt;li&gt;External Event Triggers&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;See &lt;a href="https://testing-library.com/docs/dom-testing-library/api-async/" rel="noopener noreferrer"&gt;Async Methods&lt;/a&gt;&lt;/p&gt;

</description>
      <category>jest</category>
      <category>react</category>
      <category>testing</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Jest - Testing with React and React Testing Library: Useful APIs</title>
      <dc:creator>Jen C.</dc:creator>
      <pubDate>Wed, 21 May 2025 03:02:05 +0000</pubDate>
      <link>https://dev.to/jenchen/jest-testing-with-react-and-react-testing-library-useful-apis-4l17</link>
      <guid>https://dev.to/jenchen/jest-testing-with-react-and-react-testing-library-useful-apis-4l17</guid>
      <description>&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://testing-library.com/docs/react-testing-library/api" rel="noopener noreferrer"&gt;react-testing-library API&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://testing-library.com/docs/dom-testing-library/api-events" rel="noopener noreferrer"&gt;Firing Events&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://testing-library.com/docs/dom-testing-library/api-async#waitfor" rel="noopener noreferrer"&gt;waitFor&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://testing-library.com/docs/queries/about/" rel="noopener noreferrer"&gt;About Queries&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector" rel="noopener noreferrer"&gt;Document: querySelector() method&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  render
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Render into a container which is appended to document.body.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For example, render a component before each test and store the result in the &lt;code&gt;renderResult&lt;/code&gt; variable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;describe('&amp;lt;Card /&amp;gt;', () =&amp;gt; {
  let renderResult = null;
  const props = {
    id: '1',
    title: 'test title',
    };

  beforeEach(() =&amp;gt; {
    renderResult = render(&amp;lt;Card {...props} /&amp;gt;);
    });

...

});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Logging the HTML
&lt;/h3&gt;

&lt;p&gt;If you want to see the actual HTML output of your component (as it would appear in the browser), you can log the &lt;code&gt;innerHTML&lt;/code&gt; of the container from the &lt;code&gt;renderResult&lt;/code&gt; like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;console.log(renderResult.container.innerHTML);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div class="card__info"&amp;gt;
    &amp;lt;a class="card__txt-link" href="/video/1"&amp;gt;
      &amp;lt;div class="card__top"&amp;gt;
        &amp;lt;div class="card__title-wrapper"&amp;gt;
          &amp;lt;h3 class="card__title"&amp;gt;test title&amp;lt;/h3&amp;gt;
        &amp;lt;/div&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/a&amp;gt;
  &amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Get an element from the render result's container.
&lt;/h3&gt;

&lt;p&gt;For example, use &lt;code&gt;querySelector()&lt;/code&gt; to select the first matching element by CSS selector:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;expect(
      contentRenderResult.container.querySelector('.detail-meta__studio')
        .textContent
    ).toBe(props.studio);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  rerender
&lt;/h2&gt;

&lt;p&gt;Use when you need to update props, re-render the component, and verify the updated props are rendered correctly.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;it('should render watermark if "watermark" prop is set', () =&amp;gt; {
    const getWatermark = (container) =&amp;gt;
      container.querySelector('.card__watermark');
    const waterMarkUrl = '/static/images/logo-water.svg';

    let watermark = getWatermark(renderResult.container);
    expect(watermark).not.toBeInTheDocument();

    renderResult.rerender(
      &amp;lt;Card
        {...{
          ...props,
          watermark: {
            url: waterMarkUrl,
            alt: 'water',
          },
        }}
      /&amp;gt;
    );
    watermark = getWatermark(renderResult.container);
    expect(watermark).toBeInTheDocument();
    expect(watermark.querySelector('img')).toHaveAttribute('src', waterMarkUrl);
  });
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  User Actions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  fireEvent
&lt;/h3&gt;

&lt;p&gt;For example, simulate a click on an element and check that the &lt;code&gt;onClickItem&lt;/code&gt; callback function is called:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const handleItemClick = jest.fn();

it('should trigger "onClickItem" prop when item is clicked', () =&amp;gt; {
    fireEvent.click(screen.getByRole('menuitem'));
    expect(handleItemClick).toHaveBeenCalledTimes(1);
  });
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Note: Make sure &lt;code&gt;handleItemClick&lt;/code&gt; is passed as the &lt;code&gt;onClickItem&lt;/code&gt; prop to the component being tested. Otherwise, the callback won't be triggered.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  waitFor
&lt;/h3&gt;

&lt;p&gt;When you need to wait for something to either pass or fail.&lt;/p&gt;

&lt;p&gt;For example, wait for the button to appear:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;UserProfile.jsx&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { useEffect, useState } from 'react';

export default function UserProfile() {
  const [user, setUser] = useState(null);

  useEffect(() =&amp;gt; {
    // Simulate network request
    setTimeout(() =&amp;gt; {
      setUser({ name: 'Alice' });
    }, 500);
  }, []);

  return (
    &amp;lt;div&amp;gt;
      {user ? &amp;lt;button className="logout-button"&amp;gt;Logout&amp;lt;/button&amp;gt; : &amp;lt;p&amp;gt;Loading...&amp;lt;/p&amp;gt;}
    &amp;lt;/div&amp;gt;
  );
}

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;UserProfile.test.jsx&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;it('shows logout button after user is loaded', async () =&amp;gt; {
  render(&amp;lt;UserProfile /&amp;gt;);

  await waitFor(() =&amp;gt; {
    expect(screen.getByText('Logout')).toBeInTheDocument();
  });
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Queries
&lt;/h2&gt;

&lt;h3&gt;
  
  
  screen
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;DOM Testing Library also exports a screen object which has every query that is pre-bound to document.body&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For example, check that the heading renders correctly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;it('renders the title heading correctly', () =&amp;gt; {
  expect(screen.getByRole('heading', { level: 1 })).toHaveTextContent(props.title);
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>jest</category>
      <category>react</category>
      <category>testing</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Optimize Core Web Vitals - FCP and LCP: Handle Offscreen &lt;iframe&gt; (3) - Use memo, startTransition, and setTimeout</title>
      <dc:creator>Jen C.</dc:creator>
      <pubDate>Mon, 19 May 2025 10:25:50 +0000</pubDate>
      <link>https://dev.to/jenchen/optimize-core-web-vitals-fcp-and-lcp-handle-offscreen-3-use-memo-starttransition-1n80</link>
      <guid>https://dev.to/jenchen/optimize-core-web-vitals-fcp-and-lcp-handle-offscreen-3-use-memo-starttransition-1n80</guid>
      <description>&lt;h2&gt;
  
  
  Pre-study
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://react.dev/reference/react/startTransition" rel="noopener noreferrer"&gt;startTransition&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://react.dev/reference/react/memo" rel="noopener noreferrer"&gt;memo&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/setTimeout" rel="noopener noreferrer"&gt;Window: setTimeout() method&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/iframe" rel="noopener noreferrer"&gt;&lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt;: The Inline Frame element&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.developerway.com/posts/use-transition" rel="noopener noreferrer"&gt;React useTransition: performance game changer or...?&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;Previously, I tried implementing lazy loading for the &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt; or updating the URL of the &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt; (&lt;code&gt;src&lt;/code&gt; attribute) on the window load event, as suggested in the posts below. However, when users interact with the &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt; before all the resources are fully loaded (since the URL is still an empty string), it results in a significant delay while waiting to load the iframe’s resources and perform callback actions.&lt;/p&gt;


&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/jenchen/improve-page-performance-by-lazy-loading-offscreen-5921" class="crayons-story__hidden-navigation-link"&gt;Optimize Core Web Vitals - FCP and LCP: Lazy-Loading Offscreen &amp;lt;iframe&amp;gt;&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/jenchen" class="crayons-avatar  crayons-avatar--l  "&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%2Fuser%2Fprofile_image%2F1095199%2Ff14eb943-e036-49ea-b10c-87b5a191351b.JPG" alt="jenchen profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/jenchen" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Jen C.
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Jen C.
                
              
              &lt;div id="story-author-preview-content-2340532" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/jenchen" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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%2Fuser%2Fprofile_image%2F1095199%2Ff14eb943-e036-49ea-b10c-87b5a191351b.JPG" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Jen C.&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/jenchen/improve-page-performance-by-lazy-loading-offscreen-5921" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Mar 18 '25&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/jenchen/improve-page-performance-by-lazy-loading-offscreen-5921" id="article-link-2340532"&gt;
          Optimize Core Web Vitals - FCP and LCP: Lazy-Loading Offscreen &amp;lt;iframe&amp;gt;
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/webdev"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;webdev&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/html"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;html&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/frontend"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;frontend&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/webperf"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;webperf&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
            &lt;a href="https://dev.to/jenchen/improve-page-performance-by-lazy-loading-offscreen-5921#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              &lt;span class="hidden s:inline"&gt;Add Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            2 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;



&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/jenchen/optimize-core-web-vitals-fcp-and-lcp-lazy-loading-offscreen-2-2jbj" class="crayons-story__hidden-navigation-link"&gt;Optimize Core Web Vitals - FCP and LCP: Lazy-Loading Offscreen &amp;lt;iframe&amp;gt; (2)&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/jenchen" class="crayons-avatar  crayons-avatar--l  "&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%2Fuser%2Fprofile_image%2F1095199%2Ff14eb943-e036-49ea-b10c-87b5a191351b.JPG" alt="jenchen profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/jenchen" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Jen C.
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Jen C.
                
              
              &lt;div id="story-author-preview-content-2406102" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/jenchen" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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%2Fuser%2Fprofile_image%2F1095199%2Ff14eb943-e036-49ea-b10c-87b5a191351b.JPG" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Jen C.&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/jenchen/optimize-core-web-vitals-fcp-and-lcp-lazy-loading-offscreen-2-2jbj" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Apr 17 '25&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/jenchen/optimize-core-web-vitals-fcp-and-lcp-lazy-loading-offscreen-2-2jbj" id="article-link-2406102"&gt;
          Optimize Core Web Vitals - FCP and LCP: Lazy-Loading Offscreen &amp;lt;iframe&amp;gt; (2)
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/webdev"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;webdev&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/frontend"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;frontend&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/tutorial"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;tutorial&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/webperf"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;webperf&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
            &lt;a href="https://dev.to/jenchen/optimize-core-web-vitals-fcp-and-lcp-lazy-loading-offscreen-2-2jbj#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              &lt;span class="hidden s:inline"&gt;Add Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            2 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


&lt;h2&gt;
  
  
  Solution: Step-by-Step Guide
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Use &lt;code&gt;setTimeout&lt;/code&gt; to delay URL state update:
&lt;/h3&gt;

&lt;p&gt;When the &lt;code&gt;LazyIframe&lt;/code&gt; component mounts, use &lt;code&gt;setTimeout&lt;/code&gt; to delay updating the iframe's URL state variable. This ensures the resources load with lower priority but still before the user interacts with the iframe.&lt;/p&gt;

&lt;h3&gt;
  
  
  Check if iframe URL is already the target URL:
&lt;/h3&gt;

&lt;p&gt;To avoid unnecessary timer creation and state updates, check if the current iframe URL is already equal to the target URL. If they are the same, return early to prevent further action.&lt;/p&gt;

&lt;h3&gt;
  
  
  Wrap state update with &lt;code&gt;startTransition&lt;/code&gt;:
&lt;/h3&gt;

&lt;p&gt;Since the iframe should load with lower priority than user interactions, wrap the state update inside &lt;code&gt;startTransition&lt;/code&gt; to ensure that it doesn’t block active user interactions on the page.&lt;/p&gt;

&lt;h3&gt;
  
  
  Wrap the &lt;code&gt;LazyIframe&lt;/code&gt; component with &lt;code&gt;memo&lt;/code&gt;:
&lt;/h3&gt;

&lt;p&gt;Use &lt;code&gt;memo&lt;/code&gt; to wrap the &lt;code&gt;LazyIframe&lt;/code&gt; component. This prevents unnecessary re-renders of the component, improving performance.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example Implementation of &lt;code&gt;LazyIframe&lt;/code&gt; Component
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { useEffect, useState, memo, startTransition } from 'react';

import { MY_AUTH_URL } from '../configs';

const iframeSrc = `${MY_AUTH_URL}/sso/iframePage.xhtml`;

const LazyIframe = memo(function LazyIframe() {
  const [src, setSrc] = useState('');
  useEffect(() =&amp;gt; {
    if (src === iframeSrc) {
      return;
    }
    const timer = setTimeout(() =&amp;gt; {
      startTransition(() =&amp;gt; {
        setSrc(iframeSrc);
      });
    }, 100);
    return () =&amp;gt; clearTimeout(timer);
  }, [src]);

  return (
    &amp;lt;iframe
      src={src}
      style={{ display: 'none' }}
      id='pidiframe'
      title='log out iframe'
    /&amp;gt;
  );
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>webdev</category>
      <category>frontend</category>
      <category>javascript</category>
      <category>webperf</category>
    </item>
    <item>
      <title>Jest - Mocking Next.js Image to handle dynamic properties in tests</title>
      <dc:creator>Jen C.</dc:creator>
      <pubDate>Mon, 05 May 2025 10:53:07 +0000</pubDate>
      <link>https://dev.to/jenchen/jest-mocking-nextjs-image-to-handle-dynamic-properties-in-tests-33m4</link>
      <guid>https://dev.to/jenchen/jest-mocking-nextjs-image-to-handle-dynamic-properties-in-tests-33m4</guid>
      <description>&lt;h2&gt;
  
  
  Pre-study
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://nextjs.org/docs/pages/api-reference/components/image" rel="noopener noreferrer"&gt;Next.js - Image Component&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/vercel/next.js/issues/26749#issuecomment-885431747" rel="noopener noreferrer"&gt;Importing images used the 'import' keyword for the image 'src' breaks jest test #26749&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://jestjs.io/docs/manual-mocks" rel="noopener noreferrer"&gt;Jest - Manual Mocks&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://testing-library.com/docs/react-testing-library/intro/" rel="noopener noreferrer"&gt;React Testing Library&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters" rel="noopener noreferrer"&gt;MDN JavaScript - Rest parameters&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;When testing a React component with Jest and the React Testing Library, this error occurs if the component renders a Next.js Image component and the Next.js Image component is not mocked.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error: Uncaught [Error: Image with src "test-file-stub" is missing required "width" property.]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;To allow passing dynamic properties into the mock image component so that it can handle different test cases, modify the mock image to accept an indefinite number of arguments as an array.&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;jest/setup.js&lt;/strong&gt;, add the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;jest.mock('next/image', () =&amp;gt; ({
  __esModule: true,
  default: (props) =&amp;gt; {
    return &amp;lt;img {...props} /&amp;gt;;
  },
}));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  To check what is rendered
&lt;/h2&gt;

&lt;p&gt;For example,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const renderResult = render(&amp;lt;Header {...props} /&amp;gt;);
container = renderResult.container;
console.debug(container.innerHTML);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will output the following in the terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;header class="main-header"&amp;gt;&amp;lt;div class="container-h"&amp;gt;&amp;lt;div class="main-header__top"&amp;gt;&amp;lt;a class="main-header__logo-link" href="/"&amp;gt;&amp;lt;img class="main-header__logo" src="test-file-stub" alt="test alt"&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;div class="main-header__top-right"&amp;gt;&amp;lt;div class="main-header__dropdown-btns"&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;div class="main-header__bottom"&amp;gt;&amp;lt;nav role="navigation"&amp;gt;Mocked Nav&amp;lt;/nav&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/header&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>jest</category>
      <category>react</category>
      <category>testing</category>
      <category>nextjs</category>
    </item>
    <item>
      <title>Jest - Solution for "Jest encountered an unexpected token"</title>
      <dc:creator>Jen C.</dc:creator>
      <pubDate>Mon, 05 May 2025 10:52:37 +0000</pubDate>
      <link>https://dev.to/jenchen/jest-solution-for-jest-encountered-an-unexpected-toke-4303</link>
      <guid>https://dev.to/jenchen/jest-solution-for-jest-encountered-an-unexpected-toke-4303</guid>
      <description>&lt;h2&gt;
  
  
  Pre-study
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://crystallize.com/answers/tech-dev/compiling-vs-transpiling" rel="noopener noreferrer"&gt;Compiling Vs. Transpiling&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://jestjs.io/docs/code-transformation" rel="noopener noreferrer"&gt;Jest Code Transformation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://jestjs.io/docs/configuration#transformignorepatterns-arraystring" rel="noopener noreferrer"&gt;Jest docs – transformIgnorePatterns&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;When running unit tests using Jest, get the error as below&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; Jest encountered an unexpected token

    Jest failed to parse a file. This happens e.g. when your code or its dependencies use non-standard JavaScript syntax, or when Jest is not configured to support such syntax.

    Out of the box Jest supports Babel, which will be used to transform your files into valid JS based on your Babel configuration.

    By default "node_modules" folder is ignored by transformers.

    Here's what you can do:
     • If you are trying to use ECMAScript Modules, see https://jestjs.io/docs/ecmascript-modules for how to enable it.
     • To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config.
     • If you need a custom transformation specify a "transform" option in your config.
     • If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the "moduleNameMapper" config option.

    You'll find more details and examples of these config options in the docs:
    https://jestjs.io/docs/configuration
    For information about custom transformations, see:
    https://jestjs.io/docs/code-transformation

    ...

    SyntaxError: Cannot use import statement outside a module

    &amp;gt; 1 | import axios, { isCancel } from 'axios';
        | ^
      2 | import merge from 'lodash/merge';

      at Runtime.createScriptFromCode (node_modules/jest-runtime/build/index.js:1479:14)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Root cause
&lt;/h2&gt;

&lt;p&gt;When a unit test imports a function from a module that uses &lt;strong&gt;ECMAScript Module (ESM)&lt;/strong&gt; syntax, for example, &lt;code&gt;import axios, { isCancel } from 'axios'&lt;/code&gt;, which leads to Jest fail to parse because the module (e.g., &lt;code&gt;axios&lt;/code&gt;) is located in &lt;strong&gt;node_modules&lt;/strong&gt; and uses modern syntax that Jest does not transform by default.&lt;/p&gt;

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

&lt;p&gt;Whitelist the &lt;code&gt;axios&lt;/code&gt; package so that Jest transforms it, even though it is located in &lt;strong&gt;node_modules&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In the Jest configuration file, add &lt;code&gt;axios&lt;/code&gt; to &lt;code&gt;transformIgnorePatterns&lt;/code&gt;, for example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...

"transformIgnorePatterns": [
  "/node_modules/(?!(axios)/)"
]

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Multiple packages?
&lt;/h3&gt;

&lt;p&gt;If there are more than one package need to whitelist, for example, &lt;code&gt;axios&lt;/code&gt; and &lt;code&gt;intl-messageformat&lt;/code&gt;, can do:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...

 "transformIgnorePatterns": [
      "/node_modules/(?!(axios|intl-messageformat)/)"
    ],

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

&lt;/div&gt;



</description>
      <category>jest</category>
      <category>javascript</category>
      <category>testing</category>
      <category>babel</category>
    </item>
    <item>
      <title>JavaScript – The Boundary of Date</title>
      <dc:creator>Jen C.</dc:creator>
      <pubDate>Thu, 01 May 2025 04:55:50 +0000</pubDate>
      <link>https://dev.to/jenchen/javascript-the-boundary-of-date-3771</link>
      <guid>https://dev.to/jenchen/javascript-the-boundary-of-date-3771</guid>
      <description>&lt;h2&gt;
  
  
  Resource
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date" rel="noopener noreferrer"&gt;MDN Date&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Date objects encapsulate an integral number that represents milliseconds since the midnight at the beginning of January 1, 1970, UTC (the epoch).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;Recently, we encountered an issue related to retrieving the milliseconds of a Date object for calculations by calling &lt;code&gt;getTime()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The bug occurred because the system did not account for negative values returned by &lt;code&gt;getTime()&lt;/code&gt; when the selected date is earlier than January 1, 1970. As a result, both the backend and frontend need to address this limitation and align on a solution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Some experiments to test the boundaries
&lt;/h2&gt;

&lt;p&gt;The last moment before 1970:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const lastMomentBefore1970 = new Date('1969-12-31T23:59:59.999Z');

console.log(lastMomentBefore1970.getTime());
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[LOG]: -1 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The moment right at 1970:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const epochMoment = new Date('1970-01-01T00:00:00.000Z');

console.log(epochMoment.getTime());
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[LOG]: 0 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The moment right after 1970:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const firstMomentAfter1970 = new Date('1970-01-01T00:00:00.001Z');

console.log(firstMomentAfter1970.getTime());
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[LOG]: 1 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>beginners</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
