amoifr/pickle-panther-bundle
YAML-driven end-to-end testing for Symfony on top of Panther. Write browser scenarios in French or English, map steps to PHP “sentences,” and run them via a BasePantherTest. Generates a self-contained HTML report; supports context (desktop/mobile) and auth hooks.
Install the Bundle
composer require amoifr/pickle-panther-bundle
Add to config/bundles.php if not auto-discovered:
Amoifr\PicklePantherBundle\PicklePantherBundle::class => ['all' => true],
Create a Scenario File
Place a YAML file in tests/E2E/Scenario/ (e.g., homepage.yaml):
scenarios:
- nom: "Homepage loads successfully"
contexte:
navigateur: desktop
etapes:
- action: "Visits the page with the [/]"
- action: "Checks that the text [Welcome] is present in the selector [h1]"
Write a Test Class
Extend BasePantherTest and run the scenario:
use Amoifr\PicklePantherBundle\Test\BasePantherTest;
final class HomepageTest extends BasePantherTest {
public function testHomepage(): void {
$this->createScenarioRunner()->runTest(__DIR__.'/Scenario/homepage.yaml');
}
}
Run Tests
php bin/phpunit tests/E2E/Scenario/HomepageTest
Use the bundle to test a homepage by:
#[Sentence] provider (see below) to handle the actions.Create services annotated with #[Sentence] to map YAML actions to PHP logic.
Example:
use Amoifr\PicklePantherBundle\Attribute\Sentence;
#[Sentence('Visits the page with the [/]')]
#[Sentence('Visits the page with the [/contact]')]
class PageVisitProvider {
public function visitPageWithPath(string $path, PantherClient $client): void {
$client->request('GET', $path);
}
}
#[Sentence] with the exact YAML action string (supports placeholders like [/]).PantherClient or other services as needed.Define context-specific logic (e.g., desktop/mobile) via:
ContextProviderInterface to modify the PantherClient before scenario execution.
use Amoifr\PicklePantherBundle\Context\ContextProviderInterface;
class MobileContextProvider implements ContextProviderInterface {
public function applyContext(array $context, PantherClient $client): void {
$client->setBrowserEngine('chrome', ['options' => ['deviceScaleFactor' => 2]]);
}
}
#[AsContextProvider] and map them in config/packages/pickle_panther.yaml:
pickle_panther:
context_providers:
mobile: Amoifr\PicklePantherBundle\Context\MobileContextProvider
Pass dynamic values via YAML args or placeholders:
- action: "Checks that the text [text] is present in the selector [selector]"
args:
text: "Welcome"
selector: "h1"
#[Sentence('Checks that the text [text] is present in the selector [selector]')]
public function checkTextInSelector(string $text, string $selector, PantherClient $client): void {
$client->assertSelectorTextContains($selector, $text);
}
Generate HTML reports by configuring the ScenarioRunner:
$runner = $this->createScenarioRunner();
$runner->setReportPath(__DIR__.'/reports');
$runner->runTest($yamlPath);
Kernel::boot().PantherTestCase for browser isolation per test.--parallel flag.Sentence Mismatches
#[Sentence] tags (including placeholders).phpstan to validate sentence providers or enable strict mode in config/packages/pickle_panther.yaml:
pickle_panther:
strict_sentence_matching: true
Context Overrides
config/packages/pickle_panther.yaml:
pickle_panther:
context_providers_order: [mobile, desktop]
Dynamic Argument Parsing
[/path] must align with provider method signatures.[path]) and ensure provider methods accept them in order:
#[Sentence('Visits the path [path]')]
public function visitPath(string $path, PantherClient $client): void { ... }
Report Path Conflicts
$runner->setReportPath(__DIR__.'/reports/' . uniqid());
Enable Verbose Logging
Configure in config/packages/pickle_panther.yaml:
pickle_panther:
debug: true
Logs appear in var/log/pickle_panther.log.
Inspect the Sentence Registry Dump registered sentences during test setup:
$registry = $this->get(SentenceRegistry::class);
dump($registry->getSentences());
Panther-Specific Issues
PantherTestCase for browser isolation.$client->waitFor(10)->until(
fn() => $client->has('css', $selector)
);
Custom Assertions
Extend PantherClient or create a new #[Sentence] provider for domain-specific assertions.
Plugin System
Override ScenarioRunner to add pre/post hooks:
$runner = $this->createScenarioRunner();
$runner->addPreScenarioHook(function() { /* setup */ });
$runner->addPostScenarioHook(function() { /* teardown */ });
Localization
Add support for additional languages by extending the SentenceParser and updating YAML validators.
CI Integration Upload reports to artifacts (e.g., GitHub Actions):
# .github/workflows/tests.yml
- uses: actions/upload-artifact@v3
with:
name: e2e-reports
path: tests/E2E/reports/
How can I help you explore Laravel packages today?