with one click
create-behat-context
// Create the PHP feature context class that implements step definitions for a domain, and register it in behat.yml. Covers the PHP implementation side of Behat tests. Trigger: "create behat context for {Domain}".
// Create the PHP feature context class that implements step definitions for a domain, and register it in behat.yml. Covers the PHP implementation side of Behat tests. Trigger: "create behat context for {Domain}".
| name | create-behat-context |
| description | Create the PHP feature context class that implements step definitions for a domain, and register it in behat.yml. Covers the PHP implementation side of Behat tests. Trigger: "create behat context for {Domain}". |
| needs | ["create-cqrs-commands","create-cqrs-queries"] |
| produces | {Domain}FeatureContext.php + behat.yml registration |
Read @.ai/Component/Behat/CONTEXT.md for conventions (base class, entity references, stateless steps, bus access).
Create tests/Integration/Behaviour/Features/Context/Domain/{Domain}/{Domain}FeatureContext.php:
AbstractDomainFeatureContext@Given, @When, @Then annotations$this->getCommandBus()->handle(...) for write operations$this->getQueryBus()->handle(...) for read/verification$this->referenceToId($reference) to resolve string references to integer IDs$this->getSharedStorage()->set($reference, $newId)Reference: tests/Integration/Behaviour/Features/Context/Domain/Tax/TaxFeatureContext.php (simple)
/**
* @When I add a {domain} :reference with following properties:
*/
public function iAddDomainWithProperties(string $reference, TableNode $table): void
{
$data = $this->localizeByRows($table);
$command = new Add{Domain}Command(
$data['name'],
(bool) $data['active'],
);
$id = $this->getCommandBus()->handle($command);
$this->getSharedStorage()->set($reference, $id->getValue());
}
/**
* @Then {domain} :reference should have the following properties:
*/
public function domainShouldHaveProperties(string $reference, TableNode $table): void
{
$id = $this->referenceToId($reference);
$result = $this->getQueryBus()->handle(new Get{Domain}ForEditing($id));
// Assert each field independently
}
The assertion loads the entity fresh from the database — it does NOT rely on state from a previous step.
Errors are tested in two paired steps: the @When step catches the domain exception and stores it via $this->setLastException(...); the next @Then step asserts the stored exception via $this->assertLastErrorIs(...).
/**
* @When I add a {domain} :reference with invalid name
*/
public function addWithInvalidName(string $reference): void
{
try {
$this->getCommandBus()->handle(new Add{Domain}Command(''));
} catch ({Domain}ConstraintException $e) {
$this->setLastException($e);
}
}
/**
* @Then I should get error that {domain} name is invalid
*/
public function assertLastErrorIsInvalidName(): void
{
$this->assertLastErrorIs(
{Domain}ConstraintException::class,
{Domain}ConstraintException::INVALID_NAME, // optional error code
);
}
Two safety nets enforced by CommonFeatureContext:
assertLastErrorIs, the checkExpectedExceptionAfterStep @AfterStep hook re-throws it as a RuntimeException — preventing a domain exception from being silently swallowed.cleanStoredExceptionsBeforeScenario (@BeforeScenario) clears any leftover exception so scenarios don't leak state into each other.So: never try/catch and ignore — always pair the capture with an assertion in the very next step.
Open tests/Integration/Behaviour/behat.yml and add the context to the domain suite:
domain:
contexts:
- PrestaShop\Tests\Integration\Behaviour\Features\Context\Domain\{Domain}\{Domain}FeatureContext
Verify: php vendor/bin/behat --dry-run to confirm all steps are matched.
Conventions (stateless steps, referenceToId, deterministic steps, typed exceptions, error scenarios) are in Behat/CONTEXT.md. Skill-specific reminders:
referenceToId / referencesToIds — not getSharedStorage()->get() directlyPair the core PR with a ps_apiresources fork branch for coordinated review, or revert that pairing to upstream dev-dev once both PRs are merged. Edits composer.json (repository URL + dev-<branch> pin) and refreshes composer.lock. Trigger: "pair core with ps_apiresources branch", "link ps_apiresources fork branch", "use my ps_apiresources fork in the core PR", "revert ps_apiresources to dev-dev", "unlink the ps_apiresources fork".
Configure modules/ps_apiresources as a development-ready git checkout (symlink to existing local clone or fresh clone with upstream + fork remotes), then provision the local test DB. Trigger: "set up ps_apiresources for development", "develop on the API module", "fix the API integration tests", "work on ps_apiresources locally".
Create the Symfony form type for a CRUD (identifiable) entity's add/edit form. Covers standard field types, translatable fields, money fields, file uploads, and choice providers. For multi-tab layout with NavigationTabType, see create-form-tab-layout. For settings/configuration forms, see create-settings-form. Trigger: "create CRUD form type for {Domain}".
Create a PrestaShop settings form (options block writing to ps_configuration): DataConfiguration + FormDataProvider + FormType + 4 YAML service entries (base Handler reused, never subclassed). For CRUD entity forms, use create-crud-form-type instead. Trigger: "create settings form for {Page}", "add options block for {Page}", "migrate fields_options for {Page}".
Generates a CONTEXT.md file for a PrestaShop shared component inside the `.ai/Component/` folder. Trigger this skill when the user asks to "generate a context for [Component]", "document the [X] component", "fill in the CONTEXT.md for [Component]", or when working inside `.ai/Component/` directories. Components live under `src/Core/{Name}/` and/or `src/Adapter/{Name}/` — they are shared infrastructure, not business domains. Examples: Grid, Form, Hook, CQRS, Translation, Router.
Create form page actions in the admin controller. Covers both patterns: CRUD (create/edit via FormBuilder + FormHandler pair) and settings (index + save via the base FormHandler). Never builds commands directly. Trigger: "create form actions for {Domain}", "create add/edit for {Domain}", "create settings save action for {Page}".