Skip to content

tests: adds cypress tests for fullstack_demo #662

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion lib/javascript/fullstack_demo/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,5 @@ db.json

# Cypress
/cypress/snapshots/actual
/cypress/snapshots/diff
/cypress/snapshots/diff
cypress.env.json
52 changes: 52 additions & 0 deletions lib/javascript/fullstack_demo/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,56 @@ By default, the app is configured to store data on the web server in a JSON file

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.

## Testing

The application includes both unit tests (Vitest) and end-to-end tests (Cypress).

### End-to-End Testing with Cypress and Clerk

The app is configured with Cypress for end-to-end testing, including authentication testing with Clerk.

1. Set up testing environment:
- Create a `cypress.env.json` file with your Clerk testing keys:
```json
{
"CLERK_PUBLISHABLE_KEY": "your_clerk_publishable_key_for_testing",
"CLERK_SECRET_KEY": "your_clerk_secret_key_for_testing"
}
```

2. Run Cypress tests:
```bash
# Open Cypress test runner in interactive mode
npm run cy:open:e2e

# Run tests headlessly
npm run cy:run:e2e
```

3. Authentication approach:
- We use Clerk's Testing Tokens approach to bypass the UI authentication flow entirely.
- This avoids cross-origin issues that would require `cy.origin()`.
- The `setupClerkTestingToken()` function from `@clerk/testing/cypress` handles authentication behind the scenes.
- This allows tests to directly access protected routes without redirects to Clerk's authentication domain.

4. Test example:
```typescript
import { setupClerkTestingToken } from '@clerk/testing/cypress';

describe('Authentication Test', () => {
beforeEach(() => {
// Set up authentication token - no UI interaction needed
setupClerkTestingToken();

// Visit protected route directly
cy.visit('/');

// Verify authentication successful
cy.get('header').find('button').should('exist');
});
});
```

## Tools

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.
Expand All @@ -39,3 +89,5 @@ Here are a list of tools used to compile this demo. You can read the official do
- React (https://react.dev)
- LowDB (https://github.com/typicode/lowdb)
- Upstash Redis (https://upstash.com/docs/redis/overall/getstarted)
- Cypress (https://cypress.io)
- @clerk/testing (https://clerk.dev/docs/testing/integration-testing)
12 changes: 12 additions & 0 deletions lib/javascript/fullstack_demo/cypress.config.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { clerkSetup } from '@clerk/testing/cypress';
import { defineConfig } from 'cypress';
import { configureVisualRegression } from 'cypress-visual-regression';

Expand All @@ -15,4 +16,15 @@ export default defineConfig({
configureVisualRegression(on);
},
},
e2e: {
baseUrl: 'http://localhost:3000',
setupNodeEvents(on, config) {
configureVisualRegression(on);
return clerkSetup({ config });
},
env: {
visualRegressionType: 'regression',
},
screenshotsFolder: './cypress/snapshots/actual',
},
});
22 changes: 22 additions & 0 deletions lib/javascript/fullstack_demo/cypress/e2e/auth.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { setupClerkTestingToken } from '@clerk/testing/cypress';

describe('Authentication Flow', () => {
beforeEach(() => {
setupClerkTestingToken();

cy.visit('/'); // Visit the homepage

// Use our custom command to login with Clerk testing
cy.clerkSignIn({
strategy: 'email_code',
identifier: '[email protected]', // Use a test email that Clerk recognizes
});
});

it('should successfully login to the homepage', () => {
// Verify the Todo App components are visible (adjust selector to match your app)
cy.origin('http://localhost:3000', () => {
cy.contains('Todo App', { timeout: 5000 }).should('be.visible');
});
Comment on lines +18 to +20
Copy link
Preview

Copilot AI Jun 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Using cy.origin here may not be needed given the testing token setup and baseUrl. Consider simplifying by removing the cy.origin block for clearer test flow.

Suggested change
cy.origin('http://localhost:3000', () => {
cy.contains('Todo App', { timeout: 5000 }).should('be.visible');
});
cy.contains('Todo App', { timeout: 5000 }).should('be.visible');

Copilot uses AI. Check for mistakes.

});
});
45 changes: 45 additions & 0 deletions lib/javascript/fullstack_demo/cypress/e2e/todo.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { setupClerkTestingToken } from '@clerk/testing/cypress';

describe('Todo App Functionality', () => {
beforeEach(() => {
setupClerkTestingToken();

cy.visit('/'); // Visit the homepage

// Use our custom command to login with Clerk testing
cy.clerkSignIn({
strategy: 'email_code',
identifier: '[email protected]', // Use a test email that Clerk recognizes
});
});

it('should allow adding a new todo item', () => {
cy.origin('http://localhost:3000', () => {
// Create a unique todo item
const todoText = `Test Todo ${Date.now()}`;

// Find the input field and type the text
cy.get('input[placeholder*="Add"]').type(todoText);

// Submit the form (either by clicking a button or pressing Enter)
cy.get('form').submit();
// Or use this if the form doesn't have a submit button: cy.get('input').type('{enter}');

// Verify the new todo appeared in the list
cy.contains(todoText).should('be.visible');
});
});

it('should allow marking a todo item as completed', () => {
cy.origin('http://localhost:3000', () => {
// First, ensure there is at least one todo item to mark as completed
cy.get('.todo').last().as('lastTodo');

// Click the checkbox or toggle button to mark it as completed
cy.get('@lastTodo').find('input[type="checkbox"]').check();

// Verify the todo item is marked as completed (adjust selector as needed)
cy.get('@lastTodo').find('input[type="checkbox"]').should('be.checked');
});
Comment on lines +17 to +43
Copy link
Preview

Copilot AI Jun 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Wrapping test interactions in cy.origin is unnecessary when using setupClerkTestingToken() and a configured baseUrl. You can simplify tests by removing cy.origin blocks and directly interact with the app.

Suggested change
cy.origin('http://localhost:3000', () => {
// Create a unique todo item
const todoText = `Test Todo ${Date.now()}`;
// Find the input field and type the text
cy.get('input[placeholder*="Add"]').type(todoText);
// Submit the form (either by clicking a button or pressing Enter)
cy.get('form').submit();
// Or use this if the form doesn't have a submit button: cy.get('input').type('{enter}');
// Verify the new todo appeared in the list
cy.contains(todoText).should('be.visible');
});
});
it('should allow marking a todo item as completed', () => {
cy.origin('http://localhost:3000', () => {
// First, ensure there is at least one todo item to mark as completed
cy.get('.todo').last().as('lastTodo');
// Click the checkbox or toggle button to mark it as completed
cy.get('@lastTodo').find('input[type="checkbox"]').check();
// Verify the todo item is marked as completed (adjust selector as needed)
cy.get('@lastTodo').find('input[type="checkbox"]').should('be.checked');
});
// Create a unique todo item
const todoText = `Test Todo ${Date.now()}`;
// Find the input field and type the text
cy.get('input[placeholder*="Add"]').type(todoText);
// Submit the form (either by clicking a button or pressing Enter)
cy.get('form').submit();
// Or use this if the form doesn't have a submit button: cy.get('input').type('{enter}');
// Verify the new todo appeared in the list
cy.contains(todoText).should('be.visible');
});
it('should allow marking a todo item as completed', () => {
// First, ensure there is at least one todo item to mark as completed
cy.get('.todo').last().as('lastTodo');
// Click the checkbox or toggle button to mark it as completed
cy.get('@lastTodo').find('input[type="checkbox"]').check();
// Verify the todo item is marked as completed (adjust selector as needed)
cy.get('@lastTodo').find('input[type="checkbox"]').should('be.checked');

Copilot uses AI. Check for mistakes.

});
});
2 changes: 2 additions & 0 deletions lib/javascript/fullstack_demo/cypress/support/commands.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
import { addCompareSnapshotCommand } from 'cypress-visual-regression/dist/command';

// Add visual regression testing command
addCompareSnapshotCommand();
22 changes: 22 additions & 0 deletions lib/javascript/fullstack_demo/cypress/support/e2e.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/// <reference types="cypress" />
import { addClerkCommands } from '@clerk/testing/cypress';
import './commands';

// Cypress-specific imports
import 'cypress-visual-regression';
import { addCompareSnapshotCommand } from 'cypress-visual-regression/dist/command';

// Add visual regression testing command
addCompareSnapshotCommand();

Comment on lines +9 to +11
Copy link
Preview

Copilot AI Jun 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate invocation of addCompareSnapshotCommand() already provided via commands.ts. Consider removing this line or the one in commands.ts to centralize command registration.

Suggested change
// Add visual regression testing command
addCompareSnapshotCommand();
// Removed duplicate invocation of addCompareSnapshotCommand()

Copilot uses AI. Check for mistakes.

// Add Clerk commands for testing
addClerkCommands({ Cypress, cy });

// This ensures TypeScript understands Cypress commands
declare global {
namespace Cypress {
interface Chainable {
// Add any custom command types here
}
}
}
Loading
Loading