Files
portifolio-evandrodevbr/app/components/CompanyLogos.altText.pbt.test.tsx
T
Evandro Fonseca Junior b791b7de9e feat: control room hero, SEO/AEO optimization and design system fixes
Hero: replace particle field with cobe globe (Capricorn view, multi-axis drag, reduced-motion safe), telemetry HUD with pointer parallax, particle text build effect, Space Grotesk display font. SEO: short titles with template, PT-BR accents, twitter cards on dynamic cases, escaped JSON-LD via lib/jsonld.ts, CSP report-only, 25 service FAQs answer-first. Design: fix TW4 layered reset killing spacing utilities, literal @theme tokens, mobile hero overflow, header overlap, empty case cards. Remove framer-motion (unified on motion/react), header version badge, phone number from hero CTA. Production build verified (24 static pages).
2026-08-27 10:55:21 -03:00

321 lines
10 KiB
TypeScript

import { render } from "@testing-library/react";
import fc from "fast-check";
import CompanyLogos from "./CompanyLogos";
// Mock motion/react with configurable useReducedMotion
let mockReducedMotion = false;
jest.mock("motion/react", () => {
const actual = jest.requireActual("motion/react");
return {
...actual,
useReducedMotion: () => mockReducedMotion,
motion: {
div: ({ children, animate, transition, ...props }: any) => {
return <div {...props}>{children}</div>;
},
},
};
});
describe("Property 10: Alt Text for Images", () => {
afterEach(() => {
mockReducedMotion = false;
});
it("should have non-empty alt attributes for all company logo images", () => {
/**
* **Validates: Requirements 6.2**
*
* Property: For any company logo image in the CompanyLogos component,
* it should have a non-empty alt attribute describing the company.
* This ensures accessibility for screen readers and assistive technologies.
*/
fc.assert(
fc.property(
fc.boolean(), // Random reduced motion preference
(shouldReduce) => {
mockReducedMotion = shouldReduce;
const { container } = render(<CompanyLogos />);
// Find all logo images
const logoImages = container.querySelectorAll("img");
// Verify we found images
if (logoImages.length === 0) {
return false;
}
// Check that all images have non-empty alt text
return Array.from(logoImages).every((img) => {
const alt = img.getAttribute("alt");
return alt !== null && alt.trim().length > 0;
});
},
),
{ numRuns: 100 },
);
});
it("should have descriptive alt text that includes company name", () => {
/**
* **Validates: Requirements 6.2**
*
* Property: For any company logo image, the alt text should be descriptive
* and include identifying information about the company. This ensures
* meaningful accessibility information is provided.
*/
fc.assert(
fc.property(
fc.boolean(), // Random reduced motion preference
(shouldReduce) => {
mockReducedMotion = shouldReduce;
const { container } = render(<CompanyLogos />);
// Find all logo images
const logoImages = container.querySelectorAll("img");
// Verify we found images
if (logoImages.length === 0) {
return false;
}
// Check that all alt text contains "logo" or is descriptive
return Array.from(logoImages).every((img) => {
const alt = img.getAttribute("alt");
if (!alt) return false;
const altLower = alt.toLowerCase();
// Alt text should contain "logo" or be at least 5 characters (descriptive)
return altLower.includes("logo") || alt.trim().length >= 5;
});
},
),
{ numRuns: 100 },
);
});
it("should maintain alt text consistency across multiple renders", () => {
/**
* **Validates: Requirements 6.2**
*
* Property: For any number of component renders, the alt text for each
* company logo should remain consistent. This ensures stable accessibility
* information across re-renders.
*/
fc.assert(
fc.property(
fc.integer({ min: 2, max: 5 }), // Test 2-5 renders
fc.boolean(), // Random reduced motion preference
(numRenders, shouldReduce) => {
mockReducedMotion = shouldReduce;
let firstRenderAltTexts: string[] = [];
for (let i = 0; i < numRenders; i++) {
const { container, unmount } = render(<CompanyLogos />);
const logoImages = container.querySelectorAll("img");
const currentAltTexts = Array.from(logoImages).map(
(img) => img.getAttribute("alt") || "",
);
if (i === 0) {
firstRenderAltTexts = currentAltTexts;
} else {
// Compare with first render
if (currentAltTexts.length !== firstRenderAltTexts.length) {
unmount();
return false;
}
// Check that alt texts match (accounting for duplicates in animated mode)
const expectedCount = shouldReduce ? 5 : 10;
if (currentAltTexts.length !== expectedCount) {
unmount();
return false;
}
}
unmount();
}
return true;
},
),
{ numRuns: 100 },
);
});
it("should have unique alt text for each distinct company", () => {
/**
* **Validates: Requirements 6.2**
*
* Property: For any set of company logos, each distinct company should have
* its own unique alt text. This ensures each company is properly identified
* for accessibility purposes.
*/
fc.assert(
fc.property(fc.constant(null), () => {
mockReducedMotion = true; // Use static mode to get unique logos
const { container } = render(<CompanyLogos />);
const logoImages = container.querySelectorAll("img");
const altTexts = Array.from(logoImages).map(
(img) => img.getAttribute("alt") || "",
);
// In static mode, we should have 5 unique alt texts
const uniqueAltTexts = new Set(altTexts);
// Should have 5 unique alt texts (one per company)
return uniqueAltTexts.size === 5 && altTexts.length === 5;
}),
{ numRuns: 100 },
);
});
it("should preserve alt text in both animated and static modes", () => {
/**
* **Validates: Requirements 6.2**
*
* Property: For any reduced motion preference, all company logo images
* should maintain their alt text attributes. The animation mode should
* not affect accessibility attributes.
*/
fc.assert(
fc.property(
fc.boolean(), // Random reduced motion preference
(shouldReduce) => {
mockReducedMotion = shouldReduce;
const { container } = render(<CompanyLogos />);
const logoImages = container.querySelectorAll("img");
// Verify all images have alt attributes
const allHaveAlt = Array.from(logoImages).every((img) => {
const alt = img.getAttribute("alt");
return alt !== null && alt.trim().length > 0;
});
// Verify correct number of images based on mode
const expectedCount = shouldReduce ? 5 : 10;
const hasCorrectCount = logoImages.length === expectedCount;
return allHaveAlt && hasCorrectCount;
},
),
{ numRuns: 100 },
);
});
it("should not have empty or whitespace-only alt text", () => {
/**
* **Validates: Requirements 6.2**
*
* Property: For any company logo image, the alt text should not be empty
* or consist only of whitespace. This ensures meaningful accessibility
* information is always provided.
*/
fc.assert(
fc.property(
fc.boolean(), // Random reduced motion preference
(shouldReduce) => {
mockReducedMotion = shouldReduce;
const { container } = render(<CompanyLogos />);
const logoImages = container.querySelectorAll("img");
// Check that no image has empty or whitespace-only alt text
return Array.from(logoImages).every((img) => {
const alt = img.getAttribute("alt");
// Alt must exist and not be empty after trimming
if (!alt || alt.trim().length === 0) {
return false;
}
// Alt should not be just whitespace
return alt !== alt.trim() ? false : true;
});
},
),
{ numRuns: 100 },
);
});
it("should have alt text that matches the expected company names", () => {
/**
* **Validates: Requirements 6.2**
*
* Property: For any company logo image, the alt text should correspond to
* one of the expected company names (Apolo Energia, Julia Beauty Lashes,
* Innovo, TeraByteShop, Pichau). This ensures correct identification.
*/
fc.assert(
fc.property(fc.constant(null), () => {
mockReducedMotion = true; // Use static mode for clarity
const { container } = render(<CompanyLogos />);
const logoImages = container.querySelectorAll("img");
const altTexts = Array.from(logoImages).map(
(img) => img.getAttribute("alt") || "",
);
// Expected company names in alt text
const expectedCompanies = [
"Apolo Energia",
"Julia Beauty Lashes",
"Innovo",
"TeraByteShop",
"Pichau",
];
// Check that each alt text contains one of the expected company names
return altTexts.every((alt) =>
expectedCompanies.some((company) => alt.includes(company)),
);
}),
{ numRuns: 100 },
);
});
it("should maintain alt text when images fail to load", () => {
/**
* **Validates: Requirements 6.2**
*
* Property: For any company logo image that fails to load, the alt text
* should still be present in the DOM. This ensures accessibility is
* maintained even when images are unavailable.
*/
fc.assert(
fc.property(
fc.boolean(), // Random reduced motion preference
(shouldReduce) => {
mockReducedMotion = shouldReduce;
const { container } = render(<CompanyLogos />);
const logoImages = container.querySelectorAll("img");
// Verify all images have alt text before any error handling
const allHaveAltBeforeError = Array.from(logoImages).every((img) => {
const alt = img.getAttribute("alt");
return alt !== null && alt.trim().length > 0;
});
// The alt attribute should be present regardless of load state
return allHaveAltBeforeError;
},
),
{ numRuns: 100 },
);
});
});