Cases da SEV Moda Íntima e da Lavação 047; case no formato cliente, problema, entrega, mídia e contato, com detalhes técnicos recolhidos; sem GitHub, ressalvas e fontes de commit. /cases separa clientes e Laboratório. Serviços mostram o case de cliente certo e o bloco de investimento e prazo só aparece preenchido. Metadata social própria por página, not-found com noindex, sitemap com /ia.
147 lines
5.2 KiB
TypeScript
147 lines
5.2 KiB
TypeScript
import { getFeaturedProjects, portfolio } from "./portfolio-data";
|
|
import type { ProjectHealthSnapshot } from "./project-health";
|
|
|
|
describe("getFeaturedProjects", () => {
|
|
it("keeps every featured project and classifies demo availability", () => {
|
|
const snapshot: ProjectHealthSnapshot = {
|
|
checkedAt: "2026-07-10T12:00:00.000Z",
|
|
projects: {
|
|
"sev-modaintima": {
|
|
status: 200,
|
|
contentType: "text/html; charset=utf-8",
|
|
checkedAt: "2026-07-10T12:00:00.000Z",
|
|
},
|
|
},
|
|
};
|
|
|
|
const result = getFeaturedProjects(snapshot);
|
|
|
|
expect(result.map(({ id }) => id)).toEqual(
|
|
portfolio.projects
|
|
.filter(({ featured }) => featured !== false)
|
|
.map(({ id }) => id),
|
|
);
|
|
expect(
|
|
result.find(({ id }) => id === "sev-modaintima")?.demoAvailability,
|
|
).toBe("available");
|
|
expect(
|
|
result.find(({ id }) => id === "sev-modaintima")?.demoCheckedAt,
|
|
).toBe("2026-07-10T12:00:00.000Z");
|
|
expect(result.find(({ id }) => id === "twins-lab")?.demoAvailability).toBe(
|
|
"none",
|
|
);
|
|
expect(
|
|
result.find(({ id }) => id === "twins-lab")?.demoCheckedAt,
|
|
).toBeNull();
|
|
});
|
|
|
|
it("excludes an isolated featured false fixture and restores the portfolio", () => {
|
|
const originalLength = portfolio.projects.length;
|
|
const hiddenProject = {
|
|
...portfolio.projects[0],
|
|
id: "hidden-test-fixture",
|
|
featured: false,
|
|
};
|
|
portfolio.projects.push(hiddenProject);
|
|
|
|
try {
|
|
expect(
|
|
getFeaturedProjects(null).some(
|
|
({ id }) => id === "hidden-test-fixture",
|
|
),
|
|
).toBe(false);
|
|
} finally {
|
|
portfolio.projects.length = originalLength;
|
|
}
|
|
|
|
expect(portfolio.projects).toHaveLength(originalLength);
|
|
});
|
|
});
|
|
|
|
describe("factual portfolio records", () => {
|
|
function serializeProject(id: string) {
|
|
const project = portfolio.projects.find((item) => item.id === id);
|
|
expect(project).toBeDefined();
|
|
if (!project) throw new Error(`Missing project ${id}`);
|
|
// "kind" (cliente ou laboratório) é classificação do portfólio, não afirmação sobre o projeto
|
|
const { kind: _kind, ...claims } = project;
|
|
return JSON.stringify(claims);
|
|
}
|
|
|
|
it("keeps every public field of the three cases within verified claims", () => {
|
|
const nutriscan = serializeProject("nutriscan");
|
|
const twins = serializeProject("twins-lab");
|
|
const asServicos = serializeProject("as-servicos");
|
|
|
|
expect(nutriscan).toContain("Open Food Facts");
|
|
expect(nutriscan).not.toMatch(
|
|
/Next\.js\s*\d|PWA|Service Worker|MongoDB|históric|scanner|offline|NutriScore|24\/7|2M\+|disponibilidade|volume de produtos|resultado de saúde/i,
|
|
);
|
|
|
|
expect(twins).toContain("SSE");
|
|
expect(twins).not.toMatch(
|
|
/WebSockets?|OpenRouter|sub[- ]segundo|produç|preditiv|ROI|precisão|equipamento real/i,
|
|
);
|
|
|
|
expect(asServicos).toContain("Three.js");
|
|
expect(asServicos).not.toMatch(
|
|
/produç|clientes?|leads?|ranking|performance|resultado comercial|anime\.js|shadcn/i,
|
|
);
|
|
});
|
|
it("keeps Reddit Posts Explorer and MinGW GCC Installer within what their public repositories support", () => {
|
|
const reddit = serializeProject("reddit-explorer");
|
|
const gcc = serializeProject("gcc-installer");
|
|
|
|
expect(reddit).toContain("MongoDB");
|
|
expect(reddit).not.toMatch(
|
|
/SSG|rate limit|cache|tempo real|real-time|busca em|otimiza|responsiv/i,
|
|
);
|
|
expect(gcc).toContain("mingw64");
|
|
expect(gcc).not.toMatch(
|
|
/assíncron|monitoramento|drastic|Watchdog|dependências|intuitiv/i,
|
|
);
|
|
});
|
|
|
|
it("does not claim measured performance for SC Plus without a stored measurement", () => {
|
|
// Lighthouse mobile local: LCP 3,7 s em 2026-10-05; a alegação anterior (LCP < 2,5 s) não se sustentava
|
|
expect(serializeProject("sc-plus")).not.toMatch(
|
|
/LCP|Core Web Vitals|otimizad|velocidade|rápid|Performance/i,
|
|
);
|
|
});
|
|
|
|
it("does not publish the unavailable Twins Lab demo", () => {
|
|
expect(
|
|
portfolio.projects.find((item) => item.id === "twins-lab")?.demoUrl,
|
|
).toBeUndefined();
|
|
});
|
|
|
|
it("leaves no GitHub link in the portfolio data", () => {
|
|
expect(JSON.stringify(portfolio)).not.toMatch(/github\.com/i);
|
|
for (const project of portfolio.projects) {
|
|
expect(project.githubUrl).toBe("");
|
|
}
|
|
});
|
|
|
|
it("does not call a project public code while no repository link is published", () => {
|
|
expect(JSON.stringify(portfolio)).not.toMatch(/código público/i);
|
|
const status = (id: string) =>
|
|
portfolio.projects.find((item) => item.id === id)?.status;
|
|
// o NutriScan tem demonstração no ar; o site da AS Serviços está concluído, mas não tem endereço público
|
|
expect(status("nutriscan")).toBe("Em Produção");
|
|
expect(status("as-servicos")).toBe("Concluído");
|
|
});
|
|
|
|
it("keeps a single LinkedIn profile", () => {
|
|
const linkedinUrls = JSON.stringify(portfolio).match(
|
|
/https:\/\/www\.linkedin\.com\/in\/[^"]+/g,
|
|
);
|
|
expect(new Set(linkedinUrls)).toEqual(
|
|
new Set(["https://www.linkedin.com/in/evandrodevbr/"]),
|
|
);
|
|
});
|
|
|
|
it("states no limitation about the Reddit Posts Explorer in its public text", () => {
|
|
expect(serializeProject("reddit-explorer")).not.toMatch(/nem CI|não tem/i);
|
|
});
|
|
});
|