import { readFileSync } from "node:fs"; import { join } from "node:path"; import portfolioData from "@/public/portfolio-data.json"; import { buttonLabels, caseStudies, clientCases, clientCasesForListing, getRelatedCases, getRelatedServices, isClientCase, labCases, pageUpdatedAt, serviceOffers, 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, with the SEV only where it is the proof: the systems service", () => { expect(getService("criacao-de-sites").relatedCaseIds).toEqual([ "lavacao-047", "sc-plus", "as-servicos", ]); expect(getService("engenharia-de-software").relatedCaseIds).toEqual([ "sev-modaintima", "lavacao-047", ]); expect(getService("consultoria-nextjs").relatedCaseIds).toEqual([ "sc-plus", "lavacao-047", ]); expect( servicePages .filter(({ relatedCaseIds }) => relatedCaseIds.includes("sev-modaintima")) .map(({ slug }) => slug), ).toEqual(["engenharia-de-software"]); // 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"); expect(caseRelevance["lavacao-047"]).toMatch(/construí em Next\.js 16/); expect(caseRelevance["sc-plus"]).toMatch(/Next\.js 15/); for (const sentence of Object.values(caseRelevance)) { expect(sentence).not.toMatch(/diagn|conheço por dentro/i); } }); it("calls the desktop case a personal Windows app project, without claiming it is made in Tauri", () => { const sentence = getService("tauri-rust").caseRelevance["gcc-installer"]; expect(sentence).toMatch(/projeto próprio de aplicativo para Windows/); expect(sentence).not.toMatch(/Tauri|Python/); }); it("opens the two offer services with the name of the offer as the h1, keeping the search title", () => { expect(getService("criacao-de-sites").headline).toBe(serviceOffers[0].name); expect(getService("criacao-de-sites").headline).toBe( "Site que traz pedidos pelo WhatsApp", ); expect(getService("engenharia-de-software").headline).toBe( serviceOffers[1].name, ); expect(getService("engenharia-de-software").headline).toBe( "Sistema para tirar a operação da planilha", ); expect(getService("criacao-de-sites").metaTitle).toBe( "Criação de sites em Joinville e SC", ); expect(getService("engenharia-de-software").metaTitle).toBe( "Sistemas web e aplicativos sob medida", ); }); it("picks the cover by subject: sites -> Lavação 047, Next.js -> SC Plus, systems -> SEV on the phone, nothing for the rest", () => { expect(getService("criacao-de-sites").heroCase).toEqual({ id: "lavacao-047", device: "desktop", }); expect(getService("consultoria-nextjs").heroCase).toEqual({ id: "sc-plus", device: "desktop", }); expect(getService("engenharia-de-software").heroCase).toEqual({ id: "sev-modaintima", device: "mobile", }); for (const slug of [ "tauri-rust", "integracao-hardware-software-iot", "arquitetura-de-software", ]) { expect(getService(slug).heroCase).toBeUndefined(); } // a capa é sempre um dos cases relacionados, e nenhuma capa se repete entre os serviços const heroIds = servicePages.flatMap(({ heroCase }) => heroCase ? [heroCase.id] : [], ); expect(new Set(heroIds).size).toBe(heroIds.length); for (const { heroCase, relatedCaseIds } of servicePages) { if (heroCase) expect(relatedCaseIds).toContain(heroCase.id); } }); it("gives each service without a client cover a flow diagram instead, and never both", () => { expect(getService("tauri-rust").diagram).toBe("desktop"); expect(getService("integracao-hardware-software-iot").diagram).toBe("iot"); expect(getService("arquitetura-de-software").diagram).toBe("arquitetura"); for (const { heroCase, diagram } of servicePages) { expect(Boolean(heroCase) !== Boolean(diagram)).toBe(true); } }); it("lists the client cases for the mosaic and /cases with the SEV last, never first", () => { expect(clientCasesForListing.map(({ id }) => id)).toEqual([ "lavacao-047", "sc-plus", "as-servicos", "sev-modaintima", ]); expect(clientCasesForListing).toHaveLength(clientCases.length); }); it("keeps one list of button labels: 'Falar sobre meu projeto' for contact and 'Ver case' for a case", () => { expect(buttonLabels.contact).toBe("Falar sobre meu projeto"); expect(buttonLabels.case).toBe("Ver case"); expect(Object.values(buttonLabels).join("|")).not.toMatch( /Ver o case|Ler case|Conversar|oportunidade/, ); }); it("reduces 'direto' and 'diretamente' in the case and service text", () => { const text = JSON.stringify({ caseStudies, servicePages }); expect(text.match(/diret[oa]s?|diretamente/gi) ?? []).toEqual([]); }); it("explains the three offers of the home with the same names, 3 or 4 deliverables and a place to see them", () => { expect(serviceOffers.map(({ name }) => name)).toEqual([ "Site que traz pedidos pelo WhatsApp", "Sistema para tirar a operação da planilha", "Revisão de sistema que já existe, inclusive feito com IA", ]); expect(serviceOffers.map(({ href }) => href)).toEqual([ "/servicos/criacao-de-sites", "/servicos/engenharia-de-software", "/ia", ]); for (const offer of serviceOffers) { expect(offer.deliverables.length).toBeGreaterThanOrEqual(3); expect(offer.deliverables.length).toBeLessThanOrEqual(4); expect( offer.proof.caseIds.every((id) => caseStudies.some((c) => c.id === id)), ).toBe(true); // sem número, preço, prazo ou depoimento inventados const text = [ offer.name, offer.summary, ...offer.deliverables, offer.proof.note ?? "", ].join(" "); expect(text).not.toMatch(/R\$|\d+\s?%|\d+\s?dias|["“”]/); } expect(serviceOffers[0].proof.caseIds).toEqual([ "lavacao-047", "sev-modaintima", ]); expect(serviceOffers[1].proof.caseIds).toEqual(["sev-modaintima"]); expect(serviceOffers[1].proof.note).toMatch(/Apolo Energia/); expect(serviceOffers[1].proof.note).toMatch(/uso interno/); expect(serviceOffers[2].proof.caseIds).toEqual([]); }); it("writes 2 or 3 capabilities for every client case, without numbers, and none for the laboratory", () => { for (const caseStudy of clientCases) { expect(caseStudy.capabilities?.length).toBeGreaterThanOrEqual(2); expect(caseStudy.capabilities?.length).toBeLessThanOrEqual(3); expect(JSON.stringify(caseStudy.capabilities)).not.toMatch( /\d+\s?%|R\$|\d+\s?(clientes|pedidos|dias|vezes)/, ); } expect( caseStudies.find(({ id }) => id === "lavacao-047")?.capabilities?.[0], ).toBe( "O dono muda preços, textos, fotos e o mapa sem pedir a ninguém, e a mudança aparece no site sem nova publicação.", ); for (const caseStudy of labCases) { expect(caseStudy.capabilities).toBeUndefined(); } }); it("dates every service, case and fixed page with a plain calendar day for the sitemap", () => { const days = [ ...servicePages.map(({ updatedAt }) => updatedAt), ...caseStudies.map(({ updatedAt }) => updatedAt), ...Object.values(pageUpdatedAt), ]; for (const day of days) expect(day).toMatch(/^\d{4}-\d{2}-\d{2}$/); // a política de privacidade informa a própria vigência: a data do mapa é a dela expect(pageUpdatedAt.privacidade).toBe("2026-09-13"); }); 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)).toEqual([ "engenharia-de-software", ]); expect( getRelatedServices("lavacao-047") .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:/); } });