Files
portifolio-evandrodevbr/lib/project-showcase.paths.test.ts
Evandro Fonseca Junior 204ebd5fa5 fix(navegacao): foco em âncora de outra página, vitrine acessível e 3D mais leve
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.
2026-10-14 19:14:48 -03:00

201 lines
5.6 KiB
TypeScript

import {
chapterAnchors,
chapterFraction,
chapterIndexAt,
desktopFractionAt,
getShowcaseAssets,
getShowcaseConfig,
mobileFractionAt,
SHOWCASE,
type ShowcaseConfig,
} from "./project-showcase";
/**
* Testes de caminhos-base de lib/project-showcase.ts (complemento de project-showcase.test.ts):
* getShowcaseConfig/getShowcaseAssets (id da vitrine x id fora dela x chaves herdadas de Object, nos dois aparelhos; o
* antigo getProjectVideo saiu do módulo por não ter mais uso) e os trechos
* de chapterAnchors/interpolate/chapterFraction que o arquivo principal não exercita.
*/
function config(
chapters: ShowcaseConfig["chapters"],
durations = { desktop: 10, mobile: 20 },
): ShowcaseConfig {
return { id: "fixture", accent: "#fff", durations, weight: 1, chapters };
}
describe("getShowcaseConfig e getShowcaseAssets", () => {
it.each(SHOWCASE.map(({ id }) => id))(
"path: showcase project %s -> its config and the assets of each device",
(id) => {
expect(getShowcaseConfig(id)?.id).toBe(id);
expect(getShowcaseAssets(id, "desktop").poster).toBe(
`/videos/${id}/desktop.webp`,
);
expect(getShowcaseAssets(id, "mobile").poster).toBe(
`/videos/${id}/mobile.webp`,
);
},
);
it.each([
"",
"nao-existe",
"constructor",
"__proto__",
"toString",
"SEV-MODAINTIMA",
])("path: id %j is not a showcase project -> no config", (id) => {
expect(getShowcaseConfig(id)).toBeUndefined();
});
it("path: the two devices differ in size and in poster, not in the project folder", () => {
const desktop = getShowcaseAssets("nutriscan", "desktop");
const mobile = getShowcaseAssets("nutriscan", "mobile");
expect(desktop).toMatchObject({ width: 1440, height: 900 });
expect(mobile).toMatchObject({ width: 390, height: 844 });
expect(desktop.poster).toBe("/videos/nutriscan/desktop.webp");
expect(mobile.poster).toBe("/videos/nutriscan/mobile.webp");
expect(desktop.sources.map(({ src }) => src)).toEqual([
"/videos/nutriscan/desktop.webm",
"/videos/nutriscan/desktop.mp4",
]);
});
});
describe("chapterAnchors", () => {
it("path: chapters that advance on both videos -> one anchor each between [0,0] and [1,1]", () => {
const anchors = chapterAnchors(
config([
{ label: "A", desktop: 2, mobile: 4 },
{ label: "B", desktop: 5, mobile: 10 },
]),
);
expect(anchors).toEqual([
[0, 0],
[0.2, 0.2],
[0.5, 0.5],
[1, 1],
]);
});
it("path: a chapter at second 0 on both videos -> no duplicate of the origin", () => {
const anchors = chapterAnchors(
config([
{ label: "Abertura", desktop: 0, mobile: 0 },
{ label: "B", desktop: 5, mobile: 10 },
]),
);
expect(anchors).toEqual([
[0, 0],
[0.5, 0.5],
[1, 1],
]);
});
it("path: a chapter that does not advance on the desktop video -> skipped", () => {
const anchors = chapterAnchors(
config([
{ label: "A", desktop: 4, mobile: 4 },
{ label: "B", desktop: 4, mobile: 12 },
]),
);
expect(anchors).toEqual([
[0, 0],
[0.4, 0.2],
[1, 1],
]);
});
it("path: a chapter that does not advance on the mobile video -> skipped", () => {
const anchors = chapterAnchors(
config([
{ label: "A", desktop: 4, mobile: 10 },
{ label: "B", desktop: 8, mobile: 10 },
]),
);
expect(anchors).toEqual([
[0, 0],
[0.4, 0.5],
[1, 1],
]);
});
});
describe("mobileFractionAt / desktopFractionAt (interpolation)", () => {
const sample = config([
{ label: "A", desktop: 2, mobile: 10 },
{ label: "B", desktop: 8, mobile: 15 },
]);
it.each([
[-1, 0],
[0, 0],
[0.2, 0.5],
[0.5, 0.5 + (0.3 / 0.6) * 0.25],
[0.8, 0.75],
[1, 1],
[2, 1],
])(
"path: desktop fraction %f -> mobile fraction %f",
(fraction, expected) => {
expect(mobileFractionAt(sample, fraction)).toBeCloseTo(expected, 9);
},
);
it("path: desktopFractionAt is the inverse on the anchors", () => {
expect(desktopFractionAt(sample, 0.5)).toBeCloseTo(0.2, 9);
expect(desktopFractionAt(sample, 0.75)).toBeCloseTo(0.8, 9);
expect(desktopFractionAt(sample, 0)).toBe(0);
expect(desktopFractionAt(sample, 1)).toBe(1);
});
it("path: a chapter right at the end makes a zero-length span -> no division by zero", () => {
const flat = config([{ label: "Fim", desktop: 10, mobile: 20 }]);
expect(mobileFractionAt(flat, 1)).toBe(1);
expect(Number.isFinite(mobileFractionAt(flat, 0.999))).toBe(true);
expect(Number.isFinite(desktopFractionAt(flat, 1))).toBe(true);
});
});
describe("chapterIndexAt / chapterFraction", () => {
const sample = config([
{ label: "A", desktop: 0, mobile: 0 },
{ label: "B", desktop: 4, mobile: 8 },
{ label: "C", desktop: 8, mobile: 16 },
]);
it.each([
[-5, 0],
[0, 0],
[0.399, 0],
[0.4, 1],
[0.7999, 1],
[0.8, 2],
[1, 2],
[9, 2],
])("path: fraction %f -> chapter %i", (fraction, expected) => {
expect(chapterIndexAt(sample, fraction)).toBe(expected);
});
it("path: chapter index in range -> start fraction on each video", () => {
expect(chapterFraction(sample, 1, "desktop")).toBeCloseTo(0.4, 9);
expect(chapterFraction(sample, 1, "mobile")).toBeCloseTo(0.4, 9);
expect(chapterFraction(sample, 2, "desktop")).toBeCloseTo(0.8, 9);
});
it.each([-1, 3, 99])(
"path: chapter index %i does not exist -> 0",
(index) => {
expect(chapterFraction(sample, index, "desktop")).toBe(0);
expect(chapterFraction(sample, index, "mobile")).toBe(0);
},
);
});