//center instance * 'circles' => array(// array of circles instances ) * ) * ); * ``` */ public static function getACL() { if (null === static::$kernel) { static::bootKernel(); } $em = static::getContainer()->get(EntityManagerInterface::class); $centers = $em->getRepository(\Chill\MainBundle\Entity\Center::class) ->findAll(); $circles = $em->getRepository(\Chill\MainBundle\Entity\Scope::class) ->findAll(); if (0 === \count($centers)) { throw new \RuntimeException('No center found. Did you forget to run `doctrine:fixtures:load` command before ?'); } if (0 === \count($circles)) { throw new \RuntimeException('No circle found. Did you forget to run `doctrine:fixtures:load` command before ?'); } return [[ 'center' => $centers[0], 'circles' => [ $circles, ], ]]; } /** * Create an instance of the report to test. * * @return ExportInterface|DirectExportInterface|iterable|iterable an instance of the export to test */ abstract public function getExport(); /** * Create possible combinaison of data (produced by the form). * * This data will be used to generate data providers using this data. * * @return array an array of data. Example : `array( array(), array('fields' => array(1,2,3), ...)` where an empty array and `array(1,2,3)` are possible values */ abstract public static function getFormData(): array; /** * get the possible modifiers which could apply in combination to this * export. * . * * @return list> of string[] an array which contains an array of possible modifiers. Example : `array( array('modifier_1', 'modifier_2'), array('modifier_1'), ...)` */ abstract public static function getModifiersCombination(): array; protected function getUser(): User { $em = static::getContainer()->get(EntityManagerInterface::class); if (null === $user = $em->createQueryBuilder()->select('u') ->from(User::class, 'u') ->setMaxResults(1) ->getQuery()->getOneOrNullResult()) { throw new \RuntimeException('User not found'); } return $user; } protected function getParameters(bool $filterStatsByCenter): ParameterBagInterface { return new ParameterBag(['chill_main' => ['acl' => ['filter_stats_by_center' => $filterStatsByCenter]]]); } /** * wrap the results of @see{self::getExports()}, which may be an iterable or an export into an iterble. */ private function getExports(): iterable { $exports = $this->getExport(); if (is_iterable($exports)) { return $exports; } return [$exports]; } /** * @dataProvider dataProviderFormDataToNormalize */ public function testDataNormalization(array $data, int $version, array $customAssert): void { $export = $this->getExport(); if (is_iterable($export)) { foreach ($export as $e) { $this->testOneDataNormalization($e, $data, $version, $customAssert); } } else { $this->testOneDataNormalization($export, $data, $version, $customAssert); } } /** * A list of data to normalize. * * @return iterable{array} */ public static function dataProviderFormDataToNormalize(): iterable { foreach (static::getFormData() as $data) { yield [$data, 1, []]; } } private function testOneDataNormalization(ExportInterface|DirectExportInterface $export, array $data, int $version, array $customAssert): void { $normalized = $export->normalizeFormData($data); $actual = $export->denormalizeFormData($normalized, $version); self::assertEqualsCanonicalizing(array_keys($data), array_keys($actual)); foreach ($data as $key => $value) { self::assertArrayHasKey($key, $actual); if (array_key_exists($key, $customAssert)) { call_user_func($customAssert[$key], $actual[$key], $value); } elseif (is_iterable($value)) { continue; } elseif (is_object($value) && method_exists($value, 'getId')) { self::assertEquals($value->getId(), $actual[$key]->getId()); } else { self::assertEquals($value, $actual[$key]); } } } /** * Test the formatters type are string. */ public function testGetAllowedFormattersType(): void { foreach ($this->getExports() as $export) { $formattersTypes = $export->getAllowedFormattersTypes(); $this->assertContainsOnly( 'string', $formattersTypes, true, 'Test that the method `getAllowedFormattersTypes` returns an array of string' ); } } /** * Test that the description is not empty. */ public function testGetDescription(): void { foreach ($this->getExports() as $export) { $this->assertIsString( $export->getDescription(), 'Assert that the `getDescription` method return a string' ); $this->assertNotEmpty( $export->getDescription(), 'Assert that the `getDescription` method does not return an empty ' .'string.' ); } } /** * Test that the query keys are strings. * * @dataProvider dataProviderGetQueryKeys */ public function testGetQueryKeys(array $data): void { foreach ($this->getExports() as $export) { $queryKeys = $export->getQueryKeys($data); $this->assertContainsOnly( 'string', $queryKeys, true, 'test that the query keys returned by `getQueryKeys` are only strings' ); $this->assertGreaterThanOrEqual( 1, \count($queryKeys), 'test that there are at least one query key returned' ); } } public static function dataProviderGetQueryKeys() { foreach (static::getFormData() as $data) { yield [$data]; } } /** * Test that. * * - the results have a correct form (are arrays or traversable) * - each key in a row are present in getQueryKeys ; * - each returned object of the `getLabels` method is callable * - each result can be converted to string using this callable * - each of this callable can provide a string for '_header' * * @param string[] $modifiers * @param array $acl * * @dataProvider dataProviderInitiateQuery */ public function testGetResultsAndLabels($modifiers, $acl, array $data): void { foreach ($this->getExports() as $export) { // it is more convenient to group the `getResult` and `getLabels` test // due to the fact that testing both methods use the same tools. $queryKeys = $export->getQueryKeys($data); $query = $export->initiateQuery($modifiers, $acl, $data, $exportGenerationContext = new ExportGenerationContext($this->getUser())); // limit the result for the query for performance reason (only for QueryBuilder, // not possible in NativeQuery) if ($query instanceof QueryBuilder) { $query->setMaxResults(1); } $results = $export->getResult($query, $data, $exportGenerationContext); $this->assertIsArray( $results, 'assert that the returned result is an array' ); if (0 === \count($results)) { $this->markTestIncomplete('The result is empty. We cannot process tests ' .'on results'); } // testing the result $result = $results[0]; $this->assertTrue( is_iterable($result), 'test that each row in the result is traversable or an array' ); $i = 0; foreach ($result as $key => $value) { $this->assertContains( $key, $queryKeys, 'test that each key is present in `getQueryKeys`' ); $closure = $export->getLabels($key, [$value], $data); $this->assertTrue( \is_callable($closure, false), 'test that the `getLabels` for key is a callable' ); $this->assertTrue( // conditions \is_string((string) \call_user_func($closure, '_header')) && !empty(\call_user_func($closure, '_header')) && '_header' !== \call_user_func($closure, '_header'), // message sprintf('Test that the callable return by `getLabels` for key %s ' .'can provide an header', $key) ); ++$i; if ($i > 15) { // do not iterate on each result break; } } } } /** * Test that the getType method return a string. */ public function testGetType(): void { foreach ($this->getExports() as $export) { $this->assertIsString( $export->getType(), 'Assert that the `getType` method return a string' ); $this->assertNotEmpty($export->getType(), 'Assert that the `getType` method' .' does not return an empty string.'); } } /** * test that the query returned is a QueryBuilder or a NativeQuery. * * If the query is a QueryBuilder, test that select and from is not empty. * * If the query is a native sql, test the query is not empty (length is * > 0). * * @dataProvider dataProviderInitiateQuery */ public function testInitiateQuery(mixed $modifiers, mixed $acl, mixed $data): void { foreach ($this->getExports() as $export) { $query = $export->initiateQuery($modifiers, $acl, $data, new ExportGenerationContext($this->getUser())); $this->assertTrue( $query instanceof QueryBuilder || $query instanceof NativeQuery, sprintf( 'Assert that the returned query is an instance of %s or %s', QueryBuilder::class, NativeQuery::class ) ); if ($query instanceof QueryBuilder) { $this->assertGreaterThanOrEqual( 1, \count($query->getDQLPart('select')), "assert there is at least one 'select' part" ); $this->assertGreaterThanOrEqual( 1, \count($query->getDQLPart('from')), "assert there is at least one 'from' part" ); } elseif ($query instanceof NativeQuery) { $this->assertNotEmpty( $query->getSQL(), 'check that the SQL query is not empty' ); } } } /** * Test required role is an instance of Role. */ public function testRequiredRole(): void { foreach ($this->getExports() as $export) { $role = $export->requiredRole(); self::assertIsString($role); } } /** * Test that supportsModifier return :. * * - an array of string, if the query is a QueryBuilder ; * - nothing, if the query is a native SQL * * @dataProvider dataProviderInitiateQuery */ public function testSupportsModifier(mixed $modifiers, mixed $acl, mixed $data): void { foreach ($this->getExports() as $export) { $query = $export->initiateQuery($modifiers, $acl, $data, new ExportGenerationContext($this->getUser())); if ($query instanceof QueryBuilder) { $this->assertContainsOnly( 'string', $export->supportsModifiers(), true, 'Test that the `supportsModifiers` method returns only strings' ); } elseif ($query instanceof NativeQuery) { $this->assertTrue( null === $export->supportsModifiers() || 0 === \count($export->supportsModifiers()), 'Test that the `supportsModifier` methods returns null or an empty array' ); } } } /** * create data for `ìnitiateQuery` method. */ public static function dataProviderInitiateQuery() { $acl = static::getAcl(); foreach (static::getModifiersCombination() as $modifiers) { foreach (static::getFormData() as $data) { yield [$modifiers, $acl, $data]; } } } }