fix(web): hygiene — OG image/metadataBase, Header a11y text + nav flag, dead code
CI Pipeline with Test Management / 🧪 Run Tests & Generate Reports (push) Waiting to run
CI Pipeline with Test Management / 🐳 Docker Integration Tests (push) Blocked by required conditions
CI Pipeline with Test Management / 🏗️ Build Docker Image (push) Blocked by required conditions
CI Pipeline with Test Management / 📊 Generate Test Summary (push) Blocked by required conditions
Test Reporting & Gherkin Analysis / 🧪 Run Tests & Generate Reports (push) Waiting to run
Test Reporting & Gherkin Analysis / 📊 Analyze Test Coverage (push) Blocked by required conditions
Test Reporting & Gherkin Analysis / 🔄 Sync with Linear (push) Blocked by required conditions
Test Reporting & Gherkin Analysis / ⚡ Performance Monitoring (push) Blocked by required conditions
CI Pipeline with Test Management / 🧪 Run Tests & Generate Reports (push) Waiting to run
CI Pipeline with Test Management / 🐳 Docker Integration Tests (push) Blocked by required conditions
CI Pipeline with Test Management / 🏗️ Build Docker Image (push) Blocked by required conditions
CI Pipeline with Test Management / 📊 Generate Test Summary (push) Blocked by required conditions
Test Reporting & Gherkin Analysis / 🧪 Run Tests & Generate Reports (push) Waiting to run
Test Reporting & Gherkin Analysis / 📊 Analyze Test Coverage (push) Blocked by required conditions
Test Reporting & Gherkin Analysis / 🔄 Sync with Linear (push) Blocked by required conditions
Test Reporting & Gherkin Analysis / ⚡ Performance Monitoring (push) Blocked by required conditions
- siteConfig.ogImage pointed to an existing asset; metadataBase added so relative OG/Twitter URLs resolve against the real origin instead of localhost - Header sr-only menu label moved to common.json (a11y.openMenu/closeMenu); navigation primary styling driven by a NavigationItem.primary flag instead of brittle 'Kapcsolat' string comparisons - remove dead code: lib/site-config.ts (unused hybrid config) and the unused, schema-divergent siteConfig.contact.form - ContactConfig type cleaned up (address/form removed) Closes MITHOME-77
This commit is contained in:
@@ -1,382 +0,0 @@
|
||||
import { getSiteConfig, saveSiteConfig, initializeDefaultConfig } from './site-config'
|
||||
import { getCollection } from './mongodb'
|
||||
import { siteConfig as staticConfig } from '@/config/site'
|
||||
import { SiteConfig } from '@/types/site'
|
||||
import logger from './logger'
|
||||
|
||||
// Mock dependencies
|
||||
jest.mock('./mongodb')
|
||||
jest.mock('./logger', () => ({
|
||||
__esModule: true,
|
||||
default: {
|
||||
info: jest.fn(),
|
||||
warn: jest.fn(),
|
||||
error: jest.fn()
|
||||
}
|
||||
}))
|
||||
jest.mock('@/config/site', () => ({
|
||||
siteConfig: {
|
||||
general: {
|
||||
name: 'Test Site',
|
||||
description: 'Test Description',
|
||||
url: 'https://test.com',
|
||||
ogImage: 'https://test.com/og.jpg',
|
||||
locale: 'en'
|
||||
},
|
||||
navigation: {
|
||||
main: [
|
||||
{ label: 'Home', href: '/' },
|
||||
{ label: 'About', href: '/about' }
|
||||
],
|
||||
footer: [
|
||||
{ label: 'Privacy', href: '/privacy' },
|
||||
{ label: 'Terms', href: '/terms' }
|
||||
]
|
||||
},
|
||||
hero: {
|
||||
title: 'Welcome',
|
||||
subtitle: 'Test Subtitle',
|
||||
description: 'Test description',
|
||||
cta: {
|
||||
primary: {
|
||||
text: 'Get Started',
|
||||
href: '/get-started'
|
||||
}
|
||||
}
|
||||
},
|
||||
services: {
|
||||
title: 'Our Services',
|
||||
subtitle: 'What we offer',
|
||||
services: []
|
||||
},
|
||||
about: {
|
||||
title: 'About Us',
|
||||
description: ['Test about section'],
|
||||
usps: []
|
||||
},
|
||||
footer: {
|
||||
copyright: '© 2024 Test Site',
|
||||
links: []
|
||||
},
|
||||
contact: {
|
||||
email: 'test@test.com',
|
||||
address: 'Test Address',
|
||||
form: {
|
||||
title: 'Contact Us',
|
||||
description: 'Get in touch',
|
||||
submitText: 'Send Message',
|
||||
fields: {
|
||||
name: { label: 'Name', placeholder: 'Your name', required: true },
|
||||
email: { label: 'Email', placeholder: 'your@email.com', required: true },
|
||||
message: { label: 'Message', placeholder: 'Your message', required: true },
|
||||
consent: { label: 'I agree', required: true }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}))
|
||||
|
||||
const mockCollection = {
|
||||
findOne: jest.fn(),
|
||||
replaceOne: jest.fn()
|
||||
}
|
||||
|
||||
|
||||
|
||||
describe('Site Config', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
;(getCollection as jest.Mock).mockResolvedValue(mockCollection)
|
||||
})
|
||||
|
||||
describe('getSiteConfig', () => {
|
||||
it('should return MongoDB config when available', async () => {
|
||||
const mockConfig: SiteConfig = {
|
||||
general: {
|
||||
name: 'MongoDB Site',
|
||||
description: 'MongoDB Description',
|
||||
url: 'https://mongodb.com',
|
||||
ogImage: 'https://mongodb.com/og.jpg',
|
||||
locale: 'en'
|
||||
},
|
||||
navigation: {
|
||||
main: [{ label: 'Home', href: '/' }],
|
||||
footer: [{ label: 'Privacy', href: '/privacy' }]
|
||||
},
|
||||
hero: {
|
||||
title: 'MongoDB Hero',
|
||||
subtitle: 'MongoDB Subtitle',
|
||||
description: 'MongoDB Description',
|
||||
cta: {
|
||||
primary: {
|
||||
text: 'Get Started',
|
||||
href: '/get-started'
|
||||
}
|
||||
}
|
||||
},
|
||||
services: {
|
||||
title: 'Services',
|
||||
subtitle: 'Our services',
|
||||
services: []
|
||||
},
|
||||
about: {
|
||||
title: 'About',
|
||||
description: ['About us'],
|
||||
usps: []
|
||||
},
|
||||
footer: {
|
||||
copyright: '© 2024 MongoDB',
|
||||
links: []
|
||||
},
|
||||
contact: {
|
||||
email: 'mongodb@test.com',
|
||||
address: 'MongoDB Address',
|
||||
form: {
|
||||
title: 'Contact',
|
||||
description: 'Get in touch',
|
||||
submitText: 'Send',
|
||||
fields: {
|
||||
name: { label: 'Name', placeholder: 'Name', required: true },
|
||||
email: { label: 'Email', placeholder: 'Email', required: true },
|
||||
message: { label: 'Message', placeholder: 'Message', required: true },
|
||||
consent: { label: 'Consent', required: true }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mockCollection.findOne.mockResolvedValue({
|
||||
_id: 'mock-id',
|
||||
data: mockConfig
|
||||
})
|
||||
|
||||
const config = await getSiteConfig()
|
||||
|
||||
expect(config).toEqual(mockConfig)
|
||||
expect(mockCollection.findOne).toHaveBeenCalledWith({
|
||||
type: 'site_config',
|
||||
environment: 'development'
|
||||
})
|
||||
expect(logger.info).toHaveBeenCalledWith(
|
||||
'Loaded site config from MongoDB',
|
||||
{ configId: 'mock-id' }
|
||||
)
|
||||
})
|
||||
|
||||
it('should return static config when MongoDB config not found', async () => {
|
||||
mockCollection.findOne.mockResolvedValue(null)
|
||||
|
||||
const config = await getSiteConfig()
|
||||
|
||||
expect(config).toEqual(staticConfig)
|
||||
expect(logger.info).toHaveBeenCalledWith(
|
||||
'MongoDB config not found, using static fallback'
|
||||
)
|
||||
})
|
||||
|
||||
it('should return static config when MongoDB is unavailable', async () => {
|
||||
mockCollection.findOne.mockRejectedValue(new Error('Connection failed'))
|
||||
|
||||
const config = await getSiteConfig()
|
||||
|
||||
expect(config).toEqual(staticConfig)
|
||||
expect(logger.warn).toHaveBeenCalledWith(
|
||||
'MongoDB unavailable, using static config',
|
||||
{ error: 'Connection failed' }
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('saveSiteConfig', () => {
|
||||
it('should save config to MongoDB successfully', async () => {
|
||||
const newConfig: SiteConfig = {
|
||||
general: {
|
||||
name: 'New Site',
|
||||
description: 'New Description',
|
||||
url: 'https://new.com',
|
||||
ogImage: 'https://new.com/og.jpg',
|
||||
locale: 'en'
|
||||
},
|
||||
navigation: {
|
||||
main: [{ label: 'Home', href: '/' }],
|
||||
footer: [{ label: 'Privacy', href: '/privacy' }]
|
||||
},
|
||||
hero: {
|
||||
title: 'New Hero',
|
||||
subtitle: 'New Subtitle',
|
||||
description: 'New Description',
|
||||
cta: {
|
||||
primary: {
|
||||
text: 'Get Started',
|
||||
href: '/get-started'
|
||||
}
|
||||
}
|
||||
},
|
||||
services: {
|
||||
title: 'Services',
|
||||
subtitle: 'Our services',
|
||||
services: []
|
||||
},
|
||||
about: {
|
||||
title: 'About',
|
||||
description: ['About us'],
|
||||
usps: []
|
||||
},
|
||||
footer: {
|
||||
copyright: '© 2024 New',
|
||||
links: []
|
||||
},
|
||||
contact: {
|
||||
email: 'new@test.com',
|
||||
address: 'New Address',
|
||||
form: {
|
||||
title: 'Contact',
|
||||
description: 'Get in touch',
|
||||
submitText: 'Send',
|
||||
fields: {
|
||||
name: { label: 'Name', placeholder: 'Name', required: true },
|
||||
email: { label: 'Email', placeholder: 'Email', required: true },
|
||||
message: { label: 'Message', placeholder: 'Message', required: true },
|
||||
consent: { label: 'Consent', required: true }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mockCollection.replaceOne.mockResolvedValue({ acknowledged: true })
|
||||
|
||||
const result = await saveSiteConfig(newConfig)
|
||||
|
||||
expect(result).toBe(true)
|
||||
expect(mockCollection.replaceOne).toHaveBeenCalledWith(
|
||||
{ type: 'site_config', environment: 'development' },
|
||||
{
|
||||
type: 'site_config',
|
||||
environment: 'development',
|
||||
data: newConfig,
|
||||
lastModified: expect.any(Date)
|
||||
},
|
||||
{ upsert: true }
|
||||
)
|
||||
expect(logger.info).toHaveBeenCalledWith('Saved site config to MongoDB')
|
||||
})
|
||||
|
||||
it('should return false when save fails', async () => {
|
||||
const newConfig: SiteConfig = {
|
||||
general: {
|
||||
name: 'New Site',
|
||||
description: 'New Description',
|
||||
url: 'https://new.com',
|
||||
ogImage: 'https://new.com/og.jpg',
|
||||
locale: 'en'
|
||||
},
|
||||
navigation: {
|
||||
main: [{ label: 'Home', href: '/' }],
|
||||
footer: [{ label: 'Privacy', href: '/privacy' }]
|
||||
},
|
||||
hero: {
|
||||
title: 'New Hero',
|
||||
subtitle: 'New Subtitle',
|
||||
description: 'New Description',
|
||||
cta: {
|
||||
primary: {
|
||||
text: 'Get Started',
|
||||
href: '/get-started'
|
||||
}
|
||||
}
|
||||
},
|
||||
services: {
|
||||
title: 'Services',
|
||||
subtitle: 'Our services',
|
||||
services: []
|
||||
},
|
||||
about: {
|
||||
title: 'About',
|
||||
description: ['About us'],
|
||||
usps: []
|
||||
},
|
||||
footer: {
|
||||
copyright: '© 2024 New',
|
||||
links: []
|
||||
},
|
||||
contact: {
|
||||
email: 'new@test.com',
|
||||
address: 'New Address',
|
||||
form: {
|
||||
title: 'Contact',
|
||||
description: 'Get in touch',
|
||||
submitText: 'Send',
|
||||
fields: {
|
||||
name: { label: 'Name', placeholder: 'Name', required: true },
|
||||
email: { label: 'Email', placeholder: 'Email', required: true },
|
||||
message: { label: 'Message', placeholder: 'Message', required: true },
|
||||
consent: { label: 'Consent', required: true }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mockCollection.replaceOne.mockRejectedValue(new Error('Save failed'))
|
||||
|
||||
const result = await saveSiteConfig(newConfig)
|
||||
|
||||
expect(result).toBe(false)
|
||||
expect(logger.error).toHaveBeenCalledWith(
|
||||
'Failed to save config to MongoDB',
|
||||
{ error: 'Save failed' }
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('initializeDefaultConfig', () => {
|
||||
it('should initialize default config when none exists', async () => {
|
||||
mockCollection.findOne.mockResolvedValue(null)
|
||||
mockCollection.replaceOne.mockResolvedValue({ acknowledged: true })
|
||||
|
||||
await initializeDefaultConfig()
|
||||
|
||||
expect(mockCollection.findOne).toHaveBeenCalledWith({
|
||||
type: 'site_config',
|
||||
environment: 'development'
|
||||
})
|
||||
expect(mockCollection.replaceOne).toHaveBeenCalledWith(
|
||||
{ type: 'site_config', environment: 'development' },
|
||||
{
|
||||
type: 'site_config',
|
||||
environment: 'development',
|
||||
data: staticConfig,
|
||||
lastModified: expect.any(Date)
|
||||
},
|
||||
{ upsert: true }
|
||||
)
|
||||
expect(logger.info).toHaveBeenCalledWith(
|
||||
'Initialized default site config in MongoDB'
|
||||
)
|
||||
})
|
||||
|
||||
it('should not initialize when config already exists', async () => {
|
||||
mockCollection.findOne.mockResolvedValue({
|
||||
_id: 'existing-id',
|
||||
data: staticConfig
|
||||
})
|
||||
|
||||
await initializeDefaultConfig()
|
||||
|
||||
expect(mockCollection.replaceOne).not.toHaveBeenCalled()
|
||||
expect(logger.info).toHaveBeenCalledWith(
|
||||
'Site config already exists in MongoDB'
|
||||
)
|
||||
})
|
||||
|
||||
it('should handle initialization errors gracefully', async () => {
|
||||
mockCollection.findOne.mockRejectedValue(new Error('Connection failed'))
|
||||
|
||||
await initializeDefaultConfig()
|
||||
|
||||
expect(logger.error).toHaveBeenCalledWith(
|
||||
'Failed to initialize site config',
|
||||
{ error: 'Connection failed' }
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,74 +0,0 @@
|
||||
// Hybrid approach: File-based fallback + MongoDB integration for future
|
||||
|
||||
import { siteConfig as staticConfig } from '@/config/site'
|
||||
import { SiteConfig } from '@/types/site'
|
||||
import { getCollection } from './mongodb'
|
||||
import logger from './logger'
|
||||
|
||||
/**
|
||||
* Get site configuration with hybrid approach
|
||||
* 1. Try MongoDB first (production-ready)
|
||||
* 2. Fall back to static file (development/development safe)
|
||||
*/
|
||||
export async function getSiteConfig(): Promise<SiteConfig> {
|
||||
try {
|
||||
const collection = await getCollection('site_config')
|
||||
const doc = await collection.findOne({ type: 'site_config', environment: 'development' })
|
||||
|
||||
if (doc && doc.data) {
|
||||
logger.info('Loaded site config from MongoDB', { configId: doc._id })
|
||||
return doc.data as SiteConfig
|
||||
} else {
|
||||
logger.info('MongoDB config not found, using static fallback')
|
||||
return staticConfig
|
||||
}
|
||||
} catch (error) {
|
||||
logger.warn('MongoDB unavailable, using static config', { error: (error as Error).message })
|
||||
return staticConfig
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save site configuration to MongoDB
|
||||
* For future admin panel integration
|
||||
*/
|
||||
export async function saveSiteConfig(config: SiteConfig): Promise<boolean> {
|
||||
try {
|
||||
const collection = await getCollection('site_config')
|
||||
await collection.replaceOne(
|
||||
{ type: 'site_config', environment: 'development' },
|
||||
{
|
||||
type: 'site_config',
|
||||
environment: 'development',
|
||||
data: config,
|
||||
lastModified: new Date()
|
||||
},
|
||||
{ upsert: true }
|
||||
)
|
||||
|
||||
logger.info('Saved site config to MongoDB')
|
||||
return true
|
||||
} catch (error) {
|
||||
logger.error('Failed to save config to MongoDB', { error: (error as Error).message })
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize default config in MongoDB (one-time setup)
|
||||
*/
|
||||
export async function initializeDefaultConfig(): Promise<void> {
|
||||
try {
|
||||
const collection = await getCollection('site_config')
|
||||
const existingConfig = await collection.findOne({ type: 'site_config', environment: 'development' })
|
||||
|
||||
if (!existingConfig) {
|
||||
await saveSiteConfig(staticConfig)
|
||||
logger.info('Initialized default site config in MongoDB')
|
||||
} else {
|
||||
logger.info('Site config already exists in MongoDB')
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Failed to initialize site config', { error: (error as Error).message })
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user