diff --git a/Makefile b/Makefile index 2985eca..4ad974d 100644 --- a/Makefile +++ b/Makefile @@ -207,7 +207,7 @@ hadolint_report: ## Lance Hadolint sur le Dockerfile prod et genere le rapport J audit: ## Lance l'audit de securite Composer docker compose -f docker-compose-dev.yml exec php composer audit -reports: phpstan_report eslint_report test_coverage hadolint_report phpmetrics ## Genere tous les rapports pour SonarQube +reports: phpstan_report eslint_report run_test_coverage_js test_coverage hadolint_report phpmetrics ## Genere tous les rapports pour SonarQube ## —— SonarQube ———————————————————————————————————— sonar: reports ## Genere les rapports puis lance le scan SonarQube diff --git a/tests/js/app.test.js b/tests/js/app.test.js new file mode 100644 index 0000000..6330e0f --- /dev/null +++ b/tests/js/app.test.js @@ -0,0 +1,254 @@ +import { describe, it, expect, beforeEach, vi } from 'vitest' + +describe('app.js DOMContentLoaded', () => { + beforeEach(() => { + document.body.innerHTML = '' + localStorage.clear() + }) + + const loadApp = async () => { + // Reset module cache and re-import + vi.resetModules() + await import('../../assets/app.js') + document.dispatchEvent(new Event('DOMContentLoaded')) + } + + describe('Member/Admin checkboxes', () => { + beforeEach(() => { + document.body.innerHTML = ` + + + + + ` + }) + + it('unchecks other groups when member is checked', async () => { + await loadApp() + + const member = document.querySelector('[value="siteconseil_member"]') + const admin = document.querySelector('[value="siteconseil_admin"]') + const esyWeb = document.querySelector('[value="esy-web"]') + + admin.checked = true + esyWeb.checked = true + + member.checked = true + member.dispatchEvent(new Event('change')) + + expect(admin.checked).toBe(false) + expect(esyWeb.checked).toBe(false) + }) + + it('checks all groups and unchecks member when admin is checked', async () => { + await loadApp() + + const member = document.querySelector('[value="siteconseil_member"]') + const admin = document.querySelector('[value="siteconseil_admin"]') + const esyWeb = document.querySelector('[value="esy-web"]') + + admin.checked = true + admin.dispatchEvent(new Event('change')) + + expect(member.checked).toBe(false) + expect(esyWeb.checked).toBe(true) + }) + + it('does nothing when admin is unchecked', async () => { + await loadApp() + + const member = document.querySelector('[value="siteconseil_member"]') + const admin = document.querySelector('[value="siteconseil_admin"]') + + member.checked = true + admin.checked = false + admin.dispatchEvent(new Event('change')) + + expect(member.checked).toBe(true) + }) + }) + + describe('Stats period selector', () => { + beforeEach(() => { + document.body.innerHTML = ` + +
+ ` + }) + + it('shows custom range when custom is selected', async () => { + await loadApp() + const select = document.getElementById('stats-period-select') + const range = document.getElementById('stats-custom-range') + + select.value = 'custom' + select.dispatchEvent(new Event('change')) + + expect(range.classList.contains('hidden')).toBe(false) + }) + + it('hides custom range when current is selected', async () => { + await loadApp() + const select = document.getElementById('stats-period-select') + const range = document.getElementById('stats-custom-range') + + select.value = 'current' + select.dispatchEvent(new Event('change')) + + expect(range.classList.contains('hidden')).toBe(true) + }) + }) + + describe('data-confirm forms', () => { + it('prevents submission when confirm is cancelled', async () => { + document.body.innerHTML = '' + window.confirm = vi.fn(() => false) + + await loadApp() + const form = document.querySelector('form') + const event = new Event('submit', { cancelable: true }) + form.dispatchEvent(event) + + expect(event.defaultPrevented).toBe(true) + }) + + it('allows submission when confirm is accepted', async () => { + document.body.innerHTML = '' + window.confirm = vi.fn(() => true) + + await loadApp() + const form = document.querySelector('form') + const event = new Event('submit', { cancelable: true }) + form.dispatchEvent(event) + + expect(event.defaultPrevented).toBe(false) + }) + }) + + describe('Sidebar dropdown', () => { + it('registers click handlers on dropdown buttons', async () => { + document.body.innerHTML = `