fix: optimize PageSpeed Insights performance (#115)

* fix: optimize PageSpeed Insights performance

This commit addresses several PageSpeed Insights issues to improve
the performance score from 56 to a target of 90+.

Changes:
- Add SWC config to output modern ES2022 JavaScript, eliminating
  unnecessary polyfills (~12 KiB savings)
- Convert vocalinux.png to WebP format (17KB vs 207KB - 92% reduction)
- Add OptimizedImage component with WebP/PNG fallback support
- Add _headers file for proper static asset caching (1 year cache
  for immutable assets)
- Configure Next.js with optimizePackageImports for framer-motion
  and lucide-react
- Fix heading hierarchy issue (Quick Install h3 -> h2)
- Add console.log removal in production builds

Expected improvements:
- Image delivery: ~41 KiB savings
- Legacy JavaScript: ~12 KiB savings
- Cache lifetime: 754 KiB proper caching
- Accessibility score: 98 -> 100 (heading order fix)

* test: add coverage for PageSpeed optimization

Add comprehensive test suite for PageSpeed Insights optimization changes:

- WebP image loading tests (optimized-image.tsx)
- next/image component configuration tests (next.config.js)
- _headers file generation and cache header tests
- Lazy loading behavior tests

Includes:
- Jest configuration with TypeScript support
- React Testing Library setup
- 54 test cases covering all PageSpeed optimization features
- Build verification

All tests pass: npm run test
Build passes: npm run build

* ci: trigger workflow

* ci: trigger workflow run

* ci: trigger workflow
This commit is contained in:
Jatin K Malik
2026-02-04 19:25:34 -08:00
committed by GitHub
parent 5e7fcc5578
commit 0d9c488d82
17 changed files with 5271 additions and 126 deletions
@@ -0,0 +1,143 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import { OptimizedImage, VocalinuxLogo } from '../optimized-image';
// Mock Next.js Image component
jest.mock('next/image', () => ({
__esModule: true,
default: (props: any) => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { priority, ...imgProps } = props;
// eslint-disable-next-line @next/next/no-img-element
return <img {...imgProps} alt={props.alt} />;
},
}));
describe('OptimizedImage', () => {
const defaultProps = {
src: '/test-image.png',
alt: 'Test Image',
width: 100,
height: 100,
};
it('renders without crashing', () => {
render(<OptimizedImage {...defaultProps} />);
const img = screen.getByAltText('Test Image');
expect(img).toBeInTheDocument();
});
it('renders a picture element with WebP source', () => {
const { container } = render(<OptimizedImage {...defaultProps} />);
const picture = container.querySelector('picture');
expect(picture).toBeInTheDocument();
const source = container.querySelector('source');
expect(source).toBeInTheDocument();
expect(source).toHaveAttribute('srcset', '/test-image.webp');
expect(source).toHaveAttribute('type', 'image/webp');
});
it('provides PNG fallback for browsers without WebP support', () => {
render(<OptimizedImage {...defaultProps} />);
const img = screen.getByAltText('Test Image');
expect(img).toHaveAttribute('src', '/test-image.png');
});
it('converts jpg to webp in source srcset', () => {
const { container } = render(
<OptimizedImage {...defaultProps} src="/image.jpg" />
);
const source = container.querySelector('source');
expect(source).toHaveAttribute('srcset', '/image.webp');
});
it('converts jpeg to webp in source srcset', () => {
const { container } = render(
<OptimizedImage {...defaultProps} src="/image.jpeg" />
);
const source = container.querySelector('source');
expect(source).toHaveAttribute('srcset', '/image.webp');
});
it('handles case-insensitive extensions', () => {
const { container } = render(
<OptimizedImage {...defaultProps} src="/image.PNG" />
);
const source = container.querySelector('source');
expect(source).toHaveAttribute('srcset', '/image.webp');
});
it('applies custom className to picture element', () => {
const { container } = render(
<OptimizedImage {...defaultProps} className="custom-class" />
);
const picture = container.querySelector('picture');
expect(picture).toHaveClass('custom-class');
});
it('passes priority prop to Image component', () => {
render(<OptimizedImage {...defaultProps} priority />);
const img = screen.getByAltText('Test Image');
// Priority images should not have loading="lazy"
expect(img).not.toHaveAttribute('loading', 'lazy');
});
it('sets correct dimensions on the image', () => {
render(<OptimizedImage {...defaultProps} width={200} height={150} />);
const img = screen.getByAltText('Test Image');
expect(img).toHaveAttribute('width', '200');
expect(img).toHaveAttribute('height', '150');
});
});
describe('VocalinuxLogo', () => {
it('renders without crashing', () => {
render(<VocalinuxLogo />);
const img = screen.getByAltText('Vocalinux');
expect(img).toBeInTheDocument();
});
it('renders a picture element with WebP source for logo', () => {
const { container } = render(<VocalinuxLogo />);
const picture = container.querySelector('picture');
expect(picture).toBeInTheDocument();
const source = container.querySelector('source');
expect(source).toBeInTheDocument();
expect(source).toHaveAttribute('srcset', '/vocalinux.webp');
expect(source).toHaveAttribute('type', 'image/webp');
});
it('provides PNG fallback for the logo', () => {
render(<VocalinuxLogo />);
const img = screen.getByAltText('Vocalinux');
expect(img).toHaveAttribute('src', '/vocalinux.png');
});
it('uses default dimensions when not specified', () => {
render(<VocalinuxLogo />);
const img = screen.getByAltText('Vocalinux');
expect(img).toHaveAttribute('width', '32');
expect(img).toHaveAttribute('height', '32');
});
it('accepts custom dimensions', () => {
render(<VocalinuxLogo width={64} height={64} />);
const img = screen.getByAltText('Vocalinux');
expect(img).toHaveAttribute('width', '64');
expect(img).toHaveAttribute('height', '64');
});
it('applies custom className', () => {
const { container } = render(<VocalinuxLogo className="logo-class" />);
const picture = container.querySelector('picture');
expect(picture).toHaveClass('logo-class');
});
it('passes priority prop for eager loading', () => {
render(<VocalinuxLogo priority />);
const img = screen.getByAltText('Vocalinux');
expect(img).not.toHaveAttribute('loading', 'lazy');
});
});
+5 -2
View File
@@ -3,8 +3,11 @@
import React from "react";
import Image from "next/image";
// Logo component using the actual application icon
export const VocalinuxLogo: React.FC<{ className?: string }> = ({ className = "" }) => {
// Re-export from optimized-image component for backward compatibility
export { VocalinuxLogo } from "./optimized-image";
// Legacy export - use VocalinuxLogo from optimized-image instead
export const VocalinuxLogoLegacy: React.FC<{ className?: string }> = ({ className = "" }) => {
return (
<Image
src="/vocalinux.png"
+70
View File
@@ -0,0 +1,70 @@
"use client";
import React, { useState } from "react";
import Image from "next/image";
interface OptimizedImageProps {
src: string;
alt: string;
width: number;
height: number;
className?: string;
priority?: boolean;
}
/**
* Optimized image component that uses WebP format with PNG fallback.
* Browsers that support WebP will load the smaller WebP file, while older
* browsers will fallback to PNG format.
*/
export const OptimizedImage: React.FC<OptimizedImageProps> = ({
src,
alt,
width,
height,
className = "",
priority = false,
}) => {
// Get WebP version of the image
const webpSrc = src.replace(/\.(png|jpg|jpeg)$/i, '.webp');
// For Next.js Image component with priority and proper optimization
// we use the Next.js Image with WebP source
return (
<picture className={className}>
<source srcSet={webpSrc} type="image/webp" />
<Image
src={src}
alt={alt}
width={width}
height={height}
className={className}
priority={priority}
/>
</picture>
);
};
/**
* Specialized Vocalinux logo component with WebP support
*/
export const VocalinuxLogo: React.FC<{
className?: string;
width?: number;
height?: number;
priority?: boolean;
}> = ({ className = "", width = 32, height = 32, priority = false }) => {
return (
<picture className={className}>
<source srcSet="/vocalinux.webp" type="image/webp" />
<Image
src="/vocalinux.png"
alt="Vocalinux"
width={width}
height={height}
className={className}
priority={priority}
/>
</picture>
);
};