This repository has been archived by the owner on Mar 30, 2018. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 73
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Run Humbug in isolation during Behat tests
- Loading branch information
Showing
6 changed files
with
156 additions
and
186 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
default: | ||
suites: | ||
application: | ||
contexts: [ FeatureContext, FilesystemContext ] | ||
contexts: [FeatureContext] | ||
formatters: | ||
progress: ~ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,115 +1,210 @@ | ||
<?php | ||
|
||
use Behat\Behat\Context\Context; | ||
use Behat\Behat\Context\SnippetAcceptingContext; | ||
use Behat\Gherkin\Node\PyStringNode; | ||
use Behat\Gherkin\Node\TableNode; | ||
use Behat\Behat\Tester\Exception\PendingException; | ||
use SebastianBergmann\Diff\Differ; | ||
use Symfony\Component\Console\Tester\ApplicationTester; | ||
use Humbug\Console\Application; | ||
use Humbug\Command\Humbug as HumbugCommand; | ||
|
||
/** | ||
* Defines application features from the specific context. | ||
*/ | ||
class FeatureContext implements Context, SnippetAcceptingContext | ||
use Symfony\Component\Filesystem\Filesystem; | ||
use Symfony\Component\Process\PhpExecutableFinder; | ||
use Symfony\Component\Process\Process; | ||
|
||
final class FeatureContext implements Context | ||
{ | ||
private $application; | ||
/** | ||
* @var string | ||
*/ | ||
private static $workingDir; | ||
|
||
private $appTester; | ||
/** | ||
* @var Filesystem | ||
*/ | ||
private static $filesystem; | ||
|
||
private $startingDirectory; | ||
/** | ||
* @var string | ||
*/ | ||
private static $phpBin; | ||
|
||
/** | ||
* @var Differ | ||
*/ | ||
private $differ; | ||
|
||
/** | ||
* Initializes context. | ||
* | ||
* Every scenario gets its own context instance. | ||
* You can also pass arbitrary arguments to the | ||
* context constructor through behat.yml. | ||
* @var Process | ||
*/ | ||
private $process; | ||
|
||
public function __construct() | ||
{ | ||
require_once __DIR__ . '/../../bootstrap.php'; | ||
|
||
$this->application = new Application; | ||
$this->application->setAutoExit(false); | ||
$this->application->add(new HumbugCommand); | ||
$this->appTester = new ApplicationTester($this->application); | ||
$this->differ = new Differ(); | ||
} | ||
|
||
/** | ||
* @beforeScenario | ||
* @BeforeFeature | ||
*/ | ||
public function retainStartingDirectory() | ||
public static function beforeFeature() | ||
{ | ||
$this->startingDirectory = getcwd(); | ||
self::$workingDir = sprintf('%s/%s/', sys_get_temp_dir(), uniqid('', true)); | ||
self::$filesystem = new Filesystem(); | ||
self::$phpBin = self::findPhpBinary(); | ||
} | ||
|
||
/** | ||
* @afterScenario | ||
* @BeforeScenario | ||
*/ | ||
public function restoreWorkingDirectory() | ||
public function beforeScenario() | ||
{ | ||
if ($this->startingDirectory !== getcwd()) { | ||
chdir($this->startingDirectory); | ||
} | ||
self::$filesystem->remove(self::$workingDir); | ||
self::$filesystem->mkdir(self::$workingDir, 0777); | ||
self::$filesystem->symlink(__DIR__ . '/../../composer.json', self::$workingDir . '/composer.json'); | ||
self::$filesystem->symlink(__DIR__ . '/../../vendor', self::$workingDir . '/vendor'); | ||
} | ||
|
||
/** | ||
* @Given I am in any directory | ||
* @AfterScenario | ||
*/ | ||
public function iAmInAnyDirectory() | ||
public function afterScenario() | ||
{ | ||
chdir(sys_get_temp_dir()); | ||
self::$filesystem->remove(self::$workingDir); | ||
} | ||
|
||
/** | ||
* @When I run humbug | ||
* @When I run humbug with :arguments | ||
*/ | ||
public function iRunHumbug() | ||
public function iRunHumbug($arguments = '--no-progress-bar') | ||
{ | ||
$this->appTester->run(['run', '--no-progress-bar'=>true]); | ||
$this->process = new Process(sprintf('%s %s run %s', self::$phpBin, escapeshellarg(__DIR__ . '/../../bin/humbug'), $arguments)); | ||
$this->process->setWorkingDirectory(self::$workingDir); | ||
$this->process->start(); | ||
$this->process->wait(); | ||
} | ||
|
||
/** | ||
* @When I run humbug with :arg1 | ||
* @Then I should see: | ||
* @Then I should see containing: | ||
* @Then I should see output containing: | ||
*/ | ||
public function iRunHumbugWith($arg1) | ||
public function iShouldSeeContaining(PyStringNode $string) | ||
{ | ||
$arguments = explode(' ', $arg1); | ||
$this->appTester->run($arguments); | ||
if (!preg_match('/' . preg_quote((string) $string, '/') . '/', $this->getProcessOutput())) { | ||
throw new \RuntimeException(sprintf( | ||
'Output did not match expected pattern:%s%s', PHP_EOL, $this->getProcessOutput() | ||
)); | ||
} | ||
} | ||
|
||
/** | ||
* @Then I should see: | ||
* @Given the class file :file contains: | ||
* @Given the test file :file contains: | ||
* @Given the phpunit config file :file contains: | ||
* @Given the phpunit bootstrap file :file contains: | ||
*/ | ||
public function iShouldSee(PyStringNode $string) | ||
public function theClassOrTestFileContains($file, PyStringNode $contents) | ||
{ | ||
$string = (string) $string; | ||
$output = $this->appTester->getDisplay(); | ||
if (trim($output) !== trim($string)) { | ||
throw new \RuntimeException(sprintf( | ||
'Output difference:%s%s', | ||
$this->theFileContains($file, $contents); | ||
} | ||
|
||
/** | ||
* @Given the humbug config file contains: | ||
*/ | ||
public function theConfigFileContains(PyStringNode $contents) | ||
{ | ||
$this->theFileContains('humbug.json', $contents); | ||
} | ||
|
||
/** | ||
* @Given there is no file :file | ||
*/ | ||
public function thereIsNoFile($file) | ||
{ | ||
$this->throwExceptionIfFalse(!self::$filesystem->exists(self::getAbsolutePath($file)), sprintf('Expected file to not exist: %s', $file)); | ||
} | ||
|
||
/** | ||
* @Then the file :file should exist | ||
*/ | ||
public function theFileShouldExist($file) | ||
{ | ||
$this->throwExceptionIfFalse(self::$filesystem->exists(self::getAbsolutePath($file)), sprintf('Expected file to exist: %s', $file)); | ||
} | ||
|
||
/** | ||
* @Then the file :file should contain: | ||
*/ | ||
public function theFileShouldContain($file, PyStringNode $expectedContent) | ||
{ | ||
$this->theFileShouldExist($file); | ||
|
||
$actualContent = file_get_contents(self::getAbsolutePath($file)); | ||
|
||
$this->throwExceptionIfFalse( | ||
trim($actualContent) === trim((string) $expectedContent), | ||
sprintf( | ||
'Actual file content differs:%s%s', | ||
PHP_EOL, | ||
$this->differ->diff(trim($string), trim($output)) | ||
)); | ||
$this->differ->diff((string) $expectedContent, $actualContent) | ||
) | ||
); | ||
} | ||
|
||
private function throwExceptionIfFalse($result, $message) | ||
{ | ||
if ($result === false) { | ||
throw new \RuntimeException($message); | ||
} | ||
} | ||
|
||
/** | ||
* @Then I should see containing: | ||
* @Then I should see output containing: | ||
* @param string $file | ||
* @param PyStringNode $contents | ||
*/ | ||
public function iShouldSeeContaining(PyStringNode $string) | ||
private function theFileContains($file, PyStringNode $contents) | ||
{ | ||
if (!preg_match('/' . preg_quote((string) $string) . '/', $this->appTester->getDisplay())) { | ||
throw new \RuntimeException(sprintf( | ||
'Output did not match expected pattern:%s%s', PHP_EOL, $this->appTester->getDisplay() | ||
)); | ||
self::$filesystem->dumpFile(self::getAbsolutePath($file), (string) $contents); | ||
} | ||
|
||
/** | ||
* @return string | ||
*/ | ||
private function getProcessOutput() | ||
{ | ||
$this->assertProcessIsAvailable(); | ||
|
||
return $this->process->getErrorOutput() . $this->process->getOutput(); | ||
} | ||
|
||
/** | ||
* @throws \BadMethodCallException | ||
*/ | ||
private function assertProcessIsAvailable() | ||
{ | ||
if (null === $this->process) { | ||
throw new \BadMethodCallException('Behat proccess cannot be found. Did you run it before making assertions?'); | ||
} | ||
} | ||
|
||
/** | ||
* @return string | ||
* | ||
* @throws \RuntimeException | ||
*/ | ||
private static function findPhpBinary() | ||
{ | ||
$phpBinary = (new PhpExecutableFinder())->find(); | ||
if (false === $phpBinary) { | ||
throw new \RuntimeException('Unable to find the PHP executable.'); | ||
} | ||
return $phpBinary; | ||
} | ||
|
||
/** | ||
* @param string $relativePath | ||
* | ||
* @return string | ||
*/ | ||
private static function getAbsolutePath($relativePath) | ||
{ | ||
return self::$workingDir . '/' . $relativePath; | ||
} | ||
} |
Oops, something went wrong.