feat(tests): integrate Vitest for testing framework and add test scripts
- Added Vitest as a dependency for improved testing capabilities. - Updated package.json with new test scripts for running tests, watching, and coverage reporting. - Configured ESLint to recognize test runner scripts and included them in the linting process. - Modified tsconfig.json to include Vitest types for better TypeScript support in tests.
This commit is contained in:
@@ -18,6 +18,8 @@ const eslintConfig = [
|
||||
'out/**',
|
||||
'build/**',
|
||||
'next-env.d.ts',
|
||||
'scripts/test-runner.js', // Script Node.js qui utilise require() légitimement
|
||||
'scripts/generate-icons-from-jpg.ts', // Script utilitaire avec require()
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
@@ -19,6 +19,10 @@
|
||||
"cache:stats": "pnpm tsx scripts/cache-monitor.ts stats",
|
||||
"cache:cleanup": "pnpm tsx scripts/cache-monitor.ts cleanup",
|
||||
"cache:clear": "pnpm tsx scripts/cache-monitor.ts clear",
|
||||
"test": "node scripts/test-runner.js",
|
||||
"test:watch": "vitest --watch --reporter=verbose",
|
||||
"test:coverage": "vitest --coverage --reporter=verbose",
|
||||
"test:ui": "vitest --ui",
|
||||
"test:story-points": "pnpm tsx scripts/test-story-points.ts",
|
||||
"test:jira-fields": "pnpm tsx scripts/test-jira-fields.ts",
|
||||
"prettier:format": "prettier --write .",
|
||||
@@ -71,7 +75,8 @@
|
||||
"sharp": "^0.34.5",
|
||||
"tailwindcss": "^4.1.14",
|
||||
"tsx": "^4.19.2",
|
||||
"typescript": "^5"
|
||||
"typescript": "^5",
|
||||
"vitest": "^2.1.8"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{js,jsx,ts,tsx,json,css,md}": [
|
||||
|
||||
766
pnpm-lock.yaml
generated
766
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
62
scripts/test-runner.js
Executable file
62
scripts/test-runner.js
Executable file
@@ -0,0 +1,62 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Script pour gérer PostCSS pendant les tests
|
||||
* Renomme temporairement postcss.config.mjs pour éviter les erreurs Vitest
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { spawn } = require('child_process');
|
||||
|
||||
const postcssConfigPath = path.join(process.cwd(), 'postcss.config.mjs');
|
||||
const postcssConfigBackupPath = path.join(
|
||||
process.cwd(),
|
||||
'postcss.config.mjs.testbak'
|
||||
);
|
||||
|
||||
// Fonction pour restaurer le fichier
|
||||
function restorePostCSS() {
|
||||
if (fs.existsSync(postcssConfigBackupPath)) {
|
||||
fs.renameSync(postcssConfigBackupPath, postcssConfigPath);
|
||||
console.log('✓ PostCSS config restauré');
|
||||
}
|
||||
}
|
||||
|
||||
// Renommer le fichier PostCSS avant les tests
|
||||
if (fs.existsSync(postcssConfigPath)) {
|
||||
fs.renameSync(postcssConfigPath, postcssConfigBackupPath);
|
||||
console.log('✓ PostCSS config temporairement désactivé pour les tests');
|
||||
|
||||
// Lancer Vitest avec reporter verbose pour plus de détails
|
||||
const vitest = spawn('pnpm', ['vitest', '--run', '--reporter=verbose'], {
|
||||
stdio: 'inherit',
|
||||
});
|
||||
|
||||
// Restaurer le fichier après que Vitest ait terminé
|
||||
vitest.on('close', (code) => {
|
||||
restorePostCSS();
|
||||
process.exit(code || 0);
|
||||
});
|
||||
|
||||
// Gérer les signaux d'interruption
|
||||
process.on('SIGINT', () => {
|
||||
vitest.kill('SIGINT');
|
||||
restorePostCSS();
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
process.on('SIGTERM', () => {
|
||||
vitest.kill('SIGTERM');
|
||||
restorePostCSS();
|
||||
process.exit(0);
|
||||
});
|
||||
} else {
|
||||
// Si le fichier n'existe pas, lancer Vitest directement
|
||||
const vitest = spawn('pnpm', ['vitest', '--run'], {
|
||||
stdio: 'inherit',
|
||||
});
|
||||
|
||||
vitest.on('close', (code) => {
|
||||
process.exit(code || 0);
|
||||
});
|
||||
}
|
||||
9
src/services/__tests__/setup.ts
Normal file
9
src/services/__tests__/setup.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* Configuration de setup pour les tests Vitest
|
||||
* Désactive PostCSS pour éviter les erreurs de chargement
|
||||
*/
|
||||
|
||||
// Désactiver PostCSS pendant les tests
|
||||
process.env.VITE_DISABLE_POSTCSS = 'true';
|
||||
|
||||
// Note: NODE_ENV est géré automatiquement par Vitest
|
||||
171
src/services/integrations/jira/__tests__/sync.test.ts
Normal file
171
src/services/integrations/jira/__tests__/sync.test.ts
Normal file
@@ -0,0 +1,171 @@
|
||||
/**
|
||||
* Tests unitaires pour la synchronisation Jira
|
||||
*/
|
||||
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { buildSyncUpdateData } from '@/services/task-management/readonly-fields';
|
||||
|
||||
describe('Synchronisation Jira - Logique de préservation des champs', () => {
|
||||
describe('buildSyncUpdateData pour Jira', () => {
|
||||
it('devrait préserver title si modifié localement', () => {
|
||||
const existingTask = {
|
||||
title: 'Titre modifié localement',
|
||||
description: 'Description locale',
|
||||
status: 'todo',
|
||||
priority: 'high',
|
||||
dueDate: new Date('2024-01-01'),
|
||||
};
|
||||
|
||||
const syncData = {
|
||||
title: 'Titre original Jira',
|
||||
description: 'Description Jira',
|
||||
status: 'in_progress',
|
||||
priority: 'medium',
|
||||
dueDate: new Date('2024-01-02'),
|
||||
};
|
||||
|
||||
const result = buildSyncUpdateData('jira', existingTask, syncData);
|
||||
|
||||
expect(result.title).toBe('Titre modifié localement');
|
||||
expect(result.description).toBe('Description Jira'); // Écrasé
|
||||
expect(result.status).toBe('in_progress'); // Écrasé
|
||||
expect(result.priority).toBe('high'); // Préservé
|
||||
expect(result.dueDate).toEqual(new Date('2024-01-02')); // Écrasé
|
||||
});
|
||||
|
||||
it('devrait préserver priority si modifié localement', () => {
|
||||
const existingTask = {
|
||||
title: 'Même titre',
|
||||
description: 'Description locale',
|
||||
status: 'todo',
|
||||
priority: 'low', // Modifié localement
|
||||
dueDate: new Date('2024-01-01'),
|
||||
};
|
||||
|
||||
const syncData = {
|
||||
title: 'Même titre',
|
||||
description: 'Description Jira',
|
||||
status: 'done',
|
||||
priority: 'high', // Valeur Jira
|
||||
dueDate: null,
|
||||
};
|
||||
|
||||
const result = buildSyncUpdateData('jira', existingTask, syncData);
|
||||
|
||||
expect(result.priority).toBe('low'); // Préservé car modifié localement
|
||||
});
|
||||
|
||||
it('devrait préserver status archived même si Jira dit done', () => {
|
||||
const existingTask = {
|
||||
title: 'Tâche archivée',
|
||||
description: 'Description',
|
||||
status: 'archived', // Archivé localement
|
||||
priority: 'medium',
|
||||
dueDate: null,
|
||||
};
|
||||
|
||||
const syncData = {
|
||||
title: 'Tâche archivée',
|
||||
description: 'Description Jira',
|
||||
status: 'done', // Jira dit done
|
||||
priority: 'medium',
|
||||
dueDate: null,
|
||||
};
|
||||
|
||||
const result = buildSyncUpdateData('jira', existingTask, syncData);
|
||||
|
||||
expect(result.status).toBe('archived'); // Préservé car archived
|
||||
});
|
||||
|
||||
it('devrait écraser status si pas archived', () => {
|
||||
const existingTask = {
|
||||
title: 'Tâche',
|
||||
description: 'Description',
|
||||
status: 'todo',
|
||||
priority: 'medium',
|
||||
dueDate: null,
|
||||
};
|
||||
|
||||
const syncData = {
|
||||
title: 'Tâche',
|
||||
description: 'Description Jira',
|
||||
status: 'in_progress', // Jira dit in_progress
|
||||
priority: 'medium',
|
||||
dueDate: null,
|
||||
};
|
||||
|
||||
const result = buildSyncUpdateData('jira', existingTask, syncData);
|
||||
|
||||
expect(result.status).toBe('in_progress'); // Écrasé
|
||||
});
|
||||
|
||||
it('devrait toujours écraser description', () => {
|
||||
const existingTask = {
|
||||
title: 'Tâche',
|
||||
description: 'Description locale modifiée',
|
||||
status: 'todo',
|
||||
priority: 'medium',
|
||||
dueDate: null,
|
||||
};
|
||||
|
||||
const syncData = {
|
||||
title: 'Tâche',
|
||||
description: 'Description Jira',
|
||||
status: 'todo',
|
||||
priority: 'medium',
|
||||
dueDate: null,
|
||||
};
|
||||
|
||||
const result = buildSyncUpdateData('jira', existingTask, syncData);
|
||||
|
||||
expect(result.description).toBe('Description Jira'); // Toujours écrasé
|
||||
});
|
||||
|
||||
it('devrait toujours écraser dueDate', () => {
|
||||
const existingTask = {
|
||||
title: 'Tâche',
|
||||
description: 'Description',
|
||||
status: 'todo',
|
||||
priority: 'medium',
|
||||
dueDate: new Date('2024-01-01'), // Date locale
|
||||
};
|
||||
|
||||
const syncData = {
|
||||
title: 'Tâche',
|
||||
description: 'Description',
|
||||
status: 'todo',
|
||||
priority: 'medium',
|
||||
dueDate: new Date('2024-12-31'), // Date Jira
|
||||
};
|
||||
|
||||
const result = buildSyncUpdateData('jira', existingTask, syncData);
|
||||
|
||||
expect(result.dueDate).toEqual(new Date('2024-12-31')); // Toujours écrasé
|
||||
});
|
||||
|
||||
it('devrait préserver priority même si title identique', () => {
|
||||
const existingTask = {
|
||||
title: 'Titre identique',
|
||||
description: 'Description locale',
|
||||
status: 'todo',
|
||||
priority: 'medium', // Modifié localement
|
||||
dueDate: null,
|
||||
};
|
||||
|
||||
const syncData = {
|
||||
title: 'Titre identique', // Même titre
|
||||
description: 'Description Jira',
|
||||
status: 'done',
|
||||
priority: 'high', // Valeur Jira différente
|
||||
dueDate: new Date('2024-01-01'),
|
||||
};
|
||||
|
||||
const result = buildSyncUpdateData('jira', existingTask, syncData);
|
||||
|
||||
// Title identique → utilise syncData
|
||||
expect(result.title).toBe('Titre identique');
|
||||
// Priority différente → préservée même si title identique
|
||||
expect(result.priority).toBe('medium'); // Préservé car modifié localement
|
||||
});
|
||||
});
|
||||
});
|
||||
147
src/services/integrations/tfs/__tests__/sync.test.ts
Normal file
147
src/services/integrations/tfs/__tests__/sync.test.ts
Normal file
@@ -0,0 +1,147 @@
|
||||
/**
|
||||
* Tests unitaires pour la synchronisation TFS
|
||||
*/
|
||||
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { buildSyncUpdateData } from '@/services/task-management/readonly-fields';
|
||||
|
||||
describe("Synchronisation TFS - Logique d'écrasement des champs", () => {
|
||||
describe('buildSyncUpdateData pour TFS', () => {
|
||||
it('devrait écraser tous les champs même si modifiés localement', () => {
|
||||
const existingTask = {
|
||||
title: 'Titre modifié localement',
|
||||
description: 'Description locale modifiée',
|
||||
status: 'todo',
|
||||
priority: 'high',
|
||||
dueDate: new Date('2024-01-01'),
|
||||
};
|
||||
|
||||
const syncData = {
|
||||
title: 'PR: Nouvelle Pull Request',
|
||||
description: 'Description TFS',
|
||||
status: 'done',
|
||||
priority: 'medium',
|
||||
dueDate: null,
|
||||
};
|
||||
|
||||
const result = buildSyncUpdateData('tfs', existingTask, syncData);
|
||||
|
||||
// Tous les champs doivent être écrasés
|
||||
expect(result.title).toBe('PR: Nouvelle Pull Request');
|
||||
expect(result.description).toBe('Description TFS');
|
||||
expect(result.status).toBe('done');
|
||||
expect(result.priority).toBe('medium');
|
||||
expect(result.dueDate).toBeNull();
|
||||
});
|
||||
|
||||
it('devrait écraser title même si différent', () => {
|
||||
const existingTask = {
|
||||
title: 'Ancien titre',
|
||||
description: 'Description',
|
||||
status: 'todo',
|
||||
priority: 'medium',
|
||||
dueDate: null,
|
||||
};
|
||||
|
||||
const syncData = {
|
||||
title: 'PR: Nouveau titre',
|
||||
description: 'Description',
|
||||
status: 'todo',
|
||||
priority: 'medium',
|
||||
dueDate: null,
|
||||
};
|
||||
|
||||
const result = buildSyncUpdateData('tfs', existingTask, syncData);
|
||||
|
||||
expect(result.title).toBe('PR: Nouveau titre'); // Écrasé
|
||||
});
|
||||
|
||||
it('devrait écraser priority même si modifié localement', () => {
|
||||
const existingTask = {
|
||||
title: 'PR: Tâche',
|
||||
description: 'Description',
|
||||
status: 'todo',
|
||||
priority: 'low', // Modifié localement
|
||||
dueDate: null,
|
||||
};
|
||||
|
||||
const syncData = {
|
||||
title: 'PR: Tâche',
|
||||
description: 'Description',
|
||||
status: 'todo',
|
||||
priority: 'high', // Valeur TFS
|
||||
dueDate: null,
|
||||
};
|
||||
|
||||
const result = buildSyncUpdateData('tfs', existingTask, syncData);
|
||||
|
||||
expect(result.priority).toBe('high'); // Écrasé
|
||||
});
|
||||
|
||||
it('devrait écraser status même si archived localement', () => {
|
||||
const existingTask = {
|
||||
title: 'PR: Tâche',
|
||||
description: 'Description',
|
||||
status: 'archived', // Archivé localement
|
||||
priority: 'medium',
|
||||
dueDate: null,
|
||||
};
|
||||
|
||||
const syncData = {
|
||||
title: 'PR: Tâche',
|
||||
description: 'Description',
|
||||
status: 'done', // TFS dit done
|
||||
priority: 'medium',
|
||||
dueDate: null,
|
||||
};
|
||||
|
||||
const result = buildSyncUpdateData('tfs', existingTask, syncData);
|
||||
|
||||
expect(result.status).toBe('done'); // Écrasé même si archived
|
||||
});
|
||||
|
||||
it('devrait écraser description même si modifiée localement', () => {
|
||||
const existingTask = {
|
||||
title: 'PR: Tâche',
|
||||
description: 'Description locale modifiée',
|
||||
status: 'todo',
|
||||
priority: 'medium',
|
||||
dueDate: null,
|
||||
};
|
||||
|
||||
const syncData = {
|
||||
title: 'PR: Tâche',
|
||||
description: 'Description TFS',
|
||||
status: 'todo',
|
||||
priority: 'medium',
|
||||
dueDate: null,
|
||||
};
|
||||
|
||||
const result = buildSyncUpdateData('tfs', existingTask, syncData);
|
||||
|
||||
expect(result.description).toBe('Description TFS'); // Écrasé
|
||||
});
|
||||
|
||||
it('devrait écraser dueDate même si défini localement', () => {
|
||||
const existingTask = {
|
||||
title: 'PR: Tâche',
|
||||
description: 'Description',
|
||||
status: 'todo',
|
||||
priority: 'medium',
|
||||
dueDate: new Date('2024-01-01'), // Date locale
|
||||
};
|
||||
|
||||
const syncData = {
|
||||
title: 'PR: Tâche',
|
||||
description: 'Description',
|
||||
status: 'todo',
|
||||
priority: 'medium',
|
||||
dueDate: null, // TFS n'a pas de dueDate
|
||||
};
|
||||
|
||||
const result = buildSyncUpdateData('tfs', existingTask, syncData);
|
||||
|
||||
expect(result.dueDate).toBeNull(); // Écrasé
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -13,6 +13,7 @@
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"incremental": true,
|
||||
"types": ["vitest/globals"],
|
||||
"plugins": [
|
||||
{
|
||||
"name": "next"
|
||||
@@ -22,6 +23,12 @@
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||
"include": [
|
||||
"next-env.d.ts",
|
||||
"vitest.d.ts",
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
".next/types/**/*.ts"
|
||||
],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
|
||||
44
vitest.config.ts
Normal file
44
vitest.config.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import { defineConfig } from 'vitest/config';
|
||||
import path from 'path';
|
||||
|
||||
// Solution: créer un mock de postcss.config.mjs pour les tests
|
||||
// En renommant temporairement le fichier ou en créant une config vide
|
||||
|
||||
export default defineConfig({
|
||||
test: {
|
||||
globals: true,
|
||||
environment: 'node',
|
||||
include: ['src/**/__tests__/**/*.test.ts'],
|
||||
setupFiles: ['./src/services/__tests__/setup.ts'],
|
||||
// Afficher plus de détails dans la sortie
|
||||
reporters: ['verbose'],
|
||||
// Afficher les tests qui passent aussi
|
||||
silent: false,
|
||||
coverage: {
|
||||
provider: 'v8',
|
||||
reporter: ['text', 'json', 'html'],
|
||||
exclude: [
|
||||
'node_modules/',
|
||||
'src/**/__tests__/**',
|
||||
'**/*.config.*',
|
||||
'**/dist/**',
|
||||
],
|
||||
// Afficher les fichiers non couverts
|
||||
reportOnFailure: true,
|
||||
// Afficher plus de détails
|
||||
all: false,
|
||||
},
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': path.resolve(__dirname, './src'),
|
||||
},
|
||||
},
|
||||
// Désactiver le traitement CSS pour éviter PostCSS
|
||||
// Le fichier postcss.config.mjs est renommé par le script test-runner.js
|
||||
css: {
|
||||
modules: {
|
||||
localsConvention: 'camelCase',
|
||||
},
|
||||
},
|
||||
});
|
||||
1
vitest.d.ts
vendored
Normal file
1
vitest.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/// <reference types="vitest/globals" />
|
||||
Reference in New Issue
Block a user