test(js): add node:test unit tests for countdown and pleroma adapter
This commit is contained in:
@@ -0,0 +1,190 @@
|
||||
/**
|
||||
* Tests unitaires pour js/countdown.js (classe CountdownTimer).
|
||||
*
|
||||
* Le script est chargé dans un contexte vm avec un DOM simulé :
|
||||
* la date courante est figée (FakeDate) et setInterval est factice,
|
||||
* ce qui rend les calculs de temps restant déterministes.
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
const { describe, test } = require('node:test');
|
||||
const assert = require('node:assert');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const vm = require('node:vm');
|
||||
|
||||
const COUNTDOWN_PATH = path.join(__dirname, '..', '..', 'js', 'countdown.js');
|
||||
const COUNTDOWN_SOURCE = fs.readFileSync(COUNTDOWN_PATH, 'utf8');
|
||||
|
||||
// Date courante figée pour tous les tests
|
||||
const FIXED_NOW = new Date('2030-01-01T00:00:00Z').getTime();
|
||||
|
||||
function makeElements() {
|
||||
return {
|
||||
'countdown-days': { textContent: '' },
|
||||
'countdown-hours': { textContent: '' },
|
||||
'countdown-minutes': { textContent: '' },
|
||||
'countdown-seconds': { textContent: '' }
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Charge countdown.js dans un contexte vm isolé et retourne la classe.
|
||||
*
|
||||
* @param {object|null} options.elements Éléments DOM simulés (null = absents)
|
||||
* @param {number} options.now Timestamp courant simulé
|
||||
*/
|
||||
function loadCountdown({ elements = null, now = FIXED_NOW } = {}) {
|
||||
class FakeDate extends Date {
|
||||
constructor(...args) {
|
||||
if (args.length === 0) {
|
||||
super(now);
|
||||
} else {
|
||||
super(...args);
|
||||
}
|
||||
}
|
||||
|
||||
static now() {
|
||||
return now;
|
||||
}
|
||||
}
|
||||
|
||||
const windowMock = {
|
||||
addEventListener() {},
|
||||
location: { href: '' }
|
||||
};
|
||||
|
||||
// Timers factices : le timer ne tourne jamais tout seul,
|
||||
// on enregistre simplement les appels pour vérification.
|
||||
const timers = { intervals: [], cleared: [] };
|
||||
let nextIntervalId = 1;
|
||||
|
||||
const sandbox = {
|
||||
document: {
|
||||
getElementById: (id) => (elements ? elements[id] || null : null),
|
||||
querySelectorAll: () => [],
|
||||
addEventListener() {}
|
||||
},
|
||||
window: windowMock,
|
||||
Date: FakeDate,
|
||||
setInterval: (fn, delay) => {
|
||||
const id = nextIntervalId++;
|
||||
timers.intervals.push({ id, fn, delay });
|
||||
return id;
|
||||
},
|
||||
clearInterval: (id) => {
|
||||
timers.cleared.push(id);
|
||||
},
|
||||
setTimeout: () => 0,
|
||||
console
|
||||
};
|
||||
|
||||
vm.createContext(sandbox);
|
||||
const CountdownTimer = vm.runInContext(COUNTDOWN_SOURCE + '\nCountdownTimer;', sandbox);
|
||||
|
||||
return { CountdownTimer, window: windowMock, timers };
|
||||
}
|
||||
|
||||
describe('CountdownTimer', () => {
|
||||
test('formatNumber complète les nombres sur deux chiffres', () => {
|
||||
const { CountdownTimer } = loadCountdown();
|
||||
|
||||
assert.strictEqual(CountdownTimer.prototype.formatNumber(0), '00');
|
||||
assert.strictEqual(CountdownTimer.prototype.formatNumber(5), '05');
|
||||
assert.strictEqual(CountdownTimer.prototype.formatNumber(42), '42');
|
||||
assert.strictEqual(CountdownTimer.prototype.formatNumber(123), '123');
|
||||
});
|
||||
|
||||
test('calcule correctement le temps restant (jours/heures/minutes/secondes)', () => {
|
||||
const distance =
|
||||
2 * 24 * 60 * 60 * 1000 + // 2 jours
|
||||
3 * 60 * 60 * 1000 + // 3 heures
|
||||
4 * 60 * 1000 + // 4 minutes
|
||||
5 * 1000; // 5 secondes
|
||||
|
||||
const elements = makeElements();
|
||||
const { CountdownTimer } = loadCountdown({ elements });
|
||||
const timer = new CountdownTimer(FIXED_NOW + distance);
|
||||
|
||||
try {
|
||||
assert.strictEqual(elements['countdown-days'].textContent, '02');
|
||||
assert.strictEqual(elements['countdown-hours'].textContent, '03');
|
||||
assert.strictEqual(elements['countdown-minutes'].textContent, '04');
|
||||
assert.strictEqual(elements['countdown-seconds'].textContent, '05');
|
||||
} finally {
|
||||
timer.stop();
|
||||
}
|
||||
});
|
||||
|
||||
test('reporte les heures au-delà de 24h dans les jours', () => {
|
||||
const distance = 36 * 60 * 60 * 1000; // 36 heures = 1 jour + 12 heures
|
||||
|
||||
const elements = makeElements();
|
||||
const { CountdownTimer } = loadCountdown({ elements });
|
||||
const timer = new CountdownTimer(FIXED_NOW + distance);
|
||||
|
||||
try {
|
||||
assert.strictEqual(elements['countdown-days'].textContent, '01');
|
||||
assert.strictEqual(elements['countdown-hours'].textContent, '12');
|
||||
assert.strictEqual(elements['countdown-minutes'].textContent, '00');
|
||||
assert.strictEqual(elements['countdown-seconds'].textContent, '00');
|
||||
} finally {
|
||||
timer.stop();
|
||||
}
|
||||
});
|
||||
|
||||
test('une date passée arrête le compte à rebours et redirige vers /', () => {
|
||||
const elements = makeElements();
|
||||
const { CountdownTimer, window } = loadCountdown({ elements });
|
||||
const timer = new CountdownTimer(FIXED_NOW - 1000);
|
||||
|
||||
try {
|
||||
// onComplete() redirige vers la page principale
|
||||
assert.strictEqual(window.location.href, '/');
|
||||
|
||||
// Les éléments ne sont pas mis à jour quand le compte est terminé
|
||||
assert.strictEqual(elements['countdown-days'].textContent, '');
|
||||
assert.strictEqual(elements['countdown-hours'].textContent, '');
|
||||
assert.strictEqual(elements['countdown-minutes'].textContent, '');
|
||||
assert.strictEqual(elements['countdown-seconds'].textContent, '');
|
||||
} finally {
|
||||
timer.stop();
|
||||
}
|
||||
});
|
||||
|
||||
test('une distance nulle affiche zéro partout sans rediriger', () => {
|
||||
const elements = makeElements();
|
||||
const { CountdownTimer, window } = loadCountdown({ elements });
|
||||
const timer = new CountdownTimer(FIXED_NOW);
|
||||
|
||||
try {
|
||||
assert.strictEqual(elements['countdown-days'].textContent, '00');
|
||||
assert.strictEqual(elements['countdown-hours'].textContent, '00');
|
||||
assert.strictEqual(elements['countdown-minutes'].textContent, '00');
|
||||
assert.strictEqual(elements['countdown-seconds'].textContent, '00');
|
||||
assert.strictEqual(window.location.href, '');
|
||||
} finally {
|
||||
timer.stop();
|
||||
}
|
||||
});
|
||||
|
||||
test('fonctionne sans éléments DOM présents', () => {
|
||||
const { CountdownTimer } = loadCountdown({ elements: null });
|
||||
const timer = new CountdownTimer(FIXED_NOW + 60 * 1000);
|
||||
|
||||
// Aucune erreur attendue malgré l'absence des éléments
|
||||
timer.stop();
|
||||
});
|
||||
|
||||
test('démarre un intervalle d\'une seconde et stop() le nettoie', () => {
|
||||
const { CountdownTimer, timers } = loadCountdown();
|
||||
const timer = new CountdownTimer(FIXED_NOW + 60 * 1000);
|
||||
|
||||
assert.strictEqual(timers.intervals.length, 1);
|
||||
assert.strictEqual(timers.intervals[0].delay, 1000);
|
||||
assert.strictEqual(timers.cleared.length, 0);
|
||||
|
||||
timer.stop();
|
||||
assert.deepStrictEqual(timers.cleared, [timer.interval]);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user