From 474540f6904289d20812471ebb22316d47f5a771 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 28 Oct 2016 16:59:51 -0500 Subject: [PATCH 1/9] initial progress on cleaning up testing --- .../Testing/Concerns/ImpersonatesUsers.php | 34 ---- .../Concerns/InteractsWithAuthentication.php | 30 ++++ .../Concerns/InteractsWithDatabase.php | 26 --- .../Testing/Concerns/InteractsWithSession.php | 162 ------------------ .../Foundation/Testing/TestCase.php | 2 - 5 files changed, 30 insertions(+), 224 deletions(-) delete mode 100644 src/Illuminate/Foundation/Testing/Concerns/ImpersonatesUsers.php delete mode 100644 src/Illuminate/Foundation/Testing/Concerns/InteractsWithSession.php diff --git a/src/Illuminate/Foundation/Testing/Concerns/ImpersonatesUsers.php b/src/Illuminate/Foundation/Testing/Concerns/ImpersonatesUsers.php deleted file mode 100644 index f07e2f030dd6..000000000000 --- a/src/Illuminate/Foundation/Testing/Concerns/ImpersonatesUsers.php +++ /dev/null @@ -1,34 +0,0 @@ -be($user, $driver); - - return $this; - } - - /** - * Set the currently logged in user for the application. - * - * @param \Illuminate\Contracts\Auth\Authenticatable $user - * @param string|null $driver - * @return void - */ - public function be(UserContract $user, $driver = null) - { - $this->app['auth']->guard($driver)->setUser($user); - } -} diff --git a/src/Illuminate/Foundation/Testing/Concerns/InteractsWithAuthentication.php b/src/Illuminate/Foundation/Testing/Concerns/InteractsWithAuthentication.php index 413ff5fc9781..30e82011c16d 100644 --- a/src/Illuminate/Foundation/Testing/Concerns/InteractsWithAuthentication.php +++ b/src/Illuminate/Foundation/Testing/Concerns/InteractsWithAuthentication.php @@ -2,8 +2,38 @@ namespace Illuminate\Foundation\Testing\Concerns; +use Illuminate\Contracts\Auth\Authenticatable as UserContract; + trait InteractsWithAuthentication { + /** + * Set the currently logged in user for the application. + * + * @param \Illuminate\Contracts\Auth\Authenticatable $user + * @param string|null $driver + * @return $this + */ + public function actingAs(UserContract $user, $driver = null) + { + $this->be($user, $driver); + + return $this; + } + + /** + * Set the currently logged in user for the application. + * + * @param \Illuminate\Contracts\Auth\Authenticatable $user + * @param string|null $driver + * @return void + */ + public function be(UserContract $user, $driver = null) + { + $this->app['auth']->guard($driver)->setUser($user); + + $this->app['auth']->shouldUse($driver); + } + /** * Assert that the user is authenticated. * diff --git a/src/Illuminate/Foundation/Testing/Concerns/InteractsWithDatabase.php b/src/Illuminate/Foundation/Testing/Concerns/InteractsWithDatabase.php index e5aae7731018..5ef823166c24 100644 --- a/src/Illuminate/Foundation/Testing/Concerns/InteractsWithDatabase.php +++ b/src/Illuminate/Foundation/Testing/Concerns/InteractsWithDatabase.php @@ -27,19 +27,6 @@ protected function seeInDatabase($table, array $data, $connection = null) return $this; } - /** - * Assert that a given where condition does not exist in the database. - * - * @param string $table - * @param array $data - * @param string $connection - * @return $this - */ - protected function missingFromDatabase($table, array $data, $connection = null) - { - return $this->notSeeInDatabase($table, $data, $connection); - } - /** * Assert that a given where condition does not exist in the database. * @@ -49,19 +36,6 @@ protected function missingFromDatabase($table, array $data, $connection = null) * @return $this */ protected function dontSeeInDatabase($table, array $data, $connection = null) - { - return $this->notSeeInDatabase($table, $data, $connection); - } - - /** - * Assert that a given where condition does not exist in the database. - * - * @param string $table - * @param array $data - * @param string $connection - * @return $this - */ - protected function notSeeInDatabase($table, array $data, $connection = null) { $database = $this->app->make('db'); diff --git a/src/Illuminate/Foundation/Testing/Concerns/InteractsWithSession.php b/src/Illuminate/Foundation/Testing/Concerns/InteractsWithSession.php deleted file mode 100644 index d5f805feb462..000000000000 --- a/src/Illuminate/Foundation/Testing/Concerns/InteractsWithSession.php +++ /dev/null @@ -1,162 +0,0 @@ -session($data); - - return $this; - } - - /** - * Set the session to the given array. - * - * @param array $data - * @return void - */ - public function session(array $data) - { - $this->startSession(); - - foreach ($data as $key => $value) { - $this->app['session']->put($key, $value); - } - } - - /** - * Start the session for the application. - * - * @return void - */ - protected function startSession() - { - if (! $this->app['session']->isStarted()) { - $this->app['session']->start(); - } - } - - /** - * Flush all of the current session data. - * - * @return void - */ - public function flushSession() - { - $this->startSession(); - - $this->app['session']->flush(); - } - - /** - * Assert that the session has a given value. - * - * @param string|array $key - * @param mixed $value - * @return void - */ - public function seeInSession($key, $value = null) - { - $this->assertSessionHas($key, $value); - - return $this; - } - - /** - * Assert that the session has a given value. - * - * @param string|array $key - * @param mixed $value - * @return void - */ - public function assertSessionHas($key, $value = null) - { - if (is_array($key)) { - return $this->assertSessionHasAll($key); - } - - if (is_null($value)) { - PHPUnit::assertTrue($this->app['session.store']->has($key), "Session missing key: $key"); - } else { - PHPUnit::assertEquals($value, $this->app['session.store']->get($key)); - } - } - - /** - * Assert that the session has a given list of values. - * - * @param array $bindings - * @return void - */ - public function assertSessionHasAll(array $bindings) - { - foreach ($bindings as $key => $value) { - if (is_int($key)) { - $this->assertSessionHas($value); - } else { - $this->assertSessionHas($key, $value); - } - } - } - - /** - * Assert that the session does not have a given key. - * - * @param string|array $key - * @return void - */ - public function assertSessionMissing($key) - { - if (is_array($key)) { - foreach ($key as $k) { - $this->assertSessionMissing($k); - } - } else { - PHPUnit::assertFalse($this->app['session.store']->has($key), "Session has unexpected key: $key"); - } - } - - /** - * Assert that the session has errors bound. - * - * @param string|array $bindings - * @param mixed $format - * @return void - */ - public function assertSessionHasErrors($bindings = [], $format = null) - { - $this->assertSessionHas('errors'); - - $bindings = (array) $bindings; - - $errors = $this->app['session.store']->get('errors'); - - foreach ($bindings as $key => $value) { - if (is_int($key)) { - PHPUnit::assertTrue($errors->has($value), "Session missing error: $value"); - } else { - PHPUnit::assertContains($value, $errors->get($key, $format)); - } - } - } - - /** - * Assert that the session has old input. - * - * @return void - */ - public function assertHasOldInput() - { - $this->assertSessionHas('_old_input'); - } -} diff --git a/src/Illuminate/Foundation/Testing/TestCase.php b/src/Illuminate/Foundation/Testing/TestCase.php index 26e2540e9b8a..29da5586f2d6 100755 --- a/src/Illuminate/Foundation/Testing/TestCase.php +++ b/src/Illuminate/Foundation/Testing/TestCase.php @@ -11,11 +11,9 @@ abstract class TestCase extends PHPUnit_Framework_TestCase { use Concerns\InteractsWithContainer, Concerns\MakesHttpRequests, - Concerns\ImpersonatesUsers, Concerns\InteractsWithAuthentication, Concerns\InteractsWithConsole, Concerns\InteractsWithDatabase, - Concerns\InteractsWithSession, Concerns\MocksApplicationServices; /** From 73cb709b654a71bc545be60cf0699d81c02f1deb Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 31 Oct 2016 15:39:39 -0500 Subject: [PATCH 2/9] continuing to work on testing --- .../Concerns/MocksApplicationServices.php | 132 ------------------ 1 file changed, 132 deletions(-) diff --git a/src/Illuminate/Foundation/Testing/Concerns/MocksApplicationServices.php b/src/Illuminate/Foundation/Testing/Concerns/MocksApplicationServices.php index 8ee4ab5b3a42..26e947e31cec 100644 --- a/src/Illuminate/Foundation/Testing/Concerns/MocksApplicationServices.php +++ b/src/Illuminate/Foundation/Testing/Concerns/MocksApplicationServices.php @@ -109,127 +109,6 @@ protected function withoutEvents() return $this; } - /** - * Specify a list of events that should be fired for the given operation. - * - * These events will be mocked, so that handlers will not actually be executed. - * - * @param string $model - * @param array|string $events - * @return $this - * - * @throws \Exception - */ - public function expectsModelEvents($model, $events) - { - $events = $this->formatModelEvents($model, $events); - - $this->withoutModelEvents(); - - $this->beforeApplicationDestroyed(function () use ($events) { - $fired = $this->getFiredModelEvents($events); - - if ($eventsNotFired = array_diff($events, $fired)) { - throw new Exception( - 'These expected Eloquent events were not fired: ['.implode(', ', $eventsNotFired).']' - ); - } - }); - - return $this; - } - - /** - * Specify a list of events that should not be fired for the given operation. - * - * These events will be mocked, so that handlers will not actually be executed. - * - * @param string $model - * @param array|string $events - * @return $this - * - * @throws \Exception - */ - public function doesntExpectModelEvents($model, $events) - { - $events = $this->formatModelEvents($model, $events); - - $this->withoutModelEvents(); - - $this->beforeApplicationDestroyed(function () use ($events) { - if ($fired = $this->getFiredModelEvents($events)) { - throw new Exception( - 'These unexpected Eloquent events were fired: ['.implode(', ', $fired).']' - ); - } - }); - - return $this; - } - - /** - * Convert a model and a list of events into the Eloquent's format. - * - * @param string $model - * @param array|string $events - * @return string[] - */ - private function formatModelEvents($model, $events) - { - $events = (array) $events; - - return array_map(function ($event) use ($model) { - return "eloquent.{$event}: {$model}"; - }, (array) $events); - } - - /** - * Mock the model event dispatcher so all Eloquent events are silenced. - * - * @return $this - */ - protected function withoutModelEvents() - { - $mock = Mockery::mock('Illuminate\Contracts\Events\Dispatcher'); - - $mock->shouldReceive('fire')->andReturnUsing(function ($called) { - $this->firedModelEvents[] = $called; - }); - - $mock->shouldReceive('until')->andReturnUsing(function ($called) { - $this->firedModelEvents[] = $called; - - return true; - }); - - $mock->shouldReceive('listen')->andReturnUsing(function ($event, $listener, $priority) { - // - }); - - Model::setEventDispatcher($mock); - - return $this; - } - - /** - * Specify a list of observers that will not run for the given operation. - * - * @param array|string $observers - * @return $this - */ - public function withoutObservers($observers) - { - $observers = is_array($observers) ? $observers : [$observers]; - - array_map(function ($observer) { - $this->app->bind($observer, function () use ($observer) { - return $this->getMockBuilder($observer)->disableOriginalConstructor()->getMock(); - }); - }, $observers); - - return $this; - } - /** * Filter the given events against the fired events. * @@ -241,17 +120,6 @@ protected function getFiredEvents(array $events) return $this->getDispatched($events, $this->firedEvents); } - /** - * Filter the given events against the fired events. - * - * @param array $events - * @return array - */ - protected function getFiredModelEvents(array $events) - { - return $this->getDispatched($events, $this->firedModelEvents); - } - /** * Specify a list of jobs that should be dispatched for the given operation. * From bbf280a9c58c59fc6dd5723cdaafc3c3ba130162 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 3 Nov 2016 09:44:29 -0500 Subject: [PATCH 3/9] wip --- .../Testing/Concerns/InteractsWithPages.php | 753 ------------------ .../Testing/Concerns/MakesHttpRequests.php | 624 ++------------- 2 files changed, 49 insertions(+), 1328 deletions(-) delete mode 100644 src/Illuminate/Foundation/Testing/Concerns/InteractsWithPages.php diff --git a/src/Illuminate/Foundation/Testing/Concerns/InteractsWithPages.php b/src/Illuminate/Foundation/Testing/Concerns/InteractsWithPages.php deleted file mode 100644 index 903c296f1096..000000000000 --- a/src/Illuminate/Foundation/Testing/Concerns/InteractsWithPages.php +++ /dev/null @@ -1,753 +0,0 @@ -makeRequest('GET', $uri); - } - - /** - * Visit the given named route with a GET request. - * - * @param string $route - * @param array $parameters - * @return $this - */ - public function visitRoute($route, $parameters = []) - { - return $this->makeRequest('GET', route($route, $parameters)); - } - - /** - * Make a request to the application and create a Crawler instance. - * - * @param string $method - * @param string $uri - * @param array $parameters - * @param array $cookies - * @param array $files - * @return $this - */ - protected function makeRequest($method, $uri, $parameters = [], $cookies = [], $files = []) - { - $uri = $this->prepareUrlForRequest($uri); - - $this->call($method, $uri, $parameters, $cookies, $files); - - $this->clearInputs()->followRedirects()->assertPageLoaded($uri); - - $this->currentUri = $this->app->make('request')->fullUrl(); - - $this->crawler = new Crawler($this->response->getContent(), $this->currentUri); - - return $this; - } - - /** - * Clean the crawler and the subcrawlers values to reset the page context. - * - * @return void - */ - protected function resetPageContext() - { - $this->crawler = null; - - $this->subCrawlers = []; - } - - /** - * Make a request to the application using the given form. - * - * @param \Symfony\Component\DomCrawler\Form $form - * @param array $uploads - * @return $this - */ - protected function makeRequestUsingForm(Form $form, array $uploads = []) - { - $files = $this->convertUploadsForTesting($form, $uploads); - - return $this->makeRequest( - $form->getMethod(), $form->getUri(), $this->extractParametersFromForm($form), [], $files - ); - } - - /** - * Extract the parameters from the given form. - * - * @param \Symfony\Component\DomCrawler\Form $form - * @return array - */ - protected function extractParametersFromForm(Form $form) - { - parse_str(http_build_query($form->getValues()), $parameters); - - return $parameters; - } - - /** - * Follow redirects from the last response. - * - * @return $this - */ - protected function followRedirects() - { - while ($this->response->isRedirect()) { - $this->makeRequest('GET', $this->response->getTargetUrl()); - } - - return $this; - } - - /** - * Clear the inputs for the current page. - * - * @return $this - */ - protected function clearInputs() - { - $this->inputs = []; - - $this->uploads = []; - - return $this; - } - - /** - * Assert that the current page matches a given URI. - * - * @param string $uri - * @return $this - */ - protected function seePageIs($uri) - { - $this->assertPageLoaded($uri = $this->prepareUrlForRequest($uri)); - - $this->assertEquals( - $uri, $this->currentUri, "Did not land on expected page [{$uri}].\n" - ); - - return $this; - } - - /** - * Assert that the current page matches a given named route. - * - * @param string $route - * @param array $parameters - * @return $this - */ - protected function seeRouteIs($route, $parameters = []) - { - return $this->seePageIs(route($route, $parameters)); - } - - /** - * Assert that a given page successfully loaded. - * - * @param string $uri - * @param string|null $message - * @return void - * - * @throws \Illuminate\Foundation\Testing\HttpException - */ - protected function assertPageLoaded($uri, $message = null) - { - $status = $this->response->getStatusCode(); - - try { - $this->assertEquals(200, $status); - } catch (PHPUnitException $e) { - $message = $message ?: "A request to [{$uri}] failed. Received status code [{$status}]."; - - $responseException = isset($this->response->exception) - ? $this->response->exception : null; - - throw new HttpException($message, null, $responseException); - } - } - - /** - * Narrow the test content to a specific area of the page. - * - * @param string $element - * @param \Closure $callback - * @return $this - */ - public function within($element, Closure $callback) - { - $this->subCrawlers[] = $this->crawler()->filter($element); - - $callback(); - - array_pop($this->subCrawlers); - - return $this; - } - - /** - * Get the current crawler according to the test context. - * - * @return \Symfony\Component\DomCrawler\Crawler - */ - protected function crawler() - { - if (! empty($this->subCrawlers)) { - return end($this->subCrawlers); - } - - return $this->crawler; - } - - /** - * Assert the given constraint. - * - * @param \Illuminate\Foundation\Testing\Constraints\PageConstraint $constraint - * @param bool $reverse - * @param string $message - * @return $this - */ - protected function assertInPage(PageConstraint $constraint, $reverse = false, $message = '') - { - if ($reverse) { - $constraint = new ReversePageConstraint($constraint); - } - - self::assertThat( - $this->crawler() ?: $this->response->getContent(), - $constraint, $message - ); - - return $this; - } - - /** - * Assert that a given string is seen on the current HTML. - * - * @param string $text - * @param bool $negate - * @return $this - */ - public function see($text, $negate = false) - { - return $this->assertInPage(new HasSource($text), $negate); - } - - /** - * Assert that a given string is not seen on the current HTML. - * - * @param string $text - * @return $this - */ - public function dontSee($text) - { - return $this->assertInPage(new HasSource($text), true); - } - - /** - * Assert that an element is present on the page. - * - * @param string $selector - * @param array $attributes - * @param bool $negate - * @return $this - */ - public function seeElement($selector, array $attributes = [], $negate = false) - { - return $this->assertInPage(new HasElement($selector, $attributes), $negate); - } - - /** - * Assert that an element is not present on the page. - * - * @param string $selector - * @param array $attributes - * @return $this - */ - public function dontSeeElement($selector, array $attributes = []) - { - return $this->assertInPage(new HasElement($selector, $attributes), true); - } - - /** - * Assert that a given string is seen on the current text. - * - * @param string $text - * @param bool $negate - * @return $this - */ - public function seeText($text, $negate = false) - { - return $this->assertInPage(new HasText($text), $negate); - } - - /** - * Assert that a given string is not seen on the current text. - * - * @param string $text - * @return $this - */ - public function dontSeeText($text) - { - return $this->assertInPage(new HasText($text), true); - } - - /** - * Assert that a given string is seen inside an element. - * - * @param string $element - * @param string $text - * @param bool $negate - * @return $this - */ - public function seeInElement($element, $text, $negate = false) - { - return $this->assertInPage(new HasInElement($element, $text), $negate); - } - - /** - * Assert that a given string is not seen inside an element. - * - * @param string $element - * @param string $text - * @return $this - */ - public function dontSeeInElement($element, $text) - { - return $this->assertInPage(new HasInElement($element, $text), true); - } - - /** - * Assert that a given link is seen on the page. - * - * @param string $text - * @param string|null $url - * @param bool $negate - * @return $this - */ - public function seeLink($text, $url = null, $negate = false) - { - return $this->assertInPage(new HasLink($text, $url), $negate); - } - - /** - * Assert that a given link is not seen on the page. - * - * @param string $text - * @param string|null $url - * @return $this - */ - public function dontSeeLink($text, $url = null) - { - return $this->assertInPage(new HasLink($text, $url), true); - } - - /** - * Assert that an input field contains the given value. - * - * @param string $selector - * @param string $expected - * @param bool $negate - * @return $this - */ - public function seeInField($selector, $expected, $negate = false) - { - return $this->assertInPage(new HasValue($selector, $expected), $negate); - } - - /** - * Assert that an input field does not contain the given value. - * - * @param string $selector - * @param string $value - * @return $this - */ - public function dontSeeInField($selector, $value) - { - return $this->assertInPage(new HasValue($selector, $value), true); - } - - /** - * Assert that the expected value is selected. - * - * @param string $selector - * @param string $value - * @param bool $negate - * @return $this - */ - public function seeIsSelected($selector, $value, $negate = false) - { - return $this->assertInPage(new IsSelected($selector, $value), $negate); - } - - /** - * Assert that the given value is not selected. - * - * @param string $selector - * @param string $value - * @return $this - */ - public function dontSeeIsSelected($selector, $value) - { - return $this->assertInPage(new IsSelected($selector, $value), true); - } - - /** - * Assert that the given checkbox is selected. - * - * @param string $selector - * @param bool $negate - * @return $this - */ - public function seeIsChecked($selector, $negate = false) - { - return $this->assertInPage(new IsChecked($selector), $negate); - } - - /** - * Assert that the given checkbox is not selected. - * - * @param string $selector - * @return $this - */ - public function dontSeeIsChecked($selector) - { - return $this->assertInPage(new IsChecked($selector), true); - } - - /** - * Click a link with the given body, name, or ID attribute. - * - * @param string $name - * @return $this - * - * @throws \InvalidArgumentException - */ - protected function click($name) - { - $link = $this->crawler()->selectLink($name); - - if (! count($link)) { - $link = $this->filterByNameOrId($name, 'a'); - - if (! count($link)) { - throw new InvalidArgumentException( - "Could not find a link with a body, name, or ID attribute of [{$name}]." - ); - } - } - - $this->visit($link->link()->getUri()); - - return $this; - } - - /** - * Fill an input field with the given text. - * - * @param string $text - * @param string $element - * @return $this - */ - protected function type($text, $element) - { - return $this->storeInput($element, $text); - } - - /** - * Check a checkbox on the page. - * - * @param string $element - * @return $this - */ - protected function check($element) - { - return $this->storeInput($element, true); - } - - /** - * Uncheck a checkbox on the page. - * - * @param string $element - * @return $this - */ - protected function uncheck($element) - { - return $this->storeInput($element, false); - } - - /** - * Select an option from a drop-down. - * - * @param string $option - * @param string $element - * @return $this - */ - protected function select($option, $element) - { - return $this->storeInput($element, $option); - } - - /** - * Attach a file to a form field on the page. - * - * @param string $absolutePath - * @param string $element - * @return $this - */ - protected function attach($absolutePath, $element) - { - $this->uploads[$element] = $absolutePath; - - return $this->storeInput($element, $absolutePath); - } - - /** - * Submit a form using the button with the given text value. - * - * @param string $buttonText - * @return $this - */ - protected function press($buttonText) - { - return $this->submitForm($buttonText, $this->inputs, $this->uploads); - } - - /** - * Submit a form on the page with the given input. - * - * @param string $buttonText - * @param array $inputs - * @param array $uploads - * @return $this - */ - protected function submitForm($buttonText, $inputs = [], $uploads = []) - { - $this->makeRequestUsingForm($this->fillForm($buttonText, $inputs), $uploads); - - return $this; - } - - /** - * Fill the form with the given data. - * - * @param string $buttonText - * @param array $inputs - * @return \Symfony\Component\DomCrawler\Form - */ - protected function fillForm($buttonText, $inputs = []) - { - if (! is_string($buttonText)) { - $inputs = $buttonText; - - $buttonText = null; - } - - return $this->getForm($buttonText)->setValues($inputs); - } - - /** - * Get the form from the page with the given submit button text. - * - * @param string|null $buttonText - * @return \Symfony\Component\DomCrawler\Form - * - * @throws \InvalidArgumentException - */ - protected function getForm($buttonText = null) - { - try { - if ($buttonText) { - return $this->crawler()->selectButton($buttonText)->form(); - } - - return $this->crawler()->filter('form')->form(); - } catch (InvalidArgumentException $e) { - throw new InvalidArgumentException( - "Could not find a form that has submit button [{$buttonText}]." - ); - } - } - - /** - * Store a form input in the local array. - * - * @param string $element - * @param string $text - * @return $this - */ - protected function storeInput($element, $text) - { - $this->assertFilterProducesResults($element); - - $element = str_replace(['#', '[]'], '', $element); - - $this->inputs[$element] = $text; - - return $this; - } - - /** - * Assert that a filtered Crawler returns nodes. - * - * @param string $filter - * @return void - * - * @throws \InvalidArgumentException - */ - protected function assertFilterProducesResults($filter) - { - $crawler = $this->filterByNameOrId($filter); - - if (! count($crawler)) { - throw new InvalidArgumentException( - "Nothing matched the filter [{$filter}] CSS query provided for [{$this->currentUri}]." - ); - } - } - - /** - * Filter elements according to the given name or ID attribute. - * - * @param string $name - * @param array|string $elements - * @return \Symfony\Component\DomCrawler\Crawler - */ - protected function filterByNameOrId($name, $elements = '*') - { - $name = str_replace('#', '', $name); - - $id = str_replace(['[', ']'], ['\\[', '\\]'], $name); - - $elements = is_array($elements) ? $elements : [$elements]; - - array_walk($elements, function (&$element) use ($name, $id) { - $element = "{$element}#{$id}, {$element}[name='{$name}']"; - }); - - return $this->crawler()->filter(implode(', ', $elements)); - } - - /** - * Convert the given uploads to UploadedFile instances. - * - * @param \Symfony\Component\DomCrawler\Form $form - * @param array $uploads - * @return array - */ - protected function convertUploadsForTesting(Form $form, array $uploads) - { - $files = $form->getFiles(); - - $names = array_keys($files); - - $files = array_map(function (array $file, $name) use ($uploads) { - return isset($uploads[$name]) - ? $this->getUploadedFileForTesting($file, $uploads, $name) - : $file; - }, $files, $names); - - $uploads = array_combine($names, $files); - - foreach ($uploads as $key => $file) { - if (preg_match('/.*?(?:\[.*?\])+/', $key)) { - $this->prepareArrayBasedFileInput($uploads, $key, $file); - } - } - - return $uploads; - } - - /** - * Store an array based file upload with the proper nested array structure. - * - * @param array $uploads - * @param string $key - * @param mixed $file - */ - protected function prepareArrayBasedFileInput(&$uploads, $key, $file) - { - preg_match_all('/([^\[\]]+)/', $key, $segments); - - $segments = array_reverse($segments[1]); - - $newKey = array_pop($segments); - - foreach ($segments as $segment) { - $file = [$segment => $file]; - } - - $uploads[$newKey] = $file; - - unset($uploads[$key]); - } - - /** - * Create an UploadedFile instance for testing. - * - * @param array $file - * @param array $uploads - * @param string $name - * @return \Illuminate\Http\UploadedFile - */ - protected function getUploadedFileForTesting($file, $uploads, $name) - { - return new UploadedFile( - $file['tmp_name'], basename($uploads[$name]), $file['type'], $file['size'], $file['error'], true - ); - } -} diff --git a/src/Illuminate/Foundation/Testing/Concerns/MakesHttpRequests.php b/src/Illuminate/Foundation/Testing/Concerns/MakesHttpRequests.php index 4efbc88a14f2..a51d07d23b5c 100644 --- a/src/Illuminate/Foundation/Testing/Concerns/MakesHttpRequests.php +++ b/src/Illuminate/Foundation/Testing/Concerns/MakesHttpRequests.php @@ -3,34 +3,17 @@ namespace Illuminate\Foundation\Testing\Concerns; use Closure; -use Illuminate\Support\Arr; use Illuminate\Support\Str; use Illuminate\Http\Request; use Illuminate\Http\UploadedFile; -use Illuminate\Contracts\View\View; use PHPUnit_Framework_Assert as PHPUnit; use PHPUnit_Framework_ExpectationFailedException; +use Illuminate\Contracts\Http\Kernel as HttpKernel; use Symfony\Component\HttpFoundation\Request as SymfonyRequest; use Symfony\Component\HttpFoundation\File\UploadedFile as SymfonyUploadedFile; trait MakesHttpRequests { - use InteractsWithPages; - - /** - * The last response returned by the application. - * - * @var \Illuminate\Http\Response - */ - protected $response; - - /** - * The current URL being viewed. - * - * @var string - */ - protected $currentUri; - /** * Additional server variables for the request. * @@ -39,64 +22,41 @@ trait MakesHttpRequests protected $serverVariables = []; /** - * Disable middleware for the test. + * Define a set of server variables to be sent with the requests. * + * @param array $server * @return $this */ - public function withoutMiddleware() + protected function withServerVariables(array $server) { - $this->app->instance('middleware.disable', true); + $this->serverVariables = $server; return $this; } /** - * Visit the given URI with a JSON request. + * Disable middleware for the test. * - * @param string $method - * @param string $uri - * @param array $data - * @param array $headers * @return $this */ - public function json($method, $uri, array $data = [], array $headers = []) + public function withoutMiddleware() { - $files = $this->extractFilesFromDataArray($data); - - $content = json_encode($data); - - $headers = array_merge([ - 'CONTENT_LENGTH' => mb_strlen($content, '8bit'), - 'CONTENT_TYPE' => 'application/json', - 'Accept' => 'application/json', - ], $headers); - - $this->call( - $method, $uri, [], [], $files, $this->transformHeadersToServerVars($headers), $content - ); + $this->app->instance('middleware.disable', true); return $this; } /** - * Extract the file uploads from the given data array. + * Send the given request through the application. * - * @param array $data - * @return array + * This method allows you to fully customize the entire Request object. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\Response $response */ - protected function extractFilesFromDataArray(&$data) + public function handle(Request $request) { - $files = []; - - foreach ($data as $key => $value) { - if ($value instanceof SymfonyUploadedFile) { - $files[$key] = $value; - - unset($data[$key]); - } - } - - return $files; + return $this->app->prepareResponse($this->app->handle($request)); } /** @@ -110,9 +70,7 @@ public function get($uri, array $headers = []) { $server = $this->transformHeadersToServerVars($headers); - $this->call('GET', $uri, [], [], [], $server); - - return $this; + return $this->call('GET', $uri, [], [], [], $server); } /** @@ -139,9 +97,7 @@ public function post($uri, array $data = [], array $headers = []) { $server = $this->transformHeadersToServerVars($headers); - $this->call('POST', $uri, $data, [], [], $server); - - return $this; + return $this->call('POST', $uri, $data, [], [], $server); } /** @@ -169,9 +125,7 @@ public function put($uri, array $data = [], array $headers = []) { $server = $this->transformHeadersToServerVars($headers); - $this->call('PUT', $uri, $data, [], [], $server); - - return $this; + return $this->call('PUT', $uri, $data, [], [], $server); } /** @@ -199,9 +153,7 @@ public function patch($uri, array $data = [], array $headers = []) { $server = $this->transformHeadersToServerVars($headers); - $this->call('PATCH', $uri, $data, [], [], $server); - - return $this; + return $this->call('PATCH', $uri, $data, [], [], $server); } /** @@ -229,9 +181,7 @@ public function delete($uri, array $data = [], array $headers = []) { $server = $this->transformHeadersToServerVars($headers); - $this->call('DELETE', $uri, $data, [], [], $server); - - return $this; + return $this->call('DELETE', $uri, $data, [], [], $server); } /** @@ -248,310 +198,52 @@ public function deleteJson($uri, array $data = [], array $headers = []) } /** - * Send the given request through the application. - * - * This method allows you to fully customize the entire Request object. - * - * @param \Illuminate\Http\Request $request - * @return $this - */ - public function handle(Request $request) - { - $this->currentUri = $request->fullUrl(); - - $this->response = $this->app->prepareResponse($this->app->handle($request)); - - return $this; - } - - /** - * Assert that the response contains JSON. - * - * @param array|null $data - * @return $this - */ - protected function shouldReturnJson(array $data = null) - { - return $this->receiveJson($data); - } - - /** - * Assert that the response contains JSON. - * - * @param array|null $data - * @return $this|null - */ - protected function receiveJson($data = null) - { - return $this->seeJson($data); - } - - /** - * Assert that the response contains an exact JSON array. - * - * @param array $data - * @return $this - */ - public function seeJsonEquals(array $data) - { - $actual = json_encode(Arr::sortRecursive( - json_decode($this->response->getContent(), true) - )); - - $this->assertEquals(json_encode(Arr::sortRecursive($data)), $actual); - - return $this; - } - - /** - * Assert that the response contains JSON. - * - * @param array|null $data - * @param bool $negate - * @return $this - */ - public function seeJson(array $data = null, $negate = false) - { - if (is_null($data)) { - $this->assertJson( - $this->response->getContent(), "JSON was not returned from [{$this->currentUri}]." - ); - - return $this; - } - - try { - $this->seeJsonEquals($data); - - return $this; - } catch (PHPUnit_Framework_ExpectationFailedException $e) { - return $this->seeJsonContains($data, $negate); - } - } - - /** - * Assert that the response doesn't contain JSON. - * - * @param array|null $data - * @return $this - */ - public function dontSeeJson(array $data = null) - { - return $this->seeJson($data, true); - } - - /** - * Assert that the JSON response has a given structure. - * - * @param array|null $structure - * @param array|null $responseData - * @return $this - */ - public function seeJsonStructure(array $structure = null, $responseData = null) - { - if (is_null($structure)) { - return $this->seeJson(); - } - - if (! $responseData) { - $responseData = json_decode($this->response->getContent(), true); - } - - foreach ($structure as $key => $value) { - if (is_array($value) && $key === '*') { - $this->assertInternalType('array', $responseData); - - foreach ($responseData as $responseDataItem) { - $this->seeJsonStructure($structure['*'], $responseDataItem); - } - } elseif (is_array($value)) { - $this->assertArrayHasKey($key, $responseData); - $this->seeJsonStructure($structure[$key], $responseData[$key]); - } else { - $this->assertArrayHasKey($value, $responseData); - } - } - - return $this; - } - - /** - * Assert that the response contains the given JSON. + * Visit the given URI with a JSON request. * + * @param string $method + * @param string $uri * @param array $data - * @param bool $negate + * @param array $headers * @return $this */ - protected function seeJsonContains(array $data, $negate = false) + public function json($method, $uri, array $data = [], array $headers = []) { - $method = $negate ? 'assertFalse' : 'assertTrue'; + $files = $this->extractFilesFromDataArray($data); - $actual = json_encode(Arr::sortRecursive( - (array) $this->decodeResponseJson() - )); + $content = json_encode($data); - foreach (Arr::sortRecursive($data) as $key => $value) { - $expected = $this->formatToExpectedJson($key, $value); + $headers = array_merge([ + 'CONTENT_LENGTH' => mb_strlen($content, '8bit'), + 'CONTENT_TYPE' => 'application/json', + 'Accept' => 'application/json', + ], $headers); - $this->{$method}( - Str::contains($actual, $expected), - ($negate ? 'Found unexpected' : 'Unable to find').' JSON fragment'.PHP_EOL."[{$expected}]".PHP_EOL.'within'.PHP_EOL."[{$actual}]." - ); - } + $this->call( + $method, $uri, [], [], $files, $this->transformHeadersToServerVars($headers), $content + ); return $this; } /** - * Assert that the response is a superset of the given JSON. + * Extract the file uploads from the given data array. * * @param array $data - * @return $this - */ - protected function seeJsonSubset(array $data) - { - $this->assertArraySubset($data, $this->decodeResponseJson()); - - return $this; - } - - /** - * Validate and return the decoded response JSON. - * * @return array */ - protected function decodeResponseJson() - { - $decodedResponse = json_decode($this->response->getContent(), true); - - if (is_null($decodedResponse) || $decodedResponse === false) { - $this->fail('Invalid JSON was returned from the route. Perhaps an exception was thrown?'); - } - - return $decodedResponse; - } - - /** - * Format the given key and value into a JSON string for expectation checks. - * - * @param string $key - * @param mixed $value - * @return string - */ - protected function formatToExpectedJson($key, $value) - { - $expected = json_encode([$key => $value]); - - if (Str::startsWith($expected, '{')) { - $expected = substr($expected, 1); - } - - if (Str::endsWith($expected, '}')) { - $expected = substr($expected, 0, -1); - } - - return trim($expected); - } - - /** - * Asserts that the status code of the response matches the given code. - * - * @param int $status - * @return $this - */ - protected function seeStatusCode($status) - { - $this->assertEquals($status, $this->response->getStatusCode()); - - return $this; - } - - /** - * Asserts that the response contains the given header and equals the optional value. - * - * @param string $headerName - * @param mixed $value - * @return $this - */ - protected function seeHeader($headerName, $value = null) - { - $headers = $this->response->headers; - - $this->assertTrue($headers->has($headerName), "Header [{$headerName}] not present on response."); - - if (! is_null($value)) { - $this->assertEquals( - $headers->get($headerName), $value, - "Header [{$headerName}] was found, but value [{$headers->get($headerName)}] does not match [{$value}]." - ); - } - - return $this; - } - - /** - * Asserts that the response contains the given cookie and equals the optional value. - * - * @param string $cookieName - * @param mixed $value - * @return $this - */ - protected function seePlainCookie($cookieName, $value = null) - { - return $this->seeCookie($cookieName, $value, false); - } - - /** - * Asserts that the response contains the given cookie and equals the optional value. - * - * @param string $cookieName - * @param mixed $value - * @param bool $encrypted - * @return $this - */ - protected function seeCookie($cookieName, $value = null, $encrypted = true) + protected function extractFilesFromDataArray(&$data) { - $headers = $this->response->headers; + $files = []; - $exist = false; + foreach ($data as $key => $value) { + if ($value instanceof SymfonyUploadedFile) { + $files[$key] = $value; - foreach ($headers->getCookies() as $cookie) { - if ($cookie->getName() === $cookieName) { - $exist = true; - break; + unset($data[$key]); } } - $this->assertTrue($exist, "Cookie [{$cookieName}] not present on response."); - - if (! $exist || is_null($value)) { - return $this; - } - - $cookieValue = $cookie->getValue(); - - $actual = $encrypted - ? $this->app['encrypter']->decrypt($cookieValue) : $cookieValue; - - $this->assertEquals( - $actual, $value, - "Cookie [{$cookieName}] was found, but value [{$actual}] does not match [{$value}]." - ); - - return $this; - } - - /** - * Define a set of server variables to be sent with the requests. - * - * @param array $server - * @return $this - */ - protected function withServerVariables(array $server) - { - $this->serverVariables = $server; - - return $this; + return $files; } /** @@ -568,83 +260,20 @@ protected function withServerVariables(array $server) */ public function call($method, $uri, $parameters = [], $cookies = [], $files = [], $server = [], $content = null) { - $kernel = $this->app->make('Illuminate\Contracts\Http\Kernel'); - - $this->currentUri = $this->prepareUrlForRequest($uri); - - $this->resetPageContext(); + $kernel = $this->app->make(HttpKernel::class); $symfonyRequest = SymfonyRequest::create( - $this->currentUri, $method, $parameters, + $this->prepareUrlForRequest($uri), $method, $parameters, $cookies, $this->filterFiles($files), array_replace($this->serverVariables, $server), $content ); - $request = Request::createFromBase($symfonyRequest); - - $response = $kernel->handle($request); + $response = $kernel->handle( + $request = Request::createFromBase($symfonyRequest) + ); $kernel->terminate($request, $response); - return $this->response = $response; - } - - /** - * Call the given HTTPS URI and return the Response. - * - * @param string $method - * @param string $uri - * @param array $parameters - * @param array $cookies - * @param array $files - * @param array $server - * @param string $content - * @return \Illuminate\Http\Response - */ - public function callSecure($method, $uri, $parameters = [], $cookies = [], $files = [], $server = [], $content = null) - { - $uri = $this->app['url']->secure(ltrim($uri, '/')); - - return $this->response = $this->call($method, $uri, $parameters, $cookies, $files, $server, $content); - } - - /** - * Call a controller action and return the Response. - * - * @param string $method - * @param string $action - * @param array $wildcards - * @param array $parameters - * @param array $cookies - * @param array $files - * @param array $server - * @param string $content - * @return \Illuminate\Http\Response - */ - public function action($method, $action, $wildcards = [], $parameters = [], $cookies = [], $files = [], $server = [], $content = null) - { - $uri = $this->app['url']->action($action, $wildcards, true); - - return $this->response = $this->call($method, $uri, $parameters, $cookies, $files, $server, $content); - } - - /** - * Call a named route and return the Response. - * - * @param string $method - * @param string $name - * @param array $routeParameters - * @param array $parameters - * @param array $cookies - * @param array $files - * @param array $server - * @param string $content - * @return \Illuminate\Http\Response - */ - public function route($method, $name, $routeParameters = [], $parameters = [], $cookies = [], $files = [], $server = [], $content = null) - { - $uri = $this->app['url']->route($name, $routeParameters); - - return $this->response = $this->call($method, $uri, $parameters, $cookies, $files, $server, $content); + return $response; } /** @@ -718,159 +347,4 @@ protected function filterFiles($files) return $files; } - - /** - * Assert that the client response has an OK status code. - * - * @return $this - */ - public function assertResponseOk() - { - $actual = $this->response->getStatusCode(); - - PHPUnit::assertTrue($this->response->isOk(), "Expected status code 200, got {$actual}."); - - return $this; - } - - /** - * Assert that the client response has a given code. - * - * @param int $code - * @return $this - */ - public function assertResponseStatus($code) - { - $actual = $this->response->getStatusCode(); - - PHPUnit::assertEquals($code, $this->response->getStatusCode(), "Expected status code {$code}, got {$actual}."); - - return $this; - } - - /** - * Assert that the response view has a given piece of bound data. - * - * @param string|array $key - * @param mixed $value - * @return $this - */ - public function assertViewHas($key, $value = null) - { - if (is_array($key)) { - return $this->assertViewHasAll($key); - } - - if (! isset($this->response->original) || ! $this->response->original instanceof View) { - return PHPUnit::assertTrue(false, 'The response was not a view.'); - } - - if (is_null($value)) { - PHPUnit::assertArrayHasKey($key, $this->response->original->getData()); - } elseif ($value instanceof Closure) { - PHPUnit::assertTrue($value($this->response->original->$key)); - } else { - PHPUnit::assertEquals($value, $this->response->original->$key); - } - - return $this; - } - - /** - * Assert that the view has a given list of bound data. - * - * @param array $bindings - * @return $this - */ - public function assertViewHasAll(array $bindings) - { - foreach ($bindings as $key => $value) { - if (is_int($key)) { - $this->assertViewHas($value); - } else { - $this->assertViewHas($key, $value); - } - } - - return $this; - } - - /** - * Assert that the response view is missing a piece of bound data. - * - * @param string $key - * @return $this - */ - public function assertViewMissing($key) - { - if (! isset($this->response->original) || ! $this->response->original instanceof View) { - return PHPUnit::assertTrue(false, 'The response was not a view.'); - } - - PHPUnit::assertArrayNotHasKey($key, $this->response->original->getData()); - - return $this; - } - - /** - * Assert whether the client was redirected to a given URI. - * - * @param string $uri - * @param array $with - * @return $this - */ - public function assertRedirectedTo($uri, $with = []) - { - PHPUnit::assertInstanceOf('Illuminate\Http\RedirectResponse', $this->response); - - PHPUnit::assertEquals($this->app['url']->to($uri), $this->response->headers->get('Location')); - - $this->assertSessionHasAll($with); - - return $this; - } - - /** - * Assert whether the client was redirected to a given route. - * - * @param string $name - * @param array $parameters - * @param array $with - * @return $this - */ - public function assertRedirectedToRoute($name, $parameters = [], $with = []) - { - return $this->assertRedirectedTo($this->app['url']->route($name, $parameters), $with); - } - - /** - * Assert whether the client was redirected to a given action. - * - * @param string $name - * @param array $parameters - * @param array $with - * @return $this - */ - public function assertRedirectedToAction($name, $parameters = [], $with = []) - { - return $this->assertRedirectedTo($this->app['url']->action($name, $parameters), $with); - } - - /** - * Dump the content from the last response. - * - * @return void - */ - public function dump() - { - $content = $this->response->getContent(); - - $json = json_decode($content); - - if (json_last_error() === JSON_ERROR_NONE) { - $content = $json; - } - - dd($content); - } } From a71bbac6b33d738a3f5ba76f856bf62cf63c7e82 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 3 Nov 2016 14:38:49 -0500 Subject: [PATCH 4/9] wip --- .../Testing/Concerns/MakesHttpRequests.php | 54 ++---- .../Foundation/Testing/TestResponse.php | 177 ++++++++++++++++++ 2 files changed, 191 insertions(+), 40 deletions(-) create mode 100644 src/Illuminate/Foundation/Testing/TestResponse.php diff --git a/src/Illuminate/Foundation/Testing/Concerns/MakesHttpRequests.php b/src/Illuminate/Foundation/Testing/Concerns/MakesHttpRequests.php index a51d07d23b5c..34f037fce6db 100644 --- a/src/Illuminate/Foundation/Testing/Concerns/MakesHttpRequests.php +++ b/src/Illuminate/Foundation/Testing/Concerns/MakesHttpRequests.php @@ -7,6 +7,7 @@ use Illuminate\Http\Request; use Illuminate\Http\UploadedFile; use PHPUnit_Framework_Assert as PHPUnit; +use Illuminate\Foundation\Testing\TestResponse; use PHPUnit_Framework_ExpectationFailedException; use Illuminate\Contracts\Http\Kernel as HttpKernel; use Symfony\Component\HttpFoundation\Request as SymfonyRequest; @@ -225,27 +226,6 @@ public function json($method, $uri, array $data = [], array $headers = []) return $this; } - /** - * Extract the file uploads from the given data array. - * - * @param array $data - * @return array - */ - protected function extractFilesFromDataArray(&$data) - { - $files = []; - - foreach ($data as $key => $value) { - if ($value instanceof SymfonyUploadedFile) { - $files[$key] = $value; - - unset($data[$key]); - } - } - - return $files; - } - /** * Call the given URI and return the Response. * @@ -262,9 +242,11 @@ public function call($method, $uri, $parameters = [], $cookies = [], $files = [] { $kernel = $this->app->make(HttpKernel::class); + $files = array_merge($files, $this->extractFilesFromDataArray($parameters)); + $symfonyRequest = SymfonyRequest::create( $this->prepareUrlForRequest($uri), $method, $parameters, - $cookies, $this->filterFiles($files), array_replace($this->serverVariables, $server), $content + $cookies, $files, array_replace($this->serverVariables, $server), $content ); $response = $kernel->handle( @@ -273,7 +255,7 @@ public function call($method, $uri, $parameters = [], $cookies = [], $files = [] $kernel->terminate($request, $response); - return $response; + return TestResponse::fromBaseResponse($response); } /** @@ -320,29 +302,21 @@ protected function transformHeadersToServerVars(array $headers) } /** - * Filter the given array of files, removing any empty values. + * Extract the file uploads from the given data array. * - * @param array $files - * @return mixed + * @param array $data + * @return array */ - protected function filterFiles($files) + protected function extractFilesFromDataArray(&$data) { - foreach ($files as $key => $file) { - if ($file instanceof UploadedFile) { - continue; - } + $files = []; - if (is_array($file)) { - if (! isset($file['name'])) { - $files[$key] = $this->filterFiles($files[$key]); - } elseif (isset($files[$key]['error']) && $files[$key]['error'] !== 0) { - unset($files[$key]); - } + foreach ($data as $key => $value) { + if ($value instanceof SymfonyUploadedFile) { + $files[$key] = $value; - continue; + unset($data[$key]); } - - unset($files[$key]); } return $files; diff --git a/src/Illuminate/Foundation/Testing/TestResponse.php b/src/Illuminate/Foundation/Testing/TestResponse.php new file mode 100644 index 000000000000..1abc2a8d6762 --- /dev/null +++ b/src/Illuminate/Foundation/Testing/TestResponse.php @@ -0,0 +1,177 @@ +getContent(), $response->status() + ); + + $testResponse->headers = $response->headers; + + return $testResponse; + } + + /** + * Assert that the response has an OK status code. + * + * @return void + */ + public function assertHasStatus($status) + { + $actual = $this->getStatusCode(); + + PHPUnit::assertTrue($this->isOk(), "Expected status code 200, got {$actual}."); + } + + /** + * Assert whether the response is redirecting to a given URI. + * + * @param string $uri + * @return void + */ + public function assertIsRedirect($uri) + { + PHPUnit::assertTrue( + $this->isRedirect(), + 'Response status code ['.$this->status().'] is not a redirect status code.' + ); + + PHPUnit::assertEquals(app('url')->to($uri), $this->headers->get('Location')); + } + + /** + * Asserts that the response contains the given header and equals the optional value. + * + * @param string $headerName + * @param mixed $value + * @return $this + */ + public function assertHasHeader($headerName, $value = null) + { + PHPUnit::assertTrue( + $this->headers->has($headerName), "Header [{$headerName}] not present on response." + ); + + if (! is_null($value)) { + PHPUnit::assertEquals( + $this->headers->get($headerName), $value, + "Header [{$headerName}] was found, but value [{$this->headers->get($headerName)}] does not match [{$value}]." + ); + } + + return $this; + } + + /** + * Asserts that the response contains the given cookie and equals the optional value. + * + * @param string $cookieName + * @param mixed $value + * @return void + */ + public function assertHasPlainCookie($cookieName, $value = null) + { + $this->seeCookie($cookieName, $value, false); + } + + /** + * Asserts that the response contains the given cookie and equals the optional value. + * + * @param string $cookieName + * @param mixed $value + * @param bool $encrypted + * @return void + */ + public function assertHasCookie($cookieName, $value = null, $encrypted = true) + { + $headers = $this->headers; + + $exist = false; + + foreach ($headers->getCookies() as $cookie) { + if ($cookie->getName() === $cookieName) { + $exist = true; + break; + } + } + + PHPUnit::assertTrue($exist, "Cookie [{$cookieName}] not present on response."); + + if (! $exist || is_null($value)) { + return $this; + } + + $cookieValue = $cookie->getValue(); + + $actual = $encrypted + ? app('encrypter')->decrypt($cookieValue) : $cookieValue; + + PHPUnit::assertEquals( + $actual, $value, + "Cookie [{$cookieName}] was found, but value [{$actual}] does not match [{$value}]." + ); + } + + /** + * Assert that the response is a superset of the given JSON. + * + * @param array $data + * @return void + */ + public function assertHasJson(array $data) + { + PHPUnit::assertArraySubset($data, $this->decodeResponseJson()); + } + + /** + * Validate and return the decoded response JSON. + * + * @return array + */ + protected function decodeResponseJson() + { + $decodedResponse = json_decode($this->getContent(), true); + + if (is_null($decodedResponse) || $decodedResponse === false) { + PHPUnit::fail('Invalid JSON was returned from the route. Perhaps an exception was thrown?'); + } + + return $decodedResponse; + } + + /** + * Format the given key and value into a JSON string for expectation checks. + * + * @param string $key + * @param mixed $value + * @return string + */ + protected function formatToExpectedJson($key, $value) + { + $expected = json_encode([$key => $value]); + + if (Str::startsWith($expected, '{')) { + $expected = substr($expected, 1); + } + + if (Str::endsWith($expected, '}')) { + $expected = substr($expected, 0, -1); + } + + return trim($expected); + } +} From 203f1d3ca17469668fb940a7792890936c4ac77a Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 3 Nov 2016 15:41:25 -0500 Subject: [PATCH 5/9] wip --- .../Foundation/Testing/TestResponse.php | 135 +++++++++++++++++- 1 file changed, 131 insertions(+), 4 deletions(-) diff --git a/src/Illuminate/Foundation/Testing/TestResponse.php b/src/Illuminate/Foundation/Testing/TestResponse.php index 1abc2a8d6762..ac1d962b6e88 100644 --- a/src/Illuminate/Foundation/Testing/TestResponse.php +++ b/src/Illuminate/Foundation/Testing/TestResponse.php @@ -2,8 +2,10 @@ namespace Illuminate\Foundation\Testing; +use Closure; use Illuminate\Support\Str; use Illuminate\Http\Response; +use Illuminate\Contracts\View\View; use PHPUnit_Framework_Assert as PHPUnit; class TestResponse extends Response @@ -22,6 +24,14 @@ public static function fromBaseResponse($response) $testResponse->headers = $response->headers; + if (isset($response->original)) { + $testResponse->original = $response->original; + } + + if (isset($response->exception)) { + $testResponse->exception = $response->exception; + } + return $testResponse; } @@ -46,8 +56,7 @@ public function assertHasStatus($status) public function assertIsRedirect($uri) { PHPUnit::assertTrue( - $this->isRedirect(), - 'Response status code ['.$this->status().'] is not a redirect status code.' + $this->isRedirect(), 'Response status code ['.$this->status().'] is not a redirect status code.' ); PHPUnit::assertEquals(app('url')->to($uri), $this->headers->get('Location')); @@ -66,10 +75,12 @@ public function assertHasHeader($headerName, $value = null) $this->headers->has($headerName), "Header [{$headerName}] not present on response." ); + $actual = $this->headers->get($headerName); + if (! is_null($value)) { PHPUnit::assertEquals( $this->headers->get($headerName), $value, - "Header [{$headerName}] was found, but value [{$this->headers->get($headerName)}] does not match [{$value}]." + "Header [{$headerName}] was found, but value [{$actual}] does not match [{$value}]." ); } @@ -147,7 +158,11 @@ protected function decodeResponseJson() $decodedResponse = json_decode($this->getContent(), true); if (is_null($decodedResponse) || $decodedResponse === false) { - PHPUnit::fail('Invalid JSON was returned from the route. Perhaps an exception was thrown?'); + if ($this->exception) { + throw $this->exception; + } else { + PHPUnit::fail('Invalid JSON was returned from the route.'); + } } return $decodedResponse; @@ -174,4 +189,116 @@ protected function formatToExpectedJson($key, $value) return trim($expected); } + + /** + * Assert that the response view has a given piece of bound data. + * + * @param string|array $key + * @param mixed $value + * @return void + */ + public function assertViewHas($key, $value = null) + { + if (is_array($key)) { + return $this->assertViewHasAll($key); + } + + if (! isset($this->original) || ! $this->original instanceof View) { + return PHPUnit::assertTrue(false, 'The response is not a view.'); + } + + if (is_null($value)) { + PHPUnit::assertArrayHasKey($key, $this->original->getData()); + } elseif ($value instanceof Closure) { + PHPUnit::assertTrue($value($this->original->$key)); + } else { + PHPUnit::assertEquals($value, $this->original->$key); + } + } + + /** + * Assert that the response view has a given list of bound data. + * + * @param array $bindings + * @return void + */ + public function assertViewHasAll(array $bindings) + { + foreach ($bindings as $key => $value) { + if (is_int($key)) { + $this->assertViewHas($value); + } else { + $this->assertViewHas($key, $value); + } + } + } + + /** + * Assert that the response view is missing a piece of bound data. + * + * @param string $key + * @return void + */ + public function assertViewMissing($key) + { + if (! isset($this->original) || ! $this->original instanceof View) { + return PHPUnit::fail('The response is not a view.'); + } + + PHPUnit::assertArrayNotHasKey($key, $this->original->getData()); + } + + /** + * Assert that the session has a given value. + * + * @param string|array $key + * @param mixed $value + * @return void + */ + public function assertSessionHas($key, $value = null) + { + if (is_array($key)) { + return $this->assertSessionHasAll($key); + } + + if (is_null($value)) { + PHPUnit::assertTrue(app('session.store')->has($key), "Session is missing expected key [{$key}]."); + } else { + PHPUnit::assertEquals($value, app('session.store')->get($key)); + } + } + + /** + * Assert that the session has a given list of values. + * + * @param array $bindings + * @return void + */ + public function assertSessionHasAll(array $bindings) + { + foreach ($bindings as $key => $value) { + if (is_int($key)) { + $this->assertSessionHas($value); + } else { + $this->assertSessionHas($key, $value); + } + } + } + + /** + * Assert that the session does not have a given key. + * + * @param string|array $key + * @return void + */ + public function assertSessionMissing($key) + { + if (is_array($key)) { + foreach ($key as $value) { + $this->assertSessionMissing($value); + } + } else { + PHPUnit::assertFalse(app('session.store')->has($key), "Session has unexpected key [{$key}]."); + } + } } From 2a2c54f95c4a3d8916699d315910d65a13f83e62 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 3 Nov 2016 20:10:04 -0500 Subject: [PATCH 6/9] wip --- .../Foundation/Testing/TestResponse.php | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/Illuminate/Foundation/Testing/TestResponse.php b/src/Illuminate/Foundation/Testing/TestResponse.php index ac1d962b6e88..2fa7143207d4 100644 --- a/src/Illuminate/Foundation/Testing/TestResponse.php +++ b/src/Illuminate/Foundation/Testing/TestResponse.php @@ -262,7 +262,10 @@ public function assertSessionHas($key, $value = null) } if (is_null($value)) { - PHPUnit::assertTrue(app('session.store')->has($key), "Session is missing expected key [{$key}]."); + PHPUnit::assertTrue( + $this->session()->has($key), + "Session is missing expected key [{$key}]." + ); } else { PHPUnit::assertEquals($value, app('session.store')->get($key)); } @@ -298,7 +301,20 @@ public function assertSessionMissing($key) $this->assertSessionMissing($value); } } else { - PHPUnit::assertFalse(app('session.store')->has($key), "Session has unexpected key [{$key}]."); + PHPUnit::assertFalse( + $this->session()->has($key), + "Session has unexpected key [{$key}]." + ); } } + + /** + * Get the current session store. + * + * @return \Illuminate\Session\Store + */ + protected function session() + { + return app('session.store'); + } } From 4ddd0012a1c055b458bc644cea9c95bdffc1185c Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 4 Nov 2016 08:23:39 -0500 Subject: [PATCH 7/9] wip --- .../Testing/Concerns/InteractsWithConsole.php | 9 +-------- .../Testing/Concerns/MakesHttpRequests.php | 14 -------------- 2 files changed, 1 insertion(+), 22 deletions(-) diff --git a/src/Illuminate/Foundation/Testing/Concerns/InteractsWithConsole.php b/src/Illuminate/Foundation/Testing/Concerns/InteractsWithConsole.php index 5b184286ed9a..fb54aadeb909 100644 --- a/src/Illuminate/Foundation/Testing/Concerns/InteractsWithConsole.php +++ b/src/Illuminate/Foundation/Testing/Concerns/InteractsWithConsole.php @@ -6,13 +6,6 @@ trait InteractsWithConsole { - /** - * The last code returned by Artisan CLI. - * - * @var int - */ - protected $code; - /** * Call artisan command and return code. * @@ -22,6 +15,6 @@ trait InteractsWithConsole */ public function artisan($command, $parameters = []) { - return $this->code = $this->app[Kernel::class]->call($command, $parameters); + return $this->app[Kernel::class]->call($command, $parameters); } } diff --git a/src/Illuminate/Foundation/Testing/Concerns/MakesHttpRequests.php b/src/Illuminate/Foundation/Testing/Concerns/MakesHttpRequests.php index 34f037fce6db..67ea4c7455b7 100644 --- a/src/Illuminate/Foundation/Testing/Concerns/MakesHttpRequests.php +++ b/src/Illuminate/Foundation/Testing/Concerns/MakesHttpRequests.php @@ -6,7 +6,6 @@ use Illuminate\Support\Str; use Illuminate\Http\Request; use Illuminate\Http\UploadedFile; -use PHPUnit_Framework_Assert as PHPUnit; use Illuminate\Foundation\Testing\TestResponse; use PHPUnit_Framework_ExpectationFailedException; use Illuminate\Contracts\Http\Kernel as HttpKernel; @@ -47,19 +46,6 @@ public function withoutMiddleware() return $this; } - /** - * Send the given request through the application. - * - * This method allows you to fully customize the entire Request object. - * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\Http\Response $response - */ - public function handle(Request $request) - { - return $this->app->prepareResponse($this->app->handle($request)); - } - /** * Visit the given URI with a GET request. * From 01bfc25a7142e332477a490baeb113295a249958 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 16 Nov 2016 09:15:43 -0600 Subject: [PATCH 8/9] extract test response creation --- .../Testing/Concerns/MakesHttpRequests.php | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/Illuminate/Foundation/Testing/Concerns/MakesHttpRequests.php b/src/Illuminate/Foundation/Testing/Concerns/MakesHttpRequests.php index 5dc2fec5060e..d6e9292293cf 100644 --- a/src/Illuminate/Foundation/Testing/Concerns/MakesHttpRequests.php +++ b/src/Illuminate/Foundation/Testing/Concerns/MakesHttpRequests.php @@ -241,7 +241,7 @@ public function call($method, $uri, $parameters = [], $cookies = [], $files = [] $kernel->terminate($request, $response); - return TestResponse::fromBaseResponse($response); + return $this->createTestResponse($response); } /** @@ -307,4 +307,15 @@ protected function extractFilesFromDataArray(&$data) return $files; } + + /** + * Create the test response instance from the given response. + * + * @param \Illuminate\Http\Response $response + * @return \Illuminate\Foundation\Testing\TestResponse + */ + protected function createTestResponse($response) + { + return TestResponse::fromBaseResponse($response); + } } From b186b7ebb62d1d60b9007b6e5be7884ab868cd9d Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 5 Dec 2016 12:37:35 -0600 Subject: [PATCH 9/9] Apply fixes from StyleCI (#16668) --- .../Foundation/Testing/Concerns/MakesHttpRequests.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Illuminate/Foundation/Testing/Concerns/MakesHttpRequests.php b/src/Illuminate/Foundation/Testing/Concerns/MakesHttpRequests.php index d6e9292293cf..779aa184a00e 100644 --- a/src/Illuminate/Foundation/Testing/Concerns/MakesHttpRequests.php +++ b/src/Illuminate/Foundation/Testing/Concerns/MakesHttpRequests.php @@ -2,12 +2,9 @@ namespace Illuminate\Foundation\Testing\Concerns; -use Closure; use Illuminate\Support\Str; use Illuminate\Http\Request; -use Illuminate\Http\UploadedFile; use Illuminate\Foundation\Testing\TestResponse; -use PHPUnit_Framework_ExpectationFailedException; use Illuminate\Contracts\Http\Kernel as HttpKernel; use Symfony\Component\HttpFoundation\Request as SymfonyRequest; use Symfony\Component\HttpFoundation\File\UploadedFile as SymfonyUploadedFile;