zenstruck / browser

A fluent interface for your Symfony functional tests.
MIT License
185 stars 17 forks source link

fill a form, listen for page-changing events #147

Open tacman opened 4 months ago

tacman commented 4 months ago

First, what a cool library! I was fighting with authenticated users in my tests, still not sure what the problem was, but the actingAs() method works so easily that I'm switching my tests to this.

Two related feature requests. I'd like to be able to fill out a form more easily, and I'd like an event for clicks and submits.

$browser->getForm('registration')
   ->fill(['email' => 'ed@example.com', 'name' => 'Mr. Ed', 'plainPassword' => 'passw0rd'])
   ->submit()
   ->assert...

Perhaps something like this already exists?

I'd like to create a log that shows the page the test is on, and movement sequence (usually clicked on a link or submitted a form). I used to do that in behat and found it valuable. Start on the homepage (/). Click 'register'. Fill out the form with [...], click "My profile", click "add interests", etc.

But really I just wanted to say how great the tool is, but in order to create an issue I figured I'd add some low-priority wish list item. Thanks again for releasing this, it's so much easier to work with than the regular phpunit client.

kbond commented 4 months ago

Hey! Thanks for the kind words.

If I understand correctly, what you want should be possible:

$this->browser()
    ->visit('/register')
    ->fillField('email', 'ed@example.com')
    // ...
    ->click('Submit')
    ->assertOn('/')
    ->assertSuccessful()
    ->assertSee('You have successfully registered')
    ->assertAuthenticated('ed@example.com')
;
tacman commented 4 months ago

Not quite. I want a way to fill a form with a single object rather than repeating fillField.

And ideally a way to fill it by first getting a form, for example if a registration form and login form are on the same page, I'd like to do something like

$this->browser()
    ->visit('/login_or_register_or_forgot_password')
    ->getForm('register')
    ->fillFields(['email' => 'ed@example.com', 'name' => 'Mr. Ed.', 'plainPassword' => 'AHorseOfCourse'])
    ->submit()
    ->assertOn('/')
    ->assertSuccessful()
    ->assertSee('You have successfully registered')
    ->assertAuthenticated('ed@example.com')
;

And somewhere else in the code, I'd like to register a listener for click() and submit(), so that I create a log of my tests, which will end up looking somethink like behat.

Given I am on '/register'
And I submit the 'login' form with
    email: ed@example.com
    password: 'abc'
Then I should see "you are logged in"

I realize that behat scenarios are used for creating tests, but they're pretty readable, so I'd like to come up with a way to create my own report like that by listening for click and submit events.

The use case is for creating detailed answers to questions, like "how do I add a new administrator?". The idea is that I would write a test, run it, the listener would create a file in markdown that would then become part of the FAQ.

I know it's a bit of a stretch, I did have this working a few years ago with behat 2, but they changed the implementation in Behat 3

kbond commented 4 months ago

What about this:

use Symfony\Component\BrowserKit\AbstractBrowser;

$this->browser()
    ->visit('/login_or_register_or_forgot_password')
    ->use(function(AbstractBrowser $client) {
        $client->submitForm('Register', ['email' => 'ed@example.com', 'name' => 'Mr. Ed.', 'plainPassword' => 'AHorseOfCourse']);
    })
    ->assertOn('/')
    ->assertSuccessful()
    ->assertSee('You have successfully registered')
    ->assertAuthenticated('ed@example.com')
;

I want to think a bit more about adding this feature as a native method on Zenstruck\Browser.

tacman commented 4 months ago

yes, I think that'd work. The sometimes-tricky thing is getting the name/identifier of the form elements, especially checkboxes. I prefer name/id to labels, so that working in a multi-lingual environment is easier.

But this solution looks nice.

What do you think about a click/submit listener?

kbond commented 4 months ago

The use case is for creating detailed answers to questions, like "how do I add a new administrator?". The idea is that I would write a test, run it, the listener would create a file in markdown that would then become part of the FAQ.

That is a really cool idea. A test drives/validates feature and builds docs.

Just so I understand, you want to use browser to generate files like this (one per test or something)?

Given I am on '/register'
And I submit the 'login' form with
    email: ed@example.com
    password: 'abc'
Then I should see "you are logged in"
tacman commented 4 months ago

Yep!!

On Wed, Apr 10, 2024, 6:38 PM Kevin Bond @.***> wrote:

The use case is for creating detailed answers to questions, like "how do I add a new administrator?". The idea is that I would write a test, run it, the listener would create a file in markdown that would then become part of the FAQ.

That is a really cool idea. A test drives/validates feature and builds docs.

Just so I understand, you want to use browser to generate files like this (one per test or something)?

Given I am on '/register' And I submit the 'login' form with email: @.*** password: 'abc' Then I should see "you are logged in"

— Reply to this email directly, view it on GitHub https://github.com/zenstruck/browser/issues/147#issuecomment-2048550095, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAEXIQPB2A3PGW2DQFMHOILY4W5PBAVCNFSM6AAAAABFUIF4KGVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDANBYGU2TAMBZGU . You are receiving this because you authored the thread.Message ID: @.***>

kbond commented 4 months ago

I can see how a some kind of listener can help with generating:

And I submit the 'login' form with
    email: ed@example.com
    password: 'abc'

But how would Then I should see "you are logged in" and Given I am on '/register' be generated?

tacman commented 4 months ago
->assertOn('/')
    ->assertSuccessful()
    ->assertSee('You have successfully registered')

Brainstorming: In each assertX() method, adding this?

this->dispatch(new BrowserEvent(payload: {method: __METHOD__, params: get_env_vars()});
tacman commented 2 months ago

I want to think a bit more about adding this feature as a native method on Zenstruck\Browser.

Any more thoughts? Maybe mark dispatching an event as experimental until we see how it plays out? Or maybe I can add what I want to the consoleLog and then parse the logs to get what I want for now.

nikophil commented 2 months ago

this whole idea sounds really cool!

maybe this can be enable like this?

$this->browser()
    ->recordScenario($scenarioName)
    // ... do some recorded stuff
    ->endRecordScenario() // optional