diff --git a/README.md b/README.md index e7a5070..784b5fa 100644 --- a/README.md +++ b/README.md @@ -87,3 +87,72 @@ Contributions to the **Scaffold Testing Library** are welcome! Please feel free #### License This library is provided under the MIT License. See the LICENSE file for more information. + +# Breaking Changes in Version 0.4.2 + +## Important: Trait-based Step Definitions + +Starting from version 0.4.2, we've moved to a trait-based approach for step definitions. This is a breaking change that requires manual intervention for existing projects. + +### For Existing Projects + +If you're upgrading from a previous version, you'll need to: + +1. Remove the old step definitions from your `FeatureContext.php` +2. Add the trait to your `FeatureContext.php`: + +```php +use Salsadigitalauorg\ScaffoldTesting\Traits\ScaffoldTestingTrait; + +class FeatureContext implements Context +{ + use ScaffoldTestingTrait; + + // Your custom step definitions... +} +``` + +### For New Projects + +The installer will automatically set up your `FeatureContext.php` with the trait. You can add your custom step definitions alongside the trait. + +### Combining Custom Steps + +You can add your own custom step definitions alongside the trait: + +```php +class FeatureContext implements Context +{ + use ScaffoldTestingTrait; + + /** + * @Given I have my custom step + */ + public function iHaveMyCustomStep(): void + { + // Your custom step implementation + } +} +``` + +## Requirements + +- PHP 8.3 or higher +- Composer 2.x +- Behat 3.13 or higher +- PHPUnit 9.6.13 or higher (compatible with Drupal core) + +## Dependencies + +This package requires the following dependencies which will be installed automatically: + +```json +{ + "require": { + "behat/behat": "^3.13", + "composer/composer": "^2.6", + "symfony/process": "^6.0|^7.0", + "phpunit/phpunit": "^9.6.13" + } +} +``` diff --git a/composer.json b/composer.json index 03b94c5..23afaf0 100644 --- a/composer.json +++ b/composer.json @@ -1,17 +1,20 @@ { "name": "salsadigitalauorg/scaffold-testing", "description": "Provides a set of default Behat tests for Drupal projects, ensuring consistent testing across deployments.", - "version": "0.4.1", + "version": "0.4.2", "type": "library", + "require": { + "php": ">=8.3", + "behat/behat": "^3.13", + "composer/composer": "^2.6", + "symfony/process": "^6.0|^7.0", + "phpunit/phpunit": "^9.6.13" + }, "require-dev": { - "php": ">=8.2", - "behat/behat": "^3.6", "drupal/drupal-extension": "^5.0", "drevops/behat-format-progress-fail": "^1", "drevops/behat-screenshot": "^1.5.0", - "drevops/behat-steps": "^2", - "phpunit/phpunit": "^9.5", - "composer/composer": "^2.0" + "drevops/behat-steps": "^2" }, "autoload": { "psr-4": { diff --git a/src/Installer/Installer.php b/src/Installer/Installer.php index 15bba87..7e03486 100644 --- a/src/Installer/Installer.php +++ b/src/Installer/Installer.php @@ -1,15 +1,34 @@ io = $io; + } + + /** + * Post install/update command hook. + */ + public static function features(Event $event): void { $io = $event->getIO(); - $io->write('Installer::features method called'); + $io->write('[scaffold-testing] Installer::features method called'); $composer = $event->getComposer(); $extras = $composer->getPackage()->getExtra(); @@ -59,7 +78,7 @@ public static function features(Event $event) $io->writeError("Failed to update $file file in " . dirname($destPath)); } } else { - $io->write("Skipped $file file as it already exists and override is set to false."); + $io->write("[scaffold-testing] Skipped $file file as it already exists and override is set to false."); } } else { $io->writeError("Source file not found: $file"); @@ -72,22 +91,115 @@ public static function features(Event $event) if (file_exists($contextSourcePath)) { if (!file_exists($contextDestPath) || $override_feature_context) { if (copy($contextSourcePath, $contextDestPath)) { - $io->write("Installed FeatureContext.php file to $bootstrapPath"); + $io->write("[scaffold-testing] Installed FeatureContext.php file to $bootstrapPath"); } else { - $io->writeError("Failed to install FeatureContext.php file to $bootstrapPath"); + $io->writeError("[scaffold-testing] Failed to install FeatureContext.php file to $bootstrapPath"); } } else { - $io->write("Skipped FeatureContext.php file as it already exists and override is set to false."); + $io->write("[scaffold-testing] Skipped FeatureContext.php file as it already exists and override is set to false."); } } else { - $io->writeError("Source file not found: FeatureContext.php"); + $io->writeError("[scaffold-testing] Source file not found: FeatureContext.php"); + } + + if (self::shouldUpdateFeatureContext($config)) { + self::updateFeatureContext($targetPath, $io); } } - private static function createDirectory($io, $path) + /** + * Creates necessary directories for test files. + */ + private static function createDirectory(IOInterface $io, string $path): void { if (!is_dir($path) && !mkdir($path, 0777, true)) { $io->writeError("Failed to create directory: $path"); } } + + /** + * Updates or creates the FeatureContext file. + */ + private static function updateFeatureContext(string $projectRoot, IOInterface $io): void + { + $featureContextPath = $projectRoot . '/tests/behat/features/bootstrap/FeatureContext.php'; + $useStatements = [ + 'use Behat\Behat\Context\Context;', + 'use Behat\Behat\Hook\Scope\AfterScenarioScope;', + 'use Behat\Behat\Hook\Scope\BeforeScenarioScope;', + 'use Behat\Gherkin\Node\PyStringNode;', + 'use Behat\Gherkin\Node\TableNode;', + 'use PHPUnit\Framework\Assert;', + 'use Symfony\Component\Process\Process;', + 'use Salsadigitalauorg\ScaffoldTesting\Traits\ScaffoldTestingTrait;' + ]; + + if (!file_exists($featureContextPath)) { + $template = self::getFeatureContextTemplate(); + file_put_contents($featureContextPath, $template); + return; + } + + $content = file_get_contents($featureContextPath); + + // Add use statements if not present + foreach ($useStatements as $useStatement) { + if (strpos($content, $useStatement) === false) { + $content = preg_replace( + '/(namespace .*?;[\n\r]+)/s', + "$1\n" . $useStatement . "\n", + $content + ); + } + } + + // Add trait implementation if not present + if (strpos($content, 'use ScaffoldTestingTrait;') === false) { + $content = preg_replace( + '/(class FeatureContext implements Context[\n\r]*{)/s', + "$1\n use ScaffoldTestingTrait;\n", + $content + ); + } + + file_put_contents($featureContextPath, $content); + } + + /** + * Gets the template for a new FeatureContext file. + */ + private static function getFeatureContextTemplate(): string + { + return <<<'PHP' +process = new Process(['php', '-S', 'localhost:8888']); + $this->process->start(); + } + + /** + * @AfterScenario + */ + public function afterScenario(AfterScenarioScope $scope): void + { + if (isset($this->process)) { + $this->process->stop(); + } + } + + /** + * @And I belong to group :groupName + */ + public function iBelongToGroup($groupName): void + { + if (!$this->user) { + throw new \Exception("No user is currently logged in. Please log in first."); + } + + $groups = \Drupal::entityTypeManager() + ->getStorage('group') + ->loadByProperties(['label' => $groupName]); + $group = reset($groups); + + if ($group) { + $group->addMember($this->user); + $group->save(); + } else { + throw new \Exception("Group '$groupName' not found"); + } + } + + /** + * @Then I should be able to access the page + */ + public function iShouldBeAbleToAccessThePage(): void + { + $statusCode = $this->getSession()->getStatusCode(); + if ($statusCode !== 200) { + $pageContent = $this->getSession()->getPage()->getContent(); + throw new \Exception("Expected to access the page, but got status code: $statusCode. Page content: " . substr($pageContent, 0, 500)); + } + } + + /** + * @Then I should be denied access + */ + public function iShouldBeDeniedAccess(): void + { + $statusCode = $this->getSession()->getStatusCode(); + if ($statusCode !== 403) { + $pageContent = $this->getSession()->getPage()->getContent(); + throw new \Exception("Expected to be denied access (403), but got status code: $statusCode. Page content: " . substr($pageContent, 0, 500)); + } + } + + /** + * @Then I print all form fields + */ + public function iPrintAllFormFields(): void + { + $page = $this->getSession()->getPage(); + $fields = $page->findAll('css', 'input, select, textarea'); + foreach ($fields as $field) { + echo "Field: " . $field->getAttribute('name') . " | " . $field->getAttribute('id') . "\n"; + } + } + + // Add your step definitions here... +} \ No newline at end of file