import { readFileSync } from "node:fs"; import { join } from "node:path"; import portfolioData from "@/public/portfolio-data.json"; 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.metaTitle} ${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", ]); // nenhum case de cliente é um diagnóstico de sistema: o serviço fica só com a experiência na Apolo expect(getService("arquitetura-de-software").relatedCaseIds).toEqual([]); expect(getService("arquitetura-de-software").caseRelevance).toEqual({}); // o GCC Installer é um app para Windows (em Python): entra no serviço de desktop como projeto próprio expect(getService("tauri-rust").relatedCaseIds).toEqual(["gcc-installer"]); expect(getService("integracao-hardware-software-iot").relatedCaseIds).toEqual( ["twins-lab"], ); }); it("says what the Next.js service cases really prove: sites built by me, not a diagnosis", () => { const { caseRelevance } = getService("consultoria-nextjs"); for (const id of ["sev-modaintima", "lavacao-047"]) { expect(caseRelevance[id]).toMatch(/construí em Next\.js 16/); expect(caseRelevance[id]).not.toMatch(/diagn|conheço por dentro/i); } }); it("is honest that the desktop case is a Windows app made in Python, not in Tauri", () => { expect(getService("tauri-rust").caseRelevance["gcc-installer"]).toMatch( /Windows.*Python/, ); }); it("places the services in Joinville and SC instead of only 'em Garuva'", () => { expect(getService("criacao-de-sites").metaTitle).toBe( "Criação de sites em Joinville e SC", ); for (const service of servicePages) { expect(service.metaTitle).not.toMatch(/em Garuva/); expect(service.metaDescription).toMatch(/Garuva e Joinville \(SC\)/); } }); it("opens the case lists with what sells: the catalog, the order by WhatsApp and the panel", () => { const deliveries = caseStudies.find(({ id }) => id === "sev-modaintima")?.deliveries ?? []; const [catalog, whatsapp, panel] = deliveries; expect(catalog).toMatch(/^Catálogo por tamanho/); expect(whatsapp).toMatch(/^Pedido pelo WhatsApp/); expect(panel).toMatch(/^Painel para a loja cadastrar as peças/); // a vitrine com animações, que era a primeira, vai para o fim expect(deliveries[deliveries.length - 1]).toMatch( /^Uma vitrine com animações/, ); // a animação da Lavação também não abre a lista expect( caseStudies.find(({ id }) => id === "lavacao-047")?.deliveries[0], ).toMatch(/^Tabela de preços/); }); 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"].sort(), ); expect(getRelatedServices("gcc-installer").map(({ slug }) => slug)).toEqual([ "tauri-rust", ]); }); 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("only cites project ids that exist in public/portfolio-data.json", () => { const projectIds = new Set(portfolioData.projects.map(({ id }) => id)); const cited = [ ...caseStudies.map(({ id }) => id), ...servicePages.flatMap(({ relatedCaseIds }) => relatedCaseIds), ...servicePages.flatMap(({ caseRelevance }) => Object.keys(caseRelevance)), ]; expect(cited.length).toBeGreaterThan(0); expect(cited.filter((id) => !projectIds.has(id))).toEqual([]); }); it("writes one sentence on why each related case matters to its service", () => { for (const service of servicePages) { expect(Object.keys(service.caseRelevance).sort()).toEqual( [...service.relatedCaseIds].sort(), ); for (const sentence of Object.values(service.caseRelevance)) { expect(sentence.length).toBeGreaterThan(30); expect(sentence).toMatch(/[.]$/); } } }); it("cites the Apolo Energia experience, from 2022, only on the IoT and architecture services", () => { const apolo = portfolioData.experiences.find( ({ id }) => id === "apolo-energia", ); expect(apolo?.period).toMatch(/2022/); const withNote = servicePages .filter(({ experienceNote }) => experienceNote) .map(({ slug }) => slug); expect(withNote).toEqual([ "integracao-hardware-software-iot", "arquitetura-de-software", ]); for (const slug of withNote) { const note = getService(slug).experienceNote ?? ""; expect(note).toMatch(/Apolo Energia/); expect(note).toMatch(/desde 2022/i); expect(note).toMatch(/sistemas internos/i); expect(note).toMatch(/aplicativos/i); expect(note).toMatch(/APIs/); // sem link e sem detalhe de operação do cliente expect(note).not.toMatch(/https?:|www\.|\.com/i); } }); it("keeps the problem of every client case concrete, with no invented figure or quote", () => { for (const caseStudy of clientCases) { expect(caseStudy.problem.length).toBeGreaterThan(200); expect(caseStudy.problem).not.toMatch(/["“”]/); expect(caseStudy.problem).not.toMatch(/\d+\s?%|R\$\s?\d/); } expect( caseStudies.find(({ id }) => id === "sev-modaintima")?.problem, ).toMatch(/tamanho/); expect( caseStudies.find(({ id }) => id === "sev-modaintima")?.problem, ).toMatch(/WhatsApp/); }); it("leaves the optional facts of every case empty until there is real data", () => { for (const caseStudy of caseStudies) { expect(caseStudy.facts ?? []).toEqual([]); } }); it("writes project subtitles without technical jargon", () => { const jargon = /Next\.js|React|MongoDB|MinGW|\/GCC|tkinter|\bSSE\b|Three\.js|em código|r\/artificial/i; for (const caseStudy of caseStudies) { expect(caseStudy.subtitle).not.toMatch(jargon); } for (const project of portfolioData.projects) { expect(project.subtitle).not.toMatch(jargon); } expect( portfolioData.projects.find(({ id }) => id === "sc-plus")?.subtitle, ).toBe("Site institucional com áreas, projetos e contato"); }); 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:/); } });