feat(servicos): ofertas no topo de /servicos, capa por assunto e diagramas
/servicos abre com as 3 ofertas e depois outros serviços; H1 de criação de sites e de engenharia com o nome da oferta; capa de cada serviço pelo assunto (SEV fora do próprio case cai de ~11 para 2 imagens); diagrama de fluxo em SVG nos serviços sem case; cases com o que o cliente passou a poder fazer e contato logo depois de Como ficou; FAQ num componente só; lastmod por rota; ProfessionalService com areaServed.
This commit is contained in:
1 parent
e63f3c2ebd
commit
fc44a69fb3
41 files changed
+2486
-550
No files matched your search
@@ -0,0 +1,70 @@
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import type { ServiceDiagramKind } from "@/lib/site-content";
|
||||
import { ServiceDiagram } from "./ServiceDiagram";
|
||||
|
||||
/** Esquema de fluxo do topo dos serviços sem case com captura: SVG em linha, acessível, sem dado real nem logo de cliente. */
|
||||
|
||||
const expected: Record<ServiceDiagramKind, string[]> = {
|
||||
iot: ["Dispositivo ou sensor", "Gateway", "API", "Painel"],
|
||||
arquitetura: ["Interface", "API", "Serviços", "Banco de dados"],
|
||||
desktop: ["Aplicativo no computador", "Arquivos", "Sistemas locais"],
|
||||
};
|
||||
|
||||
describe("ServiceDiagram", () => {
|
||||
for (const kind of Object.keys(expected) as ServiceDiagramKind[]) {
|
||||
it(`path: ${kind} -> an inline SVG image with the flow in its label and the steps in order`, () => {
|
||||
const { container } = render(<ServiceDiagram kind={kind} />);
|
||||
|
||||
const svg = screen.getByRole("img");
|
||||
expect(svg.tagName.toLowerCase()).toBe("svg");
|
||||
expect(svg).toHaveAttribute("aria-label");
|
||||
expect(svg.getAttribute("aria-label")?.length).toBeGreaterThan(60);
|
||||
const labels = Array.from(container.querySelectorAll("svg text")).map(
|
||||
(text) => text.textContent,
|
||||
);
|
||||
expect(labels).toEqual(expected[kind]);
|
||||
// o texto do desenho está no rótulo da imagem: quem lê a tela ouve o fluxo inteiro
|
||||
for (const label of expected[kind]) {
|
||||
expect(svg.getAttribute("aria-label")?.toLowerCase()).toContain(
|
||||
label.toLowerCase().replace("dispositivo ou sensor", "dispositivo"),
|
||||
);
|
||||
}
|
||||
expect(container.querySelectorAll("svg img, svg image")).toHaveLength(0);
|
||||
});
|
||||
}
|
||||
|
||||
it("path: IoT and architecture -> three arrows between four steps; desktop -> two arrows, both ways", () => {
|
||||
const iot = render(<ServiceDiagram kind="iot" />);
|
||||
expect(iot.container.querySelectorAll("svg line")).toHaveLength(3);
|
||||
// uma ponta por seta
|
||||
expect(iot.container.querySelectorAll("svg polygon")).toHaveLength(3);
|
||||
iot.unmount();
|
||||
|
||||
const arquitetura = render(<ServiceDiagram kind="arquitetura" />);
|
||||
expect(arquitetura.container.querySelectorAll("svg line")).toHaveLength(3);
|
||||
arquitetura.unmount();
|
||||
|
||||
const desktop = render(<ServiceDiagram kind="desktop" />);
|
||||
expect(desktop.container.querySelectorAll("svg line")).toHaveLength(2);
|
||||
// duas pontas por seta: a informação vai e volta
|
||||
expect(desktop.container.querySelectorAll("svg polygon")).toHaveLength(4);
|
||||
});
|
||||
|
||||
it("path: colors -> only site tokens (paper and white), never the lime that belongs to contact, never a client logo", () => {
|
||||
const { container } = render(<ServiceDiagram kind="iot" />);
|
||||
|
||||
const html = container.innerHTML;
|
||||
expect(html).toMatch(/var\(--paper\)/);
|
||||
expect(html).toMatch(/var\(--white\)/);
|
||||
expect(html).not.toMatch(/--slash|#ccff00|ccff00/i);
|
||||
expect(html).not.toMatch(/<image|<img|href=/i);
|
||||
});
|
||||
|
||||
it("path: caption -> says it is an illustration, with no client data", () => {
|
||||
render(<ServiceDiagram kind="arquitetura" />);
|
||||
|
||||
expect(
|
||||
screen.getByText("Esquema ilustrativo, sem dados de clientes."),
|
||||
).toBeVisible();
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user