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' => 'test@example.com']);
    }
    
  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.
yandex/translate-api
voku/simple_html_dom
league/flysystem-vfs
bkwld/upchuck
filament/spatie-laravel-tags-plugin
capell-app/block-library
axium/identity
cetria/laravel-dummy-models
cetria/reflection-helper
agropredict/sso-auth-bundle
evolvestudio/spam-protection
datacore/hub-sdk
develia/commons
cuci/prototurk-sdk
cuci/prototurk-sdk-symfony
develia/geo-bundle
dreamzy/livewire-charts
touchestate-sdk/php-sdk
22h/doctrine-garbage-collection-bundle
agtp/agtp-php