Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/.php_cs.cache
/.php_cs
/.phpunit.result.cache
/composer.phar
/composer.lock
/phpunit.xml
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

[![Build Status](https://travis-ci.org/symfony/panther.svg?branch=master)](https://travis-ci.org/symfony/panther)
[![Build status](https://ci.appveyor.com/api/projects/status/bunoc4ufud4oie45?svg=true)](https://ci.appveyor.com/project/fabpot/panther)
[![SensioLabsInsight](https://insight.sensiolabs.com/projects/9ea7e78c-998a-4489-9815-7449ce8291ef/mini.png)](https://insight.sensiolabs.com/projects/9ea7e78c-998a-4489-9815-7449ce8291ef)
[![SymfonyInsight](https://insight.symfony.com/projects/9ea7e78c-998a-4489-9815-7449ce8291ef/mini.png)](https://insight.symfony.com/projects/9ea7e78c-998a-4489-9815-7449ce8291ef)

*Panther* is a convenient standalone library to scrape websites and to run end-to-end tests **using real browsers**.

Expand Down
8 changes: 6 additions & 2 deletions src/DomCrawler/Crawler.php
Original file line number Diff line number Diff line change
Expand Up @@ -181,16 +181,20 @@ public function nodeName(): string
return $this->getElementOrThrow()->getTagName();
}

public function text($default = null): string
public function text(string $default = null, bool $normalizeWhitespace = true): string
{
if (!$normalizeWhitespace) {
throw new \InvalidArgumentException('Panther only supports getting normalized text.');
}

try {
return $this->getElementOrThrow()->getText();
} catch (\InvalidArgumentException $e) {
if (null === $default) {
throw $e;
}

return (string) $default;
return $default;
}
}

Expand Down
92 changes: 20 additions & 72 deletions src/PantherTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,98 +14,46 @@
namespace Symfony\Component\Panther;

use PHPUnit\Framework\TestCase;
use Symfony\Bundle\FrameworkBundle\KernelBrowser;
use Symfony\Bundle\FrameworkBundle\Test\ForwardCompatTestTrait;
use Symfony\Bundle\FrameworkBundle\Test\WebTestAssertionsTrait;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
use Symfony\Component\Panther\Client as PantherClient;

if (\class_exists(WebTestCase::class)) {
if (trait_exists('Symfony\Bundle\FrameworkBundle\Test\WebTestAssertionsTrait') && trait_exists('Symfony\Bundle\FrameworkBundle\Test\ForwardCompatTestTrait')) {
abstract class PantherTestCase extends WebTestCase
{
use ForwardCompatTestTrait;
use PantherTestCaseTrait;
use WebTestAssertionsTrait {
assertPageTitleSame as private baseAssertPageTitleSame;
assertPageTitleContains as private baseAssertPageTitleContains;
}

public static function assertPageTitleSame(string $expectedTitle, string $message = ''): void
if (trait_exists('Symfony\Bundle\FrameworkBundle\Test\WebTestAssertionsTrait')) {
if (trait_exists('Symfony\Bundle\FrameworkBundle\Test\ForwardCompatTestTrait')) {
// Symfony 4.3
abstract class PantherTestCase extends WebTestCase
{
$client = self::getClient();
if ($client instanceof PantherClient) {
self::assertSame($expectedTitle, $client->getTitle());
use ForwardCompatTestTrait;
use WebTestAssertionsTrait;

return;
private function doTearDown()
{
parent::tearDown();
self::getClient(null);
}

self::baseAssertPageTitleSame($expectedTitle, $message);
}

public static function assertPageTitleContains(string $expectedTitle, string $message = ''): void
} else {
// Symfony 5
abstract class PantherTestCase extends WebTestCase
{
$client = self::getClient();
if ($client instanceof PantherClient) {
if (method_exists(self::class, 'assertStringContainsString')) {
self::assertStringContainsString($expectedTitle, $client->getTitle());

return;
}

self::assertContains($expectedTitle, $client->getTitle());
use WebTestAssertionsTrait;

return;
protected function tearDown(): void
{
parent::tearDown();
self::getClient(null);
}

self::baseAssertPageTitleContains($expectedTitle, $message);
}

private function doTearDown()
{
parent::tearDown();
self::getClient(null);
}

// Copied from WebTestCase to allow assertions to work with createClient

/**
* Creates a KernelBrowser.
*
* @param array $options An array of options to pass to the createKernel method
* @param array $server An array of server parameters
*
* @return KernelBrowser A KernelBrowser instance
*/
protected static function createClient(array $options = [], array $server = [])
{
$kernel = static::bootKernel($options);

try {
/**
* @var KernelBrowser
*/
$client = $kernel->getContainer()->get('test.client');
} catch (ServiceNotFoundException $e) {
if (class_exists(KernelBrowser::class)) {
throw new \LogicException('You cannot create the client used in functional tests if the "framework.test" config is not set to true.');
}
throw new \LogicException('You cannot create the client used in functional tests if the BrowserKit component is not available. Try running "composer require symfony/browser-kit"');
}

$client->setServerParameters($server);

return self::getClient($client);
}
}
} else {
// Symfony 4.3 and inferior
abstract class PantherTestCase extends WebTestCase
{
use PantherTestCaseTrait;
}
}
} else {
// Without Symfony
abstract class PantherTestCase extends TestCase
{
use PantherTestCaseTrait;
Expand Down
5 changes: 5 additions & 0 deletions src/ServerListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@
use PHPUnit\Framework\TestListenerDefaultImplementation;
use PHPUnit\Framework\TestSuite;

@trigger_error(sprintf('The "%s" class is deprecated since Panther 0.5.3, use "%s" instead.', ServerListener::class, ServerExtension::class), E_USER_DEPRECATED);

/**
* @deprecated since Panther 0.5.3, use Symfony\Component\Panther\ServerExtension instead.
*/
final class ServerListener implements TestListener
{
use TestListenerDefaultImplementation;
Expand Down
94 changes: 94 additions & 0 deletions src/WebTestAssertionsTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
<?php

/*
* This file is part of the Panther project.
*
* (c) Kévin Dunglas <dunglas@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace Symfony\Component\Panther;

use Symfony\Bundle\FrameworkBundle\KernelBrowser;
use Symfony\Bundle\FrameworkBundle\Test\WebTestAssertionsTrait as BaseWebTestAssertionsTrait;
use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
use Symfony\Component\Panther\Client as PantherClient;

/**
* Tweaks Symfony's WebTestAssertionsTrait to be compatible with Panther.
*
* @author Kévin Dunglas <dunglas@gmail.com>
*/
trait WebTestAssertionsTrait
{
use PantherTestCaseTrait;
use BaseWebTestAssertionsTrait {
assertPageTitleSame as private baseAssertPageTitleSame;
assertPageTitleContains as private baseAssertPageTitleContains;
}

public static function assertPageTitleSame(string $expectedTitle, string $message = ''): void
{
$client = self::getClient();
if ($client instanceof PantherClient) {
self::assertSame($expectedTitle, $client->getTitle());

return;
}

self::baseAssertPageTitleSame($expectedTitle, $message);
}

public static function assertPageTitleContains(string $expectedTitle, string $message = ''): void
{
$client = self::getClient();
if ($client instanceof PantherClient) {
if (method_exists(self::class, 'assertStringContainsString')) {
self::assertStringContainsString($expectedTitle, $client->getTitle());

return;
}

self::assertContains($expectedTitle, $client->getTitle());

return;
}

self::baseAssertPageTitleContains($expectedTitle, $message);
}

// Copied from WebTestCase to allow assertions to work with createClient

/**
* Creates a KernelBrowser.
*
* @param array $options An array of options to pass to the createKernel method
* @param array $server An array of server parameters
*
* @return KernelBrowser A KernelBrowser instance
*/
protected static function createClient(array $options = [], array $server = [])
{
$kernel = static::bootKernel($options);

try {
/**
* @var KernelBrowser
*/
$client = $kernel->getContainer()->get('test.client');
} catch (ServiceNotFoundException $e) {
if (class_exists(KernelBrowser::class)) {
throw new \LogicException('You cannot create the client used in functional tests if the "framework.test" config is not set to true.');
}
throw new \LogicException('You cannot create the client used in functional tests if the BrowserKit component is not available. Try running "composer require symfony/browser-kit"');
}

$client->setServerParameters($server);

return self::getClient($client);
}
}
23 changes: 22 additions & 1 deletion tests/DomCrawler/CrawlerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use Facebook\WebDriver\WebDriverElement;
use Symfony\Component\DomCrawler\Crawler;
use Symfony\Component\Panther\Client;
use Symfony\Component\Panther\Client as PantherClient;
use Symfony\Component\Panther\DomCrawler\Image;
use Symfony\Component\Panther\DomCrawler\Link;
use Symfony\Component\Panther\Tests\TestCase;
Expand Down Expand Up @@ -47,7 +48,7 @@ public function testGetUri(callable $clientFactory): void
public function testHtml(callable $clientFactory): void
{
$crawler = $this->request($clientFactory, '/basic.html');
$this->assertContains('<title>A basic page</title>', $crawler->html());
$this->assertStringContainsString('<title>A basic page</title>', $crawler->html());
}

/**
Expand Down Expand Up @@ -314,4 +315,24 @@ public function testHtmlDefault(callable $clientFactory): void
$crawler = $this->request($clientFactory, '/basic.html');
$this->assertSame('default', $crawler->filter('header')->html('default'));
}

/**
* @dataProvider clientFactoryProvider
*/
public function testNormalizeText(callable $clientFactory, string $clientClass): void
{
if (PantherClient::class !== $clientClass) {
$this->markTestSkipped('Need https://github.com/symfony/symfony/pull/34151');
}

$crawler = $this->request($clientFactory, '/normalize.html');
$this->assertSame('Foo Bar Baz', $crawler->filter('#normalize')->text());
}

public function testDoNotNormalizeText()
{
$this->expectException(\InvalidArgumentException::class);

self::createPantherClient()->request('GET', self::$baseUri.'/normalize.html')->filter('#normalize')->text(null, false);
}
}
23 changes: 18 additions & 5 deletions tests/DomCrawler/Field/FileFormFieldTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,17 @@ class FileFormFieldTest extends TestCase
{
private static $invalidUploadFileName = 'narf.txt';

private function assertValueContains($needle, $haystack): void
{
if (\is_string($haystack)) {
$this->assertStringContainsString($needle, $haystack);

return;
}

$this->assertContains($needle, $haystack);
}

/**
* @dataProvider clientFactoryProvider
*/
Expand All @@ -36,7 +47,7 @@ public function testFileUploadWithUpload(callable $clientFactory)
$this->assertInstanceOf(FileFormField::class, $fileFormField);
$fileFormField->upload($this->getUploadFilePath(self::$uploadFileName));

$this->assertContains(self::$uploadFileName, $form['file_upload']->getValue());
$this->assertValueContains(self::$uploadFileName, $form['file_upload']->getValue());
}

/**
Expand All @@ -52,13 +63,15 @@ public function testFileUploadWithSetValue(callable $clientFactory)
$this->assertInstanceOf(FileFormField::class, $fileFormField);
$fileFormField->setValue($this->getUploadFilePath(self::$uploadFileName));

$this->assertContains(self::$uploadFileName, $form['file_upload']->getValue());
$this->assertValueContains(self::$uploadFileName, $form['file_upload']->getValue());
}

/**
* @dataProvider clientFactoryProvider
*
* @param mixed $class
*/
public function testFileUploadWithSetFilePath(callable $clientFactory)
public function testFileUploadWithSetFilePath(callable $clientFactory, $class)
{
$crawler = $this->request($clientFactory, '/file-form-field.html');
$form = $crawler->filter('form')->form();
Expand All @@ -68,10 +81,10 @@ public function testFileUploadWithSetFilePath(callable $clientFactory)
$this->assertInstanceOf(FileFormField::class, $fileFormField);

$fileFormField->setFilePath($this->getUploadFilePath(self::$uploadFileName));
$this->assertContains(self::$uploadFileName, $form['file_upload']->getValue());
$this->assertValueContains(self::$uploadFileName, $form['file_upload']->getValue());

$fileFormField->setFilePath($this->getUploadFilePath(self::$anotherUploadFileName));
$this->assertContains(self::$anotherUploadFileName, $form['file_upload']->getValue());
$this->assertValueContains(self::$anotherUploadFileName, $form['file_upload']->getValue());
}

/**
Expand Down
7 changes: 3 additions & 4 deletions tests/ProcessManager/ChromeManagerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,11 @@ public function testRun()
$manager->quit();
}

/**
* @expectedException \RuntimeException
* @expectedExceptionMessage The port 9515 is already in use.
*/
public function testAlreadyRunning()
{
$this->expectException(\RuntimeException::class);
$this->expectExceptionMessage('The port 9515 is already in use.');

$driver1 = new ChromeManager();
$driver1->start();

Expand Down
Loading