diff --git a/.cursor/rules/coding-standards.mdc b/.cursor/rules/coding-standards.mdc index 4189fcd..41dda80 100644 --- a/.cursor/rules/coding-standards.mdc +++ b/.cursor/rules/coding-standards.mdc @@ -156,50 +156,238 @@ export async function createLink(url: string, slug?: string): Promise } ``` -## 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. diff --git a/.cursor/rules/project-overview.mdc b/.cursor/rules/project-overview.mdc index f888d88..34d0240 100644 --- a/.cursor/rules/project-overview.mdc +++ b/.cursor/rules/project-overview.mdc @@ -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 +``` diff --git a/.cursor/rules/unit-testing.mdc b/.cursor/rules/unit-testing.mdc new file mode 100644 index 0000000..41a9698 --- /dev/null +++ b/.cursor/rules/unit-testing.mdc @@ -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 diff --git a/.env.example b/.env.example index 7f1a286..ce2fc13 100644 --- a/.env.example +++ b/.env.example @@ -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 diff --git a/nuxt.config.ts b/nuxt.config.ts index ab3a9ec..d10682b 100644 --- a/nuxt.config.ts +++ b/nuxt.config.ts @@ -20,7 +20,7 @@ export default defineNuxtConfig({ }, runtimeConfig: { - siteToken: 'SinkCool', + siteToken: crypto.randomUUID(), redirectStatusCode: '301', linkCacheTtl: 60, redirectWithQuery: false, diff --git a/package.json b/package.json index cf36c18..d835caf 100644 --- a/package.json +++ b/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" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ee0fcc1..020f60a 100644 --- a/pnpm-lock.yaml +++ b/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 diff --git a/tests/config.ts b/tests/config.ts new file mode 100644 index 0000000..2e93997 --- /dev/null +++ b/tests/config.ts @@ -0,0 +1,3 @@ +export const config = { + host: 'http://localhost:8888', +} diff --git a/tests/sink.spec.ts b/tests/sink.spec.ts new file mode 100644 index 0000000..a880faf --- /dev/null +++ b/tests/sink.spec.ts @@ -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) + }) +})