browser-sdk / rules
Cursor rule400 starsChanged 9 months agoApache-2.0View on GitHub
We split this unit-test-best-practices.mdc into the rules it gives an agent
1 rule. About 0 are common, 0 are distinctive, and 1 is very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈0
Distinctive · estimate
This author's own choices. Worth a look.
1
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 88
Co-location: Test files use .spec.ts extension and are placed next to source files
≈ “Common” is an estimate: at least 1 in 5 of the 6,350 Cursor rule files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
---
description:
globs: **/*.spec.ts
alwaysApply: false
---
## Running Unit Tests
### Basic Commands
- **Run all unit tests**: `yarn test:unit`
- **Run a specific spec file**: `yarn test:unit --spec <file-path>`
- **Focus on specific tests**: Prepend `f` to `describe` and `it` (e.g., `fdescribe`, `fit`)
### Examples
#### Run All Tests
```bash
yarn test:unit
```
#### Run Single File
```bash
# Full path
yarn test:unit --spec packages/js-core/src/dom/addEventListener.spec.ts
# Pattern matching (if filename is unique)
yarn test:unit --spec "packages/**/addEventListener.spec.ts"
# Multiple files with wildcards
yarn test:unit --spec "packages/browser-core/**/browser/*.spec.ts"
```
#### Focus on Specific Tests
```typescript
// Run only this describe block
fdescribe('addEventListener', () => {
// All tests in this block will run
it('should add event listener', () => {})
it('should remove event listener', () => {})
})
// Run only this single test
describe('addEventListener', () => {
fit('should add event listener', () => {
// Only this test will run
})
it('should remove event listener', () => {
// This test will be skipped
})
})
```
#### Test observable/callback cleanup
- **Subscription Management**: Testing that event subscriptions are properly cleaned up
- **DOM Event Cleanup**: Ensuring event listeners are removed from DOM elements
- **Timer Cleanup**: Verifying that intervals and timeouts are cleared
- **Async Operation Cancellation**: Testing that pending promises/callbacks are cancelled
- **Observable**: Ensuring observers are unsubscribed and streams are completed
- **Memory Leak Detection**: Patterns for detecting memory leaks in tests
## Debugging Flaky Tests
```bash
# 1. Check test output for randomization seed
yarn test:unit
# Look for: "Randomized with seed 65781"
# 2. Set seed in karma configuration
# Edit test/unit/karma.base.conf.js:
module.exports = {
// ... other config
client: {
jasmine: {
// ... other jasmine config
seed: 65781 // Use the seed from step 1
}
}
}
# 3. Run tests with the same order
yarn test:unit
# 4. Remove seed after debugging
```
### File Organization
- **Co-location**: Test files use `.spec.ts` extension and are placed next to source files
- **Naming pattern**: `sourceFile.ts` → `sourceFile.spec.ts`
- **Auto-discovery**: Tests are found via glob: `packages/*/@(src|test)/**/*.spec.@(ts|tsx)`
## Common Anti-Patterns to Avoid
### ❌ Don't Do This
```typescript
// DON'T: Use afterEach for cleanup - may not run if test fails
afterEach(() => {
subscription.unsubscribe() // Might not execute
element.remove() // Memory leak potential
})
// DON'T: Create DOM manually
it('should test DOM interaction', () => {
const element = document.createElement('div')
document.body.appendChild(element)
// Missing cleanup - memory leak!
// use appendElement helper!
})
// DON'T: Use real timers - makes tests slow and flaky
it('should handle delayed action', (done) => {
setTimeout(() => {
expect(something).toBeTruthy()
done()
}, 1000) // Real 1-second delay!
})
// DON'T: Test multiple unrelated behaviors
it('should do everything', () => {
// Setup user
const user = createUser()
expect(user.id).toBeDefined()
// Test login
user.login()
expect(user.isLoggedIn).toBeTruthy()
// Test permissions
expect(user.hasPermission('admin')).toBeFalsy()
// Test logout
user.logout()
expect(user.isLoggedIn).toBeFalsy()
// Hard to debug when one part fails!
})
// DON'T: Use vague test names
it('should work', () => {
// What should work? How?
})
it('should test the function', () => {
// Which function? What aspect?
})
```
### ✅ Do This Instead
```typescript
// DO: Use registerCleanupTask for reliable cleanup
beforeEach(() => {
const subscription = observable.subscribe(handler)
registerCleanupTask(() => subscription.unsubscribe())
const element = document.createElement('div')
document.body.appendChild(element)
registerCleanupTask(() => element.parentNode?.removeChild(element))
})
// DO: use existing helpers
it('should add DOM with appendElement helper', () => {
appendElement('<input type="checkbox" id="test-checkbox" />')
const label = appendElement('<label for="test-checkbox">Check me</label>')
})
// DO: Use mocked clock for time-based tests
beforeEach(() => {
clock = mockClock()
})
it('should handle delayed operations', () => {
const callback = jasmine.createSpy()
setTimeout(callback, 1000)
clock.tick(1000) // Instant!
expect(callback).toHaveBeenCalled()
})
// DO: One focused assertion per test
describe('User', () => {
it('should generate unique ID on creation', () => {
const user = createUser()
expect(user.id).toMatch(/^user-\d+$/)
})
it('should start in logged-out state', () => {
const user = createUser()
expect(user.isLoggedIn).toBeFalsy()
})
it('should allow login with valid credentials', () => {
const user = createUser()
user.login('valid@email.com', 'password')
expect(user.isLoggedIn).toBeTruthy()
})
it('should deny admin permissions to regular user', () => {
const user = createUser()
expect(user.hasPermission('admin')).toBeFalsy()
})
})
// DO: Use descriptive, specific test names
it('should generate SHA-256 hash for user password', () => {
// Clear what's being tested
})
it('should throw ValidationError when email format is invalid', () => {
// Clear behavior and expected outcome
})
```
Where files are headingNew Cursor rule files have more rules about planning and tracking tasks, and fewer about code style.
Of the Cursor rule files we read, the 3,978 first seen in the last six months, against the older ones: the kind of rule that gained most share, and the kind that lost most. About files like this in general, not this one, and an estimate, since a kind is a pattern in the words.
In this repository8 other files it gives its agents.
- AGENTS.mdAGENTS.md
- scripts/AGENTS.mdAGENTS.md
- CLAUDE.mdCLAUDE.md
- scripts/CLAUDE.mdCLAUDE.md
- .cursor/rules/test-driven-development.mdcCursor rule
- manual-testingSkill
- running-testsSkill
- writing-commits-and-prsSkill
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

