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.
176 lines
6.0 KiB
TypeScript
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:/);
|
|
}
|
|
});
|