Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<div align="center">

<picture>
<source srcset="https://nest.owasp.org/img/owasp_icon_white_sm.png" media="(prefers-color-scheme: dark)">
<img src="https://nest.owasp.org/img/owasp_icon_black_sm.png" alt="OWASP Logo" width="200">
<source srcset="https://nest.owasp.org/img/logo_dark.png" media="(prefers-color-scheme: dark)">
<img src="https://nest.owasp.org/img/logo_light.png" alt="OWASP Logo" width="200">
</picture>

# [OWASP Nest](https://nest.owasp.org/)
Expand Down
40 changes: 37 additions & 3 deletions frontend/__tests__/unit/components/LoadingSpinner.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ describe('<LoadingSpinner />', () => {
expect(images.length).toBe(2)
// Should fallback to default images when no valid input is provided
expect(images[0].getAttribute('src')).toContain('white')
expect(images[1].getAttribute('src')).toContain('black')
expect(images[1].getAttribute('src')).toContain('blue')
})
it('has appropriate alt text for accessibility', () => {
render(<LoadingSpinner />)
Expand All @@ -43,15 +43,15 @@ describe('<LoadingSpinner />', () => {
render(<LoadingSpinner />)
const images = screen.getAllByAltText('Loading indicator')
expect(images[0].getAttribute('src')).toContain('white') // dark image
expect(images[1].getAttribute('src')).toContain('black') // light image
expect(images[1].getAttribute('src')).toContain('blue') // light image
})

it('renders custom image when imageUrl is provided', () => {
const customUrl = '/img/spinner_white.png'
render(<LoadingSpinner imageUrl={customUrl} />)
const images = screen.getAllByAltText('Loading indicator')
expect(images[0].getAttribute('src')).toBe(customUrl) // Should use the exact custom URL
expect(images[1].getAttribute('src')).toBe('/img/spinner_black.png') // Should transform white to black
expect(images[1].getAttribute('src')).toBe('/img/spinner_blue.png') // Should transform white to blue
})

it('renders spinner container with correct styles', () => {
Expand All @@ -69,4 +69,38 @@ describe('<LoadingSpinner />', () => {
expect(images[1]).toBeInTheDocument()
expect(images[1].tagName.toLowerCase()).toBe('img')
})

describe('Theme-specific image loading', () => {
it('renders spinner_white.png for dark theme', () => {
render(<LoadingSpinner />)
const images = screen.getAllByAltText('Loading indicator')

const darkThemeImage = images[0]
expect(darkThemeImage).toHaveAttribute('src', '/img/spinner_white.png')
expect(darkThemeImage).toHaveClass('hidden', 'rounded-full', 'dark:block')
})

it('renders spinner_blue.png for light theme', () => {
render(<LoadingSpinner />)
const images = screen.getAllByAltText('Loading indicator')

const lightThemeImage = images[1]
expect(lightThemeImage).toHaveAttribute('src', '/img/spinner_blue.png')
expect(lightThemeImage).toHaveClass('rounded-full', 'dark:hidden')
})

it('uses correct theme images when custom imageUrl is provided', () => {
const customUrl = '/img/spinner_white.png'
render(<LoadingSpinner imageUrl={customUrl} />)
const images = screen.getAllByAltText('Loading indicator')

const darkThemeImage = images[0]
expect(darkThemeImage).toHaveAttribute('src', customUrl)
expect(darkThemeImage).toHaveClass('hidden', 'rounded-full', 'dark:block')

const lightThemeImage = images[1]
expect(lightThemeImage).toHaveAttribute('src', '/img/spinner_blue.png')
expect(lightThemeImage).toHaveClass('rounded-full', 'dark:hidden')
})
})
})
18 changes: 15 additions & 3 deletions frontend/__tests__/unit/pages/Header.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ describe('Header Component', () => {

// Use getAllByRole for multiple elements
const logoImages = screen.getAllByRole('img', { name: /owasp logo/i })
expect(logoImages.length).toBe(4) // 2 in desktop header + 2 in mobile menu
expect(logoImages.length).toBe(2) // 1 in desktop header + 1 in mobile menu

const brandTexts = screen.getAllByText('Nest')
expect(brandTexts.length).toBe(2) // One in desktop header, one in mobile menu
Expand All @@ -235,7 +235,7 @@ describe('Header Component', () => {
expect(screen.getByRole('banner')).toBeInTheDocument()

const logoImages = screen.getAllByRole('img', { name: /owasp logo/i })
expect(logoImages.length).toBe(4)
expect(logoImages.length).toBe(2)

const brandTexts = screen.getAllByText('Nest')
expect(brandTexts.length).toBe(2)
Expand All @@ -250,7 +250,7 @@ describe('Header Component', () => {
renderWithSession(<Header isGitHubAuthEnabled />)

const logoImages = screen.getAllByRole('img', { name: /owasp logo/i })
expect(logoImages.length).toBe(4) // 2 in desktop header + 2 in mobile menu
expect(logoImages.length).toBe(2) // 1 in desktop header + 1 in mobile menu

for (const logo of logoImages) {
expect(logo).toHaveAttribute('width', '64')
Expand All @@ -259,6 +259,18 @@ describe('Header Component', () => {
}
})

it('renders logo_dark.png image in both desktop and mobile header', () => {
renderWithSession(<Header isGitHubAuthEnabled />)

const logoImages = screen.getAllByRole('img', { name: /owasp logo/i })
expect(logoImages.length).toBe(2)

for (const logo of logoImages) {
expect(logo).toHaveAttribute('src', '/img/logo_dark.png')
expect(logo).toBeInTheDocument()
}
})

it('renders Nest text branding', () => {
renderWithSession(<Header isGitHubAuthEnabled />)

Expand Down
Binary file added frontend/public/img/favicon.ico
Binary file not shown.
Binary file added frontend/public/img/logo_dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/public/img/logo_light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/public/img/nest_1200x630_light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed frontend/public/img/owasp_icon_black_sm.png
Binary file not shown.
Binary file not shown.
Binary file added frontend/public/img/spinner_blue.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 5 additions & 5 deletions frontend/src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,17 @@ const geistMono = Geist_Mono({
export const metadata: Metadata = {
description: 'Your gateway to OWASP. Discover, engage, and help shape the future!',
icons: {
apple: 'https://owasp.org/www--site-theme/favicon.ico',
icon: 'https://owasp.org/www--site-theme/favicon.ico',
shortcut: 'https://owasp.org/www--site-theme/favicon.ico',
apple: '/img/favicon.ico',
icon: '/img/favicon.ico',
shortcut: '/img/favicon.ico',
},
openGraph: {
description: 'Your gateway to OWASP. Discover, engage, and help shape the future!',
images: [
{
alt: 'OWASP logo',
height: 630,
url: 'https://nest.owasp.org/img/owasp_icon_white_background.png',
url: 'https://nest.owasp.org/img/nest_1200x630_light.png',
width: 1200,
},
],
Expand All @@ -53,7 +53,7 @@ export const metadata: Metadata = {
card: 'summary_large_image',
creator: '@owasp',
description: 'Your gateway to OWASP. Discover, engage, and help shape the future!',
images: ['https://nest.owasp.org/img/owasp_icon_white_background.png'],
images: ['https://nest.owasp.org/img/nest_1200x630_light.png'],
site: '@owasp',
title: 'Home – OWASP Nest',
},
Expand Down
52 changes: 20 additions & 32 deletions frontend/src/components/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,22 +60,16 @@ export default function Header({ isGitHubAuthEnabled }: { readonly isGitHubAuthE
{/* Logo */}
<Link href="/" onClick={() => setMobileMenuOpen(false)}>
<div className="flex h-full items-center">
<Image
width={64}
height={64}
priority={true}
src={'/img/owasp_icon_white_sm.png'}
className="hidden dark:block"
alt="OWASP Logo"
/>
<Image
width={64}
height={64}
priority={true}
src={'/img/owasp_icon_black_sm.png'}
className="block dark:hidden"
alt="OWASP Logo"
/>
<div className="flex h-16 w-16 items-center justify-center py-2">
<Image
width={64}
height={64}
priority={true}
src={'/img/logo_dark.png'}
className="h-full w-auto object-contain"
alt="OWASP Logo"
/>
</div>
<div className="text-2xl text-slate-800 dark:text-slate-300 dark:hover:text-slate-200">
Nest
</div>
Expand Down Expand Up @@ -158,22 +152,16 @@ export default function Header({ isGitHubAuthEnabled }: { readonly isGitHubAuthE
<div className="flex flex-col justify-center gap-5">
<Link href="/" onClick={() => setMobileMenuOpen(false)}>
<div className="flex h-full items-center">
<Image
width={64}
height={64}
priority={true}
src={'/img/owasp_icon_white_sm.png'}
className="hidden h-16 dark:block"
alt="OWASP Logo"
/>
<Image
width={64}
height={64}
priority={true}
src={'/img/owasp_icon_black_sm.png'}
className="block h-16 dark:hidden"
alt="OWASP Logo"
/>
<div className="flex h-16 w-16 items-center justify-center py-2">
<Image
width={64}
height={64}
priority={true}
src={'/img/logo_dark.png'}
className="h-full w-auto object-contain"
alt="OWASP Logo"
/>
</div>
<div className="text-2xl text-slate-800 dark:text-slate-300 dark:hover:text-slate-200">
Nest
</div>
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/components/LoadingSpinner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ interface LoadingSpinnerProps {
}

const LoadingSpinner: React.FC<LoadingSpinnerProps> = ({ imageUrl }) => {
const defaultImage = '/img/owasp_icon_white_sm.png'
const defaultImage = '/img/spinner_white.png'
const image = imageUrl || defaultImage
const dark = image.replace('white', 'black')
const dark = image.replace('white', 'blue')

return (
<div className="flex min-h-[60vh] items-center justify-center">
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/SearchPageLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ const SearchPageLayout = ({
searchPlaceholder,
empty,
indexName,
loadingImageUrl = '/img/owasp_icon_white_sm.png',
loadingImageUrl = '/img/spinner_light.png',
sortChildren,
children,
}: SearchPageLayoutProps) => {
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/utils/metaconfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export function generateSeoMetadata({
title,
description,
canonicalPath,
ogImage = 'https://nest.owasp.org/img/owasp_icon_white_background.png',
ogImage = 'https://nest.owasp.org/img/nest_1200x630_light.png',
keywords = [],
type = 'website',
locale = 'en_US',
Expand Down