Files
portifolio-evandrodevbr/lib/site-content.test.ts
T
Evandro Fonseca Junior 6e1022b58c feat(cases): cases de clientes, serviços ligados à prova e SEO por página
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.
2026-10-14 03:31:57 -03:00

176 lines
6.0 KiB
TypeScript

import { readFileSync } from "node:fs";
import { join } from "node:path";
import {
caseStudies,
clientCases,
getRelatedCases,
getRelatedServices,
isClientCase,
labCases,
servicePages,
} from "./site-content";
const clientIds = ["sev-modaintima", "lavacao-047", "sc-plus", "as-servicos"];
const labIds = ["nutriscan", "twins-lab", "reddit-explorer", "gcc-installer"];
function getService(slug: string) {
const service = servicePages.find((item) => item.slug === slug);
if (!service) throw new Error(`Missing service ${slug}`);
return service;
}
it("separates client work from the laboratory using the kind in the portfolio data", () => {
expect(clientCases.map(({ id }) => id)).toEqual(clientIds);
expect(labCases.map(({ id }) => id)).toEqual(labIds);
expect(caseStudies).toHaveLength(8);
expect(
caseStudies.every(
(item) => isClientCase(item) === clientIds.includes(item.id),
),
).toBe(true);
expect(isClientCase({ id: "projeto-que-nao-existe" })).toBe(false);
});
it("gives every case the sections of the business structure", () => {
for (const caseStudy of caseStudies) {
expect(caseStudy.headline.length).toBeGreaterThan(10);
expect(caseStudy.context.length).toBeGreaterThan(20);
expect(caseStudy.problem.length).toBeGreaterThan(20);
expect(caseStudy.deliveries.length).toBeGreaterThanOrEqual(3);
expect(caseStudy.stack.length).toBeGreaterThan(0);
expect(caseStudy.decisions.length).toBeGreaterThan(0);
// sem dado real e autorizado, não há seção de resultado
expect(caseStudy.result).toBeUndefined();
}
});
it("writes the NutriScan case in business language", () => {
expect(caseStudies.find(({ id }) => id === "nutriscan")?.headline).toBe(
"Consulta de alimentos por nome ou código de barras",
);
});
it("has no GitHub link, source list or disqualifying caveat in the public text", () => {
const text = JSON.stringify({ caseStudies, servicePages });
expect(text).not.toMatch(/github\.com/i);
expect(text).not.toMatch(/\/#contact/);
expect(text).not.toMatch(
/Limites desta evidência|Desafio verificável|Exemplos ilustrativos|não projetos já entregues|Sem promessas|nem CI|não há CI|escopo definido|não comprova|não representa/i,
);
for (const caseStudy of caseStudies) {
expect(caseStudy).not.toHaveProperty("githubUrl");
expect(caseStudy).not.toHaveProperty("detail");
}
});
it("never mentions an unavailable demo in the case text", () => {
expect(JSON.stringify(caseStudies)).not.toMatch(/indispon/i);
});
it("leaves the commercial offer of every service empty for the owner to fill in", () => {
for (const service of servicePages) {
expect(service.startingPrice).toBeUndefined();
expect(service.typicalTimeline).toBeUndefined();
expect(service.guarantee).toBeUndefined();
}
});
it("mentions the region and a benefit in the title and description of every service", () => {
for (const service of servicePages) {
expect(`${service.seoTitle} ${service.metaDescription}`).toMatch(
/Garuva|Joinville|Santa Catarina/,
);
expect(service.metaDescription).toMatch(/remoto|Brasil/);
}
const descriptions = servicePages.map(
({ metaDescription }) => metaDescription,
);
expect(new Set(descriptions).size).toBe(descriptions.length);
});
it("maps each service to its client cases", () => {
expect(getService("criacao-de-sites").relatedCaseIds).toEqual(clientIds);
expect(getService("engenharia-de-software").relatedCaseIds).toEqual([
"sev-modaintima",
"lavacao-047",
]);
expect(getService("consultoria-nextjs").relatedCaseIds).toEqual([
"sev-modaintima",
"lavacao-047",
"sc-plus",
]);
expect(getService("arquitetura-de-software").relatedCaseIds).toEqual([
"sev-modaintima",
]);
// GCC Installer é Python/Tkinter: não combina com um serviço de Tauri e Rust
expect(getService("tauri-rust").relatedCaseIds).toEqual([]);
expect(getService("integracao-hardware-software-iot").relatedCaseIds).toEqual(
["twins-lab"],
);
});
it("puts client cases before laboratory cases when listing related work", () => {
expect(
getRelatedCases(["twins-lab", "lavacao-047", "sev-modaintima"]).map(
({ id }) => id,
),
).toEqual(["lavacao-047", "sev-modaintima", "twins-lab"]);
expect(getRelatedCases(["nao-existe"])).toEqual([]);
});
it("derives the exact related service set from the existing relationship", () => {
expect(getRelatedServices("twins-lab").map(({ slug }) => slug)).toEqual([
"integracao-hardware-software-iot",
]);
expect(
getRelatedServices("sev-modaintima")
.map(({ slug }) => slug)
.sort(),
).toEqual(
[
"criacao-de-sites",
"engenharia-de-software",
"consultoria-nextjs",
"arquitetura-de-software",
].sort(),
);
expect(getRelatedServices("gcc-installer")).toEqual([]);
});
it("resolves every declared related case ID", () => {
const caseIds = new Set(caseStudies.map(({ id }) => id));
const unresolved = servicePages.flatMap(({ slug, relatedCaseIds }) =>
relatedCaseIds
.filter((caseId) => !caseIds.has(caseId))
.map((caseId) => ({ slug, caseId })),
);
expect(unresolved).toEqual([]);
});
it("does not publish the unavailable Twins Lab demo", () => {
expect(
caseStudies.find((item) => item.id === "twins-lab")?.demoUrl,
).toBeUndefined();
});
it("keeps AS Serviços without a public site and states the author's participation", () => {
const asServicos = caseStudies.find(({ id }) => id === "as-servicos");
expect(asServicos?.demoUrl).toBeNull();
expect(asServicos?.role).toMatch(/primeira landing page/i);
expect(asServicos?.role).toMatch(/animações 3D/i);
expect(asServicos?.role).toMatch(/painel para controle de projetos/i);
});
it("keeps site-content and the modules it imports free of Node built-ins, so client components can import it", () => {
for (const file of [
"site-content",
"portfolio-data",
"proof",
"project-health",
]) {
const source = readFileSync(join(__dirname, `${file}.ts`), "utf8");
expect(source).not.toMatch(/from "node:/);
}
});