Merge branch 'unit_test'

# Conflicts:
#	.drone.yml
#	.gitignore
#	Source/Config/Autoload.php
#	Source/Config/Clean.php
#	Source/Config/config.php
#	Source/Controller/ControllerAdmin.php
#	Source/Model/ModelAdmin.php
master
dorian.hodin 2 years ago
commit 9617eb8074

@ -2,22 +2,50 @@ kind: pipeline
type: docker
name: Témoignages_Formulaire
trigger:
event:
- push
steps:
- name: coverage-image
image: plugins/docker
settings:
dockerfile: ./Source/Tests/Dockerfile
context: Source/Tests
registry: hub.codefirst.iut.uca.fr
repo: hub.codefirst.iut.uca.fr/dorian.hodin/sae4.01_formulaire
username:
from_secret: SECRET_USERNAME
password:
from_secret: SECRET_PASSWD
#conteneur deployment
- name: deploy-coverage
image: hub.codefirst.iut.uca.fr/thomas.bellembois/codefirst-dockerproxy-clientdrone:latest
environment:
IMAGENAME: hub.codefirst.iut.uca.fr/dorian.hodin/sae4.01_formulaire:latest
CONTAINERNAME: coverage-image
COMMAND: create
OVERWRITE: true
ADMINS: dorianhodin,alexislamande,baptistebaverel,johanlachenal
depends_on: [ coverage-image ]
- name: setup_PHP_for_SonarQube
image: sonarsource/sonar-scanner-cli
environment:
SONAR_TOKEN:
from_secret: SONARQ_TOKEN
commands:
- sonar-scanner -Dsonar.projectKey=SAE4.01_FORMULAIRE -Dsonar.sources=. -Dsonar.inclusions=**/*.php -Dsonar.login=$${SONAR_TOKEN} -Dsonar.language=php -Dsonar.host.url=https://codefirst.iut.uca.fr/sonar -Dsonar.php.coverage.reportPaths=coverage.xml
- curl https://codefirst.iut.uca.fr/containers/Temoignages-coverage-image/coverage.xml -o /drone/src/coverage.xml
- sonar-scanner -Dsonar.projectKey=SAE4.01_FORMULAIRE
-Dsonar.sources=.
-Dsonar.inclusions=**/*.php
-Dsonar.login=$${SONAR_TOKEN}
-Dsonar.language=php
-Dsonar.host.url=https://codefirst.iut.uca.fr/sonar
-Dsonar.php.coverage.reportPaths=coverage.xml
depends_on: [ deploy-coverage ]

1
.gitignore vendored

@ -1 +1,2 @@
/Source/.phpunit.cache/
/Source/Config/vendor/

@ -2,6 +2,8 @@
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/Source" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/Source/Tests" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/.idea/dataSources" />
</content>
<orderEntry type="inheritedJdk" />

1
Source/.gitignore vendored

@ -1 +0,0 @@
/Config/vendor/

@ -3,7 +3,7 @@
namespace BusinessClass;
/**
* Définit une question avec plusieurs réponse mais une seule possible.
* Définit une question avec plusieurs réponses, mais une seule possible.
*/
class ListBoxQuestion extends BoxQuestion
{

@ -13,7 +13,9 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
namespace Config;
use Exception;
use RuntimeException;
use Traversable;
class AltoRouter
{
@ -21,22 +23,22 @@ class AltoRouter
/**
* @var array Array of all routes (incl. named routes).
*/
protected $routes = [];
protected array $routes = [];
/**
* @var array Array of all named routes.
*/
protected $namedRoutes = [];
protected array $namedRoutes = [];
/**
* @var string Can be used to ignore leading part of the Request URL (if main file lives in subdirectory of host)
*/
protected $basePath = '';
protected string $basePath = '';
/**
* @var array Array of default match types (regex helpers)
*/
protected $matchTypes = [
protected array $matchTypes = [
'i' => '[0-9]++',
'a' => '[0-9A-Za-z]++',
'h' => '[0-9A-Fa-f]++',
@ -53,19 +55,42 @@ class AltoRouter
* @param array $matchTypes
* @throws Exception
*/
public function __construct(array $routes = [], $basePath = '', array $matchTypes = [])
public function __construct(array $routes = [], string $basePath = '', array $matchTypes = [])
{
$this->addRoutes($routes);
$this->setBasePath($basePath);
$this->addMatchTypes($matchTypes);
}
/**
* @return string
*/
public function getBasePath(): string
{
return $this->basePath;
}
/**
* @return array
*/
public function getMatchTypes(): array
{
return $this->matchTypes;
}
/**
* @return array
*/
public function getNamedRoutes(): array
{
return $this->namedRoutes;
}
/**
* Retrieves all routes.
* Useful if you want to process or display routes.
* @return array All routes.
*/
public function getRoutes()
public function getRoutes(): array
{
return $this->routes;
}
@ -79,10 +104,10 @@ class AltoRouter
*
* @param array $routes
* @return void
* @author Koen Punt
* @throws Exception
*@author Koen Punt
*/
public function addRoutes($routes)
public function addRoutes(mixed $routes): void
{
if (!is_array($routes) && !$routes instanceof Traversable) {
throw new RuntimeException('Routes should be an array or an instance of Traversable');
@ -97,7 +122,7 @@ class AltoRouter
* Useful if you are running your application from a subdirectory.
* @param string $basePath
*/
public function setBasePath($basePath)
public function setBasePath(string $basePath): void
{
$this->basePath = $basePath;
}
@ -107,7 +132,7 @@ class AltoRouter
*
* @param array $matchTypes The key is the name and the value is the regex.
*/
public function addMatchTypes(array $matchTypes)
public function addMatchTypes(array $matchTypes): void
{
$this->matchTypes = array_merge($this->matchTypes, $matchTypes);
}
@ -118,22 +143,21 @@ class AltoRouter
* @param string $method One of 5 HTTP Methods, or a pipe-separated list of multiple HTTP Methods (GET|POST|PATCH|PUT|DELETE)
* @param string $route The route regex, custom regex must start with an @. You can use multiple pre-set regex filters, like [i:id]
* @param mixed $target The target where this route should point to. Can be anything.
* @param string $name Optional name of this route. Supply if you want to reverse route this url in your application.
* @param string|null $name Optional name of this route. Supply if you want to reverse route this url in your application.
* @throws Exception
*/
public function map($method, $route, $target, $name = null)
public function map(string $method, string $route, mixed $target, string $name = null): void
{
$this->routes[] = [$method, $route, $target, $name];
if ($name) {
if (isset($this->namedRoutes[$name])) {
throw new RuntimeException("Can not redeclare route '{$name}'");
throw new RuntimeException("Can not redeclare route ".$name);
}
$this->namedRoutes[$name] = $route;
}
return;
}
/**
@ -142,16 +166,16 @@ class AltoRouter
* Generate the URL for a named route. Replace regexes with supplied parameters
*
* @param string $routeName The name of the route.
* @param array @params Associative array of parameters to replace placeholders with.
* @param array $params @params Associative array of parameters to replace placeholders with.
* @return string The URL of the route with named parameters in place.
* @throws Exception
*/
public function generate($routeName, array $params = [])
public function generate(string $routeName, array $params = []): string
{
// Check if named route exists
if (!isset($this->namedRoutes[$routeName])) {
throw new RuntimeException("Route '{$routeName}' does not exist.");
throw new RuntimeException("Route ".$routeName." does not exist.");
}
// Replace named parameters
@ -186,18 +210,18 @@ class AltoRouter
/**
* Match a given Request Url against stored routes
* @param string $requestUrl
* @param string $requestMethod
* @param string|null $requestUrl
* @param string|null $requestMethod
* @return array|boolean Array with route information on success, false on failure (no match).
*/
public function match($requestUrl = null, $requestMethod = null)
public function match(string $requestUrl = null, string $requestMethod = null): bool|array
{
$params = [];
// set Request Url if it isn't passed as parameter
if ($requestUrl === null) {
$requestUrl = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '/';
$requestUrl = $_SERVER['REQUEST_URI'] ?? '/';
}
// strip base path from request url
@ -212,7 +236,7 @@ class AltoRouter
// set Request Method if it isn't passed as a parameter
if ($requestMethod === null) {
$requestMethod = isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : 'GET';
$requestMethod = $_SERVER['REQUEST_METHOD'] ?? 'GET';
}
foreach ($this->routes as $handler) {
@ -271,7 +295,7 @@ class AltoRouter
* @param $route
* @return string
*/
protected function compileRoute($route)
protected function compileRoute($route): string
{
if (preg_match_all('`(/|\.|)\[([^:\]]*+)(?::([^:\]]*+))?\](\?|)`', $route, $matches, PREG_SET_ORDER)) {
$matchTypes = $this->matchTypes;

@ -1,14 +1,24 @@
<?php
namespace Config;
use RuntimeException;
require_once(__DIR__.'/vendor/autoload.php');
class Autoload
{
private static $instance = null;
private static mixed $instance = null;
/**
* @return mixed
*/
public static function getInstance(): mixed
{
return self::$instance;
}
public static function charger()
public static function charger(): void
{
if (null !== self::$instance) {
throw new RuntimeException(sprintf('%s is already started', __CLASS__));
@ -22,7 +32,7 @@ class Autoload
}
}
public static function shutDown()
public static function shutDown(): void
{
if (null !== self::$instance) {
@ -34,7 +44,7 @@ class Autoload
}
}
private static function autoloader($className)
private static function autoloader($className): void
{
$folder = "./";
$className = ltrim($className, '\\');

@ -6,7 +6,7 @@ class Clean
{
/**
* Cette fonction prend une chaîne de caractères en entrée et retourne une version nettoyée de cette chaîne.
* Elle supprime les espaces de début et de fin, ainsi que toutes les balises HTML, et encode les
* Elle supprime les espaces de début et de fin, ainsi que toutes les balises HTML et encode les
* caractères spéciaux.
*
* @param string $string La chaîne à nettoyer
@ -15,9 +15,9 @@ class Clean
public static function simpleString(string $string): string
{
$string = trim($string);
$string = strip_tags($string);
return htmlspecialchars($string);
$string = trim($string);
$string = strip_tags($string);
return htmlspecialchars($string);
}
/**
@ -41,7 +41,7 @@ class Clean
/**
* Cette fonction prend une chaîne de caractères en entrée et retourne une version nettoyée de cette chaîne.
* Elle supprime les espaces de début et de fin, ainsi que toutes les balises HTML, et encode les
* Elle supprime les espaces de début et de fin, ainsi que toutes les balises HTML et encode les
* caractères spéciaux.
* @param $email
* @return string La chaîne nettoyée
@ -49,8 +49,13 @@ class Clean
public static function email($email): string
{
$email = self::simpleString($email);
// Vérifier si l'adresse e-mail est valide
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
// Supprimer les caractères incorrects
$email = preg_replace('/[^a-zA-Z0-9.@]/', '', $email);
}
return filter_var($email, FILTER_SANITIZE_EMAIL);
}
/**
@ -60,8 +65,13 @@ class Clean
* @return int Le nombre entier formaté
*/
public static function int(int $int): int
public static function int(mixed $int): int
{
if (!is_int($int)){
$cleaned = preg_replace('/[^0-9]/', '', $int);
return filter_var(intval($cleaned), FILTER_SANITIZE_NUMBER_INT);
}
return filter_var($int, FILTER_SANITIZE_NUMBER_INT);
}

@ -1,54 +0,0 @@
<?php
namespace Config;
use PDO;
use PDOStatement;
/**
* Définit une connection à la base de données.
*/
class Connection extends PDO
{
/**
* @var PDOStatement
*/
private PDOStatement $stmt;
public function __construct(string $dsn, string $username, string $password)
{
parent::__construct($dsn, $username, $password);
$this->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
/**
* Éxécute une réquête SQL.
*
* @param string $query
* @param array $parameters
* @return bool Returns `true` on success, `false` otherwise
*/
public function executeQuery(string $query, array $parameters = []): bool
{
$this->stmt = parent::prepare($query);
foreach ($parameters as $name => $value) {
$this->stmt->bindValue($name, $value[0], $value[1]);
}
return $this->stmt->execute();
}
/**
* Permet de récupère le résultat de la dernière réquête éxecuté avec
* la fonction executeQuery().
*
* @return array
*/
public function getResults(): array
{
return $this->stmt->fetchAll();
}
}

@ -1,5 +1,7 @@
<?php
namespace Config;
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
@ -67,7 +69,7 @@ class SplClassLoader
}
/**
* Gets the namespace seperator used by classes in the namespace of this class loader.
* Gets the namespace separator used by classes in the namespace of this class loader.
*
* @return string
*/

@ -2,6 +2,8 @@
namespace Config;
require_once "config.php";
class Validate
{
/**
@ -14,22 +16,25 @@ class Validate
public static function email(String $email): bool
{
global $emailMaxLength;
return (filter_var($email, FILTER_VALIDATE_EMAIL) && strlen($email) <= $emailMaxLength);
// Vérifier si l'adresse e-mail est valide
return filter_var($email, FILTER_VALIDATE_EMAIL) && strlen($email) <= $GLOBALS['emailMaxLength'];
}
/**
* Valide un pseudo en vérifiant que la longueur est suffisante, qu'il contient uniquement des
* caractères alphanumériques, et qu'il respecte la longueur maximale définie globalement.
*
* @param string $pseudo Le pseudo à valider.
* @param string $login
* @return bool Vrai si le pseudo est valide, faux sinon.
*/
public static function login(string $login) : bool
{
global $loginMaxLength;
return (strlen($login) >= 3 && preg_match("#[a-zA-Z0-9]+#", $login) && strlen($login) <= $loginMaxLength);
// Vérifier si la longueur est supérieure à 3.
if (strlen($login) <= 3 || !ctype_alnum($login) || strlen($login) >= $GLOBALS['pseudoMaxLength']) {
return false;
}
return true;
}
/**
@ -42,61 +47,56 @@ class Validate
public static function password(string $password) : bool
{
global $passwordMaxLength;
return (strlen($password) >= 8 && strlen($password) <=$passwordMaxLength &&
return (strlen($password) >= 8 && strlen($password) <=$GLOBALS['passwordMaxLength'] &&
preg_match("/\d/", $password) && preg_match("#[a-zA-Z]+#", $password));
}
/**
* Vérifie si le mot-clé est valide.
*
* @param string $keyword Le mot-clé a vérifié.
* @param string $keyword Le mot-clé à vérifier
* @return bool Vrai si le mot-clé est valide, faux sinon.
*/
public static function keyWord(string $keyword) : bool
{
global $keyWordMaxLength;
return (strlen($keyword) <= $keyWordMaxLength && strlen($keyword) >= 3);
return (strlen($keyword) <= $GLOBALS['keyWordMaxLength'] && strlen($keyword) >= 3);
}
/**
* Vérifie si le titre est valide.
*
* @param string $title Le titre a vérifié.
* @param string $title Le titre à vérifier
* @return bool Vrai si le titre est valide, faux sinon.
*/
public static function title(string $title) : bool
{
global $titleMaxLength;
return (strlen($title) <= $titleMaxLength && strlen($title) >= 3);
return (strlen($title) <= $GLOBALS['titleMaxLength'] && strlen($title) >= 3);
}
/**
* Vérifie si le type est valide.
*
* @param string $type Le type a vérifié.
* @param string $type Le type à vérifier
* @return bool Vrai si le type est valide, faux sinon.
*/
public static function type(string $type) : bool
{
global $typeMaxLength;
return (strlen($type) <= $typeMaxLength && strlen($type) >=3);
return (strlen($type) <= $GLOBALS['typeMaxLength'] && strlen($type) >=3);
}
/**
* Vérifie si la réponse est valide.
*
* @param string $response La réponse a vérifié.
* @param string $response La réponse à vérifier
* @return bool Vrai si la réponse est valide, faux sinon.
*/
public static function response(string $response) : bool
{
global $responseMaxLength;
return (strlen($response) <= $responseMaxLength);
return (strlen($response) <= $GLOBALS['responseMaxLength'] && !empty($response));
}
/**
@ -108,8 +108,10 @@ class Validate
public static function username(string $username): bool
{
global $usernameMaxLength;
return (strlen($username) >= 3 && preg_match("#[a-zA-Z0-9]+#", $username) && strlen($username) <= $usernameMaxLength);
if (strlen($username) <= 3 || !ctype_alnum($username) || strlen($username) >= $GLOBALS['usernameMaxLength']) {
return false;
}
return true;
}
/**

@ -3,6 +3,20 @@
"description": "composer for guzzle client",
"require": {
"guzzlehttp/psr7": "^2.4",
"guzzlehttp/guzzle": "^7.5"
"guzzlehttp/guzzle": "^7.5",
"composer/xdebug-handler": "^3.0"
},
"autoload": {
"classmap": [
"../../Source/"
],
"psr-4": {
"BusinessClass\\" : "../BusinessClass",
"Config\\" : "../Config"
}
},
"require-dev": {
"phpunit/phpunit": "^10.0"
}
}

File diff suppressed because it is too large Load Diff

@ -1,8 +1,5 @@
<?php
use Config\Connection;
$rep = __DIR__ . '/../';
$views['form'] = 'Views/HTML/form.php';
@ -27,28 +24,12 @@ $poppins = "https://fonts.googleapis.com/css2?family=Poppins:wght@300&display=sw
$icon = "https://cdn.uca.fr/images/favicon/favicon.ico";
$logoUCA = "https://cdn.uca.fr/images/logos/logo_uca_mini_light.png";
function connect() //temoignage formulaire
{
$dsn = "mysql:host=localhost;dbname=temoignage;charset=utf8";
$login = "root";
try {
$connection = new Connection($dsn, $login, "root");
} catch (PDOException $e) {
http_response_code(404);
return http_response_code();
}
return $connection;
}
$emailMaxLength=150;
$pseudoMaxLength=50;
$passwordMaxLength=500;
$keyWordMaxLength=50;
$titleMaxLength=50;
$typeMaxLength=50;
$responseMaxLength=200;
$categoryMaxLenght=150;
$answerMaxLength=1500;
$GLOBALS['emailMaxLength']=150;
$GLOBALS['pseudoMaxLength']=50;
$GLOBALS['passwordMaxLength']=500;
$GLOBALS['keyWordMaxLength']=50;
$GLOBALS['titleMaxLength']=50;
$GLOBALS['typeMaxLength']=50;
$GLOBALS['responseMaxLength']=200;
$GLOBALS['usernameMaxLength']=50;

@ -2,6 +2,8 @@
namespace Controller;
use Exception;
use InvalidArgumentException;
use Model\ModelAdmin;
use Config\Clean;
use Config\Validate;
@ -18,10 +20,14 @@ class ControllerAdmin
* d'ajouter des réponses prédéfinies à la question.
*
* @return void
* @throws Exception
*/
public function addQuestion(): void
{
$type = Clean::simpleString($_POST['type']);
if (empty($_POST['type'])) {
throw new InvalidArgumentException('$_POST[\'type\'] is empty');
}
$idQuestion = (new ModelAdmin())->addQuestion();
if (strcmp($type, "BusinessClass\TextQuestion") == 0) {
$this->goToQuestions();
@ -45,13 +51,17 @@ class ControllerAdmin
}
/**
* Ajoute une possibilité de réponse à une question, on assige également cette réponse
* à des catégories. On propose ensuite à l'utilisateur de continuer l'ajout d'autre réponses.
* Ajoute une possibilité de réponse à une question, on assigne également cette réponse
* à des catégories. On propose ensuite à l'utilisateur de continuer l'ajout d'autres réponses.
*
* @return void
* @throws Exception
*/
public function addResponse(): void
{
if (empty($_POST['idQuestion'] || empty($_POST['question']) || empty($_POST['type']))) {
throw new InvalidArgumentException('$_POST parameters is missing');
}
(new ModelAdmin())->addResponse();
$categories = (new ModelAdmin())->getCategories();
$idQuestion = Clean::int($_POST['idQuestion']);
@ -75,13 +85,17 @@ class ControllerAdmin
/**
* Permet de proposer à l'utiliser de continuer ou non à ajouter des possibilités de réponses à l'aide
* de la fonction addResponse(). Si non, il retourne à la page d'admnistration.
* de la fonction addResponse(). Sinon, il retourne à la page administration.
*
* @return void
* @throws Exception
*/
public function continueResponse(): void
{
$choose = Clean::simpleString($_POST['choose']);
if (empty($_POST['choose'] || empty($_POST['idQuestion']) || empty($_POST['type']) || empty($_POST['question']))) {
throw new InvalidArgumentException('$_POST parameters is missing');
}
if ($choose == "Oui") {
$idQuestion = Clean::int($_POST['idQuestion']);
$categories = (new ModelAdmin())->getCategories();
@ -99,6 +113,7 @@ class ControllerAdmin
* Permet de créer un nouveau formulaire avec un titre et une description.
*
* @return void
* @throws Exception
*/
public function createForm(): void
{
@ -107,9 +122,10 @@ class ControllerAdmin
/**
* Permet d'ajouter une catégories (mot-clef) à notre application
* Permet d'ajouter une catégorie (mot-clef) à notre application
*
* @return void
* @throws Exception
*/
public function addKeyword(): void
{
@ -132,6 +148,7 @@ class ControllerAdmin
* Permet de naviguer jusqu'à la page de gestion des catégories
*
* @return void
* @throws Exception
*/
public function goToCategories(): void
{
@ -145,6 +162,7 @@ class ControllerAdmin
* Permet de naviguer jusqu'à la page de gestion des questions
*
* @return void
* @throws Exception
*/
public function goToQuestions(): void
{
@ -157,6 +175,7 @@ class ControllerAdmin
* Permet de naviguer jusqu'à la page de gestion des réponses
*
* @return void
* @throws Exception
*/
public function goToResponses(): void
{

@ -15,6 +15,7 @@ class ControllerCandidate
* Permet de naviguer jusqu'au formulaire.
*
* @return void
* @throws Exception
*/
public function goToForm(): void
{
@ -34,6 +35,7 @@ class ControllerCandidate
* Permet de finaliser la saisie du formulaire et de le soumettre.
*
* @return void
* @throws Exception
*/
public function submitForm(): void
{

@ -3,8 +3,6 @@
namespace Controller;
use Exception;
use PDOException;
use Config\Validate;
use Config\Clean;
use Config\AltoRouter;
@ -13,9 +11,12 @@ use Config\AltoRouter;
*/
class FrontController {
private $router;
private $rights;
private AltoRouter $router;
private array $rights;
/**
* @throws Exception
*/
public function __construct() {
$this->router = new AltoRouter();
$this->router->setBasePath($_SERVER['BASE_URI']);
@ -26,7 +27,23 @@ class FrontController {
);
}
public function run() {
/**
* @return array
*/
public function getRights(): array
{
return $this->rights;
}
/**
* @return AltoRouter
*/
public function getRouter(): AltoRouter
{
return $this->router;
}
public function run(): void
{
global $error,$rep,$views;
$exists=false;
$match = $this->router->match();
@ -58,7 +75,11 @@ class FrontController {
}
}
private function mapRoutes() {
/**
* @throws Exception
*/
protected function mapRoutes(): void
{
global $controller;
$this->router->map('GET', '/', array($controller['Candidate'], 'goToForm'), 'goToForm');
$this->router->map('POST', '/submitForm', array($controller['Candidate'], 'submitForm'), 'submitForm');
@ -79,4 +100,4 @@ class FrontController {
$this->router->map('POST','/deleteKeyword',array($controller['Admin'],'deleteKeyword'),'deleteKeyword');
$this->router->map('POST','/deleteResponsesCandidate',array($controller['Admin'],'deleteResponsesCandidate'),'deleteResponsesCandidate');
}
}
}

@ -6,7 +6,7 @@ use Exception;
class InvalidUsernameOrPasswordException extends Exception
{
public function __construct($message = "nom d'utilisateur ou mot de passe invalide", $code = 0, Exception $previous = null)
public function __construct($message = "Nom d'utilisateur ou mot de passe invalide", $code = 0, Exception $previous = null)
{
parent::__construct($message, $code, $previous);
}

@ -5,7 +5,7 @@ namespace Model;
use BusinessClass\Question;
/**
* Décrit les fonctionnalités principale d'une frabique
* Décrit les fonctionnalités principales d'une fabrique
*/
abstract class Factory
{
@ -20,7 +20,7 @@ abstract class Factory
/**
* Permet de récupérer les objets créer par la fonction create().
* Permet de récupérer les objets créés par la fonction create().
*
* @param array $results
* @param string $type

@ -12,7 +12,7 @@ class FactoryQuestion extends Factory
{
/**
* Permet de créer une liste de question en fonction du retour d'une gateway
* passer en paramètre. On prend en compte les différents type de question.
* passer en paramètre. On prend en compte les différents types de question.
*
* @param array $results
*

@ -19,7 +19,7 @@ class ModelAdmin
private Client $client;
public function __construct(){
$this->client = new Client();
$this->client = new Client(['headers' => ['Content-Type' => 'application/json'], 'verify' => false]);
}
public function goToAdmin(): void
@ -47,6 +47,9 @@ class ModelAdmin
if (validate::type($type)) {
$question = new $type(0, $questionContent);
$res = $this->client->request('GET', 'https://codefirst.iut.uca.fr/containers/Temoignages-deploy_api_form/getForm');
if ($res->getStatusCode()!=200){
throw new Exception('GetForm failed');
}
$form = json_decode($res->getBody());
if (!empty($form)) {
$res = $this->client->request(
@ -56,6 +59,9 @@ class ModelAdmin
classQuestion='.get_class($question).'&
idForm='.$form[0]['id']
);
if ($res->getStatusCode()!=200){
throw new Exception('AddQuestion failed');
}
return json_decode($res->getBody());
}
} else {
@ -111,11 +117,14 @@ class ModelAdmin
if(!validate::categories($categories)){
throw new Exception('Categories invalides');
}
$this->client->request('POST', 'https://codefirst.iut.uca.fr/containers/Temoignages-deploy_api_form/insertResponseInQuestion?
$res = $this->client->request('POST', 'https://codefirst.iut.uca.fr/containers/Temoignages-deploy_api_form/insertResponseInQuestion?
response='.$response.'&
categories='.$categories.'&
$idQuestion='.$idQuestion
);
if ($res->getStatusCode()!=200){
throw new Exception('InsertResponseInQuestion failed');
}
}catch (GuzzleException $g){
throw new Exception($g->getMessage(),$g->getCode(),$g);
}
@ -151,13 +160,19 @@ class ModelAdmin
{
try {
$res = $this->client->request('GET', 'https://codefirst.iut.uca.fr/containers/Temoignages-deploy_api_form/getForm');
if ($res->getStatusCode()!=200){
throw new Exception('GetForm failed');
}
$formulaire = json_decode($res->getBody());
if (empty($formulaire)) {
$form = new Form(0, "Votre avis nous intéresse !!!", "Description de notre formulaire", array());
$this->client->request('POST', 'https://codefirst.iut.uca.fr/containers/Temoignages-deploy_api_form/insertForm?
$res = $this->client->request('POST', 'https://codefirst.iut.uca.fr/containers/Temoignages-deploy_api_form/insertForm?
title='.$form->getTitle().'&
description='.$form->getDescription()
);
if ($res->getStatusCode()!=200){
throw new Exception('InsertForm failed');
}
}
}catch (GuzzleException $g){
throw new Exception($g->getMessage(),$g->getCode(),$g);
@ -178,9 +193,12 @@ class ModelAdmin
if(!validate::keyword($keyword)){
throw new Exception('Mot-clef invalide');
}
$this->client->request('POST', 'https://codefirst.iut.uca.fr/containers/Temoignages-deploy_api_form/insertKeyword?
$res = $this->client->request('POST', 'https://codefirst.iut.uca.fr/containers/Temoignages-deploy_api_form/insertKeyword?
keyword='.$keyword
);
if ($res->getStatusCode()!=200){
throw new Exception('InsertKeyword failed');
}
}catch (GuzzleException $g){
throw new Exception($g->getMessage(),$g->getCode(),$g);
}
@ -217,6 +235,9 @@ class ModelAdmin
$categories = [];
try {
$res = $this->client->request('GET', 'https://codefirst.iut.uca.fr/containers/Temoignages-deploy_api_form/getAllKeyword');
if ($res->getStatusCode()!=200){
throw new Exception('GetAllKeyword failed');
}
$res = json_decode($res->getBody());
foreach ($res as $category) {
$categories[] = $category["word"];
@ -239,12 +260,21 @@ class ModelAdmin
{
try {
$res = $this->client->request('GET', 'https://codefirst.iut.uca.fr/containers/Temoignages-deploy_api_form/existsForm');
if ($res->getStatusCode()!=200){
throw new Exception('Exists failed');
}
if (json_decode($res->getBody())){
$res = $this->client->request('GET', 'https://codefirst.iut.uca.fr/containers/Temoignages-deploy_api_form/getForm');
if ($res->getStatusCode()!=200){
throw new Exception('GetForm failed');
}
$idForm = json_decode($res->getBody())[0]["id"];
$res = $this->client->request('GET', 'https://codefirst.iut.uca.fr/containers/Temoignages-deploy_api_form/getAllQuestions?
idForm='.$idForm
);
if ($res->getStatusCode()!=200){
throw new Exception('GetAllQuestions failed');
}
$questionsArray = json_decode($res->getBody());
return Factory::getBuiltObjects($questionsArray, "Question");
}else{
@ -266,12 +296,18 @@ class ModelAdmin
{
try {
$res = $this->client->request('GET', 'https://codefirst.iut.uca.fr/containers/Temoignages-deploy_api_form/getAllListResponsesOfCandidate');
if ($res->getStatusCode()!=200){
throw new Exception('GetAllListResponsesOfCandidate failed');
}
$responsesCandidate = json_decode($res->getBody());
$results = [];
foreach ($responsesCandidate as $response) {
$res = $this->client->request('GET', 'https://codefirst.iut.uca.fr/containers/Temoignages-deploy_api_form/getDetailsListResponsesOfCandidate?
id='.$response["id"]
);
if ($res->getStatusCode()!=200){
throw new Exception('GetDetailsListResponsesOfCandidate failed');
}
$results[] = json_decode($res->getBody());
}
return $results;

@ -20,7 +20,7 @@ class ModelCandidate
private Client $client;
public function __construct(){
$this->client = new Client();
$this->client = new Client(['headers' => ['Content-Type' => 'application/json'], 'verify' => false]);
}
/**
@ -62,14 +62,20 @@ class ModelCandidate
throw new Exception("Les réponses ne sont pas valides");
}
$res = $this->client->request('GET', 'https://codefirst.iut.uca.fr/containers/Temoignages-deploy_api_form/getForm');
if ($res->getStatusCode()!=200){
throw new Exception('GetForm failed');
}
$form = json_decode($res->getBody());
$title = $form[0]["title"];
$this->client->request('POST', 'https://codefirst.iut.uca.fr/containers/Temoignages-deploy_api_form/insertListResponsesOfCandidate?
$res =$this->client->request('POST', 'https://codefirst.iut.uca.fr/containers/Temoignages-deploy_api_form/insertListResponsesOfCandidate?
id='.implode(",",$questionsId).'&
answer='.implode(",",$answer).'&
category='.implode(",",$category).'&
titleForm='.$title
);
if ($res->getStatusCode()!=200){
throw new Exception('InsertListResponsesOfCandidate failed');
}
}catch (GuzzleException $g){
throw new Exception($g->getMessage(),$g->getCode(),$g);
}
@ -88,6 +94,9 @@ class ModelCandidate
{
try {
$res = $this->client->request('GET', 'https://codefirst.iut.uca.fr/containers/Temoignages-deploy_api_form/getForm');
if ($res->getStatusCode()!=200){
throw new Exception('GetForm failed');
}
$form = json_decode($res->getBody());
if (empty($form)) {
return "PAS DE FORMULAIRE\n";
@ -98,6 +107,9 @@ class ModelCandidate
$res = $this->client->request('GET', 'https://codefirst.iut.uca.fr/containers/Temoignages-deploy_api_form/getAllQuestions?
idForm='.$form[0]['id']
);
if ($res->getStatusCode()!=200){
throw new Exception('GetAllQuestion failed');
}
$questionsTab = json_decode($res->getBody());
}catch (GuzzleException $g){
throw new Exception($g->getMessage(),$g->getCode(),$g);
@ -160,6 +172,9 @@ class ModelCandidate
$res = $this->client->request('GET', 'https://codefirst.iut.uca.fr/containers/Temoignages-deploy_api_form/getPasswordWithLogin?
login='.$identifiant
);
if ($res->getStatusCode()!=200){
throw new Exception('GetPasswordWithLogin failed');
}
$passwordbdd = json_decode($res->getBody());
if ($passwordbdd == null) {
throw new InexistantLoginException();

@ -0,0 +1,2 @@
FROM php:8.1-apache
COPY ./coverage.xml /var/www/html

@ -0,0 +1,56 @@
<?php
namespace TestBusinessClass;
use BusinessClass\BoxQuestion;
use PHPUnit\Framework\TestCase;
class BoxQuestionTest extends TestCase
{
public function testConstructorWithFourArguments()
{
$args = [['response1', 'response2'], 'question', ['category1', 'category2'], 1];
$boxQuestion = new class(4, $args) extends BoxQuestion {
public function printStrategy(): string
{
return '';
}
};
$this->assertEquals($args[0], $boxQuestion->getPossibleResponses());
$this->assertEquals($args[2], $boxQuestion->getCategories());
}
public function testSetPossibleResponses()
{
$args = [1, 'question'];
$possibleResponses = ['response1', 'response2'];
$boxQuestion = new class(2, $args) extends BoxQuestion {
public function printStrategy(): string
{
return '';
}
};
$boxQuestion->setPossibleResponses($possibleResponses);
$this->assertEquals($possibleResponses, $boxQuestion->getPossibleResponses());
}
public function testSetCategories()
{
$args = [1, 'question'];
$categories = ['category1', 'category2'];
$boxQuestion = new class(2, $args) extends BoxQuestion {
public function printStrategy(): string
{
return '';
}
};
$boxQuestion->setCategories($categories);
$this->assertEquals($categories, $boxQuestion->getCategories());
}
}

@ -0,0 +1,66 @@
<?php
namespace TestBusinessClass;
use BusinessClass\Form;
use PHPUnit\Framework\TestCase;
class FormTest extends TestCase
{
public function testGetTitleReturnsCorrectValue()
{
$form = new Form(1, 'Titre du formulaire', 'Description du formulaire', []);
$this->assertEquals('Titre du formulaire', $form->getTitle());
}
public function testSetTitleSetsCorrectValue()
{
$form = new Form(1, 'Titre du formulaire', 'Description du formulaire', []);
$form->setTitle('Nouveau titre');
$this->assertEquals('Nouveau titre', $form->getTitle());
}
public function testGetDescriptionReturnsCorrectValue()
{
$form = new Form(1, 'Titre du formulaire', 'Description du formulaire', []);
$this->assertEquals('Description du formulaire', $form->getDescription());
}
public function testSetDescriptionSetsCorrectValue()
{
$form = new Form(1, 'Titre du formulaire', 'Description du formulaire', []);
$form->setDescription('Nouvelle description');
$this->assertEquals('Nouvelle description', $form->getDescription());
}
public function testGetQuestionsReturnsCorrectValue()
{
$questions = [
'Question 1',
'Question 2',
'Question 3'
];
$form = new Form(1, 'Titre du formulaire', 'Description du formulaire', $questions);
$this->assertEquals($questions, $form->getQuestions());
}
public function testSetQuestionsSetsCorrectValue()
{
$questions = [
'Question 1',
'Question 2',
'Question 3'
];
$form = new Form(1, 'Titre du formulaire', 'Description du formulaire', []);
$form->setQuestions($questions);
$this->assertEquals($questions, $form->getQuestions());
}
public function testGetIdReturnsCorrectValue()
{
$form = new Form(1, 'Titre du formulaire', 'Description du formulaire', []);
$this->assertEquals(1, $form->getId());
}
}

@ -0,0 +1,21 @@
<?php
namespace TestBusinessClass;
use BusinessClass\IPrintQuestionStrategy;
use PHPUnit\Framework\TestCase;
class IPrintQuestionStrategyTest extends TestCase
{
public function testPrintStrategy()
{
$strategy = new class implements IPrintQuestionStrategy {
public function printStrategy(): string
{
return '<div>Question</div>';
}
};
$this->assertEquals('<div>Question</div>', $strategy->printStrategy());
}
}

@ -0,0 +1,31 @@
<?php
namespace TestBusinessClass;
use BusinessClass\Keyword;
use PHPUnit\Framework\TestCase;
class KeywordTest extends TestCase
{
public function testConstructor()
{
$id = 1;
$word = 'example';
$keyword = new Keyword($id, $word);
$this->assertEquals($id, $keyword->getId());
$this->assertEquals($word, $keyword->getWord());
}
public function testSetWord()
{
$id = 1;
$word = 'example';
$newWord = 'new example';
$keyword = new Keyword($id, $word);
$keyword->setWord($newWord);
$this->assertEquals($newWord, $keyword->getWord());
}
}

@ -0,0 +1,39 @@
<?php
namespace TestBusinessClass;
use BusinessClass\Question;
use PHPUnit\Framework\TestCase;
class QuestionTest extends TestCase
{
public function testConstructor()
{
$id = 1;
$content = 'What is your name?';
$question = new class($id, $content) extends Question {
public function printStrategy(): string
{
return '';
}
};
$this->assertEquals($id, $question->getId());
$this->assertEquals($content, $question->getContent());
}
public function testSetContent()
{
$content = 'What is your age?';
$question = new class(1, 'question') extends Question {
public function printStrategy(): string
{
return '';
}
};
$question->setContent($content);
$this->assertEquals($content, $question->getContent());
}
}

@ -0,0 +1,49 @@
<?php
namespace TestBusinessClass;
use BusinessClass\Response;
use PHPUnit\Framework\TestCase;
class ResponseTest extends TestCase
{
private int $id = 1;
private string $date = "2022-03-18";
private string $titleForm = "My Form";
private array $questionsResponses = [
"Question 1" => "Response 1",
"Question 2" => "Response 2"
];
public function testGetters()
{
$response = new Response($this->id, $this->date, $this->titleForm, $this->questionsResponses);
$this->assertEquals($this->id, $response->getId());
$this->assertEquals($this->date, $response->getDate());
$this->assertEquals($this->titleForm, $response->getTitleForm());
$this->assertEquals($this->questionsResponses, $response->getQuestionsResponses());
}
public function testSetters()
{
$response = new Response($this->id, $this->date, $this->titleForm, $this->questionsResponses);
$newDate = "2023-03-18";
$response->setDate($newDate);
$this->assertEquals($newDate, $response->getDate());
$newTitleForm = "New Form";
$response->setTitleForm($newTitleForm);
$this->assertEquals($newTitleForm, $response->getTitleForm());
$newQuestionsResponses = [
"Question 1" => "New Response 1",
"Question 2" => "New Response 2"
];
$response->setQuestionsResponses($newQuestionsResponses);
$this->assertEquals($newQuestionsResponses, $response->getQuestionsResponses());
}
}

@ -0,0 +1,25 @@
<?php
namespace TestBusinessClass;
use BusinessClass\TextQuestion;
use PHPUnit\Framework\TestCase;
class TextQuestionTest extends TestCase
{
public function testPrintStrategy()
{
$content = 'What is your name?';
$id = 1;
$textQuestion = new TextQuestion($id, $content);
$expectedOutput = "<div class='tab'>
<h6>$content</h6>
<p>
<input data-id='$id' placeholder='...' oninput='this.className = '''' type='text' name='answers[]'>
</p>
</div>\n";
$this->assertEquals($expectedOutput, $textQuestion->printStrategy());
}
}

@ -0,0 +1,73 @@
<?php
namespace TestConfig;
use Config\AltoRouter;
use Exception;
use PHPUnit\Framework\TestCase;
use RuntimeException;
class AltoRouterTest extends TestCase
{
protected AltoRouter $router;
public function setUp(): void
{
$this->router = new AltoRouter();
}
/**
* @throws Exception
*/
public function testAddRoutesThrowsExceptionForInvalidInput()
{
$this->expectException(RuntimeException::class);
$this->router->addRoutes('invalid input');
}
public function testGetRoutesReturnsArrayOfRoutes()
{
$this->assertIsArray($this->router->getRoutes());
}
public function testSetBasePathSetsBasePath()
{
$this->router->setBasePath('/test');
$this->assertEquals('/test', $this->router->getBasePath());
}
public function testAddMatchTypesAddsMatchTypes()
{
$this->router->addMatchTypes(['test' => 'regex']);
$this->assertArrayHasKey('test', $this->router->getMatchTypes());
}
/**
* @throws Exception
*/
public function testMapAddsRouteToRoutesArray()
{
$this->router->map('GET', '/test', 'handler');
$this->assertEquals([['GET', '/test', 'handler', null]], $this->router->getRoutes());
}
/**
* @throws Exception
*/
public function testMapAddsNamedRouteToNamedRoutesArray()
{
$this->router->map('GET', '/test', 'handler', 'test');
$this->assertEquals('/test', $this->router->getNamedRoutes()['test']);
}
/**
* @throws Exception
*/
public function testMapThrowsExceptionForDuplicateNamedRoutes()
{
$this->expectException(RuntimeException::class);
$this->router->map('GET', '/test', 'handler', 'test');
$this->router->map('GET', '/test2', 'handler', 'test');
}
}

@ -0,0 +1,47 @@
<?php
namespace TestConfig;
use Config\Clean;
use PHPUnit\Framework\TestCase;
class CleanTest extends TestCase
{
public function testSimpleString()
{
// Test avec une chaîne de caractères qui contient des balises HTML et des espaces
$string = '<p> Test avec des espaces ! </p>';
$expected = 'Test avec des espaces !';
$this->assertEquals($expected, Clean::simpleString($string));
// Test avec une chaîne de caractères qui contient des caractères spéciaux
$string = 'Ceci est une chaîne & avec des "caractères" spéciaux !';
$expected = 'Ceci est une chaîne &amp; avec des &quot;caractères&quot; spéciaux !';
$this->assertEquals($expected, Clean::simpleString($string));
}
public function testEmail()
{
// Test avec une adresse email valide
$email = 'john.doe@example.com';
$expected = 'john.doe@example.com';
$this->assertEquals($expected, Clean::email($email));
// Test avec une adresse email invalide
$email = 'john.doe@<??|||""__##:;>example.com';
$this->assertEquals($expected, Clean::email($email));
}
public function testInt()
{
// Test avec un entier valide
$int = '1234';
$expected = 1234;
$this->assertEquals($expected, Clean::int($int));
// Test avec un entier invalide
$int = '1234abc';
$this->assertEquals($expected, Clean::int($int));
}
}

@ -0,0 +1,70 @@
<?php
namespace TestConfig;
use PHPUnit\Framework\TestCase;
use Config\Validate;
class ValidateTest extends TestCase
{
public function testEmail()
{
$this->assertTrue(Validate::email('john.doe@example.com'));
$this->assertFalse(Validate::email('john.doe@'));
$this->assertFalse(Validate::email('john.doe@example.commmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm'));
}
public function testLogin()
{
$this->assertTrue(Validate::login('john123'));
$this->assertFalse(Validate::login('joh'));
$this->assertFalse(Validate::login('joh!'));
$this->assertFalse(Validate::login('john123456789012345555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555'));
}
public function testPassword()
{
$this->assertTrue(Validate::password('Pa$$w0rd'));
$this->assertFalse(Validate::password('password'));
$this->assertFalse(Validate::password('12345678'));
$this->assertFalse(Validate::password('pa$$word'));
$this->assertFalse(Validate::password('P@$$worddddddddddddddddddddddddddddddddddddddddddd'));
}
public function testKeyWord()
{
$this->assertTrue(Validate::keyWord('keyword'));
$this->assertFalse(Validate::keyWord('ke'));
$this->assertFalse(Validate::keyWord('keyworddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd'));
}
public function testTitle()
{
$this->assertTrue(Validate::title('Title'));
$this->assertFalse(Validate::title('Ti'));
$this->assertFalse(Validate::title('titleddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd'));
}
public function testType()
{
$this->assertTrue(Validate::type('Type'));
$this->assertFalse(Validate::type('Ty'));
$this->assertFalse(Validate::type('typeddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd'));
}
public function testResponse()
{
$this->assertTrue(Validate::response('Response'));
$this->assertFalse(Validate::response(''));
$this->assertFalse(Validate::response('responseddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd'));
}
public function testUsername()
{
$this->assertTrue(Validate::username('john123'));
$this->assertFalse(Validate::username('jo'));
$this->assertFalse(Validate::username('joh!'));
$this->assertFalse(Validate::username('john1234567890123455555555555555555555555555555555555555555555555555555555555555555555555555555555555555555'));
}
}

@ -0,0 +1,18 @@
<?php
namespace TestException;
use Exception;
use Exceptions\InexistantLoginException;
use PHPUnit\Framework\TestCase;
class InexistantLoginExceptionTest extends TestCase
{
public function testConstructor()
{
$exception = new InexistantLoginException();
$this->assertInstanceOf(InexistantLoginException::class, $exception);
$this->assertInstanceOf(Exception::class, $exception);
$this->assertEquals("Identifiant inexistant", $exception->getMessage());
}
}

@ -0,0 +1,18 @@
<?php
namespace TestException;
use Exception;
use PHPUnit\Framework\TestCase;
use Exceptions\InvalidLoginOrPasswordException;
class InvalidLoginOrPasswordExceptionTest extends TestCase
{
public function testConstructor()
{
$exception = new InvalidLoginOrPasswordException();
$this->assertInstanceOf(InvalidLoginOrPasswordException::class, $exception);
$this->assertInstanceOf(Exception::class, $exception);
$this->assertEquals("Identifiant ou mot de passe invalide", $exception->getMessage());
}
}

@ -0,0 +1,20 @@
<?php
namespace TestException;
use Exception;
use Exceptions\InvalidUsernameOrPasswordException;
use PHPUnit\Framework\TestCase;
use Exceptions\InvalidLoginOrPasswordException;
class InvalidUsernameOrPasswordExceptionTest extends TestCase
{
public function testConstructor()
{
$exception = new InvalidUsernameOrPasswordException();
$this->assertInstanceOf(InvalidUsernameOrPasswordException::class, $exception);
$this->assertInstanceOf(Exception::class, $exception);
$this->assertEquals("Nom d'utilisateur ou mot de passe invalide", $exception->getMessage());
}
}

File diff suppressed because it is too large Load Diff

@ -1,9 +1,17 @@
<html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="Views/CSS/common.css">
<link rel="stylesheet" href="node_modules/bootstrap/dist/css/bootstrap.min.css">
<title>Error Page</title>
<body class="d-flex flex-column align-items-center">
<h1><?php echo $error ?></h1>
<h1>
<?php if (empty($error)) {
echo "Erreur";
}else{
echo $error;
}
?>
</h1>
</body>
</html>

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.0/phpunit.xsd"
bootstrap=".\Config\vendor\autoload.php"
cacheDirectory=".phpunit.cache"
executionOrder="depends,defects"
requireCoverageMetadata="true"
beStrictAboutCoverageMetadata="true"
beStrictAboutOutputDuringTests="true"
failOnRisky="true"
failOnWarning="true">
<testsuites>
<testsuite name="default">
<directory>.\Tests\</directory>
</testsuite>
</testsuites>
<coverage>
<include>
<directory suffix=".php">..\Source\</directory>
</include>
</coverage>
</phpunit>
Loading…
Cancel
Save