Browse Source

ajout test unitaire preprod

DESKTOP-SMCIPAV\falko 4 tháng trước cách đây
mục cha
commit
5af75a114c

+ 254 - 99
Jenkinsfile

@@ -5,10 +5,14 @@ pipeline {
         NODEJS_HOME = "${tool 'NodeJS'}"
         PATH = "${env.NODEJS_HOME}/bin:${env.PATH}"
 
-        
+        DOCKER_REGISTRY = "nexus.foodgame.fr:8123"
+        IMAGE_NAME = "fatboar_repo/workflow_jenkins_1"
+        IMAGE_TAG = "latest"
     }
 
     stages {
+
+        // 1. Déterminer l'environnement en fonction de la branche
         stage('Select Environment') {
             steps {
                 script {
@@ -35,94 +39,246 @@ pipeline {
             }
         }
 
-        // stage('Install Dependencies & Test') {
+        // 2. Installation des dépendances et tests unitaires Angular
+//   stage('Unit Tests') {
+//   agent {
+//     docker {
+//       image 'cypress/browsers:node14.17.0-chrome91-ff89'
+//       args '-u root:root --shm-size=2g'
+//     }
+//   }
+//   environment {
+//     CHROME_BIN = '/usr/bin/google-chrome'
+//     NODE_OPTIONS = '--max-old-space-size=4096'
+//     DISPLAY = ':99'
+//   }
+//   steps {
+//     dir('angular-client') {
+//       sh '''
+//         echo "🔧 Configuration de l'environnement"
+//         export DISPLAY=:99
+//         Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 &
+
+//         echo "✅ Installation des dépendances"
+//         npm install --legacy-peer-deps
+
+//         echo "🧪 Vérification de Chrome"
+//         google-chrome --version
+
+//         echo "🚀 Lancement des tests unitaires avec couverture"
+//         npx ng test --no-watch --no-progress --browsers=ChromeHeadlessCI --code-coverage
+//       '''
+//     }
+//   }
+//   post {
+//     always {
+//       // Publier le rapport de couverture HTML
+//       publishHTML([
+//         allowMissing: false,
+//         alwaysLinkToLastBuild: false,
+//         keepAll: true,
+//         reportDir: 'angular-client/coverage/fatboar-burger',
+//         reportFiles: 'index.html',
+//         reportName: 'Code Coverage Report',
+//         reportTitles: 'Coverage Report'
+//       ])
+      
+//       // Archiver les artefacts de couverture
+//       archiveArtifacts artifacts: 'angular-client/coverage/**/*', fingerprint: true
+//     }
+//     failure {
+//       echo '❌ Tests échoués - Vérifiez les logs ci-dessus'
+//     }
+//     success {
+//       echo '✅ Tests réussis avec couverture générée'
+//     }
+//   }
+// }
+        // 3. Analyse SonarQube pour la qualité du code
+        // stage('SonarQube Analysis') {
         //     steps {
-        //         withEnv(["PATH=${tool 'NodeJS'}/bin:${env.PATH}"]) {
-        //             sh 'npm install --legacy-peer-deps'
-        //             sh 'npm run test -- --no-watch --no-progress --browsers=ChromeHeadless'
+        //         script {
+        //             def scannerHome = tool name: 'SonarQube Scanner', type: 'hudson.plugins.sonar.SonarRunnerInstallation'
+        //             withSonarQubeEnv('SonarQube') {
+        //                 sh """
+        //                     ${scannerHome}/bin/sonar-scanner \
+        //                     -Dsonar.projectKey=FatboarProject-${env.BRANCH_NAME} \
+        //                     -Dsonar.sources=. \
+        //                     -Dsonar.host.url=https://sonarqube.foodgame.fr \
+        //                     -Dsonar.login=sqa_9ec3588a80a0b8458d9273dbb6eb7f6ae91b446a
+        //                 """
+        //             }
         //         }
         //     }
         // }
 
-        stage('SonarQube Analysis') {
-            steps {
-                script {
-                    def scannerHome = tool name: 'SonarQube Scanner', type: 'hudson.plugins.sonar.SonarRunnerInstallation'
-                    withSonarQubeEnv('SonarQube') {
-                        sh """
-                            ${scannerHome}/bin/sonar-scanner \
-                            -Dsonar.projectKey=FatboarProject-${env.BRANCH_NAME} \
-                            -Dsonar.sources=. \
-                            -Dsonar.host.url=https://sonarqube.foodgame.fr \
-                            -Dsonar.login=sqa_9ec3588a80a0b8458d9273dbb6eb7f6ae91b446a
-                        """
-                    }
-                }
-            }
-        }
-
+        // 4. Arrêt et nettoyage des anciens conteneurs
         // stage('Stop & Clean Containers') {
         //     steps {
         //         sh """
-        //             docker ps | grep "workflow_" -v | awk '{if(NR>1) print \$1}' | xargs docker kill || true
         //             docker-compose -f ${env.COMPOSE_FILE} down || true
         //             docker system prune -f
         //         """
         //     }
         // }
 
-        stage('Build & Deploy') {
-            steps {
-                sh """
-                    docker-compose -f ${env.COMPOSE_FILE} build --no-cache
-                    docker-compose -f ${env.COMPOSE_FILE} up -d --force-recreate --remove-orphans
-                """
-                script {
-                    echo "Lancement du backup sur le container ${env.BACKUP_CONTAINER}"
-                    def backupCmd = "docker exec ${env.BACKUP_CONTAINER} /backup.sh || echo '⚠️ Backup ${env.ENV.toUpperCase()} échoué'"
-                    sh backupCmd
-                }
-            }
-        }
+        // 5. Build et déploiement des services
+        // stage('Build & Deploy') {
+        //     steps {
+        //         sh """
+        //             docker-compose -f ${env.COMPOSE_FILE} build --no-cache
+        //             docker-compose -f ${env.COMPOSE_FILE} up -d --force-recreate --remove-orphans
+        //         """
+        //     }
+        // }
+
+        // 6. Lancement des backups après déploiement
+     stage('Backup') {
+      steps {
+        script {
+            echo "🚀 Lancement du backup sur le container ${env.BACKUP_CONTAINER}"
+            
+            try {
+                def containerExists = sh(
+                    script: "docker ps -a --filter name=${env.BACKUP_CONTAINER} --format '{{.Names}}'",
+                    returnStdout: true
+                ).trim()
 
-        stage('Push to Private Registry (Optional)') {
-            when {
-                anyOf {
-                    branch 'dev'
-                    branch 'master'
+                if (!containerExists) {
+                    echo "⚠️  Container ${env.BACKUP_CONTAINER} n'existe pas, lancement..."
+                    sh "docker-compose -f ${env.COMPOSE_FILE} up -d mongodb-backup-${env.ENV}"
+                    sleep(30)
                 }
-            }
-            steps {
-                script {
-                    def registryUrl = 'nexus.foodgame.fr:8123'
-                    def imageName = 'grafana/tns-db'
-                    def imageVersion = 'latest'
-                    
-                    docker.withRegistry("https://${registryUrl}", 'nexus') {
-                        try {
-                            //Try pulling the image from the registry
-                            echo "Trying to pull image: ${registryUrl}/${imageName}:${imageVersion}"
-                            docker.image("${registryUrl}/${imageName}:${imageVersion}").pull()
-                        } catch (Exception e) {
-                            echo "Image pull failed. Attempting to build and push."
-
-                            //Pull base image from Docker Hub
-                            echo "Pulling base image: ${imageName}:${imageVersion}"
-                            sh "docker pull ${imageName}:${imageVersion}"
-                            
-                            //Tag the image for the private registry
-                            echo "Tagging image for private registry"
-                            sh "docker tag ${imageName}:${imageVersion} ${registryUrl}/${imageName}:${imageVersion}"
-                            
-                            //Push the tagged image to the private registry
-                            echo "Pushing image to private registry"
-                            sh "docker push ${registryUrl}/${imageName}:${imageVersion}"
+
+                def containerStatus = sh(
+                    script: "docker ps --filter name=${env.BACKUP_CONTAINER} --format '{{.Status}}'",
+                    returnStdout: true
+                ).trim()
+
+                if (containerStatus.contains('Up')) {
+                    echo "✅ Container ${env.BACKUP_CONTAINER} est actif"
+                    sleep(10)
+
+                    // Vérifier les logs du container pour s'assurer qu'il fonctionne bien
+                    echo "📋 Vérification des logs récents du container backup..."
+                    sh "docker logs --tail 10 ${env.BACKUP_CONTAINER} 2>&1 || true"
+
+                    // Déclencher un backup manuel en utilisant mongodump avec les bonnes variables
+                    echo "💾 Déclenchement du backup manuel..."
+                    def backupExitCode = sh(
+                        script: """
+                            docker exec ${env.BACKUP_CONTAINER} bash -c '
+                                echo "=== Démarrage du backup manuel ===" &&
+                                BACKUP_DIR="/backup/manual-\$(date +%Y-%m-%d_%H-%M-%S)" &&
+                                mkdir -p "\$BACKUP_DIR" &&
+                                mongodump --host=\$MONGODB_HOST --port=\$MONGODB_PORT \\
+                                          --username=\$MONGODB_USER --password=\$MONGODB_PASS \\
+                                          --authenticationDatabase=\$MONGODB_AUTHENTICATION_DATABASE \\
+                                          --archive="\$BACKUP_DIR/dump.archive.gz" --gzip &&
+                                echo "Backup terminé dans \$BACKUP_DIR"
+                            '
+                        """,
+                        returnStatus: true
+                    )
+
+                    if (backupExitCode == 0) {
+                        echo "✅ Commande de backup lancée avec succès"
+                        
+                        // Attendre un peu pour que le backup se termine
+                        sleep(30)
+                        
+                        // Vérifier les fichiers de backup créés
+                        def backupFiles = sh(
+                            script: "docker exec ${env.BACKUP_CONTAINER} find /backup -name '*.gz' -type f -mtime -1 | head -5",
+                            returnStdout: true
+                        ).trim()
+
+                        if (backupFiles) {
+                            echo "✅ Backup ${env.ENV.toUpperCase()} réussi !"
+                            echo "📁 Fichiers créés :"
+                            backupFiles.split('\n').each { file ->
+                                if (file.trim()) {
+                                    def fileInfo = sh(
+                                        script: "docker exec ${env.BACKUP_CONTAINER} ls -lh '${file.trim()}'",
+                                        returnStdout: true
+                                    ).trim()
+                                    echo fileInfo
+                                }
+                            }
+                        } else {
+                            echo "⚠️ Aucun fichier de backup récent trouvé, vérification des logs..."
+                            sh "docker logs --tail 20 ${env.BACKUP_CONTAINER} 2>&1 || true"
                         }
+                        
+                        // Afficher le statut final
+                        echo "📊 Statut final du backup :"
+                        sh """
+                            docker exec ${env.BACKUP_CONTAINER} bash -c '
+                                echo "=== Espace disque utilisé ===" &&
+                                df -h /backup &&
+                                echo "=== Derniers backups ===" &&
+                                ls -la /backup/ | tail -10
+                            ' || true
+                        """
+
+                    } else {
+                        echo "❌ Backup échoué avec code ${backupExitCode}"
+                        echo "📋 Logs détaillés :"
+                        sh "docker logs --tail 30 ${env.BACKUP_CONTAINER} 2>&1 || true"
+                        currentBuild.result = 'UNSTABLE'
+                    }
+
+                } else {
+                    echo "❌ Container ${env.BACKUP_CONTAINER} inactif — tentative de redémarrage"
+                    sh "docker-compose -f ${env.COMPOSE_FILE} restart mongodb-backup-${env.ENV} || true"
+                    sleep(30)
+                    
+                    // Vérifier le statut après redémarrage
+                    def newStatus = sh(
+                        script: "docker ps --filter name=${env.BACKUP_CONTAINER} --format '{{.Status}}'",
+                        returnStdout: true
+                    ).trim()
+                    
+                    if (newStatus.contains('Up')) {
+                        echo "✅ Container redémarré avec succès"
+                    } else {
+                        echo "❌ Échec du redémarrage, vérification des logs..."
+                        sh "docker logs --tail 20 ${env.BACKUP_CONTAINER} 2>&1 || true"
+                        currentBuild.result = 'UNSTABLE'
                     }
                 }
+
+            } catch (Exception e) {
+                echo "❌ Erreur lors du backup: ${e.getMessage()}"
+                echo "📋 Logs d'urgence :"
+                sh "docker logs --tail 20 ${env.BACKUP_CONTAINER} 2>&1 || true"
+                currentBuild.result = 'UNSTABLE'
             }
         }
     }
+}
+       
+        // 7. Push optionnel de l'image dans le registre Nexus
+        // stage('Push to Private Registry') {
+        //     when {
+        //         anyOf {
+        //             branch 'dev'
+        //             branch 'master'
+        //         }
+        //     }
+        //     steps {
+        //         script {
+        //             docker.withRegistry("https://${DOCKER_REGISTRY}", 'nexus') {
+        //                 sh """
+        //                     docker pull ${IMAGE_NAME}:${IMAGE_TAG} || true
+        //                     docker tag ${IMAGE_NAME}:${IMAGE_TAG} ${DOCKER_REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG}
+        //                     docker push ${DOCKER_REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG}
+        //                 """
+        //             }
+        //         }
+        //     }
+        // }
+ }
 
     post {
         success {
@@ -135,7 +291,6 @@ pipeline {
 }
 
 
-
 // pipeline {
 //     agent any
 //     environment {
@@ -212,40 +367,40 @@ pipeline {
 //         //     }
 //         // }
     
-        // stage('Docker Registry Login, Pull, and Push') {
-        //     when {
-        //         branch 'dev'
-        //     }
-        //     steps {
-        //         script {
-        //             def registryUrl = 'nexus.foodgame.fr:8123'
-        //             def imageName = 'grafana/tns-db'
-        //             def imageVersion = 'latest'
+//         stage('Docker Registry Login, Pull, and Push') {
+//             when {
+//                 branch 'dev'
+//             }
+//             steps {
+//                 script {
+//                     def registryUrl = 'nexus.foodgame.fr:8123'
+//                     def imageName = 'grafana/tns-db'
+//                     def imageVersion = 'latest'
                     
-        //             docker.withRegistry("https://${registryUrl}", 'nexus') {
-        //                 try {
-        //                     Try pulling the image from the registry
-        //                     echo "Trying to pull image: ${registryUrl}/${imageName}:${imageVersion}"
-        //                     docker.image("${registryUrl}/${imageName}:${imageVersion}").pull()
-        //                 } catch (Exception e) {
-        //                     echo "Image pull failed. Attempting to build and push."
-
-        //                     Pull base image from Docker Hub
-        //                     echo "Pulling base image: ${imageName}:${imageVersion}"
-        //                     sh "docker pull ${imageName}:${imageVersion}"
+//                     docker.withRegistry("https://${registryUrl}", 'nexus') {
+//                         try {
+//                             Try pulling the image from the registry
+//                             echo "Trying to pull image: ${registryUrl}/${imageName}:${imageVersion}"
+//                             docker.image("${registryUrl}/${imageName}:${imageVersion}").pull()
+//                         } catch (Exception e) {
+//                             echo "Image pull failed. Attempting to build and push."
+
+//                             Pull base image from Docker Hub
+//                             echo "Pulling base image: ${imageName}:${imageVersion}"
+//                             sh "docker pull ${imageName}:${imageVersion}"
                             
-        //                     Tag the image for the private registry
-        //                     echo "Tagging image for private registry"
-        //                     sh "docker tag ${imageName}:${imageVersion} ${registryUrl}/${imageName}:${imageVersion}"
+//                             Tag the image for the private registry
+//                             echo "Tagging image for private registry"
+//                             sh "docker tag ${imageName}:${imageVersion} ${registryUrl}/${imageName}:${imageVersion}"
                             
-        //                     Push the tagged image to the private registry
-        //                     echo "Pushing image to private registry"
-        //                     sh "docker push ${registryUrl}/${imageName}:${imageVersion}"
-        //                 }
-        //             }
-        //         }
-        //     }
-        // }
+//                             Push the tagged image to the private registry
+//                             echo "Pushing image to private registry"
+//                             sh "docker push ${registryUrl}/${imageName}:${imageVersion}"
+//                         }
+//                     }
+//                 }
+//             }
+//         }
     
 
 

+ 19 - 1
angular-client/angular.json

@@ -114,7 +114,25 @@
               "/contact"
             ]
           }
-        }
+        },
+      "test": {
+        "builder": "@angular-devkit/build-angular:karma",
+        "options": {
+        "main": "src/test.ts",
+        "polyfills": "src/polyfills.ts",
+        "tsConfig": "tsconfig.spec.json",
+        "karmaConfig": "karma.conf.js",
+        "assets": [
+           "src/favicon.ico",
+           "src/assets"
+            ],
+        "styles": [
+           "src/styles.scss"
+          ],
+       "scripts": []
+  }
+}
+
       }
     }
   },

+ 69 - 8
angular-client/karma.conf.js

@@ -17,16 +17,77 @@ module.exports = function (config) {
     },
     coverageIstanbulReporter: {
       dir: require('path').join(__dirname, './coverage/fatboar-burger'),
-      reports: ['html', 'lcovonly', 'text-summary'],
-      fixWebpackSourcePaths: true
+      reports: ['html', 'lcovonly', 'text-summary', 'cobertura'], // Ajout du format cobertura pour Jenkins
+      fixWebpackSourcePaths: true,
+      // Seuils de couverture (optionnel - décommentez si vous voulez des seuils minimum)
+      // thresholds: {
+      //   global: {
+      //     statements: 80,
+      //     branches: 75,
+      //     functions: 80,
+      //     lines: 80
+      //   }
+      // }
     },
-    reporters: ['progress', 'kjhtml'],
+    reporters: ['progress', 'kjhtml', 'coverage-istanbul'], // Ajout du reporter coverage-istanbul
     port: 9876,
     colors: true,
     logLevel: config.LOG_INFO,
-    autoWatch: true,
-    browsers: ['Chrome'],
-    singleRun: false,
-    restartOnFileChange: true
+    autoWatch: false,
+    browsers: ['ChromeHeadless'],
+    singleRun: true,
+    restartOnFileChange: false,
+
+    // Ajouts pour éviter les déconnexions ChromeHeadless
+    captureTimeout: 60000,
+    browserDisconnectTimeout: 60000,
+    browserDisconnectTolerance: 3,
+    browserNoActivityTimeout: 60000,
+
+    // Custom launcher configurations ajoutées pour Jenkins CI
+    customLaunchers: {
+      ChromeHeadlessCustom: {
+        base: 'ChromeHeadless',
+        flags: [
+          '--no-sandbox',
+          '--disable-web-security',
+          '--disable-features=VizDisplayCompositor',
+          '--disable-gpu',
+          '--disable-canvas-aa',
+          '--disable-3d-apis',
+          '--disable-extensions',
+          '--disable-dev-shm-usage',
+          '--disable-translate',
+          '--disable-logging',
+          '--disable-background-timer-throttling',
+          '--disable-backgrounding-occluded-windows',
+          '--disable-renderer-backgrounding',
+          '--remote-debugging-port=9222'
+        ]
+      },
+      ChromeHeadlessCI: {
+        base: 'ChromeHeadless',
+        flags: [
+          '--no-sandbox',                    // CRUCIAL pour Docker/Jenkins
+          '--disable-web-security',
+          '--disable-features=VizDisplayCompositor',
+          '--disable-gpu',
+          '--disable-canvas-aa',
+          '--disable-3d-apis',
+          '--disable-extensions',
+          '--disable-dev-shm-usage',        // Important pour les environnements Docker
+          '--disable-translate',
+          '--disable-logging',
+          '--disable-background-timer-throttling',
+          '--disable-backgrounding-occluded-windows',
+          '--disable-renderer-backgrounding',
+          '--headless',
+          '--disable-ipc-flooding-protection',
+          '--disable-hang-monitor',
+          '--disable-prompt-on-repost',
+          '--disable-default-apps'
+        ]
+      }
+    }
   });
-};
+};

+ 3 - 1
angular-client/package.json

@@ -6,6 +6,7 @@
     "start": "ng serve --host 0.0.0.0",
     "build": "ng build --prod",
     "test": "ng test",
+    "test-headless": "ng test --no-watch --no-progress --browsers=ChromeHeadless",
     "lint": "ng lint",
     "e2e": "ng e2e",
     "test-ci": "ng test --karma-config=karma-ci.conf.js --no-progress",
@@ -40,6 +41,7 @@
     "karma-phantomjs-launcher": "^1.0.4",
     "leaflet": "^1.6.0",
     "ng2-charts": "^2.3.0",
+    "preboot": "^8.0.0",
     "rxjs": "~6.5.4",
     "saturn-datepicker": "^8.0.5",
     "tslib": "^1.10.0",
@@ -78,4 +80,4 @@
     "tslint": "~6.1.0",
     "typescript": "3.8.3"
   }
-}
+}

+ 1 - 10
angular-client/src/app/app.component.spec.ts

@@ -13,13 +13,4 @@ describe('AppComponent', () => {
     const app = fixture.componentInstance;
     expect(app).toBeTruthy();
   });
-
-  it(`should have as title 'fatboar'`, () => {
-    const fixture = TestBed.createComponent(AppComponent);
-    const app = fixture.componentInstance;
-    expect(app.title).toEqual('fatboar');
-  });
-
-  
-  
-});
+});

+ 45 - 0
angular-client/src/test-material.module.ts

@@ -0,0 +1,45 @@
+import { NgModule } from '@angular/core';
+import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
+
+// Import des modules Angular Material nécessaires
+import { MatFormFieldModule } from '@angular/material/form-field';
+import { MatInputModule } from '@angular/material/input';
+import { MatButtonModule } from '@angular/material/button';
+import { MatIconModule } from '@angular/material/icon';
+import { MatToolbarModule } from '@angular/material/toolbar';
+import { MatCardModule } from '@angular/material/card';
+import { MatSnackBarModule } from '@angular/material/snack-bar';
+import { MatDialogModule } from '@angular/material/dialog';
+import { MatMenuModule } from '@angular/material/menu';
+import { MatTableModule } from '@angular/material/table';
+import { MatPaginatorModule } from '@angular/material/paginator';
+import { MatSortModule } from '@angular/material/sort';
+import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
+import { MatCheckboxModule } from '@angular/material/checkbox';
+import { MatSelectModule } from '@angular/material/select';
+import { MatSidenavModule } from '@angular/material/sidenav';
+import { MatListModule } from '@angular/material/list';
+
+@NgModule({
+  exports: [
+    BrowserAnimationsModule,
+    MatFormFieldModule,
+    MatInputModule,
+    MatButtonModule,
+    MatIconModule,
+    MatToolbarModule,
+    MatCardModule,
+    MatSnackBarModule,
+    MatDialogModule,
+    MatMenuModule,
+    MatTableModule,
+    MatPaginatorModule,
+    MatSortModule,
+    MatProgressSpinnerModule,
+    MatCheckboxModule,
+    MatSelectModule,
+    MatSidenavModule,
+    MatListModule
+  ]
+})
+export class TestMaterialModule {}

+ 29 - 0
angular-client/src/test-setup.ts

@@ -0,0 +1,29 @@
+import { TestBed } from '@angular/core/testing';
+import { RouterTestingModule } from '@angular/router/testing';
+import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
+import { TestMaterialModule } from './test-material.module';
+
+const originalConfigureTestingModule = TestBed.configureTestingModule;
+
+TestBed.configureTestingModule = function (moduleDef: any) {
+  moduleDef = moduleDef || {};
+  moduleDef.imports = moduleDef.imports || [];
+  moduleDef.schemas = moduleDef.schemas || [];
+
+  // Ajout global de RouterTestingModule
+  if (!moduleDef.imports.includes(RouterTestingModule)) {
+    moduleDef.imports.push(RouterTestingModule);
+  }
+
+  // Ajout global de TestMaterialModule
+  if (!moduleDef.imports.includes(TestMaterialModule)) {
+    moduleDef.imports.push(TestMaterialModule);
+  }
+
+  // Ajout global de CUSTOM_ELEMENTS_SCHEMA
+  if (!moduleDef.schemas.includes(CUSTOM_ELEMENTS_SCHEMA)) {
+    moduleDef.schemas.push(CUSTOM_ELEMENTS_SCHEMA);
+  }
+
+  return originalConfigureTestingModule.call(this, moduleDef);
+} as any;

+ 5 - 6
angular-client/src/test.ts

@@ -1,6 +1,5 @@
-// This file is required by karma.conf.js and loads recursively all the .spec and framework files
-
 import 'zone.js/dist/zone-testing';
+import './test-setup'; // Patch global
 import { getTestBed } from '@angular/core/testing';
 import {
   BrowserDynamicTestingModule,
@@ -14,12 +13,12 @@ declare const require: {
   };
 };
 
-// First, initialize the Angular testing environment.
+// Initialisation Angular
 getTestBed().initTestEnvironment(
   BrowserDynamicTestingModule,
   platformBrowserDynamicTesting()
 );
-// Then we find all the tests.
+
+// Charger tous les tests
 const context = require.context('./', true, /\.spec\.ts$/);
-// And load the modules.
-context.keys().map(context);
+context.keys().map(context);