Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Page Object Extension Laravel Package

friends-of-behat/page-object-extension

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation:
    composer require --dev friends-of-behat/page-object-extension behat/behat
    
  2. Configure Behat (behat.yml):
    default:
      extensions:
        FriendsOfBehat\PageObjectExtension:
          alias: PageObject
    
  3. Create a Basic Page Class (src/Tests/Behat/Page/HomePage.php):
    namespace Tests\Behat\Page;
    
    use FriendsOfBehat\PageObjectExtension\Page\Page;
    
    class HomePage extends Page
    {
        public static $url = '/';
    
        public function getTitle(): string
        {
            return $this->getDriver()->getTitle();
        }
    }
    
  4. Use in a Feature Context (src/Tests/Behat/Context/FeatureContext.php):
    use FriendsOfBehat\PageObjectExtension\Page\Page;
    use Behat\Behat\Context\Context;
    use Tests\Behat\Page\HomePage;
    
    class FeatureContext implements Context
    {
        private HomePage $homePage;
    
        public function __construct(HomePage $homePage)
        {
            $this->homePage = $homePage;
        }
    
        /**
         * @When I visit the homepage
         */
        public function iVisitTheHomepage()
        {
            $this->homePage->open();
        }
    
        /**
         * @Then I should see the title :title
         */
        public function iShouldSeeTheTitle(string $title)
        {
            expect($this->homePage->getTitle())->toBe($title);
        }
    }
    
  5. Run Behat:
    vendor/bin/behat
    

First Use Case

Scenario: Test a simple homepage title. File: features/homepage.feature

Feature: Homepage
  As a visitor
  I want to see the correct title
  So I know I'm on the right site

  Scenario: Homepage displays correct title
    Given I am on the homepage
    Then I should see the title "Welcome to Our Site"

Implementation Patterns

Core Workflows

1. Page Object Pattern

  • Pages: Represent entire screens or sections (e.g., DashboardPage, CheckoutPage).
    class DashboardPage extends Page
    {
        public static $url = '/dashboard';
        public $title = 'Dashboard';
    
        public function getWelcomeMessage(): string
        {
            return $this->getDriver()->find('css', '.welcome-message')->getText();
        }
    }
    
  • Elements: Represent reusable UI components (e.g., buttons, forms).
    class LoginForm extends Element
    {
        public function fillEmail(string $email): void
        {
            $this->fillField('email', $email);
        }
    
        public function fillPassword(string $password): void
        {
            $this->fillField('password', $password);
        }
    }
    
  • Composition: Embed elements in pages.
    class LoginPage extends Page
    {
        public static $url = '/login';
        public LoginForm $form;
    
        public function __construct()
        {
            $this->form = new LoginForm($this->getDriver());
        }
    }
    

2. Symfony Integration (Adapted for Laravel)

Use SymfonyPage as a base for Laravel-specific pages by overriding Symfony dependencies:

class LaravelPage extends SymfonyPage
{
    protected function getUrl(): string
    {
        return route($this->getRouteName());
    }

    protected function getParameter(string $name)
    {
        return app($name); // Laravel's container
    }
}

3. Dependency Injection

Leverage Laravel’s service container to inject dependencies:

class FeatureContext
{
    public function __construct(
        HomePage $homePage,
        AuthService $authService // Laravel service
    ) {
        $this->homePage = $homePage;
        $this->authService = $authService;
    }
}

4. Dynamic Selectors

Use methods to generate selectors dynamically:

class UserProfilePage extends Page
{
    public function getUserNameField(): Element\Field
    {
        return $this->find('css', '.user-name', Element\Field::class);
    }
}

5. Reusable Steps

Extract common steps into context methods:

class CommonStepsContext
{
    /**
     * @Given I am logged in as :user
     */
    public function iAmLoggedInAs(string $user)
    {
        $this->authService->login($user);
    }
}

Integration Tips

  1. Laravel-Specific URL Handling: Override getUrl() to use Laravel’s route() helper:

    class AdminPage extends LaravelPage
    {
        protected function getRouteName(): string
        {
            return 'admin.dashboard';
        }
    }
    
  2. Element Locators: Use Laravel’s Blade directives or custom logic for dynamic IDs:

    public function getDynamicElement(): Element\Button
    {
        return $this->find('css', '.btn[data-id="' . $this->getDynamicId() . '"]', Element\Button::class);
    }
    
  3. Test Data Setup: Use Laravel factories or seeders to populate test data:

    /**
     * @BeforeScenario
     */
    public function createTestUser()
    {
        User::factory()->create(['email' => '[email protected]']);
    }
    
  4. Parallel Testing: Configure Behat for parallel execution:

    # behat.yml
    default:
      extensions:
        Behat\MinkExtension:
          base_url: 'http://localhost'
        FriendsOfBehat\PageObjectExtension:
          alias: PageObject
        Behat\ParallelExtension:
          workers: 4
    
  5. CI/CD Optimization: Cache dependencies and use Laravel’s queue workers for background jobs during tests.


Gotchas and Tips

Pitfalls

  1. Symfony Dependencies in Laravel:

    • Issue: SymfonyPage relies on Symfony’s ContainerInterface. Laravel’s container is compatible but may throw errors for Symfony-specific methods.
    • Fix: Extend SymfonyPage and override unsupported methods:
      class LaravelPage extends SymfonyPage
      {
          protected function getParameter(string $name)
          {
              return app($name); // Laravel's container
          }
      }
      
  2. Route Resolution:

    • Issue: Laravel’s route() helper may not work in Behat’s context if the app isn’t bootstrapped.
    • Fix: Manually resolve routes or use Laravel’s UrlGenerator:
      $url = app('url')->route('admin.dashboard');
      
  3. Driver Initialization:

    • Issue: Mink drivers (e.g., Selenium) may not auto-initialize in Laravel’s context.
    • Fix: Explicitly configure the driver in behat.yml:
      default:
        extensions:
          Behat\MinkExtension:
            base_url: 'http://localhost'
            sessions:
              default:
                selenium2: ~
      
  4. Static $url Conflicts:

    • Issue: Hardcoded $url in pages may break if routes change.
    • Fix: Use dynamic URL generation:
      class DynamicPage extends Page
      {
          public static function getUrl(): string
          {
              return route('dynamic.route', ['param' => 'value']);
          }
      }
      
  5. Element Not Found:

    • Issue: Flaky tests due to elements not being found.
    • Fix: Use explicit waits or retry logic:
      public function assertElementExists(): void
      {
          $this->waitFor(5)->until(
              fn() => $this->getDriver()->find('css', '.element') !== null
          );
      }
      
  6. Dependency Injection in Contexts:

    • Issue: Behat may not resolve Laravel services in contexts.
    • Fix: Use Behat’s setService() or bind services in behat.yml:
      default:
        extensions:
          FriendsOfBehat\PageObjectExtension:
            alias: PageObject
            services:
              auth_service: '@authService' # Laravel service
      

Debugging Tips

  1. Enable Mink Debugging:

    # behat.yml
    default:
      extensions:
        Behat\MinkExtension:
          debug: true
    
    • Outputs Mink session details for troubleshooting.
  2. Log Page/Element Interactions: Add debug methods to pages/elements:

    public function debugElement(string $selector): void
    {
        $element = $this->getDriver()->find('css', $selector);
        \Behat\Behat\Hook\Scope\BeforeScenarioScope::log("Element found: " . (bool) $element);
    }
    
  3. Use Behat’s --verbose Flag:

    vendor/bin/behat
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
calmfox/watch-sylius
damienfern/grpc-symfony-bundle
atoolo/index-bundle
atoolo/genai-bundle
coprotoai/laravel-ticket
davidjln/llm-carbon-bundle
cryonighter/valid-request-bundle
coolms/taxonomy-bundle
coolms/field-bundle
articulate-orm/symfony
aaix/laravel-tall-architect
ephoto/akeneo-connector
emmanuelballery/eb-plantumlbundle
emielburgman/symfony-visitor-beacon
emielburgman/symfony-visit-storage
emielburgman/symfony-security-headers
emielburgman/symfony-log-viewer
emarref/xdebug-bundle
emarref/pubnub-bundle
elriseio/finance-money-bundle