diff --git a/.travis.yml b/.travis.yml index a1c5395d..4b799d5b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,6 +17,8 @@ env: - DEPS=low PROFILE=default - DEPS=high PROFILE=symfony2 - DEPS=low PROFILE=symfony2 + - DEPS=high PROFILE=browserKit + - DEPS=low PROFILE=browserKit php: - 5.5 diff --git a/behat.yml.dist b/behat.yml.dist index 140bf0a6..8fa5ab3d 100644 --- a/behat.yml.dist +++ b/behat.yml.dist @@ -2,6 +2,8 @@ default: suites: default: paths: [ '%paths.base%/tests/features' ] + filters: + tags: '~@symfony' contexts: - Behat\MinkExtension\Context\MinkContext - behatch:context:browser: @@ -33,3 +35,21 @@ symfony2: extensions: Behat\MinkExtension: default_session: symfony2 + +browserKit: + suites: + default: + filters: + tags: '@symfony' + extensions: + Behat\Symfony2Extension: + kernel: + bootstrap: 'tests/features/bootstrap/Bootstrap.php' + class: App\Kernel + env: test + debug: false + Behat\MinkExtension: + default_session: browserKit + sessions: + browserKit: + symfony2: ~ diff --git a/composer.json b/composer.json index 7e39dd21..19ca2287 100644 --- a/composer.json +++ b/composer.json @@ -16,6 +16,8 @@ }, "require-dev": { + "symfony/templating": "^2.3|^3.0|^4.0", + "behat/symfony2-extension": "^2.1", "behat/mink-goutte-driver": "^1.1", "guzzlehttp/guzzle": "^6.3", "behat/mink-selenium2-driver": "^1.3", @@ -25,7 +27,8 @@ "autoload": { "psr-4": { - "Behatch\\": "src/" + "Behatch\\": "src/", + "App\\": "tests/fixtures/www/app" } }, diff --git a/src/HttpCall/Request/BrowserKit.php b/src/HttpCall/Request/BrowserKit.php index 959bf8ff..e2fcc854 100644 --- a/src/HttpCall/Request/BrowserKit.php +++ b/src/HttpCall/Request/BrowserKit.php @@ -59,19 +59,22 @@ public function getContent() public function send($method, $url, $parameters = [], $files = [], $content = null, $headers = []) { - foreach ($files as $originalName => &$file) { - if (is_string($file)) { - $file = new UploadedFile($file, $originalName); - } - } - $client = $this->mink->getSession()->getDriver()->getClient(); + $tmpFiles = []; + if (!$client instanceof GoutteClient) { + $tmpFiles = $this->convertFilesToSymfonyUploadedFiles($files); + } + $client->followRedirects(false); $client->request($method, $url, $parameters, $files, $headers, $content); $client->followRedirects(true); $this->resetHttpHeaders(); + foreach ($tmpFiles as $tmpName) { + @unlink($tmpName); + } + return $this->mink->getSession()->getPage(); } @@ -141,4 +144,37 @@ protected function resetHttpHeaders() $client->restart(); } } + + private function convertFilesToSymfonyUploadedFiles(& $files) + { + $tmpFiles = []; + foreach ($files as $key => &$file) { + $tmpName = false; + if (is_string($file)) { + $tmpName = tempnam(sys_get_temp_dir(), 'upload'); + copy($file, $tmpName); + $tmpFiles[] = $tmpName; + $originalName = $file; + } elseif (is_array($file)) { + // This mirrors Goutte\Client::addPostFiles() called from Goutte\Client::doRequest() + // so that a Symfony\Component\HttpKernel\Client can have the same behaviour + if (isset($file['tmp_name'])) { + $tmpName = $file['tmp_name']; + if (isset($file['name'])) { + $originalName = $file['name']; + } else { + $originalName = $tmpName; + } + } else { + $subTmpFiles = $this->convertFilesToSymfonyUploadedFiles($file); + $tmpFiles = array_merge($tmpFiles, $subTmpFiles); + } + } + if ($tmpName) { + $file = new UploadedFile($tmpName, $originalName, null, null, true); + } + } + + return $tmpFiles; + } } diff --git a/tests/features/bootstrap/Bootstrap.php b/tests/features/bootstrap/Bootstrap.php index 31064234..ff54c3a1 100644 --- a/tests/features/bootstrap/Bootstrap.php +++ b/tests/features/bootstrap/Bootstrap.php @@ -1,3 +1,4 @@ files->all(); + $displayFiles = []; + /** @var UploadedFile $file */ + foreach ($files as $key => $file) { + $displayFiles[$key] = [ + 'name' => $file->getClientOriginalName(), + 'error' => $file->getError(), + 'size' => $file->getSize(), + ]; + $file->move('/tmp/moved'); + } + + $view = 'templates/rest.php'; + $parameters = [ + 'server' => $request->server->all(), + 'method' => $request->getMethod(), + 'request' => $request->request->all(), + 'files' => $displayFiles, + 'body' => $request->getContent(), + ]; + $content = $this->container->get('templating')->render($view, $parameters); + return new Response($content); + } +} diff --git a/tests/fixtures/www/app/Kernel.php b/tests/fixtures/www/app/Kernel.php new file mode 100644 index 00000000..cb8c64fa --- /dev/null +++ b/tests/fixtures/www/app/Kernel.php @@ -0,0 +1,97 @@ +projectDir) { + $this->projectDir = realpath(__DIR__ . '/../framework'); + } + + return $this->projectDir; + } + + public function getCacheDir() + { + return $this->getProjectDir().'/var/cache/'.$this->environment; + } + + public function getLogDir() + { + return $this->getProjectDir().'/var/log'; + } + + public function registerContainerConfiguration(LoaderInterface $loader) + { + $loader->load(function (ContainerBuilder $container) use ($loader) { + $container->loadFromExtension('framework', [ + 'router' => [ + 'resource' => 'kernel::loadRoutes', + 'type' => 'service', + ], + ]); + + if ($this instanceof EventSubscriberInterface) { + $container->register('kernel', static::class) + ->setSynthetic(true) + ->setPublic(true) + ->addTag('kernel.event_subscriber'); + } + + $this->configureContainer($container, $loader); + + $container->addObjectResource($this); + }); + } + + public function loadRoutes(LoaderInterface $loader) + { + $routes = new RouteCollectionBuilder($loader); + $this->configureRoutes($routes); + + return $routes->build(); + } + + public function registerBundles() + { + $contents = require $this->getProjectDir().'/config/bundles.php'; + foreach ($contents as $class => $envs) { + if (isset($envs['all']) || isset($envs[$this->environment])) { + yield new $class(); + } + } + } + + protected function configureContainer(ContainerBuilder $container, LoaderInterface $loader) + { + $container->addResource(new FileResource($this->getProjectDir().'/config/bundles.php')); + // Feel free to remove the "container.autowiring.strict_mode" parameter + // if you are using symfony/dependency-injection 4.0+ as it's the default behavior + $container->setParameter('container.autowiring.strict_mode', true); + $container->setParameter('container.dumper.inline_class_loader', true); + $confDir = $this->getProjectDir().'/config'; + + $loader->load($confDir.'/packages/framework'. self::CONFIG_EXTS); + $loader->load($confDir.'/services'.self::CONFIG_EXTS); + } + + protected function configureRoutes(RouteCollectionBuilder $routes) + { + $confDir = $this->getProjectDir().'/config'; + + $routes->import($confDir.'/routes'.self::CONFIG_EXTS, '/'); + } +} diff --git a/tests/fixtures/www/app/templates/rest.php b/tests/fixtures/www/app/templates/rest.php new file mode 100644 index 00000000..367f72c9 --- /dev/null +++ b/tests/fixtures/www/app/templates/rest.php @@ -0,0 +1,32 @@ +You have sent a request. + + header(s) received. + $value): ?> +
: + + + +
No parameter received. + +
parameter(s) received. + $value): ?> +
: + + + + +
No files received. + +
file(s) received. + $value): ?> +
- name : +
- error : +
- size : + + + + +
No body received. + +
Body : + diff --git a/tests/fixtures/www/framework/config/bundles.php b/tests/fixtures/www/framework/config/bundles.php new file mode 100644 index 00000000..49d3fb6f --- /dev/null +++ b/tests/fixtures/www/framework/config/bundles.php @@ -0,0 +1,5 @@ + ['all' => true], +]; diff --git a/tests/fixtures/www/framework/config/packages/framework.yaml b/tests/fixtures/www/framework/config/packages/framework.yaml new file mode 100644 index 00000000..bea1ba1b --- /dev/null +++ b/tests/fixtures/www/framework/config/packages/framework.yaml @@ -0,0 +1,9 @@ +framework: + test: true + secret: 'xx-top-secret-yy' + + router: + strict_requirements: ~ + + templating: + engines: ['php'] diff --git a/tests/fixtures/www/framework/config/routes.yaml b/tests/fixtures/www/framework/config/routes.yaml new file mode 100644 index 00000000..6fbc211e --- /dev/null +++ b/tests/fixtures/www/framework/config/routes.yaml @@ -0,0 +1,3 @@ +rest: + path: /symfony/rest + controller: 'App\Controller\RestController:action' diff --git a/tests/fixtures/www/framework/config/services.yaml b/tests/fixtures/www/framework/config/services.yaml new file mode 100644 index 00000000..531448f3 --- /dev/null +++ b/tests/fixtures/www/framework/config/services.yaml @@ -0,0 +1,32 @@ +# This file is the entry point to configure your own services. +# Files in the packages/ subdirectory configure your dependencies. + +# Put parameters here that don't need to change on each machine where the app is deployed +# https://symfony.com/doc/current/best_practices/configuration.html#application-related-configuration +parameters: + locale: 'en' + +services: + # default configuration for services in *this* file + _defaults: + autowire: true # Automatically injects dependencies in your services. + autoconfigure: true # Automatically registers your services as commands, event subscribers, etc. + public: false # Allows optimizing the container by removing unused services; this also means + # fetching services directly from the container via $container->get() won't work. + # The best practice is to be explicit about your dependencies anyway. + + # makes classes in src/ available to be used as services + # this creates a service per class whose id is the fully-qualified class name +# App\: +# resource: '../../app/*' +# exclude: '../../app/Kernel.php}' +# public: true + + # controllers are imported separately to make sure services can be injected + # as action arguments even if you don't extend any base controller class + App\Controller\: + resource: '../../app/Controller' + tags: ['controller.service_arguments'] + + # add more service definitions when explicit configuration is needed + # please note that last definitions always *replace* previous ones