Thank you for your interest in contributing to ReplyQueue! This document provides guidelines for contributing to the project.
- Development Environment Setup
- Code Style
- Submitting Issues
- Pull Request Process
- Adding a New Platform Adapter
- Testing Requirements
- Node.js 18 or higher
- pnpm 9 or higher
- Chrome browser (for testing the extension)
- Git
-
Fork and clone the repository
git clone https://github.com/YOUR_USERNAME/replyqueue.git cd replyqueue -
Install dependencies
pnpm install
-
Start the development server
pnpm dev
-
Load the extension in Chrome
- Open
chrome://extensions/ - Enable "Developer mode"
- Click "Load unpacked"
- Select the
distfolder
- Open
-
Run tests
pnpm test
replyqueue/
├── src/
│ ├── platforms/ # Platform adapters (LinkedIn, etc.)
│ ├── content/ # Content script (runs on social media)
│ ├── background/ # Service worker
│ ├── sidepanel/ # Vue 3 side panel UI
│ └── shared/ # Shared utilities and types
├── tests/ # Test files (mirrors src/ structure)
└── public/ # Static assets
- Use TypeScript strict mode
- Prefer
interfaceovertypefor object shapes - Use explicit return types for exported functions
- Avoid
any- useunknownif type is truly unknown
// Good
export function extractPost(element: Element): ExtractedPost | null {
// ...
}
// Avoid
export function extractPost(element: any) {
// ...
}- Use Composition API with
<script setup> - Use TypeScript in components
- Keep components focused and single-purpose
- Extract reusable logic into composables
<script setup lang="ts">
import { ref, computed } from 'vue'
const count = ref(0)
const doubled = computed(() => count.value * 2)
</script>| Type | Convention | Example |
|---|---|---|
| Files | kebab-case | post-card.vue, rss-fetcher.ts |
| Components | PascalCase | PostCard.vue, StatusBar.vue |
| Composables | camelCase with use prefix |
useConfig.ts, usePosts.ts |
| Constants | SCREAMING_SNAKE_CASE | MAX_POSTS, API_BASE_URL |
| Functions | camelCase | extractPost(), validateKey() |
| Interfaces | PascalCase | ExtractedPost, PlatformAdapter |
The project uses Prettier for formatting and ESLint for linting:
pnpm lint # Check for lint errors
pnpm lint:fix # Auto-fix lint issues
pnpm format # Format code with PrettierKey formatting rules:
- 2 spaces for indentation
- Single quotes for strings
- No semicolons (unless required)
- Trailing commas in multiline
When reporting a bug, please include:
- Description - Clear description of the issue
- Steps to reproduce - Numbered steps to recreate the bug
- Expected behavior - What should happen
- Actual behavior - What actually happens
- Environment
- Chrome version
- Operating system
- Extension version
- Console errors - Any errors from DevTools console
- Screenshots - If applicable
Template:
## Bug Description
[Clear description]
## Steps to Reproduce
1. Go to...
2. Click on...
3. See error
## Expected Behavior
[What should happen]
## Actual Behavior
[What actually happens]
## Environment
- Chrome: [version]
- OS: [operating system]
- Extension: [version]
## Console Errors[paste errors here]
## Screenshots
[if applicable]
For feature requests, please include:
- Problem statement - What problem does this solve?
- Proposed solution - How should it work?
- Alternatives considered - Other approaches you thought of
- Additional context - Mockups, examples, etc.
- Check existing issues and PRs to avoid duplicates
- For significant changes, open an issue first to discuss
- Fork the repository and create a feature branch
Use descriptive branch names:
feature/twitter-adapter- New featuresfix/rss-parsing-error- Bug fixesdocs/update-readme- Documentationrefactor/storage-layer- Code refactoring
-
Create a branch
git checkout -b feature/your-feature-name
-
Make your changes
- Follow the code style guidelines
- Add tests for new functionality
- Update documentation if needed
-
Run tests
pnpm test -
Lint and format
pnpm lint:fix # Fix lint issues pnpm format # Format with Prettier
-
Build the extension
pnpm build
-
Test manually
- Load the extension in Chrome
- Test your changes in a real scenario
- Push your branch to your fork
- Open a Pull Request against
main - Fill out the PR template
- Link any related issues
PR Template:
## Description
[What does this PR do?]
## Type of Change
- [ ] Bug fix
- [ ] New feature
- [ ] Documentation
- [ ] Refactoring
## Testing
- [ ] Unit tests added/updated
- [ ] Manual testing completed
## Checklist
- [ ] Code follows project style
- [ ] Linting passes (`pnpm lint`)
- [ ] Code formatted (`pnpm format`)
- [ ] Tests pass locally (`pnpm test`)
- [ ] Build succeeds (`pnpm build`)
- [ ] Documentation updated
- [ ] No console.log in production code- Maintainers will review your PR
- Address any requested changes
- Once approved, your PR will be merged
Adding support for a new social media platform involves several steps.
cp -r src/platforms/_template src/platforms/twitterDefine CSS selectors for the platform's DOM:
export const platformSelectors: FeedSelectors = {
postItem: '[data-testid="tweet"]',
postContent: '[data-testid="tweetText"]',
authorName: '[data-testid="User-Name"] span',
authorHeadline: '[data-testid="UserName"] + div',
authorProfileLink: '[data-testid="User-Name"] a',
postTimestamp: 'time',
reactionCount: '[data-testid="like"] span',
commentCount: '[data-testid="reply"] span',
repostCount: '[data-testid="retweet"] span',
}Update the class with platform-specific logic:
export class TwitterAdapter implements PlatformAdapter {
readonly platformId = 'twitter'
readonly platformName = 'Twitter'
readonly selectors: FeedSelectors = platformSelectors
isFeedPage(url: string): boolean {
return /twitter\.com\/(home|[^/]+\/status)/.test(url)
}
extractPost(element: Element): ExtractedPost | null {
// Platform-specific extraction logic
}
getPostUrl(postId: string): string {
return `https://twitter.com/i/status/${postId}`
}
scrollToPost(postId: string): boolean {
// Scroll implementation
}
}In src/platforms/index.ts:
import { TwitterAdapter } from './twitter/adapter'
const adapters: PlatformAdapter[] = [
new LinkedInAdapter(),
new TwitterAdapter(), // Add your adapter
]In manifest.json, add:
{
"host_permissions": [
"https://twitter.com/*",
"https://x.com/*"
],
"content_scripts": [
{
"matches": ["https://twitter.com/*", "https://x.com/*"],
"js": ["src/content/index.ts"]
}
]
}Create tests/platforms/twitter-extraction.test.ts:
import { describe, it, expect, beforeEach } from 'vitest'
import { TwitterAdapter } from '../../src/platforms/twitter/adapter'
describe('TwitterAdapter', () => {
let adapter: TwitterAdapter
beforeEach(() => {
adapter = new TwitterAdapter()
})
it('should have correct platform ID', () => {
expect(adapter.platformId).toBe('twitter')
})
it('should identify feed pages', () => {
expect(adapter.isFeedPage('https://twitter.com/home')).toBe(true)
expect(adapter.isFeedPage('https://twitter.com/settings')).toBe(false)
})
// Add more tests for extraction logic
})In src/background/index.ts, add the platform's domain to the ALLOWED_CONTENT_SCRIPT_ORIGINS regex:
const ALLOWED_CONTENT_SCRIPT_ORIGINS = /^https:\/\/(www\.)?(linkedin\.com|twitter\.com|x\.com)\//;This security measure ensures the background script only accepts messages from authorized platform domains.
# Run all tests
pnpm test
# Run tests in watch mode
pnpm test:watch
# Run specific test file
pnpm test tests/shared/validation.test.tsTests mirror the source structure:
tests/
├── shared/ # Tests for shared utilities
├── platforms/ # Platform adapter tests
├── content/ # Content script tests
├── background/ # Background worker tests
├── sidepanel/ # Composable tests
└── integration/ # End-to-end flow tests
import { describe, it, expect, beforeEach, vi } from 'vitest'
describe('ComponentName', () => {
beforeEach(() => {
// Setup before each test
})
it('should do something specific', () => {
// Arrange
const input = 'test'
// Act
const result = functionUnderTest(input)
// Assert
expect(result).toBe('expected')
})
})The test setup (tests/setup.ts) provides mocks for Chrome APIs:
// Chrome storage is automatically mocked
await chrome.storage.sync.set({ key: 'value' })
const result = await chrome.storage.sync.get('key')- Unit tests: Individual functions and composables
- Integration tests: Full workflow scenarios
- Edge cases: Error handling, empty states, invalid input
Before submitting a PR:
- All existing tests must pass
- New functionality must have tests
- Test coverage should not decrease significantly
If you have questions about contributing:
- Check existing issues and discussions
- Open a new issue with the "question" label
- Reach out via the contact info in README
Thank you for contributing to ReplyQueue!