Seamless Authentication: Automating Clerk Flows with Cypress

August 08, 2024

Introduction

In today's fast-paced development environment, test automation is crucial for maintaining reliable and efficient CI/CD pipelines. In this blog post, we’ll dive into how to automate the Clerk authentication flow using Cypress. We’ll start with an overview of what Clerk is and how it can simplify user management in your applications. Then, we'll integrate the @clerk/testing library and walk through step-by-step code examples to help you get started quickly.

What is Clerk?

Clerk is a user management solution that offers out-of-the-box authentication and user management functionalities, including sign in, sign out, user profiles, and more. It helps developers easily add user authentication to their applications without building it from scratch.

@clerk/testing Library

The official @clerk/testing library makes it easy to automate interactions with Clerk’s authentication components. By leveraging this library, you can write Cypress tests to efficiently automate and validate the sign in and sign out processes, ensuring that your authentication logic is robust and reliable.

Prerequisites

Before we get started, make sure you have the following:

  1. A project with Cypress installed (this guide is based on Cypress v13).
  2. Required Clerk API keys:
    • To obtain your Clerk keys, navigate to the API keys section in the Clerk dashboard and copy your Clerk publishable and secret keys.

Implementation Steps

Step 1: Install @clerk/testing

First, add the Clerk testing library to your project by running the following command:

npm install @clerk/testing --save-dev

For more details, refer to the official Clerk Cypress documentation. (This guide is based on v1.2.1)

Step 2: Set Up Your Clerk Keys as Environment Variables

Create a .env.local file in the root directory of your project and add the following:

CLERK_PUBLISHABLE_KEY=<clerk_publishable_key>
CLERK_SECRET_KEY=<clerk_secret_key>

This will allow Cypress to securely access your Clerk API keys during tests.

🔐 Best Practice Reminder: Ensure sensitive environment variables, including API keys, are never committed to repositories. Utilize .env files for local environment variable management and adopt secure practices for storing and accessing secrets within your CI/CD pipelines.

Step 3: Configure Cypress

In your cypress.config.js file, add the following code to integrate Clerk with Cypress:

// cypress.config.js
const { clerkSetup } = require('@clerk/testing/cypress');
const { defineConfig } = require('cypress');

module.exports = defineConfig({
  e2e: {
    baseUrl: 'https://yourwebsite.com', // Replace with your app's URL
    setupNodeEvents (on, config) {
      return clerkSetup ({ config }):
    }
  },
})

This configuration ensures that Clerk is correctly set up and integrated with Cypress.

Step 4: Write a Test Using the UI

⚠️ Important: This is an example—ensure that all selectors, paths, and text are updated according to your application's specific implementation.

Now, let's write a test to perform a login through the UI. Create a new test file in your Cypress e2e folder, for example, signin-ui.cy.js, and add the following code:

// cypress/e2e/signin-ui.cy.js
import { setupClerkTestingToken } from '@clerk/testing/cypress';

describe('Clerk UI Authentication Flow', () => {
  it('should sign in using the ui', () => {
    // Set up Clerk testing token to bypass bot protection during tests
    setupClerkTestingToken();

    // Navigate to the Login page
    cy.visit('/login'); 

    // Interact with Clerk UI elements
    cy.get('[data-testid="email"]').type('testuser@example.com');
    cy.get('[data-testid="password"]').type('testpassword', { log: false });
    cy.get('[data-testid=="signin"]').click();

    // Verify successful login
    cy.contains('Welcome, Test User');
  });
});

Step 5: Write a Test Using Clerk’s Sign In and Sign Out Custom Commands

To directly use Clerk's sign in and sign out custom command, create a new test file in your Cypress e2e folder, for example, signin-clerk.cy.js, and add the following code:

// cypress/e2e/signin-clerk.cy.js
import { 
  addClerkCommands, 
  setupClerkTestingToken,
} from '@clerk/testing/cypress';

describe('Clerk Command Authentication Flow', () => {
  it('should sign in and sign out using clerk command', () => {
    // Add Clerk authentication commands to Cypress
    addClerkCommands ({ Cypress, cy });

    // Navigate to the Home page
    cy.visit('/');
    
    // Sign in with Clerk command
    cy.clerkSignIn({
      strategy: 'password', 
      identifier: 'testuser@example.com', 
      password: 'testpassword',
    });

    // Navigate to the Dashboard page and verify successful login
    cy.visit('/dashboard');
    cy.contains('Welcome, Test User');

    // Sign-out with Clerk command
    cy.clerkSignOut();
  });
});

Bonus: Create Custom Commands for Sign In with cy.session()

To avoid signing in before each test, which can slow down execution, you can create a custom Cypress command and use cy.session() to cache and restore the session. This approach reduces execution time and helps prevents flaky tests.

In your e2e.js file, add the following code:

// cypress/support/e2e.js
import { 
  addClerkCommands, 
  setupClerkTestingToken,
} from '@clerk/testing/cypress';

/**
 * Sign in using the application's UI.
 * 
 * @param {string} username
 * @param {string} password
 */
Cypress.Commands.add('signInUsingUI', (username, password) => {
  cy.session (username, () => {
    // Set up Clerk testing token to bypass bot protection during tests
    setupClerkTestingToken()

    // Navigate to the Home page
    cy.visit('/');

    // Interact with Clerk UI elements
    cy.get('[data-testid="email"]').type('testuser@example.com');
    cy.get('[data-testid="password"]').type('testpassword', { log: false });
    cy.get('[data-testid=="signin"]').click();

    // Verify successful login
    cy.contains('Welcome, Test User');
    });
});

/**
 * Sign in using Clerk's clerkSignIn command.
 * 
 * @param {string} username
 * @param {string} password
 */
Cypress.Commands.add('signInUsingClerkCommand', (username, password) => {
  cy.session (username, () => {
    // Add Clerk authentication commands to Cypress
    addClerkCommands ({ Cypress, cy });

    // Navigate to the Home page
    cy.visit('/');
    
    // Sign in with Clerk command
    cy.clerkSignIn({
      strategy: 'password', 
      identifier: 'testuser@example.com', 
      password: 'testpassword',
    });

    // Navigate to the Dashboard page and verify successful login
    cy.visit('/dashboard');
    cy.contains('Welcome, Test User');
  });
});

Now, you can update your test files as follows:

// cypress/e2e/signin-ui.cy.js
describe('Clerk UI Authentication Flow', () => {
  it('should sign in using the ui', () => {
    cy.signInUsingUI('testuser@example.com','testpassword');
  });
});
// cypress/e2e/signin-clerk.cy.js
describe('Clerk Command Authentication Flow', () => {
  it('should sign in using clerk command', () => {
    cy.signInUsingClerkCommand('testuser@example.com','testpassword');
  });
});

Wrapping Up

Automate Clerk authentication flows in Cypress to ensure your sign in and sign out processes work smoothly. By using the official Clerk Cypress testing library, you can easily write tests that validate these flows, improving reliability and speeding up development by catching issues early.

References


Profile picture

Darpan Shah

Dedication to software quality is what you'll find here front and center. Dive into insights, challenges, and solutions drawn from my experiences.