feat(test): add comprehensive testing guidelines
Establishes clear testing standards and best practices: - Mandates unit testing for all API endpoints - Defines test organization and file structure - Introduces testing scope and strategy - Documents best practices for writing tests - Sets minimum coverage requirements (80%) - Adds test data management guidelines Updates project documentation to reflect testing requirements and development workflow. Includes practical examples of good testing patterns and proper test organization.
This commit is contained in:
parent
3438170d95
commit
a6ea1391f2
9 changed files with 975 additions and 39 deletions
|
|
@ -156,50 +156,238 @@ export async function createLink(url: string, slug?: string): Promise<LinkData>
|
|||
}
|
||||
```
|
||||
|
||||
## Testing Requirements
|
||||
## Testing Standards
|
||||
|
||||
### Type Checking
|
||||
- Resolve all TypeScript errors
|
||||
- Use strict type configuration
|
||||
### Unit Testing Requirements
|
||||
- **Mandatory for all `server/api` endpoints** - Every API route must have comprehensive unit tests
|
||||
- Use Vitest as the primary testing framework with Nuxt Test Utils
|
||||
- Follow the Test-Driven Development (TDD) approach when possible
|
||||
- Maintain test coverage above 80% for API endpoints
|
||||
|
||||
### Code Style
|
||||
- Follow ESLint rules
|
||||
- Use Prettier for code formatting
|
||||
- Automatically run lint-staged on commit
|
||||
### Testing Scope and Strategy
|
||||
- **API Layer**: Test all endpoints in [server/api/](mdc:server/api/) directory
|
||||
- **Validation Logic**: Test Zod schemas and input validation
|
||||
- **Error Handling**: Verify error responses and status codes
|
||||
- **Authentication**: Test token validation and authorization flows
|
||||
- **Business Logic**: Cover edge cases and critical functionality
|
||||
|
||||
### Manual Testing
|
||||
- Test main user flows
|
||||
- Verify API endpoint functionality
|
||||
- Check responsive design
|
||||
### Test File Organization
|
||||
```typescript
|
||||
// File naming convention
|
||||
tests/api/link/create.test.ts // Unit tests for API endpoints
|
||||
tests/api/helpers/fixtures.ts // Shared test data
|
||||
tests/sink.spec.ts // Integration/E2E tests
|
||||
```
|
||||
|
||||
### Testing Best Practices
|
||||
- **Write descriptive test names** that explain the behavior being tested
|
||||
- **Test both happy path and error scenarios**
|
||||
- **Use meaningful assertions** that validate expected behavior
|
||||
- **Keep tests independent** - no test should depend on another test's state
|
||||
- **Mock external dependencies** to ensure test reliability
|
||||
|
||||
```typescript
|
||||
// ✅ Good test structure
|
||||
describe('POST /api/link/create', () => {
|
||||
it('should create a new link with valid URL and custom slug', async () => {
|
||||
const response = await $fetch('/api/link/create', {
|
||||
method: 'POST',
|
||||
body: { url: 'https://example.com', slug: 'my-link' }
|
||||
})
|
||||
|
||||
expect(response.success).toBe(true)
|
||||
expect(response.data.slug).toBe('my-link')
|
||||
expect(response.data.url).toBe('https://example.com')
|
||||
})
|
||||
|
||||
it('should reject requests with invalid URL format', async () => {
|
||||
await expect($fetch('/api/link/create', {
|
||||
method: 'POST',
|
||||
body: { url: 'not-a-valid-url' }
|
||||
})).rejects.toThrow()
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
### Test Data Management
|
||||
- Use factory functions for creating test data
|
||||
- Store common test fixtures in `tests/api/helpers/fixtures.ts`
|
||||
- Avoid hardcoded values in tests; use constants and variables
|
||||
|
||||
```typescript
|
||||
// ✅ Good test data management
|
||||
const validLinkPayload = {
|
||||
url: 'https://example.com',
|
||||
slug: 'test-slug',
|
||||
expiresAt: new Date(Date.now() + 86400000).toISOString()
|
||||
}
|
||||
|
||||
const invalidUrlPayload = {
|
||||
url: 'invalid-url-format',
|
||||
slug: 'test-slug'
|
||||
}
|
||||
```
|
||||
|
||||
### Running and Maintaining Tests
|
||||
- Run tests locally before pushing changes: `npm run test`
|
||||
- Use watch mode during development: `npm run test:watch`
|
||||
- Generate coverage reports: `npm run test:coverage`
|
||||
- Tests must pass in CI/CD pipeline before merging
|
||||
|
||||
## Git Commit Conventions
|
||||
|
||||
### Commit Message Format
|
||||
Follow the conventional commit specification:
|
||||
```
|
||||
type(scope): description
|
||||
type(scope): brief description
|
||||
|
||||
body (optional)
|
||||
Optional body explaining what and why vs how
|
||||
|
||||
footer (optional)
|
||||
Optional footer with issue references
|
||||
```
|
||||
|
||||
### Commit Types
|
||||
- `feat`: New feature
|
||||
- `fix`: Bug fix
|
||||
- `docs`: Documentation update
|
||||
- `style`: Code style (no functional impact)
|
||||
- `refactor`: Refactoring
|
||||
- `perf`: Performance optimization
|
||||
- `test`: Testing related
|
||||
- `chore`: Build process or auxiliary tool changes
|
||||
- **feat**: New feature implementation
|
||||
- **fix**: Bug fix or issue resolution
|
||||
- **docs**: Documentation updates only
|
||||
- **style**: Code formatting changes (no functional impact)
|
||||
- **refactor**: Code restructuring without changing functionality
|
||||
- **perf**: Performance optimization
|
||||
- **test**: Adding or modifying tests
|
||||
- **chore**: Build process, dependency updates, or auxiliary tool changes
|
||||
- **ci**: Continuous integration configuration changes
|
||||
|
||||
### Example
|
||||
```
|
||||
feat(api): add AI-powered slug generation
|
||||
### Scope Examples
|
||||
- **api**: Changes to server/api endpoints
|
||||
- **ui**: Frontend components or styling
|
||||
- **auth**: Authentication related changes
|
||||
- **db**: Database or data storage changes
|
||||
- **config**: Configuration file updates
|
||||
|
||||
### Writing Good Commit Messages
|
||||
|
||||
#### Good Examples
|
||||
```bash
|
||||
feat(api): add AI-powered slug generation endpoint
|
||||
|
||||
Integrate Cloudflare AI Workers to automatically generate
|
||||
meaningful slugs based on URL content and page titles.
|
||||
|
||||
- Integrate Cloudflare AI for automatic slug creation
|
||||
- Add new /api/link/ai endpoint
|
||||
- Implement content analysis logic
|
||||
- Update link creation flow to support AI suggestions
|
||||
- Add comprehensive test coverage
|
||||
|
||||
Closes #123
|
||||
```
|
||||
|
||||
```bash
|
||||
fix(auth): resolve token validation edge case
|
||||
|
||||
Handle expired tokens more gracefully by returning proper
|
||||
401 status code instead of throwing unhandled exceptions.
|
||||
|
||||
Fixes #456
|
||||
```
|
||||
|
||||
```bash
|
||||
test(api): add comprehensive coverage for link endpoints
|
||||
|
||||
- Add unit tests for create, edit, delete operations
|
||||
- Include validation error scenarios
|
||||
- Test authentication requirements
|
||||
- Achieve 85% coverage for link management APIs
|
||||
```
|
||||
|
||||
#### Avoid These Patterns
|
||||
```bash
|
||||
# ❌ Too vague
|
||||
fix: stuff
|
||||
|
||||
# ❌ Not descriptive
|
||||
update code
|
||||
|
||||
# ❌ Multiple concerns in one commit
|
||||
feat: add new API endpoint and fix UI bug and update docs
|
||||
|
||||
# ❌ All caps or inconsistent formatting
|
||||
FIX: BROKEN AUTH SYSTEM
|
||||
```
|
||||
|
||||
### Commit Best Practices
|
||||
|
||||
#### Single Responsibility
|
||||
Each commit should represent one logical change:
|
||||
- One feature addition
|
||||
- One bug fix
|
||||
- One refactoring operation
|
||||
- One documentation update
|
||||
|
||||
#### Atomic Commits
|
||||
Commits should be complete and functional:
|
||||
- Code compiles without errors
|
||||
- Tests pass
|
||||
- No broken functionality introduced
|
||||
|
||||
#### Meaningful Descriptions
|
||||
- Use imperative mood ("add feature" not "added feature")
|
||||
- Capitalize the first letter
|
||||
- No period at the end of the subject line
|
||||
- Keep subject line under 50 characters
|
||||
- Use body for detailed explanations when needed
|
||||
|
||||
### Branch Naming Conventions
|
||||
```bash
|
||||
# Feature branches
|
||||
feature/ai-slug-generation
|
||||
feature/link-expiration
|
||||
|
||||
# Bug fix branches
|
||||
fix/auth-token-validation
|
||||
fix/mobile-responsive-layout
|
||||
|
||||
# Documentation branches
|
||||
docs/api-endpoint-guide
|
||||
docs/deployment-instructions
|
||||
|
||||
# Hotfix branches
|
||||
hotfix/security-vulnerability
|
||||
hotfix/production-crash
|
||||
```
|
||||
|
||||
### Pull Request Guidelines
|
||||
|
||||
#### PR Title Format
|
||||
Use the same format as commit messages:
|
||||
```
|
||||
feat(api): add AI-powered slug generation
|
||||
fix(auth): resolve token validation edge case
|
||||
docs(readme): update installation instructions
|
||||
```
|
||||
|
||||
#### PR Description Template
|
||||
```markdown
|
||||
## Summary
|
||||
Brief description of changes made
|
||||
|
||||
## Type of Change
|
||||
- [ ] Bug fix
|
||||
- [ ] New feature
|
||||
- [ ] Documentation update
|
||||
- [ ] Refactoring
|
||||
- [ ] Performance improvement
|
||||
|
||||
## Testing
|
||||
- [ ] Unit tests added/updated
|
||||
- [ ] Integration tests pass
|
||||
- [ ] Manual testing completed
|
||||
|
||||
## Checklist
|
||||
- [ ] Code follows project style guidelines
|
||||
- [ ] Self-review completed
|
||||
- [ ] Documentation updated if needed
|
||||
- [ ] Tests cover new functionality
|
||||
- [ ] No breaking changes introduced
|
||||
```
|
||||
|
||||
This standardized approach ensures clean, readable commit history and makes it easier to track changes, generate changelogs, and collaborate effectively with team members.
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@ This is a full-stack application built with Nuxt.js, utilizing the following tec
|
|||
- **Database**: Cloudflare Workers KV
|
||||
- **Analytics Engine**: Cloudflare Workers Analytics Engine
|
||||
- **Deployment Platform**: Cloudflare Workers/Pages
|
||||
- **Testing Framework**: Vitest with Nuxt Test Utils
|
||||
- **Type Safety**: TypeScript with strict mode enabled
|
||||
|
||||
## Core Features
|
||||
|
||||
|
|
@ -34,13 +36,40 @@ This is a full-stack application built with Nuxt.js, utilizing the following tec
|
|||
- `layouts/`: Layout components
|
||||
- `middleware/`: Middleware
|
||||
- **server/**: Backend API code
|
||||
- `api/`: API route handlers
|
||||
- `api/`: API route handlers ([server/api/](mdc:server/api/))
|
||||
- `middleware/`: Server middleware
|
||||
- `utils/`: Server utility functions
|
||||
- **tests/**: Testing infrastructure
|
||||
- `api/`: Unit tests for API endpoints
|
||||
- `helpers/`: Test utilities and fixtures
|
||||
- [sink.spec.ts](mdc:tests/sink.spec.ts): Integration tests
|
||||
- **i18n/**: Internationalization configuration
|
||||
- **schemas/**: Data validation schemas
|
||||
- **scripts/**: Build scripts
|
||||
|
||||
## Testing Strategy
|
||||
|
||||
### Testing Philosophy
|
||||
The project follows a **focused testing approach** where unit testing is exclusively applied to the `server/api` directory. This ensures:
|
||||
- **API Reliability**: All backend endpoints are thoroughly tested
|
||||
- **Data Integrity**: Input validation and business logic are verified
|
||||
- **Error Handling**: Proper error responses and status codes
|
||||
- **Security**: Authentication and authorization mechanisms
|
||||
|
||||
### Testing Architecture
|
||||
- **Unit Tests**: Comprehensive coverage of all API endpoints in [server/api/](mdc:server/api/)
|
||||
- **Integration Tests**: E2E testing for critical user flows
|
||||
- **Test Framework**: Vitest with Nuxt Test Utils for API testing
|
||||
- **Coverage Target**: Minimum 80% coverage for API endpoints
|
||||
- **CI/CD Integration**: Automated testing on every pull request
|
||||
|
||||
### Key Test Areas
|
||||
1. **Link Management APIs**: Create, edit, delete, query operations
|
||||
2. **Statistics APIs**: Analytics data retrieval and processing
|
||||
3. **Authentication**: Token validation and security checks
|
||||
4. **Input Validation**: Zod schema validation testing
|
||||
5. **Error Scenarios**: Comprehensive error handling verification
|
||||
|
||||
## Configuration Files
|
||||
|
||||
- [package.json](mdc:package.json): Project dependencies and scripts
|
||||
|
|
@ -48,3 +77,29 @@ This is a full-stack application built with Nuxt.js, utilizing the following tec
|
|||
- [wrangler.jsonc](mdc:wrangler.jsonc): Cloudflare Workers configuration
|
||||
- [tailwind.config.js](mdc:tailwind.config.js): Tailwind CSS configuration
|
||||
- [eslint.config.mjs](mdc:eslint.config.mjs): ESLint configuration
|
||||
|
||||
## Development Workflow
|
||||
|
||||
### Quality Assurance
|
||||
The project maintains high code quality through:
|
||||
- **Type Safety**: Strict TypeScript configuration
|
||||
- **Code Standards**: ESLint and Prettier enforcement
|
||||
- **Testing Requirements**: Mandatory unit tests for all API endpoints
|
||||
- **Pre-commit Hooks**: Automated linting and testing before commits
|
||||
- **Code Reviews**: Comprehensive review process for all changes
|
||||
|
||||
### Development Commands
|
||||
```bash
|
||||
# Development
|
||||
npm run dev # Start development server
|
||||
npm run test # Run all tests
|
||||
|
||||
# Quality Checks
|
||||
npm run lint # Run ESLint
|
||||
npm run lint:fix # Fix ESLint issues
|
||||
npm run build # Build for production
|
||||
|
||||
# Deployment
|
||||
npm run deploy # Deploy to Cloudflare Pages
|
||||
npm run preview # Preview with Wrangler
|
||||
```
|
||||
|
|
|
|||
322
.cursor/rules/unit-testing.mdc
Normal file
322
.cursor/rules/unit-testing.mdc
Normal file
|
|
@ -0,0 +1,322 @@
|
|||
---
|
||||
description:
|
||||
globs:
|
||||
alwaysApply: false
|
||||
---
|
||||
# Unit Testing Guidelines
|
||||
|
||||
## Overview
|
||||
|
||||
Unit testing is implemented exclusively for the `server/api` directory, focusing on API endpoint functionality, data validation, and business logic. We use Vitest as our testing framework with Nuxt Test Utils for API testing.
|
||||
|
||||
## Testing Scope
|
||||
|
||||
### What to Test
|
||||
- **API Endpoints**: All routes in [server/api/](mdc:server/api/)
|
||||
- Link management APIs in [server/api/link/](mdc:server/api/link/)
|
||||
- Statistics APIs in [server/api/stats/](mdc:server/api/stats/)
|
||||
- Authentication endpoints like [verify.ts](mdc:server/api/verify.ts)
|
||||
- Location services like [location.ts](mdc:server/api/location.ts)
|
||||
- **Input Validation**: Zod schema validation
|
||||
- **Error Handling**: Error responses and status codes
|
||||
- **Business Logic**: Core functionality and edge cases
|
||||
- **Authentication**: Token validation and authorization
|
||||
|
||||
### What NOT to Test
|
||||
- Frontend components (Vue components, pages, layouts)
|
||||
- Third-party library internals
|
||||
- Cloudflare Workers runtime specifics
|
||||
- Database connection details
|
||||
|
||||
## Test Structure
|
||||
|
||||
### File Organization
|
||||
```
|
||||
tests/
|
||||
├── api/
|
||||
│ ├── link/
|
||||
│ │ ├── create.test.ts
|
||||
│ │ ├── edit.test.ts
|
||||
│ │ ├── delete.test.ts
|
||||
│ │ ├── list.test.ts
|
||||
│ │ ├── query.test.ts
|
||||
│ │ ├── search.test.ts
|
||||
│ │ ├── upsert.test.ts
|
||||
│ │ └── ai.test.ts
|
||||
│ ├── stats/
|
||||
│ │ ├── views.test.ts
|
||||
│ │ ├── counters.test.ts
|
||||
│ │ └── metrics.test.ts
|
||||
│ ├── location.test.ts
|
||||
│ ├── verify.test.ts
|
||||
│ └── helpers/
|
||||
│ ├── fixtures.ts
|
||||
│ ├── mocks.ts
|
||||
│ └── test-utils.ts
|
||||
└── sink.spec.ts (existing e2e test)
|
||||
```
|
||||
|
||||
### Naming Conventions
|
||||
- Test files: `*.test.ts` for unit tests
|
||||
- Spec files: `*.spec.ts` for integration/e2e tests
|
||||
- Test descriptions: Use descriptive, behavior-focused names
|
||||
|
||||
## Testing Patterns
|
||||
|
||||
### API Endpoint Testing Template
|
||||
```typescript
|
||||
import { describe, it, expect, beforeEach, vi } from 'vitest'
|
||||
import { $fetch, setup } from '@nuxt/test-utils/e2e'
|
||||
|
||||
describe('/api/link/create', () => {
|
||||
beforeEach(async () => {
|
||||
await setup({
|
||||
// Test configuration
|
||||
})
|
||||
})
|
||||
|
||||
describe('POST requests', () => {
|
||||
it('should create a new link with valid data', async () => {
|
||||
const payload = {
|
||||
url: 'https://example.com',
|
||||
slug: 'test-slug'
|
||||
}
|
||||
|
||||
const response = await $fetch('/api/link/create', {
|
||||
method: 'POST',
|
||||
body: payload
|
||||
})
|
||||
|
||||
expect(response.success).toBe(true)
|
||||
expect(response.data).toHaveProperty('slug', 'test-slug')
|
||||
expect(response.data).toHaveProperty('url', 'https://example.com')
|
||||
})
|
||||
|
||||
it('should return validation error for invalid URL', async () => {
|
||||
const payload = {
|
||||
url: 'invalid-url',
|
||||
slug: 'test-slug'
|
||||
}
|
||||
|
||||
await expect($fetch('/api/link/create', {
|
||||
method: 'POST',
|
||||
body: payload
|
||||
})).rejects.toThrow()
|
||||
})
|
||||
|
||||
it('should handle missing required fields', async () => {
|
||||
const payload = {
|
||||
slug: 'test-slug'
|
||||
// Missing URL
|
||||
}
|
||||
|
||||
await expect($fetch('/api/link/create', {
|
||||
method: 'POST',
|
||||
body: payload
|
||||
})).rejects.toThrow()
|
||||
})
|
||||
})
|
||||
|
||||
describe('Authentication', () => {
|
||||
it('should require valid authentication token', async () => {
|
||||
const payload = {
|
||||
url: 'https://example.com',
|
||||
slug: 'test-slug'
|
||||
}
|
||||
|
||||
await expect($fetch('/api/link/create', {
|
||||
method: 'POST',
|
||||
body: payload,
|
||||
headers: {
|
||||
Authorization: 'Bearer invalid-token'
|
||||
}
|
||||
})).rejects.toThrow()
|
||||
})
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
### Validation Testing
|
||||
```typescript
|
||||
import { z } from 'zod'
|
||||
import { describe, it, expect } from 'vitest'
|
||||
|
||||
describe('Link creation validation', () => {
|
||||
const createLinkSchema = z.object({
|
||||
url: z.string().url(),
|
||||
slug: z.string().min(1).max(50).optional(),
|
||||
expiresAt: z.string().datetime().optional()
|
||||
})
|
||||
|
||||
it('should validate correct link data', () => {
|
||||
const validData = {
|
||||
url: 'https://example.com',
|
||||
slug: 'test-slug'
|
||||
}
|
||||
|
||||
expect(() => createLinkSchema.parse(validData)).not.toThrow()
|
||||
})
|
||||
|
||||
it('should reject invalid URL format', () => {
|
||||
const invalidData = {
|
||||
url: 'not-a-url',
|
||||
slug: 'test-slug'
|
||||
}
|
||||
|
||||
expect(() => createLinkSchema.parse(invalidData)).toThrow()
|
||||
})
|
||||
|
||||
it('should reject slug that is too long', () => {
|
||||
const invalidData = {
|
||||
url: 'https://example.com',
|
||||
slug: 'a'.repeat(51) // Exceeds 50 character limit
|
||||
}
|
||||
|
||||
expect(() => createLinkSchema.parse(invalidData)).toThrow()
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
### Mock Data and Fixtures
|
||||
```typescript
|
||||
// tests/api/helpers/fixtures.ts
|
||||
export const mockLinkData = {
|
||||
valid: {
|
||||
url: 'https://example.com',
|
||||
slug: 'test-slug',
|
||||
createdAt: new Date('2024-01-01T00:00:00Z'),
|
||||
isActive: true
|
||||
},
|
||||
withExpiry: {
|
||||
url: 'https://example.com',
|
||||
slug: 'expiring-link',
|
||||
expiresAt: new Date('2024-12-31T23:59:59Z')
|
||||
}
|
||||
}
|
||||
|
||||
export const mockStatsData = {
|
||||
views: {
|
||||
total: 100,
|
||||
unique: 85,
|
||||
timeframe: '7d'
|
||||
},
|
||||
countries: [
|
||||
{ code: 'US', count: 45 },
|
||||
{ code: 'UK', count: 20 }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Error Handling Tests
|
||||
```typescript
|
||||
describe('Error handling', () => {
|
||||
it('should return 400 for malformed request body', async () => {
|
||||
await expect($fetch('/api/link/create', {
|
||||
method: 'POST',
|
||||
body: 'invalid-json'
|
||||
})).rejects.toMatchObject({
|
||||
statusCode: 400
|
||||
})
|
||||
})
|
||||
|
||||
it('should return 401 for missing authentication', async () => {
|
||||
await expect($fetch('/api/link/delete', {
|
||||
method: 'POST',
|
||||
body: { slug: 'test-slug' }
|
||||
})).rejects.toMatchObject({
|
||||
statusCode: 401
|
||||
})
|
||||
})
|
||||
|
||||
it('should return 404 for non-existent resources', async () => {
|
||||
await expect($fetch('/api/link/query', {
|
||||
query: { slug: 'non-existent-slug' }
|
||||
})).rejects.toMatchObject({
|
||||
statusCode: 404
|
||||
})
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
## Test Configuration
|
||||
|
||||
### Vitest Setup
|
||||
Ensure your `vitest.config.ts` includes:
|
||||
```typescript
|
||||
import { defineVitestConfig } from '@nuxt/test-utils/config'
|
||||
|
||||
export default defineVitestConfig({
|
||||
test: {
|
||||
environment: 'nuxt',
|
||||
include: ['tests/**/*.{test,spec}.ts'],
|
||||
coverage: {
|
||||
provider: 'v8',
|
||||
include: ['server/api/**/*.ts'],
|
||||
exclude: ['**/*.d.ts', '**/*.config.*']
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### Environment Variables
|
||||
Set up test-specific environment variables:
|
||||
```typescript
|
||||
// tests/setup.ts
|
||||
import { vi } from 'vitest'
|
||||
|
||||
// Mock environment variables for testing
|
||||
vi.stubEnv('SITE_TOKEN', 'test-token')
|
||||
vi.stubEnv('KV_NAMESPACE', 'test-kv')
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Test Independence
|
||||
- Each test should be independent and not rely on other tests
|
||||
- Use `beforeEach` to set up clean state
|
||||
- Clean up after tests when necessary
|
||||
|
||||
### Test Data
|
||||
- Use meaningful test data that reflects real-world scenarios
|
||||
- Create reusable fixtures for common test data
|
||||
- Avoid hardcoded values; use constants
|
||||
|
||||
### Assertions
|
||||
- Test both positive and negative cases
|
||||
- Verify response structure and data types
|
||||
- Check error messages and status codes
|
||||
|
||||
### Performance
|
||||
- Keep tests fast and focused
|
||||
- Use mocking to avoid external dependencies
|
||||
- Group related tests using `describe` blocks
|
||||
|
||||
### Coverage Goals
|
||||
- Aim for high coverage of critical API endpoints
|
||||
- Focus on business logic and error scenarios
|
||||
- Don't chase 100% coverage at the expense of test quality
|
||||
|
||||
## Running Tests
|
||||
|
||||
### Commands
|
||||
```bash
|
||||
# Run all tests
|
||||
npm run test
|
||||
|
||||
# Run tests in watch mode
|
||||
npm run test:watch
|
||||
|
||||
# Run tests with coverage
|
||||
npm run test:coverage
|
||||
|
||||
# Run specific test file
|
||||
npm run test tests/api/link/create.test.ts
|
||||
|
||||
# Run tests matching pattern
|
||||
npm run test -- --grep "authentication"
|
||||
```
|
||||
|
||||
### CI/CD Integration
|
||||
- Tests should run on every pull request
|
||||
- Enforce minimum coverage thresholds
|
||||
- Block merges if tests fail
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
NUXT_PUBLIC_PREVIEW_MODE=true
|
||||
NUXT_PUBLIC_PREVIEW_MODE=false
|
||||
NUXT_PUBLIC_SLUG_DEFAULT_LENGTH=5
|
||||
NUXT_SITE_TOKEN=SinkCool
|
||||
NUXT_REDIRECT_STATUS_CODE=308
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ export default defineNuxtConfig({
|
|||
},
|
||||
|
||||
runtimeConfig: {
|
||||
siteToken: 'SinkCool',
|
||||
siteToken: crypto.randomUUID(),
|
||||
redirectStatusCode: '301',
|
||||
linkCacheTtl: 60,
|
||||
redirectWithQuery: false,
|
||||
|
|
|
|||
14
package.json
14
package.json
|
|
@ -8,18 +8,20 @@
|
|||
"node": ">=20.11"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "nuxt dev",
|
||||
"build": "NODE_OPTIONS=--max-old-space-size=8192 nuxt build",
|
||||
"build:map": "node scripts/build-map.js",
|
||||
"build:colo": "node scripts/build-colo.js",
|
||||
"preview": "wrangler pages dev dist",
|
||||
"deploy": "wrangler pages deploy dist",
|
||||
"postinstall": "npm run build:map && nuxt prepare",
|
||||
"lint": "eslint .",
|
||||
"lint:fix": "eslint . --fix",
|
||||
"typecheck": "nuxt typecheck",
|
||||
"lint-staged": "lint-staged",
|
||||
"wrangler": "wrangler",
|
||||
"lint-staged": "lint-staged"
|
||||
"dev": "nuxt dev",
|
||||
"deploy": "npm run deploy:pages",
|
||||
"deploy:pages": "wrangler pages deploy dist",
|
||||
"deploy:worker": "wrangler deploy",
|
||||
"preview": "wrangler dev --port 8888 --var $(cat .env | sed 's/=/:/g')",
|
||||
"test": "vitest"
|
||||
},
|
||||
"dependencies": {
|
||||
"@intlify/message-compiler": "^11.1.3",
|
||||
|
|
@ -53,6 +55,7 @@
|
|||
"@antfu/eslint-config": "^4.13.2",
|
||||
"@nuxt/eslint": "^1.4.1",
|
||||
"@nuxt/eslint-config": "^1.4.1",
|
||||
"@nuxt/test-utils": "^3.19.1",
|
||||
"@nuxthub/core": "^0.8.27",
|
||||
"@nuxtjs/color-mode": "^3.5.2",
|
||||
"@nuxtjs/i18n": "^9.5.4",
|
||||
|
|
@ -67,6 +70,7 @@
|
|||
"simple-git-hooks": "^2.13.0",
|
||||
"tailwind-merge": "^3.3.0",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"vitest": "^3.1.4",
|
||||
"vue-tsc": "^2.2.10",
|
||||
"wrangler": "^4.16.1"
|
||||
},
|
||||
|
|
|
|||
360
pnpm-lock.yaml
360
pnpm-lock.yaml
|
|
@ -89,13 +89,16 @@ importers:
|
|||
devDependencies:
|
||||
'@antfu/eslint-config':
|
||||
specifier: ^4.13.2
|
||||
version: 4.13.2(@vue/compiler-sfc@3.5.14)(eslint@9.27.0(jiti@2.4.2))(typescript@5.7.2)
|
||||
version: 4.13.2(@vue/compiler-sfc@3.5.14)(eslint@9.27.0(jiti@2.4.2))(typescript@5.7.2)(vitest@3.1.4(@types/debug@4.1.12)(@types/node@20.12.11)(jiti@2.4.2)(terser@5.31.0)(yaml@2.8.0))
|
||||
'@nuxt/eslint':
|
||||
specifier: ^1.4.1
|
||||
version: 1.4.1(@vue/compiler-sfc@3.5.14)(eslint@9.27.0(jiti@2.4.2))(magicast@0.3.5)(typescript@5.7.2)(vite@6.3.5(@types/node@20.12.11)(jiti@2.4.2)(terser@5.31.0)(yaml@2.8.0))
|
||||
'@nuxt/eslint-config':
|
||||
specifier: ^1.4.1
|
||||
version: 1.4.1(@vue/compiler-sfc@3.5.14)(eslint@9.27.0(jiti@2.4.2))(typescript@5.7.2)
|
||||
'@nuxt/test-utils':
|
||||
specifier: ^3.19.1
|
||||
version: 3.19.1(@types/node@20.12.11)(jiti@2.4.2)(magicast@0.3.5)(terser@5.31.0)(typescript@5.7.2)(vitest@3.1.4(@types/debug@4.1.12)(@types/node@20.12.11)(jiti@2.4.2)(terser@5.31.0)(yaml@2.8.0))(yaml@2.8.0)
|
||||
'@nuxthub/core':
|
||||
specifier: ^0.8.27
|
||||
version: 0.8.27(db0@0.3.2)(ioredis@5.6.1)(magicast@0.3.5)(vite@6.3.5(@types/node@20.12.11)(jiti@2.4.2)(terser@5.31.0)(yaml@2.8.0))
|
||||
|
|
@ -138,6 +141,9 @@ importers:
|
|||
tailwindcss-animate:
|
||||
specifier: ^1.0.7
|
||||
version: 1.0.7(tailwindcss@3.4.17)
|
||||
vitest:
|
||||
specifier: ^3.1.4
|
||||
version: 3.1.4(@types/debug@4.1.12)(@types/node@20.12.11)(jiti@2.4.2)(terser@5.31.0)(yaml@2.8.0)
|
||||
vue-tsc:
|
||||
specifier: ^2.2.10
|
||||
version: 2.2.10(typescript@5.7.2)
|
||||
|
|
@ -1143,6 +1149,42 @@ packages:
|
|||
engines: {node: '>=18.12.0'}
|
||||
hasBin: true
|
||||
|
||||
'@nuxt/test-utils@3.19.1':
|
||||
resolution: {integrity: sha512-qq2ioRgPCM7JwPIeJO2OzzqCWr8NR5eQINoskX2NEXTHzucvb8N9mt2UB2+NUe8OL9yNjGDZA+oA51GUKNhqhg==}
|
||||
engines: {node: ^18.20.5 || ^20.9.0 || ^22.0.0 || >=23.0.0}
|
||||
peerDependencies:
|
||||
'@cucumber/cucumber': ^10.3.1 || ^11.0.0
|
||||
'@jest/globals': ^29.5.0
|
||||
'@playwright/test': ^1.43.1
|
||||
'@testing-library/vue': ^7.0.0 || ^8.0.1
|
||||
'@vitest/ui': '*'
|
||||
'@vue/test-utils': ^2.4.2
|
||||
happy-dom: ^9.10.9 || ^10.0.0 || ^11.0.0 || ^12.0.0 || ^13.0.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0
|
||||
jsdom: ^22.0.0 || ^23.0.0 || ^24.0.0 || ^25.0.0 || ^26.0.0
|
||||
playwright-core: ^1.43.1
|
||||
vitest: ^0.34.6 || ^1.0.0 || ^2.0.0 || ^3.0.0
|
||||
peerDependenciesMeta:
|
||||
'@cucumber/cucumber':
|
||||
optional: true
|
||||
'@jest/globals':
|
||||
optional: true
|
||||
'@playwright/test':
|
||||
optional: true
|
||||
'@testing-library/vue':
|
||||
optional: true
|
||||
'@vitest/ui':
|
||||
optional: true
|
||||
'@vue/test-utils':
|
||||
optional: true
|
||||
happy-dom:
|
||||
optional: true
|
||||
jsdom:
|
||||
optional: true
|
||||
playwright-core:
|
||||
optional: true
|
||||
vitest:
|
||||
optional: true
|
||||
|
||||
'@nuxt/vite-builder@3.17.4':
|
||||
resolution: {integrity: sha512-MRcGe02nEDpu+MnRJcmgVfHdzgt9tWvxVdJbhfd6oyX19plw/CANjgHedlpUNUxqeWXC6CQfGvoVJXn3bQlEqA==}
|
||||
engines: {node: ^18.12.0 || ^20.9.0 || >=22.0.0}
|
||||
|
|
@ -2097,6 +2139,35 @@ packages:
|
|||
vitest:
|
||||
optional: true
|
||||
|
||||
'@vitest/expect@3.1.4':
|
||||
resolution: {integrity: sha512-xkD/ljeliyaClDYqHPNCiJ0plY5YIcM0OlRiZizLhlPmpXWpxnGMyTZXOHFhFeG7w9P5PBeL4IdtJ/HeQwTbQA==}
|
||||
|
||||
'@vitest/mocker@3.1.4':
|
||||
resolution: {integrity: sha512-8IJ3CvwtSw/EFXqWFL8aCMu+YyYXG2WUSrQbViOZkWTKTVicVwZ/YiEZDSqD00kX+v/+W+OnxhNWoeVKorHygA==}
|
||||
peerDependencies:
|
||||
msw: ^2.4.9
|
||||
vite: ^5.0.0 || ^6.0.0
|
||||
peerDependenciesMeta:
|
||||
msw:
|
||||
optional: true
|
||||
vite:
|
||||
optional: true
|
||||
|
||||
'@vitest/pretty-format@3.1.4':
|
||||
resolution: {integrity: sha512-cqv9H9GvAEoTaoq+cYqUTCGscUjKqlJZC7PRwY5FMySVj5J+xOm1KQcCiYHJOEzOKRUhLH4R2pTwvFlWCEScsg==}
|
||||
|
||||
'@vitest/runner@3.1.4':
|
||||
resolution: {integrity: sha512-djTeF1/vt985I/wpKVFBMWUlk/I7mb5hmD5oP8K9ACRmVXgKTae3TUOtXAEBfslNKPzUQvnKhNd34nnRSYgLNQ==}
|
||||
|
||||
'@vitest/snapshot@3.1.4':
|
||||
resolution: {integrity: sha512-JPHf68DvuO7vilmvwdPr9TS0SuuIzHvxeaCkxYcCD4jTk67XwL45ZhEHFKIuCm8CYstgI6LZ4XbwD6ANrwMpFg==}
|
||||
|
||||
'@vitest/spy@3.1.4':
|
||||
resolution: {integrity: sha512-Xg1bXhu+vtPXIodYN369M86K8shGLouNjoVI78g8iAq2rFoHFdajNvJJ5A/9bPMFcfQqdaCpOgWKEoMQg/s0Yg==}
|
||||
|
||||
'@vitest/utils@3.1.4':
|
||||
resolution: {integrity: sha512-yriMuO1cfFhmiGc8ataN51+9ooHRuURdfAZfwFd3usWynjzpLslZdYnRegTv32qdgtJTsj15FoeZe2g15fY1gg==}
|
||||
|
||||
'@volar/language-core@2.4.11':
|
||||
resolution: {integrity: sha512-lN2C1+ByfW9/JRPpqScuZt/4OrUUse57GLI6TbLgTIqBVemdl1wNcZ1qYGEo2+Gw8coYLgCy7SuKqn6IrQcQgg==}
|
||||
|
||||
|
|
@ -2427,6 +2498,10 @@ packages:
|
|||
as-table@1.0.55:
|
||||
resolution: {integrity: sha512-xvsWESUJn0JN421Xb9MQw6AsMHRCUknCe0Wjlxvjud80mU4E6hQf1A6NzQKcYNmYw62MfzEtXc+badstZP3JpQ==}
|
||||
|
||||
assertion-error@2.0.1:
|
||||
resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
ast-kit@1.4.3:
|
||||
resolution: {integrity: sha512-MdJqjpodkS5J149zN0Po+HPshkTdUyrvF7CKTafUgv69vBSPtncrj+3IiUgqdd7ElIEkbeXCsEouBUwLrw9Ilg==}
|
||||
engines: {node: '>=16.14.0'}
|
||||
|
|
@ -2590,6 +2665,10 @@ packages:
|
|||
ccount@2.0.1:
|
||||
resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==}
|
||||
|
||||
chai@5.2.0:
|
||||
resolution: {integrity: sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
chalk@4.1.2:
|
||||
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
|
||||
engines: {node: '>=10'}
|
||||
|
|
@ -2601,6 +2680,10 @@ packages:
|
|||
character-entities@2.0.2:
|
||||
resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==}
|
||||
|
||||
check-error@2.1.1:
|
||||
resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==}
|
||||
engines: {node: '>= 16'}
|
||||
|
||||
chokidar@3.6.0:
|
||||
resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
|
||||
engines: {node: '>= 8.10.0'}
|
||||
|
|
@ -3112,6 +3195,10 @@ packages:
|
|||
decode-named-character-reference@1.0.2:
|
||||
resolution: {integrity: sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==}
|
||||
|
||||
deep-eql@5.0.2:
|
||||
resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
deep-equal@1.0.1:
|
||||
resolution: {integrity: sha512-bHtC0iYvWhyaTzvV3CZgPeZQqCOBGyGsVV7v4eevpdkLHfiSrXUdBG+qAuSz4RI70sszvjQ1QSZ98An1yNwpSw==}
|
||||
|
||||
|
|
@ -3616,6 +3703,10 @@ packages:
|
|||
resolution: {integrity: sha512-eNTPlAD67BmP31LDINZ3U7HSF8l57TxOY2PmBJ1shpCvpnxBF93mWCE8YHBnXs8qiUZJc9WDcWIeC3a2HIAMfw==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
expect-type@1.2.1:
|
||||
resolution: {integrity: sha512-/kP8CAwxzLVEeFrMm4kMmy4CCDlpipyA7MYLVrdJIkV0fYF0UaigQHRsxHiuY/GEea+bh4KSv3TIlgr+2UL6bw==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
|
||||
exsolve@1.0.5:
|
||||
resolution: {integrity: sha512-pz5dvkYYKQ1AHVrgOzBKWeP4u4FRb3a6DNK2ucr0OoNwYIU4QWsJ+NM36LLzORT+z845MzKHHhpXiUF5nvQoJg==}
|
||||
|
||||
|
|
@ -3627,6 +3718,10 @@ packages:
|
|||
engines: {node: '>= 10.17.0'}
|
||||
hasBin: true
|
||||
|
||||
fake-indexeddb@6.0.1:
|
||||
resolution: {integrity: sha512-He2AjQGHe46svIFq5+L2Nx/eHDTI1oKgoevBP+TthnjymXiKkeJQ3+ITeWey99Y5+2OaPFbI1qEsx/5RsGtWnQ==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
fast-deep-equal@3.1.3:
|
||||
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
|
||||
|
||||
|
|
@ -4441,6 +4536,9 @@ packages:
|
|||
longest-streak@3.1.0:
|
||||
resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==}
|
||||
|
||||
loupe@3.1.3:
|
||||
resolution: {integrity: sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==}
|
||||
|
||||
lru-cache@10.4.3:
|
||||
resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==}
|
||||
|
||||
|
|
@ -5116,6 +5214,10 @@ packages:
|
|||
pathe@2.0.3:
|
||||
resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
|
||||
|
||||
pathval@2.0.0:
|
||||
resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==}
|
||||
engines: {node: '>= 14.16'}
|
||||
|
||||
pbf@3.2.1:
|
||||
resolution: {integrity: sha512-ClrV7pNOn7rtmoQVF4TS1vyU0WhYRnP92fzbfF75jAIwpnzdJXf8iTd4CMEqO4yUenH6NDqLiwjqlh6QgZzgLQ==}
|
||||
hasBin: true
|
||||
|
|
@ -5725,6 +5827,9 @@ packages:
|
|||
resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
siginfo@2.0.0:
|
||||
resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==}
|
||||
|
||||
signal-exit@3.0.7:
|
||||
resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
|
||||
|
||||
|
|
@ -5818,6 +5923,9 @@ packages:
|
|||
stack-trace@0.0.10:
|
||||
resolution: {integrity: sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==}
|
||||
|
||||
stackback@0.0.2:
|
||||
resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==}
|
||||
|
||||
stacktracey@2.1.8:
|
||||
resolution: {integrity: sha512-Kpij9riA+UNg7TnphqjH7/CzctQ/owJGNbFkfEeve4Z4uxT5+JapVLFXcsurIfN34gnTWZNJ/f7NMG0E8JDzTw==}
|
||||
|
||||
|
|
@ -6039,6 +6147,9 @@ packages:
|
|||
tiny-invariant@1.3.3:
|
||||
resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==}
|
||||
|
||||
tinybench@2.9.0:
|
||||
resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==}
|
||||
|
||||
tinycolor2@1.6.0:
|
||||
resolution: {integrity: sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==}
|
||||
|
||||
|
|
@ -6052,9 +6163,21 @@ packages:
|
|||
resolution: {integrity: sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
|
||||
tinypool@1.0.2:
|
||||
resolution: {integrity: sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==}
|
||||
engines: {node: ^18.0.0 || >=20.0.0}
|
||||
|
||||
tinyqueue@2.0.3:
|
||||
resolution: {integrity: sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA==}
|
||||
|
||||
tinyrainbow@2.0.0:
|
||||
resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
|
||||
tinyspy@3.0.2:
|
||||
resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
|
||||
tmp-promise@3.0.3:
|
||||
resolution: {integrity: sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==}
|
||||
|
||||
|
|
@ -6474,6 +6597,37 @@ packages:
|
|||
yaml:
|
||||
optional: true
|
||||
|
||||
vitest-environment-nuxt@1.0.1:
|
||||
resolution: {integrity: sha512-eBCwtIQriXW5/M49FjqNKfnlJYlG2LWMSNFsRVKomc8CaMqmhQPBS5LZ9DlgYL9T8xIVsiA6RZn2lk7vxov3Ow==}
|
||||
|
||||
vitest@3.1.4:
|
||||
resolution: {integrity: sha512-Ta56rT7uWxCSJXlBtKgIlApJnT6e6IGmTYxYcmxjJ4ujuZDI59GUQgVDObXXJujOmPDBYXHK1qmaGtneu6TNIQ==}
|
||||
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
'@edge-runtime/vm': '*'
|
||||
'@types/debug': ^4.1.12
|
||||
'@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0
|
||||
'@vitest/browser': 3.1.4
|
||||
'@vitest/ui': 3.1.4
|
||||
happy-dom: '*'
|
||||
jsdom: '*'
|
||||
peerDependenciesMeta:
|
||||
'@edge-runtime/vm':
|
||||
optional: true
|
||||
'@types/debug':
|
||||
optional: true
|
||||
'@types/node':
|
||||
optional: true
|
||||
'@vitest/browser':
|
||||
optional: true
|
||||
'@vitest/ui':
|
||||
optional: true
|
||||
happy-dom:
|
||||
optional: true
|
||||
jsdom:
|
||||
optional: true
|
||||
|
||||
vscode-uri@3.1.0:
|
||||
resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==}
|
||||
|
||||
|
|
@ -6561,6 +6715,11 @@ packages:
|
|||
engines: {node: ^18.17.0 || >=20.5.0}
|
||||
hasBin: true
|
||||
|
||||
why-is-node-running@2.3.0:
|
||||
resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==}
|
||||
engines: {node: '>=8'}
|
||||
hasBin: true
|
||||
|
||||
wide-align@1.1.5:
|
||||
resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==}
|
||||
|
||||
|
|
@ -6725,7 +6884,7 @@ snapshots:
|
|||
'@jridgewell/gen-mapping': 0.3.5
|
||||
'@jridgewell/trace-mapping': 0.3.25
|
||||
|
||||
'@antfu/eslint-config@4.13.2(@vue/compiler-sfc@3.5.14)(eslint@9.27.0(jiti@2.4.2))(typescript@5.7.2)':
|
||||
'@antfu/eslint-config@4.13.2(@vue/compiler-sfc@3.5.14)(eslint@9.27.0(jiti@2.4.2))(typescript@5.7.2)(vitest@3.1.4(@types/debug@4.1.12)(@types/node@20.12.11)(jiti@2.4.2)(terser@5.31.0)(yaml@2.8.0))':
|
||||
dependencies:
|
||||
'@antfu/install-pkg': 1.1.0
|
||||
'@clack/prompts': 0.10.1
|
||||
|
|
@ -6734,7 +6893,7 @@ snapshots:
|
|||
'@stylistic/eslint-plugin': 4.2.0(eslint@9.27.0(jiti@2.4.2))(typescript@5.7.2)
|
||||
'@typescript-eslint/eslint-plugin': 8.32.1(@typescript-eslint/parser@8.32.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.7.2))(eslint@9.27.0(jiti@2.4.2))(typescript@5.7.2)
|
||||
'@typescript-eslint/parser': 8.32.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.7.2)
|
||||
'@vitest/eslint-plugin': 1.2.0(eslint@9.27.0(jiti@2.4.2))(typescript@5.7.2)
|
||||
'@vitest/eslint-plugin': 1.2.0(eslint@9.27.0(jiti@2.4.2))(typescript@5.7.2)(vitest@3.1.4(@types/debug@4.1.12)(@types/node@20.12.11)(jiti@2.4.2)(terser@5.31.0)(yaml@2.8.0))
|
||||
ansis: 4.0.0
|
||||
cac: 6.7.14
|
||||
eslint: 9.27.0(jiti@2.4.2)
|
||||
|
|
@ -7932,6 +8091,51 @@ snapshots:
|
|||
transitivePeerDependencies:
|
||||
- magicast
|
||||
|
||||
'@nuxt/test-utils@3.19.1(@types/node@20.12.11)(jiti@2.4.2)(magicast@0.3.5)(terser@5.31.0)(typescript@5.7.2)(vitest@3.1.4(@types/debug@4.1.12)(@types/node@20.12.11)(jiti@2.4.2)(terser@5.31.0)(yaml@2.8.0))(yaml@2.8.0)':
|
||||
dependencies:
|
||||
'@nuxt/kit': 3.17.4(magicast@0.3.5)
|
||||
'@nuxt/schema': 3.17.4
|
||||
c12: 3.0.4(magicast@0.3.5)
|
||||
consola: 3.4.2
|
||||
defu: 6.1.4
|
||||
destr: 2.0.5
|
||||
estree-walker: 3.0.3
|
||||
fake-indexeddb: 6.0.1
|
||||
get-port-please: 3.1.2
|
||||
h3: 1.15.3
|
||||
local-pkg: 1.1.1
|
||||
magic-string: 0.30.17
|
||||
node-fetch-native: 1.6.6
|
||||
node-mock-http: 1.0.0
|
||||
ofetch: 1.4.1
|
||||
pathe: 2.0.3
|
||||
perfect-debounce: 1.0.0
|
||||
radix3: 1.1.2
|
||||
scule: 1.3.0
|
||||
std-env: 3.9.0
|
||||
tinyexec: 1.0.1
|
||||
ufo: 1.6.1
|
||||
unplugin: 2.3.4
|
||||
vite: 6.3.5(@types/node@20.12.11)(jiti@2.4.2)(terser@5.31.0)(yaml@2.8.0)
|
||||
vitest-environment-nuxt: 1.0.1(@types/node@20.12.11)(jiti@2.4.2)(magicast@0.3.5)(terser@5.31.0)(typescript@5.7.2)(vitest@3.1.4(@types/debug@4.1.12)(@types/node@20.12.11)(jiti@2.4.2)(terser@5.31.0)(yaml@2.8.0))(yaml@2.8.0)
|
||||
vue: 3.5.14(typescript@5.7.2)
|
||||
optionalDependencies:
|
||||
vitest: 3.1.4(@types/debug@4.1.12)(@types/node@20.12.11)(jiti@2.4.2)(terser@5.31.0)(yaml@2.8.0)
|
||||
transitivePeerDependencies:
|
||||
- '@types/node'
|
||||
- jiti
|
||||
- less
|
||||
- lightningcss
|
||||
- magicast
|
||||
- sass
|
||||
- sass-embedded
|
||||
- stylus
|
||||
- sugarss
|
||||
- terser
|
||||
- tsx
|
||||
- typescript
|
||||
- yaml
|
||||
|
||||
'@nuxt/vite-builder@3.17.4(@types/node@20.12.11)(eslint@9.27.0(jiti@2.4.2))(magicast@0.3.5)(optionator@0.9.4)(rollup@4.41.0)(terser@5.31.0)(typescript@5.7.2)(vue-tsc@2.2.10(typescript@5.7.2))(vue@3.5.14(typescript@5.7.2))(yaml@2.8.0)':
|
||||
dependencies:
|
||||
'@nuxt/kit': 3.17.4(magicast@0.3.5)
|
||||
|
|
@ -8962,15 +9166,56 @@ snapshots:
|
|||
vite: 6.3.5(@types/node@20.12.11)(jiti@2.4.2)(terser@5.31.0)(yaml@2.8.0)
|
||||
vue: 3.5.14(typescript@5.7.2)
|
||||
|
||||
'@vitest/eslint-plugin@1.2.0(eslint@9.27.0(jiti@2.4.2))(typescript@5.7.2)':
|
||||
'@vitest/eslint-plugin@1.2.0(eslint@9.27.0(jiti@2.4.2))(typescript@5.7.2)(vitest@3.1.4(@types/debug@4.1.12)(@types/node@20.12.11)(jiti@2.4.2)(terser@5.31.0)(yaml@2.8.0))':
|
||||
dependencies:
|
||||
'@typescript-eslint/utils': 8.32.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.7.2)
|
||||
eslint: 9.27.0(jiti@2.4.2)
|
||||
optionalDependencies:
|
||||
typescript: 5.7.2
|
||||
vitest: 3.1.4(@types/debug@4.1.12)(@types/node@20.12.11)(jiti@2.4.2)(terser@5.31.0)(yaml@2.8.0)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@vitest/expect@3.1.4':
|
||||
dependencies:
|
||||
'@vitest/spy': 3.1.4
|
||||
'@vitest/utils': 3.1.4
|
||||
chai: 5.2.0
|
||||
tinyrainbow: 2.0.0
|
||||
|
||||
'@vitest/mocker@3.1.4(vite@6.3.5(@types/node@20.12.11)(jiti@2.4.2)(terser@5.31.0)(yaml@2.8.0))':
|
||||
dependencies:
|
||||
'@vitest/spy': 3.1.4
|
||||
estree-walker: 3.0.3
|
||||
magic-string: 0.30.17
|
||||
optionalDependencies:
|
||||
vite: 6.3.5(@types/node@20.12.11)(jiti@2.4.2)(terser@5.31.0)(yaml@2.8.0)
|
||||
|
||||
'@vitest/pretty-format@3.1.4':
|
||||
dependencies:
|
||||
tinyrainbow: 2.0.0
|
||||
|
||||
'@vitest/runner@3.1.4':
|
||||
dependencies:
|
||||
'@vitest/utils': 3.1.4
|
||||
pathe: 2.0.3
|
||||
|
||||
'@vitest/snapshot@3.1.4':
|
||||
dependencies:
|
||||
'@vitest/pretty-format': 3.1.4
|
||||
magic-string: 0.30.17
|
||||
pathe: 2.0.3
|
||||
|
||||
'@vitest/spy@3.1.4':
|
||||
dependencies:
|
||||
tinyspy: 3.0.2
|
||||
|
||||
'@vitest/utils@3.1.4':
|
||||
dependencies:
|
||||
'@vitest/pretty-format': 3.1.4
|
||||
loupe: 3.1.3
|
||||
tinyrainbow: 2.0.0
|
||||
|
||||
'@volar/language-core@2.4.11':
|
||||
dependencies:
|
||||
'@volar/source-map': 2.4.11
|
||||
|
|
@ -9377,6 +9622,8 @@ snapshots:
|
|||
dependencies:
|
||||
printable-characters: 1.0.42
|
||||
|
||||
assertion-error@2.0.1: {}
|
||||
|
||||
ast-kit@1.4.3:
|
||||
dependencies:
|
||||
'@babel/parser': 7.27.2
|
||||
|
|
@ -9544,6 +9791,14 @@ snapshots:
|
|||
|
||||
ccount@2.0.1: {}
|
||||
|
||||
chai@5.2.0:
|
||||
dependencies:
|
||||
assertion-error: 2.0.1
|
||||
check-error: 2.1.1
|
||||
deep-eql: 5.0.2
|
||||
loupe: 3.1.3
|
||||
pathval: 2.0.0
|
||||
|
||||
chalk@4.1.2:
|
||||
dependencies:
|
||||
ansi-styles: 4.3.0
|
||||
|
|
@ -9553,6 +9808,8 @@ snapshots:
|
|||
|
||||
character-entities@2.0.2: {}
|
||||
|
||||
check-error@2.1.1: {}
|
||||
|
||||
chokidar@3.6.0:
|
||||
dependencies:
|
||||
anymatch: 3.1.3
|
||||
|
|
@ -10073,6 +10330,8 @@ snapshots:
|
|||
dependencies:
|
||||
character-entities: 2.0.2
|
||||
|
||||
deep-eql@5.0.2: {}
|
||||
|
||||
deep-equal@1.0.1: {}
|
||||
|
||||
deep-is@0.1.4: {}
|
||||
|
|
@ -10659,6 +10918,8 @@ snapshots:
|
|||
|
||||
exit-hook@2.2.1: {}
|
||||
|
||||
expect-type@1.2.1: {}
|
||||
|
||||
exsolve@1.0.5: {}
|
||||
|
||||
externality@1.0.2:
|
||||
|
|
@ -10678,6 +10939,8 @@ snapshots:
|
|||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
fake-indexeddb@6.0.1: {}
|
||||
|
||||
fast-deep-equal@3.1.3: {}
|
||||
|
||||
fast-fifo@1.3.2: {}
|
||||
|
|
@ -11516,6 +11779,8 @@ snapshots:
|
|||
|
||||
longest-streak@3.1.0: {}
|
||||
|
||||
loupe@3.1.3: {}
|
||||
|
||||
lru-cache@10.4.3: {}
|
||||
|
||||
lru-cache@5.1.1:
|
||||
|
|
@ -12565,6 +12830,8 @@ snapshots:
|
|||
|
||||
pathe@2.0.3: {}
|
||||
|
||||
pathval@2.0.0: {}
|
||||
|
||||
pbf@3.2.1:
|
||||
dependencies:
|
||||
ieee754: 1.2.1
|
||||
|
|
@ -13247,6 +13514,8 @@ snapshots:
|
|||
side-channel-map: 1.0.1
|
||||
side-channel-weakmap: 1.0.2
|
||||
|
||||
siginfo@2.0.0: {}
|
||||
|
||||
signal-exit@3.0.7: {}
|
||||
|
||||
signal-exit@4.1.0: {}
|
||||
|
|
@ -13331,6 +13600,8 @@ snapshots:
|
|||
|
||||
stack-trace@0.0.10: {}
|
||||
|
||||
stackback@0.0.2: {}
|
||||
|
||||
stacktracey@2.1.8:
|
||||
dependencies:
|
||||
as-table: 1.0.55
|
||||
|
|
@ -13621,6 +13892,8 @@ snapshots:
|
|||
|
||||
tiny-invariant@1.3.3: {}
|
||||
|
||||
tinybench@2.9.0: {}
|
||||
|
||||
tinycolor2@1.6.0: {}
|
||||
|
||||
tinyexec@0.3.2: {}
|
||||
|
|
@ -13632,8 +13905,14 @@ snapshots:
|
|||
fdir: 6.4.4(picomatch@4.0.2)
|
||||
picomatch: 4.0.2
|
||||
|
||||
tinypool@1.0.2: {}
|
||||
|
||||
tinyqueue@2.0.3: {}
|
||||
|
||||
tinyrainbow@2.0.0: {}
|
||||
|
||||
tinyspy@3.0.2: {}
|
||||
|
||||
tmp-promise@3.0.3:
|
||||
dependencies:
|
||||
tmp: 0.2.3
|
||||
|
|
@ -14024,6 +14303,74 @@ snapshots:
|
|||
terser: 5.31.0
|
||||
yaml: 2.8.0
|
||||
|
||||
vitest-environment-nuxt@1.0.1(@types/node@20.12.11)(jiti@2.4.2)(magicast@0.3.5)(terser@5.31.0)(typescript@5.7.2)(vitest@3.1.4(@types/debug@4.1.12)(@types/node@20.12.11)(jiti@2.4.2)(terser@5.31.0)(yaml@2.8.0))(yaml@2.8.0):
|
||||
dependencies:
|
||||
'@nuxt/test-utils': 3.19.1(@types/node@20.12.11)(jiti@2.4.2)(magicast@0.3.5)(terser@5.31.0)(typescript@5.7.2)(vitest@3.1.4(@types/debug@4.1.12)(@types/node@20.12.11)(jiti@2.4.2)(terser@5.31.0)(yaml@2.8.0))(yaml@2.8.0)
|
||||
transitivePeerDependencies:
|
||||
- '@cucumber/cucumber'
|
||||
- '@jest/globals'
|
||||
- '@playwright/test'
|
||||
- '@testing-library/vue'
|
||||
- '@types/node'
|
||||
- '@vitest/ui'
|
||||
- '@vue/test-utils'
|
||||
- happy-dom
|
||||
- jiti
|
||||
- jsdom
|
||||
- less
|
||||
- lightningcss
|
||||
- magicast
|
||||
- playwright-core
|
||||
- sass
|
||||
- sass-embedded
|
||||
- stylus
|
||||
- sugarss
|
||||
- terser
|
||||
- tsx
|
||||
- typescript
|
||||
- vitest
|
||||
- yaml
|
||||
|
||||
vitest@3.1.4(@types/debug@4.1.12)(@types/node@20.12.11)(jiti@2.4.2)(terser@5.31.0)(yaml@2.8.0):
|
||||
dependencies:
|
||||
'@vitest/expect': 3.1.4
|
||||
'@vitest/mocker': 3.1.4(vite@6.3.5(@types/node@20.12.11)(jiti@2.4.2)(terser@5.31.0)(yaml@2.8.0))
|
||||
'@vitest/pretty-format': 3.1.4
|
||||
'@vitest/runner': 3.1.4
|
||||
'@vitest/snapshot': 3.1.4
|
||||
'@vitest/spy': 3.1.4
|
||||
'@vitest/utils': 3.1.4
|
||||
chai: 5.2.0
|
||||
debug: 4.4.1
|
||||
expect-type: 1.2.1
|
||||
magic-string: 0.30.17
|
||||
pathe: 2.0.3
|
||||
std-env: 3.9.0
|
||||
tinybench: 2.9.0
|
||||
tinyexec: 0.3.2
|
||||
tinyglobby: 0.2.13
|
||||
tinypool: 1.0.2
|
||||
tinyrainbow: 2.0.0
|
||||
vite: 6.3.5(@types/node@20.12.11)(jiti@2.4.2)(terser@5.31.0)(yaml@2.8.0)
|
||||
vite-node: 3.1.4(@types/node@20.12.11)(jiti@2.4.2)(terser@5.31.0)(yaml@2.8.0)
|
||||
why-is-node-running: 2.3.0
|
||||
optionalDependencies:
|
||||
'@types/debug': 4.1.12
|
||||
'@types/node': 20.12.11
|
||||
transitivePeerDependencies:
|
||||
- jiti
|
||||
- less
|
||||
- lightningcss
|
||||
- msw
|
||||
- sass
|
||||
- sass-embedded
|
||||
- stylus
|
||||
- sugarss
|
||||
- supports-color
|
||||
- terser
|
||||
- tsx
|
||||
- yaml
|
||||
|
||||
vscode-uri@3.1.0: {}
|
||||
|
||||
vt-pbf@3.1.3:
|
||||
|
|
@ -14114,6 +14461,11 @@ snapshots:
|
|||
dependencies:
|
||||
isexe: 3.1.1
|
||||
|
||||
why-is-node-running@2.3.0:
|
||||
dependencies:
|
||||
siginfo: 2.0.0
|
||||
stackback: 0.0.2
|
||||
|
||||
wide-align@1.1.5:
|
||||
dependencies:
|
||||
string-width: 4.2.3
|
||||
|
|
|
|||
3
tests/config.ts
Normal file
3
tests/config.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
export const config = {
|
||||
host: 'http://localhost:8888',
|
||||
}
|
||||
12
tests/sink.spec.ts
Normal file
12
tests/sink.spec.ts
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
import { fetch, setup } from '@nuxt/test-utils/e2e'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { config } from './config'
|
||||
|
||||
describe('sink test', async () => {
|
||||
await setup(config)
|
||||
|
||||
it('home page should return 200', async () => {
|
||||
const response = await fetch('/')
|
||||
expect(response.status).toBe(200)
|
||||
})
|
||||
})
|
||||
Loading…
Reference in a new issue