Foco cai no destino ao chegar de outra página; âncoras ignoram ?tipo=; movimento reduzido salta em todos os cliques; cronômetro da janela ampliada; degradê no topo de Projetos e legendas do mergulho com intervalos testados; vitrine acima da barra de cookies e com 2 paradas por projeto; reel-list.css no global; 3D montado em tarefas curtas, limitado a ~30 qps e estático com WebGL por software; pôsteres da vitrine de 330 para 158 KB; SC Plus regravado; scripts sem funções acima de complexidade 10.
140 lines
4.9 KiB
TypeScript
140 lines
4.9 KiB
TypeScript
import { existsSync, statSync } from "node:fs";
|
|
import { join } from "node:path";
|
|
import {
|
|
chapterAnchors,
|
|
chapterFraction,
|
|
chapterIndexAt,
|
|
desktopFractionAt,
|
|
getOgImagePath,
|
|
getScrubSource,
|
|
getShowcaseAssets,
|
|
MAX_CHAPTERS,
|
|
mobileFractionAt,
|
|
PROJECT_VIDEO_IDS,
|
|
SHOWCASE,
|
|
} from "./project-showcase";
|
|
|
|
const MAX_BYTES = 3 * 1024 * 1024;
|
|
const publicFile = (path: string) => join(process.cwd(), "public", path);
|
|
/** Os projetos de cliente que a home entrega à vitrine. */
|
|
const HOME_IDS = ["sev-modaintima", "lavacao-047", "sc-plus", "as-servicos"];
|
|
|
|
describe("showcase length", () => {
|
|
it.each(SHOWCASE)("$id has at most three chapters", (config) => {
|
|
expect(MAX_CHAPTERS).toBe(3);
|
|
expect(config.chapters.length).toBeLessThanOrEqual(MAX_CHAPTERS);
|
|
expect(config.chapters.length).toBeGreaterThanOrEqual(1);
|
|
});
|
|
|
|
it("pins the four home projects for about three and a half screens at most", () => {
|
|
const total = SHOWCASE.filter((config) => HOME_IDS.includes(config.id))
|
|
.map((config) => config.weight)
|
|
.reduce((sum, weight) => sum + weight, 0);
|
|
expect(HOME_IDS.every((id) => SHOWCASE.some((c) => c.id === id))).toBe(
|
|
true,
|
|
);
|
|
expect(total).toBeLessThanOrEqual(3.5);
|
|
expect(total).toBeGreaterThan(2);
|
|
});
|
|
|
|
it("keeps the NutriScan configuration for /cases", () => {
|
|
const nutriscan = SHOWCASE.find((config) => config.id === "nutriscan");
|
|
expect(nutriscan).toBeDefined();
|
|
expect(PROJECT_VIDEO_IDS).toContain("nutriscan");
|
|
expect(getOgImagePath("nutriscan")).toBe("/videos/nutriscan/og.jpg");
|
|
});
|
|
});
|
|
|
|
describe("showcase chapters", () => {
|
|
it.each(SHOWCASE)("$id has ordered chapters inside both videos", (config) => {
|
|
const [first] = config.chapters;
|
|
expect(first.desktop).toBe(0);
|
|
expect(first.mobile).toBe(0);
|
|
config.chapters.forEach((chapter, index) => {
|
|
expect(chapter.label.trim()).not.toBe("");
|
|
expect(chapter.desktop).toBeLessThan(config.durations.desktop);
|
|
expect(chapter.mobile).toBeLessThan(config.durations.mobile);
|
|
if (index > 0) {
|
|
expect(chapter.desktop).toBeGreaterThan(
|
|
config.chapters[index - 1].desktop,
|
|
);
|
|
expect(chapter.mobile).toBeGreaterThan(
|
|
config.chapters[index - 1].mobile,
|
|
);
|
|
}
|
|
});
|
|
expect(config.weight).toBeGreaterThan(0);
|
|
});
|
|
|
|
it.each(SHOWCASE)("$id keeps the two timelines in sync", (config) => {
|
|
const anchors = chapterAnchors(config);
|
|
expect(anchors[0]).toEqual([0, 0]);
|
|
expect(anchors[anchors.length - 1]).toEqual([1, 1]);
|
|
for (const fraction of [0, 0.13, 0.5, 0.77, 1]) {
|
|
const mobile = mobileFractionAt(config, fraction);
|
|
expect(mobile).toBeGreaterThanOrEqual(0);
|
|
expect(mobile).toBeLessThanOrEqual(1);
|
|
expect(desktopFractionAt(config, mobile)).toBeCloseTo(fraction, 9);
|
|
}
|
|
config.chapters.forEach((_, index) => {
|
|
expect(
|
|
mobileFractionAt(config, chapterFraction(config, index, "desktop")),
|
|
).toBeCloseTo(chapterFraction(config, index, "mobile"), 9);
|
|
});
|
|
});
|
|
|
|
it("finds the chapter on screen", () => {
|
|
const lavacao = SHOWCASE.find((config) => config.id === "lavacao-047");
|
|
expect(lavacao).toBeDefined();
|
|
if (!lavacao) return;
|
|
expect(chapterIndexAt(lavacao, 0)).toBe(0);
|
|
expect(chapterIndexAt(lavacao, 3 / lavacao.durations.desktop)).toBe(0);
|
|
expect(chapterIndexAt(lavacao, 10 / lavacao.durations.desktop)).toBe(1);
|
|
expect(chapterIndexAt(lavacao, 25 / lavacao.durations.desktop)).toBe(2);
|
|
expect(chapterIndexAt(lavacao, 1)).toBe(lavacao.chapters.length - 1);
|
|
expect(chapterIndexAt(lavacao, 2)).toBe(lavacao.chapters.length - 1);
|
|
expect(chapterIndexAt(lavacao, -1)).toBe(0);
|
|
});
|
|
});
|
|
|
|
describe("showcase assets", () => {
|
|
it("picks the version for the device", () => {
|
|
expect(getShowcaseAssets("nutriscan", "desktop").poster).toBe(
|
|
"/videos/nutriscan/desktop.webp",
|
|
);
|
|
const mobile = getShowcaseAssets("nutriscan", "mobile");
|
|
expect(mobile.poster).toBe("/videos/nutriscan/mobile.webp");
|
|
expect(mobile.sources.map((source) => source.src)).toEqual([
|
|
"/videos/nutriscan/mobile.webm",
|
|
"/videos/nutriscan/mobile.mp4",
|
|
]);
|
|
});
|
|
|
|
it.each(PROJECT_VIDEO_IDS)(
|
|
"has every file on disk for %s, each video within 3 MB",
|
|
(id) => {
|
|
const files = [
|
|
...(["desktop", "mobile"] as const).flatMap((device) => {
|
|
const assets = getShowcaseAssets(id, device);
|
|
return [assets.poster, ...assets.sources.map((s) => s.src)];
|
|
}),
|
|
getScrubSource(id),
|
|
getOgImagePath(id),
|
|
];
|
|
for (const file of files) {
|
|
expect(existsSync(publicFile(file))).toBe(true);
|
|
expect(statSync(publicFile(file)).size).toBeLessThanOrEqual(MAX_BYTES);
|
|
}
|
|
},
|
|
);
|
|
|
|
it.each(PROJECT_VIDEO_IDS)(
|
|
"does not keep a scrub video for the phone frame of %s (it only shows the poster)",
|
|
(id) => {
|
|
expect(existsSync(publicFile(`/videos/${id}/scrub-mobile.mp4`))).toBe(
|
|
false,
|
|
);
|
|
},
|
|
);
|
|
});
|