<?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: Chidume Kenneth</title>
    <description>The latest articles on DEV Community by Chidume Kenneth (@chidume_kenneth_36237b684).</description>
    <link>https://dev.to/chidume_kenneth_36237b684</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3384201%2Fdcdf6fc8-c35b-473e-9775-9e53d5bd0f1c.png</url>
      <title>DEV Community: Chidume Kenneth</title>
      <link>https://dev.to/chidume_kenneth_36237b684</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/chidume_kenneth_36237b684"/>
    <language>en</language>
    <item>
      <title>Why Your Playwright Tests Fail on Protected Routes (And the Simple Fix)</title>
      <dc:creator>Chidume Kenneth</dc:creator>
      <pubDate>Sat, 14 Nov 2026 15:34:06 +0000</pubDate>
      <link>https://dev.to/chidume_kenneth_36237b684/why-your-playwright-tests-fail-on-protected-routes-and-the-simple-fix-1d9n</link>
      <guid>https://dev.to/chidume_kenneth_36237b684/why-your-playwright-tests-fail-on-protected-routes-and-the-simple-fix-1d9n</guid>
      <description>&lt;p&gt;&lt;strong&gt;Introduction&lt;/strong&gt;&lt;br&gt;
When I first started testing protected routes with Playwright, everything looked correct. From login to UI and JWT authentication, everything worked perfectly.&lt;/p&gt;

&lt;p&gt;But the moment I tried to access a protected page in my E2E tests with Playwright, I kept running into a frustrating issue: 401 Unauthorized errors.&lt;/p&gt;

&lt;p&gt;At first, it didn’t make sense. The same flow worked perfectly in the browser. So Why was Playwright behaving differently?&lt;/p&gt;

&lt;p&gt;I even tried running the login test first before the protected route test, but it still did not work!&lt;/p&gt;

&lt;p&gt;The issue wasn’t in my backend logic. It was in how browser authentication state works during automated testing.&lt;/p&gt;

&lt;p&gt;Playwright does not automatically share authentication state between tests or sessions.&lt;/p&gt;

&lt;p&gt;In this article, I’ll show you the minimal and correct way to fix this using &lt;code&gt;storageState.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Problem&lt;/strong&gt;&lt;br&gt;
On a real device, you log in once, and the token is stored. (in cookies or localStorage). With this, you stay authenticated as long as the token is still valid.&lt;/p&gt;

&lt;p&gt;But in Playwright, things work differently because each test runs in a fresh browser context, and therefore, there are no cookies, localStorage, or tokens available for the next test.&lt;/p&gt;

&lt;p&gt;So even if you login in one test, the next test starts as a completely new user.&lt;/p&gt;

&lt;p&gt;Hence, the 401 Unauthorized error, and then the test eventually timing out.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What Doesn’t Work&lt;/strong&gt;&lt;br&gt;
Running login in one test and expecting another test to stay authenticated:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//login test
test.describe('login page', () =&amp;gt; {
  test.beforeEach(async ({ page }) =&amp;gt; {
    await page.goto('http://localhost:5173/sign-in')
  })
  test('should redirect to home page on sign in', async ({ page }) =&amp;gt; {
    await page.getByPlaceholder('Email').fill('elysian@gmail.com')
    await page.getByPlaceholder('Password').fill('12345')
    await page.getByRole('button', { name: 'sign in' }).click()
    await expect(page).toHaveURL(/\/$/)
  })
})

//listing page(protected route) test
test.describe('listing page', () =&amp;gt; {
  test('should have empty fields on initial load', async ({ page }) =&amp;gt; {
    await page.goto('http://localhost:5173/create-listing')
    await expect(page.getByPlaceholder('Name')).toBeVisible()
    await expect(page.getByPlaceholder('Address')).toHaveValue('')
  })
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see below, our login test passed, but then the listing page test, which required authentication to be accessed, failed!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv67h4qggepe95uf3hjbj.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv67h4qggepe95uf3hjbj.gif" alt="E2E test demonstration with Playwright" width="760" height="348"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;E2E test demonstration with Playwright&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How to Fix It&lt;/strong&gt;&lt;br&gt;
The clean solution is to save the authentication state after login:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;await page.context().storageState({ path: 'auth.json' });&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This code above saves the current browser session (cookies + localStorage) into a file called auth.json in the root folder(since we did not specify any folder in the path)&lt;/p&gt;

&lt;p&gt;So we add the above code immediately after the login button click, and after navigation has completed(to ensure the login was successful) in the login test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;test.describe('login page', () =&amp;gt; {
  test.beforeEach(async ({ page }) =&amp;gt; {
    await page.goto('http://localhost:5173/sign-in')
  })
  test('should redirect to home page on sign in', async ({ page }) =&amp;gt; {
    await page.getByPlaceholder('Email').fill('elysian@gmail.com')
    await page.getByPlaceholder('Password').fill('12345')
    await page.getByRole('button', { name: 'sign in' }).click()
    await expect(page).toHaveURL(/\/$/)

    // Here we save the login state
    await page.context().storageState({ path: 'auth.json' });
  })
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;page.context:&lt;/code&gt; This refers to the browser context, not just the tab. And this browser context contains the cookies, localStorage, and authentication state.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;storageState:&lt;/code&gt; This is a Playwright method that extracts and saves all auth-related browser data&lt;/p&gt;

&lt;p&gt;&lt;code&gt;{path:'auth.json'}:&lt;/code&gt; This tells Playwright to save everything into a file called &lt;code&gt;auth.json.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;So it creates something like this inside the auth.json file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "cookies": [
    {
      "name": "token",
      "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9*********",
      "domain": "localhost",
      "path": "/",
      "expires": 1774197120.965915,
      "httpOnly": true,
      "secure": false,
      "sameSite": "Strict"
    }
  ],
  "origins": []
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we configure Playwright to load and use the storageState we set above by adding this line inside the describe block of the test that involves a protected route:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;test.use({ storageState: 'auth.json' })&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;test.use():&lt;/code&gt; is used to configure how your tests run on Playwright.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;storageState:&lt;/code&gt; is one of the options passed to test.use(). This tells Playwright to load the saved login session.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;auth.json:&lt;/code&gt; is the name of the file where we saved the login session&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Full code&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;// @ts-check
import { test, expect } from '@playwright/test'

test.describe('login page', () =&amp;gt; {
  test.beforeEach(async ({ page }) =&amp;gt; {
    await page.goto('http://localhost:5173/sign-in')
  })
  test('should redirect to home page on sign in', async ({ page }) =&amp;gt; {
    await page.getByPlaceholder('Email').fill('elysian@gmail.com')
    await page.getByPlaceholder('Password').fill('12345')
    await page.getByRole('button', { name: 'sign in' }).click()

    await expect(page).toHaveURL(/\/$/)
    await page.context().storageState({ path: 'auth.json' })
  })
})


test.describe('listing page', () =&amp;gt; {
  test.use({ storageState: 'auth.json' })
  test.beforeEach(async ({ page }) =&amp;gt; {
    await page.goto('http://localhost:5173/create-listing')
  })
  test('should have empty fields on initial load', async ({ page }) =&amp;gt; {
    await expect(page.getByPlaceholder('Name')).toBeVisible()
    await expect(page.getByPlaceholder('Address')).toHaveValue('')
  })
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works because &lt;code&gt;storageState&lt;/code&gt;saves your cookies, localStorage, and session data, so that Playwright can load your app as if the user is already logged in.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Ensure you add the login session file(auth.json in our case) to .gitignore file to avoid exposing your application session&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Final Takeaway&lt;/strong&gt;&lt;br&gt;
So, if your Playwright tests keep failing on protected routes, it’s not your backend, nor is it your JWT logic, but because it is missing authentication state in the test environment&lt;/p&gt;

&lt;p&gt;Fix it once with storageState, and your E2E tests become stable and predictable.&lt;/p&gt;

</description>
      <category>automation</category>
      <category>playwright</category>
      <category>testing</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Story of a Component and a Developer’s Adventure using Bit</title>
      <dc:creator>Chidume Kenneth</dc:creator>
      <pubDate>Sat, 14 Nov 2026 15:21:33 +0000</pubDate>
      <link>https://dev.to/chidume_kenneth_36237b684/story-of-a-component-and-a-developers-adventure-using-bit-4fm9</link>
      <guid>https://dev.to/chidume_kenneth_36237b684/story-of-a-component-and-a-developers-adventure-using-bit-4fm9</guid>
      <description>&lt;p&gt;Ada works as a React developer in a tech firm where they provide solutions for travelers, they have an app where travelers, hikers can look up their places of interest and it shows them high tour points from the position to the tour point relative to their current location.&lt;/p&gt;

&lt;p&gt;It happens that Ada built a component that that marks the direction of a point on the Map to another point. She shows it to her fellow workmates in her team and they were like&lt;/p&gt;

&lt;p&gt;“Wow!! That’s lovely. Can you share it with me?”&lt;/p&gt;

&lt;p&gt;“Of, course” she responds&lt;/p&gt;

&lt;p&gt;Now, she tries sharing the component, but she is faced wih a serious problem.&lt;/p&gt;

&lt;p&gt;“How do I go about this?”&lt;/p&gt;

&lt;p&gt;She has different options:&lt;/p&gt;

&lt;h2&gt;
  
  
  Copy and Send the component
&lt;/h2&gt;

&lt;p&gt;Ada can copy the components and send them over to her teammates. But that’s not so efficient. If she makes a modification to the components, she will have to re-send the files over again. And her teammates would have to delete the previous files and paste the newly modified ones.&lt;/p&gt;

&lt;h2&gt;
  
  
  Share with Git and NPM
&lt;/h2&gt;

&lt;p&gt;Copying and pasting are time-consuming and very tedious. She may try to share the files with NPM. So her teammates can simply use Git to install the components from NPM.&lt;/p&gt;

&lt;p&gt;She will have to create another project for the component, a React library project.&lt;/p&gt;

&lt;p&gt;She will initialize another Git repository, install the necessary tools like Rollup, to enable her to compile the component. Build the component and publish the library.&lt;/p&gt;

&lt;p&gt;The overhead here is that she is forced to create another repo for the component and add it to her list of repos to maintain. If she comes up with another component her teammates like she will have to create another repo and share the component to NPM.&lt;/p&gt;

&lt;h2&gt;
  
  
  Share with Bit
&lt;/h2&gt;

&lt;p&gt;Somehow, she hears about Bit, a cloud platform that enables one to share components out from their project without an extra Git repo(s).&lt;/p&gt;

&lt;p&gt;Ada smiles because this is what she has been looking for. She looks up the Bit docs and finds out that it is super easy to use it.&lt;/p&gt;

&lt;p&gt;She fires up her dev environment, installs the Bit CLI, and initializes a Bit environment in her project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i bit-bin -g
bit init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then she adds the component’s entry point using Bit’s add command. Let’s say the project looks 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;trv-react-prj
    src/
        - /utils/
            ...
        - /map/
            ...
        - ...
        -index.js
        -App.js
    - package.json
    - bit.json
    - .bitmap
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Bit forces us to use the Single Responsibility Principle, that a component must have only one reason to change and must be responsible for only one thing.&lt;/p&gt;

&lt;p&gt;Components are divided into container and presentational components. Containers hold the business logic of the project and are not shareable, while the Presentationals hold the UI view of the project. These are shareable meaning they can be used in other projects without touching them or having any reason to change them — that’s what SRP says. So you see, Presentationals are components that obey SRP and are shareable to the Bit cloud platform.&lt;/p&gt;

&lt;p&gt;This will force Ada and her teammates to refactor their code to make it suitable for Bit launch.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;trv-react-prj
    src/
        - /utils/
            ...
            -index.js
        - components
            - /map/
                ...
            - clients/
                ...
            - index.js
        - ...
        -index.js
        -App.js
    - package.json
    - bit.json
    - .bitmap
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See, they have changed their codebase, so every component is in the component folder and inside the folder, the components have a separate folder, an index.js file at the component folder exports them all.&lt;/p&gt;

&lt;p&gt;Ada can now pick out her component, map folder and add it to Bit's tracked files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;$ bit add src/components/map
tracking 5 new components
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then she imports a React compiler:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bit import  bit.envs/compilers/react --compiler
the following component environments were installed
- bit.envs/compilers/react@0.0.14
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This compiler is a tool required by Bit to help it build components in isolation.&lt;/p&gt;

&lt;p&gt;Learn about Medium’s values&lt;br&gt;
Ada will go to bit.dev to create a collection. She names the collection &lt;code&gt;travellers-prj&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now, she will tag her tracked component with a version:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;$ bit tag --version 0.01&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Then, she will export the map component to the collection:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;$ bit export @travellers-prj.map&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;She tells her teammates it and gives them a link to the map’s Bit link. They are perplexed by how simple the stuff looks, the Bit playground and how they could import the component to their project using NPM and any other project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn add @travellers-prj.map
npm i @travellers-prj.map
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;She tells them all about Bit and how it was able to save her:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Copying and sending components to them&lt;/li&gt;
&lt;li&gt;Creating extra Git repos just to share a component.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Her project lead is happy and announces they will use Bit from now on. And also they refactor all their project to Bit’s best practices.&lt;/p&gt;

&lt;p&gt;Teams that scale code-sharing and code reuse enjoy faster development cycles and simpler maintenance.&lt;/p&gt;

&lt;p&gt;Ada was able to easily share her code with her teammates using Bit. Her teammates can reuse her code in their projects. There will be no need to rewrite codes from scratch, code in a project can used in another.&lt;/p&gt;

&lt;p&gt;Bit is designed to help teams scale the sharing and management of components to hundreds and even thousands of components. From that philosophy, it takes care of the components’ entire lifecycle from development to deployment so that your team can share and manage any number of components for any number of developers. This includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Managing and publishing any number of components from any number of repositories/libraries at any scale with 0 overhead.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ada can create more components and share them without creating repositories for them. Bit is left for the task, it will isolate, build and track the components for her.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Creating discoverability for shared components so that every developer can easily find and choose components&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ada after moving her components to the Bit.dev, she was able to give her teammates the link. And they were awwed by how they were able to play around the component in Bit’s playground before using them. The Bit’s playground provides enhanced discoverability for the components you share. It’s designed to help developers quickly find and choose the components they need through a universal hub with a rich, visual and interactive experience.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Increasing the adoption of shared components by removing the main barriers and encouraging collaboration.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The components shared on Bit aren’t for the maintainers, Bit enables it to be for the consumers of the components.&lt;/p&gt;

&lt;p&gt;All this above means you can do more than install a package; you can actually develop components from any project and sync changes between projects.&lt;/p&gt;

&lt;p&gt;Just like Ada did, she move her map component her teammates liked to Bit. This map component can be developed by her teammates and the changes synced to other projects.&lt;/p&gt;

&lt;p&gt;This becomes possible because, unlike package managers, Bit manages source code for shared code across different repos. So you can export a component from one repo, import it into another, make changes and sync them.&lt;/p&gt;

&lt;p&gt;When Ada built her map component using Bit’s bit build, Bi actually built an isolated React library project for her and extra Git repo. In this case, she has nothing to do with it, Bit does everything for her while she continues working on her project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;br&gt;
Bit is an awesome tool you can’t just pass by. Ada was finally saved from the stress of sharing her component with her teammates when she stumbled upon Bit.&lt;/p&gt;

&lt;p&gt;Bit is fast, user-friendly, and powerful. Try it out today, whether working on a team or working solo.&lt;/p&gt;

&lt;p&gt;Thanks !!!&lt;/p&gt;

</description>
      <category>frontend</category>
      <category>javascript</category>
      <category>react</category>
      <category>softwaredevelopment</category>
    </item>
  </channel>
</rss>
