: un pseudo-élément ne se déclare
// que par une règle.
function regles(texte) {
feuille = document.createElement('style');
feuille.textContent = texte;
document.head.append(feuille);
}
const $ = (selecteur) => scene.querySelector(selecteur);
describe('composantes', () => {
test('lit rgb(…) et rgba(…)', () => {
expect(composantes('rgb(12, 34, 56)')).toEqual([12, 34, 56, 1]);
expect(composantes('rgba(12, 34, 56, 0.5)')).toEqual([12, 34, 56, 0.5]);
expect(composantes('rgba(0, 0, 0, 0)')).toEqual([0, 0, 0, 0]);
});
test('lève sur une forme inconnue', () => {
for (const forme of ['hsl(0 0% 0%)', 'none', 'url("#motif")', 'rouge', '']) {
expect(() => composantes(forme), forme).toThrow(/couleur/);
}
});
test('contraste est celui de palette.js', () => {
expect(contraste).toBe(contrastePalette);
});
});
describe('ascendance et designation', () => {
test("l'ascendance d'un élément SVG remonte jusqu'à ", () => {
monter(``);
const chaine = ascendance($('text'));
expect(chaine.map((n) => n.localName).slice(0, 4)).toEqual(['text', 'g', 'svg', 'div']);
expect(chaine.at(-1)).toBe(document.documentElement);
});
test('designation nomme la balise, les attributs data-… et les classes', () => {
monter('Théox');
expect(designation($('span'))).toBe('span[data-participant="7"][data-actif].nom.gras');
expect(designation($('b'))).toBe('b');
expect(designation({ element: $('span'), pseudo: '::before' })).toBe(
'span[data-participant="7"][data-actif].nom.gras::before',
);
});
});
describe('porteursDeTexte', () => {
test('rend les éléments à texte non vide, HTML et SVG, dans leur ordre', () => {
monter(
`
Ondine Lacasse
` +
``,
);
const porteurs = porteursDeTexte(scene);
expect(porteurs.length).toBe(3);
expect(porteurs.map((p) => p.element.localName)).toEqual(['p', 'em', 'text']);
expect(porteurs.every((p) => p.pseudo === null)).toBe(true);
});
test('rend un ::before à content comme porteur, pas un ::after à content vide', () => {
regles(
'[data-scene] .epreuve-marque::before { content: "→ "; }' +
'[data-scene] .epreuve-marque::after { content: ""; }',
);
monter('');
expect(porteursDeTexte(scene)).toEqual([{ element: $('span'), pseudo: '::before' }]);
});
test("un pseudo-élément dont content n'est qu'un blanc n'est pas un porteur", () => {
regles(
'[data-scene] .epreuve-blanc::before { content: " "; }' +
'[data-scene] .epreuve-blanc::after { content: "\\a0"; }',
);
monter('a');
expect(porteursDeTexte(scene)).toEqual([{ element: $('span'), pseudo: null }]);
});
});
describe('couleurTexte', () => {
test("lit color d'un élément HTML et fill d'un SVG", () => {
monter(
`Iris` +
``,
);
expect(couleurTexte({ element: $('span'), pseudo: null })).toEqual([40, 50, 60, 1]);
expect(couleurTexte({ element: $('text'), pseudo: null })).toEqual([70, 80, 90, 1]);
expect(couleurTexte($('span'))).toEqual([40, 50, 60, 1]);
});
});
describe('couleursPeintes', () => {
test('un motif de hachure rend les deux couleurs que peignent ses enfants', () => {
monter(
``,
);
expect(couleursPeintes($('[data-forme]'))).toEqual({
fill: [
[250, 240, 200, 1],
[20, 30, 90, 1],
],
stroke: [[1, 2, 3, 1]],
});
});
test('un remplissage plein rend sa couleur, none et transparent ne rendent rien', () => {
monter(
``,
);
expect(couleursPeintes($('[data-a]'))).toEqual({ fill: [[9, 8, 7, 1]], stroke: [] });
expect(couleursPeintes($('[data-b]'))).toEqual({ fill: [], stroke: [] });
});
test('une hachure groupée dans un : les couleurs de ses formes, pas le noir du groupe', () => {
monter(
``,
);
expect(couleursPeintes($('[data-forme]')).fill).toEqual([
[250, 240, 200, 1],
[20, 30, 90, 1],
]);
});
});
describe('fondPeintSous', () => {
test("un cercle posé sur un rectangle rempli : le remplissage du rectangle", () => {
monter(
``,
);
expect(fondPeintSous($('[data-pastille]'))).toEqual([[200, 220, 240, 1]]);
});
test('un texte SVG sur un rectangle hachuré : chaque couleur du motif', () => {
monter(
``,
);
expect(fondPeintSous({ element: $('text'), pseudo: null })).toEqual([
[250, 240, 200, 1],
[20, 30, 90, 1],
]);
});
test('seules comptent les formes qui précèdent la cible : un rectangle peint après elle ne la porte pas', () => {
monter(
``,
);
expect(fondPeintSous($('[data-pastille]'))).toEqual([[200, 220, 240, 1]]);
});
// Chaque cas rend translucide la forme posée sur le rectangle noir ; la
// couleur peinte n'est plus la couleur calculée, et l'aide lève en nommant
// la forme plutôt que de rendre un blanc que l'écran ne montre pas.
const TRANSLUCIDES = [
['fill-opacity', 'fill="rgb(255, 255, 255)" fill-opacity="0.2"', '', /fill-opacity/],
['opacity', 'fill="rgb(255, 255, 255)" opacity="0.2"', '', /opacity/],
['un alpha de couleur', 'fill="rgba(255, 255, 255, 0.2)"', '', /fill/],
["l'opacity d'un groupe", 'fill="rgb(255, 255, 255)"', 'opacity="0.2"', /opacity/],
];
for (const [nom, attributs, groupe, attendu] of TRANSLUCIDES) {
test(`une forme translucide (${nom}) sur une forme opaque : lève en nommant la forme`, () => {
monter(
``,
);
expect(() => fondPeintSous($('text'))).toThrow(attendu);
expect(() => fondPeintSous($('text'))).toThrow(/rect\[data-voile\]/);
});
}
test("un motif dont une forme est translucide, un dégradé à arrêt translucide : lève en les nommant", () => {
monter(
``,
);
expect(() => fondPeintSous($('[data-haut]'))).toThrow(/rect\[data-maille\] fill-opacity: 0\.5/);
expect(() => fondPeintSous($('[data-bas]'))).toThrow(/stop stop-opacity: 0\.5/);
});
test('un élément SVG sans forme dessous : le premier fond HTML opaque', () => {
monter(
`
`,
);
expect(fondPeintSous($('text'))).toEqual([[230, 240, 250, 1]]);
});
test("un texte HTML sans fond : le premier fond opaque de son ascendance", () => {
monter(
'
' +
'Club des Merles
',
);
expect(fondPeintSous($('span'))).toEqual([[250, 250, 240, 1]]);
});
test('un texte HTML posé sur un rect SVG frère : le remplissage du rect, pas le fond de la surface', () => {
monter(
'
' +
`` +
'Iris
',
);
expect(fondPeintSous($('span'))).toEqual([[0, 0, 0, 1]]);
});
test('un texte HTML sur un div frère absolu : le fond du frère, même en pointer-events: none', () => {
monter(
'
' +
'' +
'Iris
',
);
expect(fondPeintSous($('span'))).toEqual([[0, 0, 0, 1]]);
});
test('un texte HTML sur un frère opaque mais à opacity 0,2 : lève en nommant le frère', () => {
monter(
'
' +
'' +
'Iris
',
);
expect(() => fondPeintSous($('span'))).toThrow(/div\[data-plateau\].*opacity/);
});
test('un texte HTML hors de la fenêtre : lève, rien ne se lit sous un point hors champ', () => {
monter('Iris');
expect(() => fondPeintSous($('span'))).toThrow(/fenêtre/);
});
test("le fond CSS du