Git Product home page Git Product logo

browser's People

Contributors

benr77 avatar chris53897 avatar flohw avatar jwage avatar kbond avatar kdederichs avatar nathan-de-pachtere avatar nikophil avatar nitneuk avatar norkunas avatar nyholm avatar oskarstark avatar raneomik avatar staabm avatar t-richard avatar welcomattic avatar wouterj avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

browser's Issues

RFC Rename source code dump file to have .html extension

The feature to dump the source code for a failed test to a file is very useful. It's something Codeception has done for a long time, and as a previous Codeception user, the first thing I tried to do with the dump file from a failed test was to try to open it in a browser to investigate the failure (using the PHPStorm "open in browser" feature).

However, this does not work as expected as PHPStorm will not offer the installed browsers as options, because the file extension is .txt. I see why this has been chosen, as the HTTP headers are also included at the start of the file before the source code.

Can I suggest that the source code file is instead saved with a .html extension, and that the HTTP headers at the start of the file are wrapped in an HTML comment tag, to make it a valid HTML file. This way the extension will better represent what the file contents are, and it will allow direct opening in a browser, using either PHPStorm or the OS default application.

I will submit a PR with these changes, but wanted to first double check that this change is acceptable.

Thanks

Installing zenstruck/browser on PHP 8.1 and Symfony 6.0 errors

Using version ^0.9.1 for zenstruck/browser
./composer.json has been updated
Running composer update zenstruck/browser --with-all-dependencies
Loading composer repositories with package information
Restricting packages listed in "symfony/symfony" to "6.0.*"
Updating dependencies
Your requirements could not be resolved to an installable set of packages.

  Problem 1
    - behat/mink[v1.8.0, ..., v1.9.0] require symfony/css-selector ^2.7|^3.0|^4.0|^5.0 -> found symfony/css-selector[v2.7.0, ..., v2.8.52, v3.0.0, ..., v3.4.47, v4.0.0, ..., v4.4.27, v5.0.0, ..., v5.4.0] but it conflicts with your root composer.json require (^6.0).
    - zenstruck/browser v0.9.1 requires behat/mink ^1.8 -> satisfiable by behat/mink[v1.8.0, v1.8.1, v1.9.0].
    - Root composer.json requires zenstruck/browser ^0.9.1 -> satisfiable by zenstruck/browser[v0.9.1].

You can also try re-running composer require with an explicit version constraint, e.g. "composer require zenstruck/browser:*" to figure out if any version is installable, or "composer require zenstruck/browser:^2.1" if you know which you need.

Installation failed, reverting ./composer.json and ./composer.lock to their original content.

I think the culprit is that we don't allow for newer versions of behat/mink. If there are no bc breaking changes, I can probably create a PR with this small change to see if this fixes the composer conflicts.

assertChecked and assertNotChecked on Symfony ChoiceType

I have a ChoiceType in Symfony.

->add('assignedRoles', ChoiceType::class, [
                'expanded' => true, 
                'multiple' => true, 
                'choices' => $this->allRoles,
                'choice_label' => function ($item) { return 'system.role.'.str_replace("role_","", strtolower($item)); },
                'choice_value' => function ($item) { return $item; }
 ])

HTML (github has problems with < and >)

<input type="checkbox" id="user_edit_form_assignedRoles_3" name="user_edit_form[assignedRoles][]" class="form-check-input" value="bar" />
<label class="form-check-label" for="user_edit_form_assignedRoles_3">bar</label>

<input type="checkbox" id="user_edit_form_assignedRoles_4" name="user_edit_form[assignedRoles][]" class="form-check-input" value="bar" />
<label class="form-check-label" for="user_edit_form_assignedRoles_4">foo</label>

Check/Uncheck of Checkboxes is working
->checkField('foo')
->uncheckField('foo')

But ->assertChecked('bar') is always true. But it is not checked.
->assertNotChecked('bar') fails too.

Am i doing something wrong?
I tried the assertNotSelected() function as well.
->assertNotSelected('user_edit_form[assignedRoles][]', 'foo')

assertFieldEquals does not work with hidden fields

Hi @kbond,

thank you for this great library, it's a real timesaver.

I'm not sure if this is the expected behavior but I stumbled across the following:

$this
    ->assertFieldEquals('flow_mailMessageCompose_step', '1') // doesn't work, 'Form field not found'
    ->assertElementAttributeContains('input[name="flow_mailMessageCompose_step"]', 'value', '1') // works
;

Thanks again.

Cheers
BjΓΆrn

Allow setting custom failure messages

PHPUnit's asserts (and all asserts provided by Symfony) allow you to specify a custom failure message as last argument. This gives you the possibility to add some more context to an otherwise vague error.

Would it be an idea to also add this feature to Browser?

Convert `HttpOptions` files to `UploadedFile`'s

Currently, uploading files is a bit verbose:

$browser->post('/endpoint', ['files' => [new UploadedFile($realpath, $name, test: true)]);

It would be better if you could do:

$browser->post('/endpoint', ['files' => [$realpath]);

And HttpOptions auto-converts file paths's to UploadedFile's.

Patch is missing

Seems like the patch shortcut function is missing (like you can do $this->browser()->post|put|get|delete() but not patch)
Adding that would be neat :)

`Json::matchesSchema()`

Per conversation with @nikophil. Use justinrainbow/json-schema to validate schema.

I don't have any experience with json schema but is it common the include the schema files in some kind of resource directory in your project? If so, maybe a BROWSER_JSON_SCHEMA_DIR env variable could be used to set this dir, then with: $json->matchesSchema('post.json'), post.json would be loaded relative to this env variable?

Question: Introspect/pause the browser by an error

Is it possible to configure the PantherBrowser that it introspects/pause the browser by an error?

Example:
->click('#form_wrong_id')

Actual Behaviour:

The Browser closes and an error will be shown.
Clickable element "#form_wrong_id" not found.

I insert a pause() command before the line and fix the selector. Delete the pause() command.
Check if it works and repeat the steps until it works.

Desired Behaviour:

Set an optional option (default: false)
$this->pantherBrowser(['SET_OPTION_PAUSE_BY_ERROR'] => true)

If an error will rise, it starts the introspection-Mode and give some Feedback about the error.
So i do not need add/remove the pause() function all over.

Make Browser base class abstract

This base class has the base browser methods with KernelBrowser/HttpBrowser/PantherBrowser extending and adding their own features. While there are no abstract methods currently, it would make it clear it is not to be used.

Make Browser::response() public

Is there any way that I can get the response?

Example:

         $json = $this->browser()
            ->get('/api/foo')
            ->assertStatus(200)
            ->myJsonOutput();

         $this->browser()
            ->get('/api/'.$json['my-key'])
            ->assertStatus(200);

But also, I may feel more comfortable running assertions on my body like I normally would... Or if my body is XML..

file multiupload support

Greetings !

first of all, I really enjoy your library with its Developper eXperience friendly usage.

But we came to a cases with multiple file upload in a Symfony application.
Is there a way to test a multiple upload on a file input with multiple="multiple" attribute ?

this kind of "generic code" ($files being an array of paths to fixtures files) :

    $browser = $this
        ->browser()
        ->get('/form-with-file-input-path')
     ;
     ...
     foreach($files as $file) {
         $brower->attachFile('file_input[]', "$file");
    }

    $browser->click('submit_button');

doesn't seem to work : in the controller, the concerned file field, or the $request->files seem to be systematicaly empty ....

thank you !

`KernelBrowser::clickAndValidate()`

The idea is to click a form submit and ensure there are no validation errors. If there are, fail.

public function clickAndValidate(string $locator): self
{
    return $this->clickAndIntercept($locator)
        ->use(function (ValidatorDataCollector $collector) {
            Assert::that($collector->getViolationsCount())
                ->isEmpty('There were {actual} validation violations.')
            ;
        })
    ;
}

Displaying the actual violations (not just the count) would be nice but the ValidatorDataCollector makes this difficult as the violations are inside a Symfony\Component\VarDumper\Cloner\Data object. One possibility is to actually dump this but there is a lot of other data in here that isn't terribly useful and would just clutter your console.

Test browser factory "options" array's

I recently added the ability to pass options to the test browser factory methods (79e8130):

protected function pantherBrowser(array $options = [], array $kernelOptions = [], array $managerOptions = []): PantherBrowser
protected function httpBrowser(array $kernelOptions = [], array $pantherOptions = []): HttpBrowser
protected function kernelBrowser(array $options = []): KernelBrowser

I find all these different options confusing - what about just a single $options array for each:

protected function pantherBrowser(array $options = []): PantherBrowser
protected function httpBrowser(array $options = []): HttpBrowser
protected function kernelBrowser(array $options = []): KernelBrowser

The single $options could be passed to each place it's needed. I don't think there would be any issues with key conflicts or extra keys.

KernelBrowser::actingAs($user)

In 5.1, Symfony's KernelBrowser added the loginUser() method. This should be added to Browser's KernelBrowser as actingAs().

Consolidate "dd" methods

Currently, the following "dd" methods exist:

  1. Browser::dd()
  2. PantherBrowser::ddConsoleLog()
  3. PantherBrowser::ddScreenshot()

Should 1 and 2 be consolidated into dd()? If using the PantherBrowser, anytime you call ->dd(), the normal dd() output is shown, the console log is dumped and a screenshot saved?

Undefined array key 1 when the test failed

Hi,

I am migrating a large code base and try to use zenstruck/browser to replace the symfony client.

I am having some trouble as I am testing my login process. It's a json api, I have no problem with my admin authentication but when I am trying to login a normal user, the test fails.
I am surely not asking you to fix my test for me (but if you can without the code, you're very good πŸ˜‰ )

Here is my test, I reduced the code to the minimal required here (the only thing which is not visible is that I am using a dataProvider to get $username and $password):

$response = $this->browser()
    ->post('/api/login_check', [
        'body' => [
            'username' => $username,
            'password' => $password,
        ],
    ])
    ->use(function (\Zenstruck\Browser\Response $response) {
        self::assertSame(Response::HTTP_OK, $response->statusCode());
    })
    ->response();

So the test fails at assertSame as the $response->statusCode() is 401. The problem remains the same if I change the ->use callback to a classic assertSame after the $response assignation.

The problem is that my output looks like this:

PHPUnit 9.5.0 by Sebastian Bergmann and contributors.

Testing Functional\Tests\App\Infrastructure\Symfony\Controller\LoginCheckControllerTest
Undefined array key 1

THE ERROR HANDLER HAS CHANGED!

Remaining self deprecation notices (10)

Remaining indirect deprecation notices (29)

I have no message, no file created in the var/browser/ directory so I know my test fails because I added some dd above the assertSame but I think this is not the expected behavior πŸ˜‰
Also var/log/test.log has no information.

As you might imagine, it's a legacy code migration... The architecture is a bit complex but still very clean and from what I can say the legacy tests don't interfere with this new ones (as I use different test suites and only runs the suite I am interested in, plus the test above only executes one test). I might have missed something and hopefully someone could point it out? Or at least give me some advice as I don't know where to look at for now.

If the test fails outside of the browser context (my fixtures are not loaded properly for example) I get a regular phpunit error stack trace.

Thank you for your help,
Please ask if I could add more information I didn't think of.

Replacing some service in container + validate form

Hello,

I have troubles mocking some HttpClient instance with the browser after validating a form.

Here is the code:

        $this->browser()
            ->use(function () {
                self::getContainer()->set('test.http_client', new MockHttpClient(new MockResponse()));
            })
            ->visit('/') // some http calls are made here, using the mock client
            ->assertSuccessful()
            ->fillField('form[date]', '2021-11-10') // fill some fields
            ->click('form_search') // this click redirects us on the same page
            ->assertSuccessful() // :boom: 500 => the http_client is not mocked

Adding some dumps give me some clues

            ->use(function () {
                // prints Symfony\Component\HttpClient\MockHttpClient
                dump(self::getContainer()->get('test.http_client')::class);
            })
            ->click('form_search')
            ->use(function () {
                // prints Symfony\Component\HttpClient\RetryableHttpClient
                dump(self::getContainer()->get('test.http_client')::class);
            })

any ideas?

I saw this issue which is somehow related, but I don't even know where I could reuse the same "mocked container" or something else...

thanks for your help!

Cannot click on element that is outside the viewport (bootstrap 5 issue)

$this->pantherBrowser()
    ->visit('/page')
    ->click('.something') // element not in view part
;

// Facebook\WebDriver\Exception\MoveTargetOutOfBoundsException: move target out of bounds

Possible solution:

$this->pantherBrowser()
    ->visit('/page')
    ->use(function (Browser $browser ){

        $x = $browser->client()->findElement(WebDriverBy::cssSelector('.something'))->getLocation()->getX();
        $y = $browser->client()->findElement(WebDriverBy::cssSelector('.something'))->getLocation()->getY();

        $string = 'window.scrollTo({top:'.$y.', left:'.$x.', behaviour: \'auto\'});';

        $browser->client()->executeScript($string);

    })
    ->waitUntilVisible('.something')
    ->click('.something') // element not in view part
;

How to submit a form field which is dynamically populated?

Say I have a "customer lookup" field which uses an AJAX request to search for possible matches via a backend endpoint. On page load the <select> contains no options.

How can I populate this field with a value? I know what the ID of the value should be, but obviously the Javascript lookup to the backend does not work.

I have tried ->selectField('Customer', '1234') and also ->fillField('Customer', '1234') but neither are working.

SelectField() gives me an error such as:

Select option with value|text "1234" not found.

FillField() gives me an error such as:

...cannot take "1234" as a value (possible values: "")

Is there a way to just define the name of a form field and the value that should be sent when submitting the form?

Any pointers appreciated. Thanks.

Support for testing CSV responses

Hi Kevin,

Is there any support, or plans, to support testing CSV responses? I guess in a similar manner to the JSON response testing that's currently available.

Thanks

Rename PantherBrowser::inspect()

This function, when not running your tests in headless mode, pauses the test and allows you to view what's going on in the real browser. Ryan pointed out the name isn't great, some alternatives:

  • open()
  • pause() (might get mistaken for wait())
  • freeze()

Merge core traits into browser classes

  • merge Html into Browser
  • merge Json into BrowserKitBrowser
  • merge Http into BrowserKitBrowser
  • move HttpOptions to Zenstruck/Browser namespace (or Zenstruck/Browser/Util?)
  • merge Mailer trait/component into BrowserKitBrowser?
    • allows removal of ProfileAware interface, Mailer component and trait
    • move TestEmail to Zenstruck/Browser namespace (or Zenstruck/Browser/Util?)

file multiupload support

Greetings !

first of all, I really enjoy your library with its Developper eXperience friendly usage.

But we came to cases with multiple file upload in a Symfony application.
Is there a way to test a multiple upload on a file input with multiple="multiple" attribute ?

this kind of "generic code" ($files being an array of paths to fixtures files) :

    $browser = $this
        ->browser()
        ->get('/form-with-file-input-path')
     ;
     ...
     foreach($files as $file) {
         $brower->attachFile('file_input[]', $file);
     }

    $browser->click('submit_button');

doesn't seem to work : in the controller, the concerned file field, or the $request->files seem to be systematicaly empty ....

thank you !

EDIT: sorry, in the controller, the concerned file field actually contains the last file path from the $files array. But it's problematic on forms with constraints like for example "at least 3 files are required"

Use assertRedirectedTo without interceptRedirects

I just found something that was a bit weird.

        $this->kernelBrowser()
            ->actingAs(AuthenticatedUserFactory::fromCandidate($candidate), 'default')
            ->visit(\sprintf('/candidate/applications/%s/name', $application->getUuid()))
            ->assertStatus(200)
            ->assertSeeElement('#add_name_firstName')
            ->fillField('add_name_firstName', 'Test')
            ->fillField('add_name_lastName', 'Testsson')
            ->fillField('add_name_phoneNumber', '0123456789')
            ->click('add_name_submit')
            ->assertRedirectedTo(\sprintf('/candidate/applications/%s/background', $application->getUuid()));

This will not work because when I submit the form, there is a 302 and then a page that answer 200. Since Im not using interceptRedirects(), Browser will never see the 302 and the test will fail.

A simple workaround for me is just to add ->interceptRedirects(). Im not sure any changes are needed, I just wanted to share my experience.

assertJsonMatches may be too strict

Hi,

I am still using this lib to recreate my functional tests. I used to use self::assertSame($expectedErrors, $apiErrors) to test if my form errors are the one I expect (I test on api endpoint, not on the symfony type)

When migrating I use the assertJsonMatches('errors', $expectedErrors) method on the browser.

I have a difference between the two assertion methods. The first one (based on phpunit) did not take order in account. The second method (based on zendstruck/assert lib) did it. Which is a bit annoying as I don't care of order of my errors, I only need to know if they are all present.

Is it something I am the only one with this need? Maybe we could change this behavior to have the same as the phpunit's assertSame method?

What do you think?

assertSuccessfull for pantherBrowser

I have read the README, it says
` public function test_using_kernel_browser(): void
{
$this->browser()
->visit('/my/page')
->assertSuccessful()
;
}

/**
 * Requires this test extend Symfony\Component\Panther\PantherTestCase.
 */
public function test_using_panther_browser(): void
{
    $this->pantherBrowser()
        ->visit('/my/page')
        ->assertSuccessful()
    ;
}`

but if i test with pantherbrowser i have this error:
1) App\Tests\HomeControllerTest::testSomething Error: Call to undefined method Zenstruck\Browser\PantherBrowser::assertSuccessful()
I have read src/Browser/PantherBrowser.php and Browser.php
and i has not AssertSuccessfull.

`$browser->expectException()`

Ability to catch and assert an exception was thrown for the next request:

$browser
    ->expectException(SomeException::class) // auto-enables ->throwExceptions() for the next request
    ->post('/some/url') // fail if SomeException wasn't thrown
;

Make assertions on the exception:

$browser
    ->expectException(function(SomeException $e) {
        $this->assertStringContainsString('expected message', $e->getMessage());

        // any other assertions (ie ensure database entry wasn't modified)
    })
    ->post('/some/url') // fail if SomeException wasn't thrown
;

`KernelBrowser::clickAndX()` methods

$browser
    ->clickAndIntercept('button')
    // make assertions on the redirect with profiler enabled

    // same as:
    ->interceptRedirects()
    ->withProfiling()
    ->click('button')
;
$browser
    ->clickAndValidate('button') // intercept redirect with profiler, fail if (form) validation errors
;

Rename "parameters" to "query"

I was slightly confused by the name "parameters", which actually represent query options.

A look at Symfony itself shows BrowserKit's Request uses "parameters", but HttpFoundation's Request uses "query" (e.g. $request->query->get('q')). As such, I think the average Symfony developer is more familiar with "query".

As another reference, Guzzle also uses the "query" option name (https://docs.guzzlephp.org/en/stable/request-options.html#query).

form with multiple button check

Hello !

In this context, in functional tests :

  • zenstruck/browser : 0.7.0
  • php : 8.0.9
  • phpunit : 9.5.8
  • symfony : 5.3.6

as noticed here, the multiple button check with $form->getClickedButton() doesn't seem to work when following the recommandation in best practices and put it in controller or in form - this debug check in controller :

 if ($form->isSubmitted() && $form->isValid()) {
    dd($form->getClickedButton());
}

systematically outputs null.

Even the solution brought here and "slightly"(as illustration) followed like this in our case :

  <input type="submit" name="submit" value="draft"/>
  <button type="submit" name="submit" value="publish">
        publish
  </button>

the following test :

 $this->browser()
            ->get('/form-path')
... // filling object fields
            ->click('draft')
    ;

also actually outputs null when debugging it in controller like this :

 if ($form->isSubmitted() && $form->isValid()) {
    dd($request->request->get('submit'));
}

dd($request->request); somehow only outputs all other submited data except the 'submit' button.

It seems there's somewhere a process for SF5 where the form is cleaned up from submit buttons or there's something I'm missing...

Out of the test context, I manage to have the cliked button in both cases (in template using $request->request->get('submit') or in controller/form using $form->getClickedButton()) ...

Build paths from routes in browser

It is considered best practice to hard-code the URL paths into your tests, so that if a route is modified, the test fails. This ensures a consistent user experience, is good for SEO etc. However, this only really applies to public website type apps.

I build private, Saas type apps, where the URL changing is no big deal. I find it much easier to work with route names, which are defined as constants in the controller classes.

The browser has methods which require the path to test against - e.g.

  • visit(string $path)
  • assertOn(string $path)

I think it would be handy to have a matching set of methods, that support passing a route name and parameters instead of the path:

  • visitRoute(string $route, array $parameters = [])
  • assertOnRoute(string $route, array $parameters = [])

Is this something that could be incorporated into this package, or something better implemented by my app extending things?

If extending things is the way to go, where can I inject the router service?

Thanks

$selector ambiguity

The method parameter $selector is used in several methods and means different things depending on the context:

  • css selector: ->assertSeeIn('h1', 'some text') or ->dump('h1')
  • link selector (link id/title/text/image alt): ->follow('some link')
  • button selector (button id/value/alt): ->click('button')
  • field selector (input id/name/label): ->fillField('name', 'Kevin')
  • json search expression: ->assertJsonMatches('foo.bar.baz', 1) or ->dump('foo.bar.baz') (when content-type is json)

I think adding good docblocks would help but there are some things that can be improved:

Right now, methods that select elements with link/button/field selectors cannot use css selectors to find the element. I think, if unable to find the element via the link/button/field selector, assume a css selector and try.

This opens up the possibility of clicking buttons via tr:contains(First Post) a.btn (think admin list of entities each with an edit button).

Info: Cannot construct KeyEvent from non-typeable key (Problems with Keyboard-Layout)

This problem is not related to this repo.
But i hope it will be useful for other users.

I found a problem after i started to test an E-Mail-Field at start useing the "@" -Char.
Facebook\WebDriver\Exception\UnknownErrorException : unknown error: Cannot construct KeyEvent from non-typeable key (Session info: chrome=98.0.4758.80)

Google found this Issue.
https://groups.google.com/g/chromedriver-users/c/8WTsbereIO4

My Test-Environment is Mac M1 with Chrome 98 and the driver installed via Homebrew. Non English Keyboard Layout

A temporary fix is to add english as keyboard language and switch to it.
Now tests will run fine.

Xpath support

$this->wrapMinkExpectation(
     fn() => $this->webAssert()->elementExists('xpath', $xpathString)
);

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.