Closed ivanvermeyen closed 8 years ago
Yeah, i'm going to find an "eloquent" way to skip the verification.
There are multiple options:
Awesome :) 👍
Woops! At some point I removed the required
validation rule and forgot to put it back... This complicates things... My tests worked because the captcha wasn't required... doh!
So to make it work (for real) I had to create a custom sometimes
rule...
I added this to the Request
class that holds my validation rules:
/**
* Validate conditional rules.
*
* @return \Illuminate\Contracts\Validation\Validator
*/
protected function getValidatorInstance()
{
$validator = parent::getValidatorInstance();
$validator->sometimes('g-recaptcha-response', 'required|captcha', function ($input) {
return ! app()->environment('testing');
});
return $validator;
}
So now it applies the required
and captcha
rules only if the environment isn't testing
...
Of course now I need to add this to every Request
class that needs a captcha.
Maybe you know a better approach ;)
I think the best way to do it is by using the Mockery.
https://laravel.com/docs/master/testing#mocking-facades
use Arcanedev\NoCaptcha\Facades\NoCaptcha;
// This is my dummy test
public function testContactForm()
{
$this->ignoreCaptcha();
$this->visit('/contact')
->type('John DOE', 'name')
->type('user@example.com', 'email')
->check('g-recaptcha-response')
->press('Send')
->seePageIs('/contact')
->see('Your message was sent successfully!');
}
// You can place this method to your base TestCase
protected function ignoreCaptcha($name = 'g-recaptcha-response')
{
NoCaptcha::shouldReceive('display')
->andReturn('<input type="checkbox" value="yes" name="' . $name . '">');
NoCaptcha::shouldReceive('script')
->andReturn('<script src="captcha.js"></script>');
NoCaptcha::shouldReceive('verify')
->andReturn(true);
}
Routes:
Route::group(['prefix' => 'contact'], function () {
Route::get('/', function () {
return view('contact');
});
Route::post('/', function (Illuminate\Http\Request $request) {
$validator = Validator::make($request->all(), [
'name' => 'required',
'email' => 'required|email',
'g-recaptcha-response' => 'required|captcha',
]);
if ($validator->fails()) {
return back()->withErrors($validator)->withInput();
}
return back()->with('success', 'Your message was sent successfully!');
});
});
The view:
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<span class="label label-success">{{ Session::get('success') }}</span>
<form method="POST">
{{ csrf_field() }}
<label for="name">Name :</label>
<input id="name" name="name" type="text">
{{ $errors->first('name') }}
<br>
<label for="email">Email :</label>
<input id="email" name="email" type="email">
{{ $errors->first('email') }}
<br>
{!! NoCaptcha::display() !!}
{{ $errors->first('g-recaptcha-response') }}
<input type="submit" value="Send">
</form>
{!! NoCaptcha::script() !!}
</body>
</html>
And of course you can customize the ignoreCaptcha
method for your specific tests.
Brilliant! That's a much cleaner solution! Thanks 👍
This doesn't seem to work for me:
\NoCaptcha::shouldReceive('verify')->andReturn(true);
In your validator, you reference:
return $app['arcanedev.no-captcha']->verify($value, $ip);
I tried mocking the real NoCaptcha
class or the contract, but that didn't work either.
The display()
method that I use in the view gets mocked as expected.
Ah... the validator requires a field with the name g-recaptcha-response
but in the test we used captcha
. If I use g-recaptcha-response
it all works....
Something like this:
public function testContactForm()
{
$this->ignoreCaptcha();
$this->visit('/contact')
->type('John DOE', 'name')
->type('user@example.com', 'email')
->check('g-recaptcha-response')
->press('Send')
->seePageIs('/contact')
->see('Your message was sent successfully!');
}
protected function ignoreCaptcha($name = 'g-recaptcha-response')
{
Captcha::shouldReceive('display')->andReturn('<input type="checkbox" value="yes" name="' . $name . '">');
Captcha::shouldReceive('script')->andReturn('<script src="captcha.js"></script>');
Captcha::shouldReceive('verify')->andReturn(true);
}
You're right, i've updated my response! Tnx :+1:
Hi, I cannot use this method anymore, since Facades were removed and the class returned by the helper (as referenced in https://github.com/ARCANEDEV/noCAPTCHA/issues/81) does not implement shouldReceive. How can we mock it now?
Hi @flap152, you can mock it by using the contract.
use Arcanedev\NoCaptcha\Contracts\NoCaptcha;
// ...
$this->mock(NoCaptcha::class, function ($mock) {
$mock->shouldReceive(...)->andReturn(...);
});
Or by using the Real-Time Facades:
use Facades\Arcanedev\NoCaptcha\Contracts\NoCaptcha as NoCaptchaFacade;
//...
NoCaptchaFacade::shouldReceive(...)->andReturn(...);
Check the official documantation for more details:
Hi, successfully mocked using real-time Facades, but had to modify the usage, with v2 (have not tried v3 yet):
because of CaptchaRule rule implementation:
return no_captcha($this->version)
->verify($value, $ip)
->isSuccess();
I had to change ignoreCaptcha to this (notice the textarea
vs checkbox
, and different and added shouldReceive
):
// You can place this method to your base TestCase
protected function ignoreCaptcha($name = 'g-recaptcha-response')
{
NoCaptchaFacade::shouldReceive('display')
->andReturn('<input type="textarea" value="anything, really" name="' . $name . '">');
NoCaptchaFacade::shouldReceive('script')
->andReturn('<script src="captcha.js"></script>');
NoCaptchaFacade::shouldReceive('verify')
// ->andReturn(true);
->andReturn(NoCaptchaFacade::getFacadeRoot());
NoCaptchaFacade::shouldReceive('isSuccess')
->andReturn(true);
}
Maybe there is a better way, or this will help someone...
Hi, I get
Facebook\WebDriver\Exception\NoSuchElementException: no such element: Unable to locate element: {"method":"css selector","selector":"body input[type=checkbox][name='g-recaptcha-response']"}
(Session info: headless chrome=65.0.3325.146)
use Facades\Arcanedev\NoCaptcha\Contracts\NoCaptcha as NoCaptchaFacade;
...
public function testBasicExample()
{
$this->ignoreCaptcha();
$this->browse(function (Browser $browser) {
$browser->visit('/login')
->assertSee('Login')
->type('email', 'test@test.ru')
->type('password', 'secret')
->check('g-recaptcha-response')
->press('Login')
->assertTitleContains('Main page')
->screenshot('ffs');
});
}
protected function ignoreCaptcha($name = 'g-recaptcha-response')
{
// NoCaptchaFacade::shouldReceive('display')
// ->andReturn('<input type="checkbox" value="yes" name="' . $name . '">');
// NoCaptchaFacade::shouldReceive('script')
// ->andReturn('<script src="captcha.js"></script>');
// NoCaptchaFacade::shouldReceive('verify')
// ->andReturn(true);
NoCaptchaFacade::shouldReceive('display')
->andReturn('<input type="textarea" value="yes" name="' . $name . '">');
NoCaptchaFacade::shouldReceive('script')
->andReturn('<script src="captcha.js"></script>');
NoCaptchaFacade::shouldReceive('verify')
->andReturn(NoCaptchaFacade::getFacadeRoot());
NoCaptchaFacade::shouldReceive('isSuccess')
->andReturn(true);
}
what am I doing wrong?
Hi @MadRac, i never tested NoCaptcha with Laravel Dusk, so i have no idea on how to solve your issue.
But there is another trick (Cleaner way ?) to skip the NoCaptcha validation.
// This is where you specify your validation rules
use Arcanedev\NoCaptcha\Rules\CaptchaRule;
$ips = app()->runningUnitTests() ? ['your-local-ip', 'your-testing-ip'] : [];
$rule = (new CaptchaRule)->skipIps($ips);
Or in your config file
@MadRac ,
The captcha field is now a textarea, not a checkbox. You would need to change ->check('g-recaptcha-response')
to ->type()
or something compatible with a textarea field to avoid the error.
But also, the facade returns html for a textarea with text already in it (value="yes"
), so you don't need to "input" anything during the test, so remove the ->check()
altogether.
@flap152 ,
If i just remove
->check('g-recaptcha-response')
I get
Please verify that you are not a robot
If I uncomment the code with the checkbox, the same error
use Facades\Arcanedev\NoCaptcha\Contracts\NoCaptcha as NoCaptchaFacade;
...
public function testBasicExample()
{
$this->ignoreCaptcha();
$this->browse(function (Browser $browser) {
$browser->visit('/login')
->assertSee('Login')
->type('email', 'test@test.ru')
->type('password', 'secret')
->check('g-recaptcha-response')
->press('Login')
->assertTitleContains('Main page')
->screenshot('ffs');
});
}
protected function ignoreCaptcha($name = 'g-recaptcha-response')
{
NoCaptchaFacade::shouldReceive('display')
->andReturn('<input type="checkbox" value="yes" name="' . $name . '">');
NoCaptchaFacade::shouldReceive('script')
->andReturn('<script src="captcha.js"></script>');
NoCaptchaFacade::shouldReceive('verify')
->andReturn(true);
}
I get
There was 1 error:
1) Tests\Browser\ExampleTest::testBasicExample
Facebook\WebDriver\Exception\NoSuchElementException: no such element: Unable to locate element: {"method":"css selector","selector":"body input[type=checkbox][name='g-recaptcha-response']"}
(Session info: headless chrome=65.0.3325.146)
(Driver info: chromedriver=2.36.540471 (9c759b81a907e70363c6312294d30b6ccccc2752),platform=Linux 4.14.146-119.123.amzn2.x86_64 x86_64)
Can you show the code dusk test and login.blade.php
@MadRac ,
blade excerpt - in form:
<div class="row">
<div class="col">
{!! Captcha::display() !!}
</div><!--col-->
</div><!--row-->
and for script:
@push('after-scripts')
{!! Captcha::script() !!}
@endpush
I must admit we use both Dusk and Browserkit for our tests, and Browserkit for that part of the tests...
Using Laravel 9 with Vue for the form submission.
Cant seem to make the test work with Dusk, though the Feature Test works fine. Dont think the mock is working in Dusk...
If i log the response from the rule, it spits this:
`{"Arcanedev\\NoCaptcha\\Utilities\\ResponseV2":{"success":false,"hostname":null,"challenge_ts":null,"apk_package_name":null,"error-codes":["invalid-input-response"]}}`
So, the rule response comes as false.
Code:
protected function ignoreCaptcha(string $name = 'g-recaptcha-response'): void
{
NoCaptchaFacade::shouldReceive('display')
->andReturn('<input type="textarea" value="anything, really" name="' . $name . '">');
NoCaptchaFacade::shouldReceive('script')
->andReturn('<script src="captcha.js"></script>');
NoCaptchaFacade::shouldReceive('verify')
->andReturn(NoCaptchaFacade::getFacadeRoot());
NoCaptchaFacade::shouldReceive('isSuccess')
->andReturn(true);
}
/** @test */
public function it_submits_contact_form()
{
$this->ignoreCaptcha();
$this->browse(function (Browser $browser) {
$browser->visitRoute('contact')
->type('name', $this->faker->name)
->type('email', $this->faker->safeEmail)
->type('message', $this->faker->paragraphs(3, true))
->fillHidden('g-recaptcha-response', '1')
->pressAndWaitFor('Submit', 3)
->assertSee('Message sent!');
});
}
Hi,
This isn't really an issue, just a suggestion in case anyone cares :)
I'm trying to test my code with Laravel's handy helpers and MailThief for intercepting e-mails... But getting past the captcha with an automated script is obviously not so easy...
To get around this, I read that some people just disable the captcha for testing. So to do this, I copied your validator and changed it like this:
So basically, I just check if the environment is set to
testing
, which is the default for Laravel, and if so I always let it pass.I don't know if this is something you would want to include in your package, perhaps with configurable environments that should be skipped or something...