| 
<?php
 use App\ContentLoader;
 use App\ImgCountHandler;
 use Domain\Page;
 use Domain\Site;
 use Infrastructure\Repository\PageRepository;
 use PHPUnit\Framework\TestCase;
 
 class ImgCountHandlerTest extends TestCase
 {
 public function testCountImgTagsInEmptyList(): void
 {
 $content = self::EMPTY_CONTENT;
 $handler = new ImgCountHandler(new Site(self::ROOT_URL), self::ROOT_URL, ContentLoader::getInstance());
 $this->assertEquals(0, $this->invokeMethod($handler, 'countImgTags', [&$content]));
 }
 
 public function testCountImgTags(): void
 {
 $content = self::TEST_PAGE_ROOT;
 $handler = new ImgCountHandler(new Site(self::ROOT_URL), self::ROOT_URL, ContentLoader::getInstance());
 $this->assertEquals(3, $this->invokeMethod($handler, 'countImgTags', [&$content]));
 }
 
 public function testPageProcessing(): void
 {
 $page    = new Page(self::ROOT_URL);
 $handler = new ImgCountHandler(new Site(self::ROOT_URL), self::ROOT_URL,
 ContentLoader::getInstance());
 $content = self::TEST_PAGE_ROOT;
 
 $this->invokeMethod($handler, 'pageProcessing', [$page, &$content]);
 
 $this->assertEquals(3, $page->getImgCount());
 foreach ($page->getChildren() as $child) {
 $this->assertContains($child->getUrl(), ['http://www.example.com/',
 'http://www.example.com/users/345120']);
 }
 }
 
 public function testPageProcessingRecursive(): void
 {
 $loaderStub = $this->createStubForTestPageProcessingRecursive();
 $handler    = new ImgCountHandler(new Site(self::ROOT_URL), self::ROOT_URL, $loaderStub);
 
 $this->invokeMethod($handler, 'pageProcessingRecursive', [[self::ROOT_URL]]);
 
 /** @var PageRepository $repository */
 $repository = $this->getInnerPropertyValue($handler, 'repository');
 
 $controlUrlList = [
 'http://www.example.com/',
 'http://www.example.com/users/345120',
 'http://www.example.com/users/345'
 ];
 $unexpectedUrls = [];
 foreach ($repository->getPagesIterator() as $url => $page) {
 if (($i = array_search($url, $controlUrlList, true)) === false) {
 $unexpectedUrls[] = $url;
 } else {
 unset($controlUrlList[$i]);
 }
 }
 $this->assertEmpty($controlUrlList);
 $this->assertEmpty($unexpectedUrls);
 
 $this->assertEquals(3, $repository->get('http://www.example.com/')->getImgCount());
 $this->assertEquals(2, $repository->get('http://www.example.com/users/345120')->getImgCount());
 $this->assertEquals(4, $repository->get('http://www.example.com/users/345')->getImgCount());
 
 $this->assertGreaterThan(0, $repository->get('http://www.example.com/')->getProcessingTime());
 $this->assertGreaterThan(0, $repository->get('http://www.example.com/users/345120')->getProcessingTime());
 $this->assertGreaterThan(0, $repository->get('http://www.example.com/users/345')->getProcessingTime());
 }
 
 public function testHandle(): void
 {
 $loaderStub = $this->createStubForTestPageProcessingRecursive();
 $handler    = new ImgCountHandler(new Site(self::ROOT_URL), self::ROOT_URL, $loaderStub);
 
 $report = $handler->handle(self::ROOT_URL);
 
 /** @var PageRepository $repository */
 $repository = $this->getInnerPropertyValue($handler, 'repository');
 
 $sorted = [];
 /** @var Page $page */
 foreach ($repository->getPagesIterator() as $page) {
 $sorted[] = [
 $page->getUrl(),
 $page->getImgCount()
 ];
 }
 $this->assertEquals([
 ['http://www.example.com/users/345', 4],
 ['http://www.example.com/', 3],
 ['http://www.example.com/users/345120', 2]
 ], $sorted);
 }
 
 private const
 ROOT_URL = 'http://www.example.com',
 EMPTY_CONTENT = '<body><html><div></div></html></body>',
 TEST_PAGE_ROOT = '<html>
 <body>
 <img src="http://www.example.com">Example</img>
 <a href="http://www.example.com">Example</a>
 <div>
 <img src="http://www.stackoverflow.com/users/345120">SO</img>
 <a href="http://www.stackoverflow.com/users/345120">SO</a>
 </div>
 <img src="www.stackoverflow.com/users/345120">SO</img>
 <a href="//www.stackoverflow.com/users/345120">SO</a>
 <a href="/users/345120">SO</a>
 </body>
 </html>',
 TEST_PAGE_1 = '<html>
 <body>
 <img src="http://www.example.com">Example</img>
 <a href="http://www.example.com">Example</a>
 <div>
 <img src="http://www.stackoverflow.com/users/345120">SO</img>
 <a href="http://www.stackoverflow.com/users/345120">SO</a>
 </div>
 <a href="//www.stackoverflow.com/users/345120">SO</a>
 <a href="/users/345">SO</a>
 </body>
 </html>',
 TEST_PAGE_2 = '<html>
 <body>
 <img src="http://www.example.com">Example</img>
 <a href="http://www.example.com">Example</a>
 <div>
 <img src="http://www.stackoverflow.com/users/345120">SO</img>
 <a href="http://www.stackoverflow.com/users/345120">SO</a>
 </div>
 <img src="www.stackoverflow.com/users/345120">SO</img>
 <a href="//www.stackoverflow.com/users/345120">SO</a>
 <img src="http://www.example.com">Example</img>
 <a href="/users/345120">SO</a>
 </body>
 </html>';
 
 /**
 * Call protected/private method of a class.
 *
 * @param object &$object     Instantiated object that we will run method on.
 * @param string  $methodName Method name to call
 * @param array   $parameters Array of parameters to pass into method
 *
 * @return mixed Method return.
 * @throws Throwable
 */
 public function invokeMethod(&$object, $methodName, array $parameters = array())
 {
 $reflection = new ReflectionClass(get_class($object));
 $method     = $reflection->getMethod($methodName);
 $method->setAccessible(true);
 
 return $method->invokeArgs($object, $parameters);
 }
 
 /**
 * Return value of a private property using ReflectionClass
 *
 * @param stdClass $instance
 * @param string   $property
 *
 * @return mixed
 * @throws ReflectionException
 */
 private function getInnerPropertyValue($instance, $property)
 {
 $reflector          = new ReflectionClass($instance);
 $reflector_property = $reflector->getProperty($property);
 $reflector_property->setAccessible(true);
 
 return $reflector_property->getValue($instance);
 }
 
 private function createStubForTestPageProcessingRecursive()
 {
 $loaderStub = $this->createMock(ContentLoader::class);
 $map        = [
 [
 [], []
 ], [
 ['http://www.example.com/'],
 ['http://www.example.com/' => self::TEST_PAGE_ROOT]
 ], [
 ['http://www.example.com/users/345120'],
 ['http://www.example.com/users/345120' => self::TEST_PAGE_1]
 ], [
 ['http://www.example.com/users/345'],
 ['http://www.example.com/users/345' => self::TEST_PAGE_2]
 ],
 ];
 $loaderStub->method('loadContent')
 ->willReturnMap($map);
 
 return $loaderStub;
 }
 }
 
 |