feat(cases): mídia no topo, títulos de busca curtos e CreativeWork
Case com mídia ao lado do título e play acessível; problema de cada cliente reescrito com base nos dados; serviços relacionados em 2 cards; FAQ antes do próximo passo e bloco 'Onde já apliquei isso'; Apolo citada em IoT e arquitetura. metaTitle até 43 caracteres e descrições até 155 com teste; JSON-LD CreativeWork; imagens de compartilhamento abaixo de 100 KB; 404 e robots corrigidos; server-only nos módulos com node:.
This commit is contained in:
1 parent
c9a0b80656
commit
e80f73d075
52 files changed
+2593
-439
No files matched your search
@@ -1,5 +1,6 @@
|
||||
import { readFileSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import portfolioData from "@/public/portfolio-data.json";
|
||||
import {
|
||||
caseStudies,
|
||||
clientCases,
|
||||
@@ -77,7 +78,7 @@ it("leaves the commercial offer of every service empty for the owner to fill in"
|
||||
|
||||
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(
|
||||
expect(`${service.metaTitle} ${service.metaDescription}`).toMatch(
|
||||
/Garuva|Joinville|Santa Catarina/,
|
||||
);
|
||||
expect(service.metaDescription).toMatch(/remoto|Brasil/);
|
||||
@@ -148,6 +149,89 @@ it("resolves every declared related case ID", () => {
|
||||
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,
|
||||
|
||||
Reference in new issue
Block a user