Files
core/tests/api/bootstrap/FeatureContext.php
2019-12-18 11:21:39 +01:00

329 lines
9.1 KiB
PHP

<?php
declare(strict_types=1);
namespace Context;
use Behat\Behat\Context\Context;
use Behat\Mink\Exception\ExpectationException;
use Behat\MinkExtension\Context\MinkContext;
use Behat\Gherkin\Node\PyStringNode;
use Behat\Mink\Driver\BrowserKitDriver;
use Coduo\PHPMatcher\Factory\SimpleFactory;
use Goutte\Client;
use Symfony\Component\HttpFoundation\File\UploadedFile;
/**
* Defines application features from the specific context.
*/
class FeatureContext extends MinkContext implements Context
{
private const HTTP_HOST = '127.0.0.1:8088';
/**
* @When I wait for :cssSelector
* @param $cssSelector
* @throws \Exception
*/
public function iWaitFor($cssSelector)
{
$this->spin(function(FeatureContext $context) use ($cssSelector) {
return !is_null($context->getSession()->getPage()->find('css', $cssSelector));
});
}
/**
* @When I wait for :text to appear
* @Then I should see :text appear
* @param $text
* @throws \Exception
*/
public function iWaitForTextToAppear($text)
{
$this->spin(function(FeatureContext $context) use ($text) {
$context->assertPageContainsText($text);
return true;
});
}
/**
* @When I wait for :text to disappear
* @Then I should see :text disappear
* @param $text
* @throws \Exception
*/
public function iWaitForTextToDisappear($text)
{
$this->spin(function(FeatureContext $context) use ($text) {
$context->assertPageNotContainsText($text);
return true;
});
}
public function spin(callable $lambda, int $wait = 5): bool
{
$exception = null;
for ($i = 0; $i < $wait; $i++)
{
try {
if ($result = $lambda($this)) {
return true;
}
} catch (\Exception $e) {
// do nothing
$exception = $e;
}
sleep(1);
}
if ($exception) {
throw $exception;
} else {
$backtrace = debug_backtrace();
throw new \Exception(
"Timeout thrown by " . $backtrace[1]['class'] ?? '' . "::" . $backtrace[1]['function'] ?? '' . "()\n" .
$backtrace[1]['file'] ?? '' . ", line " . $backtrace[1]['line'] ?? ''
);
}
}
/**
* @When I send a :method request to :path
* @When I send a :method request to :path with body:
*/
public function iSendARequestToWithBody($method, $path, PyStringNode $body = null)
{
$body = $body instanceof PyStringNode ? $body->getRaw() : '{}';
$this->sendRequest($method, $path, $body);
}
/**
* @When I send a :method request to :path with attachment :file
*/
public function iSendARequestToWithAttachment($method, $path, $file)
{
$headers = [
'CONTENT_TYPE' => 'multipart/form-data',
];
$files = [];
if (file_exists('features/fixtures/files/' . $file)) {
$files = ['data' => new UploadedFile('features/fixtures/files/' . $file, $file)];
}
$this->sendRequest($method, $path, '{}', $headers, $files);
}
/**
* @Then the header :name should be equal to :value
*/
public function theHeaderShouldBeEqualTo($name, $value)
{
$response = $this->getBrowser()->getResponse();
$actual = $response->headers->get($name);
if (strtolower($value) !== strtolower($actual)) {
throw new \RuntimeException("Expected header '$value', but got '$actual'");
}
}
/**
* @Then the response should contain json:
*/
public function theResponseShouldContainJson(PyStringNode $jsonString)
{
$factory = new SimpleFactory();
$matcher = $factory->createMatcher();
if (!$matcher->match($this->getPageContent(), $jsonString->getRaw())) {
throw new \RuntimeException($matcher->getError());
}
}
/**
* @Then print last response headers
*/
public function printLastResponseHeaders(): string
{
$response = $this->getBrowser()->getResponse();
$headers = $response->headers->all();
$text = '';
foreach ($headers as $name => $value) {
$text .= $name . ': ' . $response->headers->get($name) . "\n";
}
echo $text;
}
/**
* @Then save output to file
*/
public function saveOutputToFile()
{
$dir = __DIR__.'/../../var/log/api-reports';
if (!is_dir($dir)) {
mkdir($dir, 0777, true);
}
$file = $dir.'/output.html';
if (file_exists($file)) {
unlink($file);
}
file_put_contents($file, $this->getPageContent());
return true;
}
/**
* @return string
*/
protected function getPageContent()
{
return $this->getSession()->getPage()->getContent();
}
/**
* @param $method
* @param $path
* @param string $body
* @param array $headers
* @param array $files
*/
private function sendRequest($method, $path, $body = '{}', $headers = [], $files = [])
{
$method = strtoupper($method);
$this->getBrowser()->request($method, $path, [], $files, $headers, $body);
}
/**
* @BeforeScenario @allowRedirects
*/
public function allowRedirects()
{
$this->getBrowser()->followRedirects(false);
}
/**
* @return Client
*/
private function getBrowser()
{
if (!$this->getSession()->isStarted()) {
$this->getSession()->start();
}
$driver = $this->getSession()->getDriver();
if ( ! $driver instanceof BrowserKitDriver) {
throw new \RuntimeException('Unsupported driver. BrowserKit driver is required.');
}
/**
* @var Client $client
*/
$client = $driver->getClient();
$client->setServerParameter('HTTP_HOST', self::HTTP_HOST);
return $client;
}
/**
* @Given /^I am logged in as "([^"]*)"$/
*/
public function iAmAuthenticatedAs($username) {
$this->iAmLoggedInAsWithPassword($username, 'admin%1');
}
/**
* @Given /^I am logged in as "([^"]*)" with password "([^"]*)"$/
*/
public function iAmLoggedInAsWithPassword($username, $password)
{
$this->visit('/bolt/login');
$this->fillField('username', $username);
$this->fillField('password', $password);
$this->pressButton('Log in');
}
/**
* @Then /^I logout$/
*/
public function iLogout()
{
$this->visit('/bolt/logout');
}
/**
* @Given /^I wait (\d+) second(?:|s)$/
*/
public function iWaitSeconds($seconds)
{
$this->getSession()->wait(1000*$seconds);
}
/**
* @Given /^I should see at least (\d+) "([^"]*)" elements$/
*/
public function iShouldSeeAtLeastElements($number, $element)
{
$foundElements = $this->getSession()->getPage()->findAll('css', $element);
if(intval($number) > count($foundElements)){
$message = sprintf('%d %s found on the page, but should be not less than %d.', count($foundElements), $element, $number);
throw new ExpectationException($message, $this->getSession()->getDriver());
}
}
/**
* @When /^I click "([^"]*)"$/
*/
public function iClick($element)
{
$foundElement = $this->getSession()->getPage()->find('css', $element);
$foundElement->click();
}
/**
* @When I scroll :element into view
*/
public function iScrollElementIntoView($element){
$this->getSession()->getPage()->find('css', $element)->focus();
$this->getSession()->executeScript("$(':focus')[0].scrollIntoView(true);window.scrollBy(0,-100);");
}
/**
* iSwitchToTheWindow
* @When /^(?:|I )switch to (?:window|tab) "(?P<nb>\d+)"$/
*/
public function iSwitchToTheWindow($id=0){
$this->getSession()->switchToWindow($this->getSession()->getWindowNames()[$id]);
}
/**
* @Then /^I should see (\d+) "([^"]*)" elements in the "([^"]*)" element$/
*/
public function iShouldSeeElementsInTheElement($number, $element, $parent)
{
$foundElements = $this->getSession()->getPage()->find('css', $parent)->findAll('css', $element);
if(intval($number) !== count($foundElements)){
$message = sprintf('%d %s found on the page, but should be not less than %d.', count($foundElements), $element, $number);
throw new ExpectationException($message, $this->getSession()->getDriver());
}
}
/**
* @Then /^the "([^"]*)" button should be disabled$/
*/
public function theButtonShouldBeDisabled($button)
{
$foundButton = $this->getSession()->getPage()->find('css', $button);
if(! $foundButton->getAttribute('disabled')) {
$message = sprintf('%s expected to be disabled, but disabled attribute is %s', $button, $foundButton->getAttribute('disabled'));
throw new ExpectationException($message, $this->getSession()->getDriver());
}
}
}