mirror of
https://github.com/complexcaresolutions/cms.c2sgmbh.git
synced 2026-03-17 19:44:12 +00:00
Merge branch 'feature/nextjs-16-upgrade' - TypeScript and test fixes
Merged changes: - Reverted Next.js 16 upgrade (Payload CMS 3.68.4 incompatible) - Fixed all TypeScript errors in test files - ESLint and typecheck now pass 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
commit
91d00b016e
11 changed files with 42 additions and 29 deletions
|
|
@ -16,7 +16,7 @@ const nextConfig = {
|
|||
workerThreads: false,
|
||||
cpus: 1,
|
||||
},
|
||||
// Your Next.js config here
|
||||
// Webpack configuration for TypeScript/ESM compatibility
|
||||
webpack: (webpackConfig) => {
|
||||
webpackConfig.resolve.extensionAlias = {
|
||||
'.cjs': ['.cts', '.cjs'],
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@
|
|||
"vitest": "4.0.15"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.20.2 || >=20.9.0",
|
||||
"node": ">=20.9.0",
|
||||
"pnpm": "^9 || ^10"
|
||||
},
|
||||
"pnpm": {
|
||||
|
|
|
|||
|
|
@ -26,8 +26,10 @@ export interface MockTenant {
|
|||
domains?: Array<{ domain: string }>
|
||||
}
|
||||
|
||||
export interface MockPayloadRequest extends Partial<PayloadRequest> {
|
||||
// Note: Not extending PayloadRequest to allow flexible mock types for testing
|
||||
export interface MockPayloadRequest {
|
||||
user?: MockUser | null
|
||||
// Allow both Headers and plain object for testing different header formats
|
||||
headers: Headers | Record<string, string | string[] | undefined>
|
||||
payload: {
|
||||
find: ReturnType<typeof vi.fn>
|
||||
|
|
@ -126,10 +128,10 @@ export function createMockPayloadRequest(
|
|||
tenants?: MockTenant[]
|
||||
} = {},
|
||||
): MockPayloadRequest {
|
||||
const headers: Record<string, string | string[] | undefined> = {}
|
||||
const headers = new Headers()
|
||||
|
||||
if (options.host) {
|
||||
headers['host'] = options.host
|
||||
headers.set('host', options.host)
|
||||
}
|
||||
|
||||
// Mock payload.find to resolve tenant from host
|
||||
|
|
@ -306,9 +308,11 @@ export async function executeAccess(
|
|||
data?: Record<string, unknown>
|
||||
} = {},
|
||||
): Promise<AccessResult> {
|
||||
// Convert string ID to number if needed (Payload access functions expect number | undefined)
|
||||
const numericId = typeof options.id === 'string' ? parseInt(options.id, 10) : options.id
|
||||
const result = await accessFn({
|
||||
req: request as unknown as PayloadRequest,
|
||||
id: options.id,
|
||||
id: numericId,
|
||||
data: options.data,
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -3,12 +3,13 @@ import type { Payload } from 'payload'
|
|||
import type { Tenant } from '@/payload-types'
|
||||
|
||||
const mockSendMail = vi.fn(async () => ({ messageId: 'mocked-id' }))
|
||||
const mockCreateTransport = vi.fn(() => ({ sendMail: mockSendMail }))
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const mockCreateTransport = vi.fn((_options?: unknown) => ({ sendMail: mockSendMail }))
|
||||
|
||||
vi.mock('nodemailer', () => ({
|
||||
__esModule: true,
|
||||
default: {
|
||||
createTransport: (...args: unknown[]) => mockCreateTransport(...args),
|
||||
createTransport: (options: unknown) => mockCreateTransport(options),
|
||||
},
|
||||
}))
|
||||
|
||||
|
|
|
|||
|
|
@ -75,8 +75,13 @@ describe('Payload Localization Integration', () => {
|
|||
it('payload config has localization enabled', async () => {
|
||||
const payloadConfig = await config
|
||||
expect(payloadConfig.localization).toBeDefined()
|
||||
expect(payloadConfig.localization?.locales).toBeDefined()
|
||||
expect(payloadConfig.localization?.defaultLocale).toBe('de')
|
||||
expect(payloadConfig.localization).not.toBe(false)
|
||||
// Type guard for localization config
|
||||
const localization = payloadConfig.localization
|
||||
if (localization && typeof localization === 'object') {
|
||||
expect(localization.locales).toBeDefined()
|
||||
expect(localization.defaultLocale).toBe('de')
|
||||
}
|
||||
})
|
||||
|
||||
it('payload config has i18n enabled', async () => {
|
||||
|
|
|
|||
|
|
@ -242,10 +242,12 @@ describe('Search API Integration', () => {
|
|||
try {
|
||||
const post = await payload.create({
|
||||
collection: 'posts',
|
||||
draft: false,
|
||||
data: {
|
||||
title: 'Searchable Test Post Title',
|
||||
slug: `searchable-test-post-${Date.now()}`,
|
||||
excerpt: 'This is a searchable excerpt for testing',
|
||||
type: 'blog',
|
||||
status: 'published',
|
||||
publishedAt: new Date().toISOString(),
|
||||
tenant: testTenantId,
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
*/
|
||||
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
|
||||
import type { Access, PayloadRequest } from 'payload'
|
||||
import type { Access, PayloadRequest, Where } from 'payload'
|
||||
import {
|
||||
createSuperAdmin,
|
||||
createTenantUser,
|
||||
|
|
@ -122,7 +122,7 @@ describe('EmailLogs Collection Access', () => {
|
|||
const result = await executeAccess(emailLogsAccess.read, request)
|
||||
|
||||
expect(hasFilteredAccess(result)).toBe(true)
|
||||
const tenantIds = getTenantIdsFromInFilter(result as Record<string, unknown>)
|
||||
const tenantIds = getTenantIdsFromInFilter(result as Where)
|
||||
expect(tenantIds).toContain(1) // porwoll tenant ID
|
||||
})
|
||||
|
||||
|
|
@ -131,7 +131,7 @@ describe('EmailLogs Collection Access', () => {
|
|||
const result = await executeAccess(emailLogsAccess.read, request)
|
||||
|
||||
expect(hasFilteredAccess(result)).toBe(true)
|
||||
const tenantIds = getTenantIdsFromInFilter(result as Record<string, unknown>)
|
||||
const tenantIds = getTenantIdsFromInFilter(result as Where)
|
||||
expect(tenantIds).toEqual(expect.arrayContaining([1, 4, 5]))
|
||||
})
|
||||
|
||||
|
|
@ -141,7 +141,7 @@ describe('EmailLogs Collection Access', () => {
|
|||
const result = await executeAccess(emailLogsAccess.read, request)
|
||||
|
||||
expect(hasFilteredAccess(result)).toBe(true)
|
||||
const tenantIds = getTenantIdsFromInFilter(result as Record<string, unknown>)
|
||||
const tenantIds = getTenantIdsFromInFilter(result as Where)
|
||||
expect(tenantIds).toContain(1)
|
||||
expect(tenantIds).toContain(4)
|
||||
})
|
||||
|
|
@ -159,7 +159,7 @@ describe('EmailLogs Collection Access', () => {
|
|||
const result = await executeAccess(emailLogsAccess.read, request)
|
||||
|
||||
expect(hasFilteredAccess(result)).toBe(true)
|
||||
const tenantIds = getTenantIdsFromInFilter(result as Record<string, unknown>)
|
||||
const tenantIds = getTenantIdsFromInFilter(result as Where)
|
||||
expect(tenantIds).toEqual([])
|
||||
})
|
||||
})
|
||||
|
|
@ -377,7 +377,7 @@ describe('Access Control Edge Cases', () => {
|
|||
const result = await executeAccess(emailLogsAccess.read, request)
|
||||
|
||||
expect(hasFilteredAccess(result)).toBe(true)
|
||||
const tenantIds = getTenantIdsFromInFilter(result as Record<string, unknown>)
|
||||
const tenantIds = getTenantIdsFromInFilter(result as Where)
|
||||
expect(tenantIds).toHaveLength(0)
|
||||
})
|
||||
|
||||
|
|
@ -428,7 +428,7 @@ describe('Access Control Edge Cases', () => {
|
|||
const result = await executeAccess(emailLogsAccess.read, request)
|
||||
|
||||
expect(hasFilteredAccess(result)).toBe(true)
|
||||
const tenantIds = getTenantIdsFromInFilter(result as Record<string, unknown>)
|
||||
const tenantIds = getTenantIdsFromInFilter(result as Where)
|
||||
expect(tenantIds.sort()).toEqual([1, 2, 3])
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
||||
import type { PayloadRequest } from 'payload'
|
||||
import type { PayloadRequest, Where } from 'payload'
|
||||
import {
|
||||
createSuperAdmin,
|
||||
createTenantUser,
|
||||
|
|
@ -173,7 +173,7 @@ describe('tenantScopedPublicRead', () => {
|
|||
const result = await executeAccess(tenantScopedPublicRead, request)
|
||||
|
||||
expect(hasFilteredAccess(result)).toBe(true)
|
||||
expect(getTenantIdFromFilter(result as Record<string, unknown>)).toBe(1)
|
||||
expect(getTenantIdFromFilter(result as Where)).toBe(1)
|
||||
})
|
||||
|
||||
it('returns different tenant filter for different domain', async () => {
|
||||
|
|
@ -181,7 +181,7 @@ describe('tenantScopedPublicRead', () => {
|
|||
const result = await executeAccess(tenantScopedPublicRead, request)
|
||||
|
||||
expect(hasFilteredAccess(result)).toBe(true)
|
||||
expect(getTenantIdFromFilter(result as Record<string, unknown>)).toBe(4)
|
||||
expect(getTenantIdFromFilter(result as Where)).toBe(4)
|
||||
})
|
||||
|
||||
it('denies access for unknown domain', async () => {
|
||||
|
|
@ -286,7 +286,7 @@ describe('Access Control Integration Scenarios', () => {
|
|||
|
||||
// Should only see porwoll.de posts
|
||||
expect(hasFilteredAccess(result)).toBe(true)
|
||||
expect(getTenantIdFromFilter(result as Record<string, unknown>)).toBe(1)
|
||||
expect(getTenantIdFromFilter(result as Where)).toBe(1)
|
||||
})
|
||||
|
||||
it('admin editing posts from any tenant', async () => {
|
||||
|
|
|
|||
|
|
@ -288,7 +288,7 @@ describe('Data Masking', () => {
|
|||
it('handles non-Error objects', () => {
|
||||
const notAnError = { message: 'password=secret', code: 500 }
|
||||
|
||||
const masked = maskError(notAnError as Error)
|
||||
const masked = maskError(notAnError as unknown as Error)
|
||||
|
||||
expect(masked).toBeDefined()
|
||||
})
|
||||
|
|
|
|||
|
|
@ -205,7 +205,7 @@ describe('Rate Limiter', () => {
|
|||
resetIn: 45000,
|
||||
}
|
||||
|
||||
const headers = rateLimitHeaders(result, 30)
|
||||
const headers = rateLimitHeaders(result, 30) as Record<string, string>
|
||||
|
||||
expect(headers['X-RateLimit-Limit']).toBe('30')
|
||||
expect(headers['X-RateLimit-Remaining']).toBe('25')
|
||||
|
|
@ -220,7 +220,7 @@ describe('Rate Limiter', () => {
|
|||
retryAfter: 30,
|
||||
}
|
||||
|
||||
const headers = rateLimitHeaders(result, 10)
|
||||
const headers = rateLimitHeaders(result, 10) as Record<string, string>
|
||||
|
||||
expect(headers['Retry-After']).toBe('30')
|
||||
expect(headers['X-RateLimit-Remaining']).toBe('0')
|
||||
|
|
@ -233,8 +233,8 @@ describe('Rate Limiter', () => {
|
|||
resetIn: 60000,
|
||||
}
|
||||
|
||||
const headers = rateLimitHeaders(result, 10)
|
||||
const resetValue = headers['X-RateLimit-Reset'] as string
|
||||
const headers = rateLimitHeaders(result, 10) as Record<string, string>
|
||||
const resetValue = headers['X-RateLimit-Reset']
|
||||
|
||||
// The reset value should be a number (either timestamp or seconds)
|
||||
expect(resetValue).toBeDefined()
|
||||
|
|
|
|||
|
|
@ -30,15 +30,16 @@
|
|||
"./src/payload.config.ts"
|
||||
]
|
||||
},
|
||||
"target": "ES2022",
|
||||
"target": "ES2022"
|
||||
},
|
||||
"include": [
|
||||
"next-env.d.ts",
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
".next/types/**/*.ts"
|
||||
".next/types/**/*.ts",
|
||||
".next/dev/types/**/*.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
],
|
||||
]
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue