Background: Clerk is a hosted authentication and user management product.
I recently started writing E2E tests in Cypress for an app that uses Clerk for authentication, and there wasn’t anything out there to guide me, so here’s what I ended up with after fiddling with it for a bit.
(Note: In my case, this is a Next.js app using Clerk’s Next.js SDK, but my understanding is that this code will work everywhere, because their client SDKs all ultimately use ClerkJS under the hood.)
I wrote a custom Cypress command that waits for Clerk to load, signs the user out if they aren’t signed out already, and then signs in with test credentials (see here for how you can set these so that they’re accessible via Cypress.env()
).
Cypress.Commands.add(`initializeAuth`, () => {
cy.log(`Initializing auth state.`);
cy.visit(`/`);
cy.window()
.should((window) => {
expect(window).to.not.have.property(`Clerk`, undefined);
expect(window.Clerk.isReady()).to.eq(true);
})
.then(async (window) => {
await window.Clerk.signOut();
await window.Clerk.client.signIn.create({
identifier: Cypress.env(`TEST_USER_IDENTIFIER`),
password: Cypress.env(`TEST_USER_PASSWORD`),
});
});
});
If you’re using TypeScript to write your tests (which I recommend!), you’ll also want to add this command to your custom command types.
declare global {
namespace Cypress {
interface Chainable {
/**
* Initialize auth to a state where you're
* logged in as the test user.
*
* @example cy.initializeAuth()
*/
initializeAuth(): Chainable<void>;
}
}
}
Ultimately, you’ll probably want to use this command in a before
or beforeEach
hook to reset the auth state before every test, like so:
describe(`Test Page`, () => {
beforeEach(() => {
cy.resetDatabase(); // another custom command
cy.initializeAuth();
});
// ... tests go here
});
Happy testing! Please let me know if you run into problems with this approach.