Skip to content

Commit 47dca55

Browse files
authored
tests: adds cypress tests for fullstack_demo (#662)
* tests: configures e2e Signed-off-by: Anthony D. Mays <[email protected]> * chore: removes unused types ref Signed-off-by: Anthony D. Mays <[email protected]> --------- Signed-off-by: Anthony D. Mays <[email protected]>
1 parent 060dad8 commit 47dca55

File tree

9 files changed

+309
-39
lines changed

9 files changed

+309
-39
lines changed

lib/javascript/fullstack_demo/.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,4 +48,5 @@ db.json
4848

4949
# Cypress
5050
/cypress/snapshots/actual
51-
/cypress/snapshots/diff
51+
/cypress/snapshots/diff
52+
cypress.env.json

lib/javascript/fullstack_demo/README.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,56 @@ By default, the app is configured to store data on the web server in a JSON file
2929

3030
To deploy this app, sign up for an account on Vercel and create a project pointing to your fork of this repo. You'll need to configure the environment variables you setup in your local `.env` file using the Vercel UI.
3131

32+
## Testing
33+
34+
The application includes both unit tests (Vitest) and end-to-end tests (Cypress).
35+
36+
### End-to-End Testing with Cypress and Clerk
37+
38+
The app is configured with Cypress for end-to-end testing, including authentication testing with Clerk.
39+
40+
1. Set up testing environment:
41+
- Create a `cypress.env.json` file with your Clerk testing keys:
42+
```json
43+
{
44+
"CLERK_PUBLISHABLE_KEY": "your_clerk_publishable_key_for_testing",
45+
"CLERK_SECRET_KEY": "your_clerk_secret_key_for_testing"
46+
}
47+
```
48+
49+
2. Run Cypress tests:
50+
```bash
51+
# Open Cypress test runner in interactive mode
52+
npm run cy:open:e2e
53+
54+
# Run tests headlessly
55+
npm run cy:run:e2e
56+
```
57+
58+
3. Authentication approach:
59+
- We use Clerk's Testing Tokens approach to bypass the UI authentication flow entirely.
60+
- This avoids cross-origin issues that would require `cy.origin()`.
61+
- The `setupClerkTestingToken()` function from `@clerk/testing/cypress` handles authentication behind the scenes.
62+
- This allows tests to directly access protected routes without redirects to Clerk's authentication domain.
63+
64+
4. Test example:
65+
```typescript
66+
import { setupClerkTestingToken } from '@clerk/testing/cypress';
67+
68+
describe('Authentication Test', () => {
69+
beforeEach(() => {
70+
// Set up authentication token - no UI interaction needed
71+
setupClerkTestingToken();
72+
73+
// Visit protected route directly
74+
cy.visit('/');
75+
76+
// Verify authentication successful
77+
cy.get('header').find('button').should('exist');
78+
});
79+
});
80+
```
81+
3282
## Tools
3383

3484
Here are a list of tools used to compile this demo. You can read the official documentation to learn how various aspects of the demo function.
@@ -39,3 +89,5 @@ Here are a list of tools used to compile this demo. You can read the official do
3989
- React (https://react.dev)
4090
- LowDB (https://github.com/typicode/lowdb)
4191
- Upstash Redis (https://upstash.com/docs/redis/overall/getstarted)
92+
- Cypress (https://cypress.io)
93+
- @clerk/testing (https://clerk.dev/docs/testing/integration-testing)

lib/javascript/fullstack_demo/cypress.config.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { clerkSetup } from '@clerk/testing/cypress';
12
import { defineConfig } from 'cypress';
23
import { configureVisualRegression } from 'cypress-visual-regression';
34

@@ -15,4 +16,15 @@ export default defineConfig({
1516
configureVisualRegression(on);
1617
},
1718
},
19+
e2e: {
20+
baseUrl: 'http://localhost:3000',
21+
setupNodeEvents(on, config) {
22+
configureVisualRegression(on);
23+
return clerkSetup({ config });
24+
},
25+
env: {
26+
visualRegressionType: 'regression',
27+
},
28+
screenshotsFolder: './cypress/snapshots/actual',
29+
},
1830
});
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { setupClerkTestingToken } from '@clerk/testing/cypress';
2+
3+
describe('Authentication Flow', () => {
4+
beforeEach(() => {
5+
setupClerkTestingToken();
6+
7+
cy.visit('/'); // Visit the homepage
8+
9+
// Use our custom command to login with Clerk testing
10+
cy.clerkSignIn({
11+
strategy: 'email_code',
12+
identifier: '[email protected]', // Use a test email that Clerk recognizes
13+
});
14+
});
15+
16+
it('should successfully login to the homepage', () => {
17+
// Verify the Todo App components are visible (adjust selector to match your app)
18+
cy.origin('http://localhost:3000', () => {
19+
cy.contains('Todo App', { timeout: 5000 }).should('be.visible');
20+
});
21+
});
22+
});
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { setupClerkTestingToken } from '@clerk/testing/cypress';
2+
3+
describe('Todo App Functionality', () => {
4+
beforeEach(() => {
5+
setupClerkTestingToken();
6+
7+
cy.visit('/'); // Visit the homepage
8+
9+
// Use our custom command to login with Clerk testing
10+
cy.clerkSignIn({
11+
strategy: 'email_code',
12+
identifier: '[email protected]', // Use a test email that Clerk recognizes
13+
});
14+
});
15+
16+
it('should allow adding a new todo item', () => {
17+
cy.origin('http://localhost:3000', () => {
18+
// Create a unique todo item
19+
const todoText = `Test Todo ${Date.now()}`;
20+
21+
// Find the input field and type the text
22+
cy.get('input[placeholder*="Add"]').type(todoText);
23+
24+
// Submit the form (either by clicking a button or pressing Enter)
25+
cy.get('form').submit();
26+
// Or use this if the form doesn't have a submit button: cy.get('input').type('{enter}');
27+
28+
// Verify the new todo appeared in the list
29+
cy.contains(todoText).should('be.visible');
30+
});
31+
});
32+
33+
it('should allow marking a todo item as completed', () => {
34+
cy.origin('http://localhost:3000', () => {
35+
// First, ensure there is at least one todo item to mark as completed
36+
cy.get('.todo').last().as('lastTodo');
37+
38+
// Click the checkbox or toggle button to mark it as completed
39+
cy.get('@lastTodo').find('input[type="checkbox"]').check();
40+
41+
// Verify the todo item is marked as completed (adjust selector as needed)
42+
cy.get('@lastTodo').find('input[type="checkbox"]').should('be.checked');
43+
});
44+
});
45+
});
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
import { addCompareSnapshotCommand } from 'cypress-visual-regression/dist/command';
2+
3+
// Add visual regression testing command
24
addCompareSnapshotCommand();
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/// <reference types="cypress" />
2+
import { addClerkCommands } from '@clerk/testing/cypress';
3+
import './commands';
4+
5+
// Cypress-specific imports
6+
import 'cypress-visual-regression';
7+
import { addCompareSnapshotCommand } from 'cypress-visual-regression/dist/command';
8+
9+
// Add visual regression testing command
10+
addCompareSnapshotCommand();
11+
12+
// Add Clerk commands for testing
13+
addClerkCommands({ Cypress, cy });
14+
15+
// This ensures TypeScript understands Cypress commands
16+
declare global {
17+
namespace Cypress {
18+
interface Chainable {
19+
// Add any custom command types here
20+
}
21+
}
22+
}

0 commit comments

Comments
 (0)