diff --git a/resources/assets/src/js/__tests__/user.test.js b/resources/assets/src/js/__tests__/user.test.js index e83d7e6c..722081a3 100644 --- a/resources/assets/src/js/__tests__/user.test.js +++ b/resources/assets/src/js/__tests__/user.test.js @@ -3,6 +3,54 @@ const $ = require('jquery'); window.$ = window.jQuery = $; +describe('tests for "verification" module', () => { + const modulePath = '../user/verification'; + + it('send verification email', async () => { + const url = jest.fn(path => path); + const swal = jest.fn(); + const showAjaxError = jest.fn(); + window.url = url; + window.swal = swal; + window.showAjaxError = showAjaxError; + + const fetch = jest.fn() + .mockImplementationOnce(option => { + option.beforeSend(); + return Promise.resolve({ errno: 0, msg: 'success' }); + }) + .mockImplementationOnce(() => Promise.resolve( + { errno: 1, msg: 'warning' } + )) + .mockImplementationOnce(() => Promise.reject(new Error)); + window.fetch = fetch; + + document.body.innerHTML = ` + Send + + `; + + require(modulePath); + + await $('a').click(); + expect(fetch).toBeCalledWith(expect.objectContaining({ + type: 'POST', + url: 'user/email-verification', + dataType: 'json' + })); + expect(swal).toBeCalledWith({ type: 'success', html: 'success' }); + // I don't know why $(el).is(':visible') does not work here + expect($('#send-verification-email').css('display')).toBe(''); + expect($('#sending-indicator').css('display')).toBe('none'); + + await $('a').click(); + expect(swal).toBeCalledWith({ type: 'warning', html: 'warning' }); + + await $('a').click(); + expect(showAjaxError).toBeCalled(); + }); +}); + describe('tests for "closet" module', () => { const modulePath = '../user/closet'; diff --git a/tests/AuthControllerTest.php b/tests/AuthControllerTest.php index 7c372e8d..38b3f760 100644 --- a/tests/AuthControllerTest.php +++ b/tests/AuthControllerTest.php @@ -664,6 +664,27 @@ class AuthControllerTest extends TestCase $this->assertTrue($user->verifyPassword('12345678')); } + public function testVerify() + { + $user = factory(User::class, 'unverified')->create(); + + // Should be forbidden if `uid` or `token` is empty + $this->visit('/auth/verify') + ->see(trans('auth.verify.invalid')); + + // Should be forbidden if `uid` is invalid + $this->visit('/auth/verify?uid=-1&token=nothing') + ->see(trans('auth.verify.invalid')); + + // Should be forbidden if `token` is invalid + $this->visit("/auth/verify?uid={$user->uid}&token=nothing") + ->see(trans('auth.verify.expired')); + + // Success + $this->visit("/auth/verify?uid={$user->uid}&token={$user->verification_token}") + ->see(trans('auth.verify.success')); + } + public function testCaptcha() { if (!function_exists('imagettfbbox') || getenv('TRAVIS_PHP_VERSION' == '5.5')) { diff --git a/tests/MiddlewareTest.php b/tests/MiddlewareTest.php index 671fbf30..c2ac8309 100644 --- a/tests/MiddlewareTest.php +++ b/tests/MiddlewareTest.php @@ -62,6 +62,18 @@ class MiddlewareTest extends TestCase ])->visit('/user')->seePageIs('/auth/login'); } + public function testCheckUserVerified() + { + $this->actAs('unverified') + ->get('/skinlib/upload') + ->assertResponseStatus(403) + ->see(trans('auth.check.verified')); + + $this->actAs('normal') + ->get('/skinlib/upload') + ->assertResponseOk(); + } + public function testCheckAdministrator() { // Without logged in diff --git a/tests/UserControllerTest.php b/tests/UserControllerTest.php index df77d7e9..f066f928 100644 --- a/tests/UserControllerTest.php +++ b/tests/UserControllerTest.php @@ -30,6 +30,11 @@ class UserControllerTest extends TestCase ->see(0) // Storage ->see(bs_announcement()) ->see($user->score); + + $unverified = factory(User::class, 'unverified')->create(); + $this->actAs($unverified) + ->visit('/user') + ->see(trans('user.verification.notice.title')); } public function testSign() @@ -96,6 +101,82 @@ class UserControllerTest extends TestCase ]); } + public function testSendVerificationEmail() + { + $user = factory(User::class, 'unverified')->create(); + $verified = factory(User::class)->create(); + + // Too fast + $this->actAs($user) + ->withSession([ + 'last_mail_time' => time() - 10 + ]) + ->post('/user/email-verification') + ->seeJson([ + 'errno' => 1, + 'msg' => trans('user.verification.frequent-mail') + ]); + $this->flushSession(); + + // Already verified + $this->actAs($verified) + ->post('/user/email-verification') + ->seeJson([ + 'errno' => 1, + 'msg' => trans('user.verification.verified') + ]); + + // Should handle exception when sending email + Mail::shouldReceive('send') + ->once() + ->andThrow(new Mockery\Exception('A fake exception.')); + $this->actAs($user) + ->post('/user/email-verification') + ->seeJson([ + 'errno' => 2, + 'msg' => trans('user.verification.failed', ['msg' => 'A fake exception.']) + ]); + + $user->fresh(); + $url = option('site_url')."/auth/verify?uid={$user->uid}&token={$user->verification_token}"; + + Mail::shouldReceive('send') + ->once() + ->with( + 'mails.email-verification', + Mockery::on(function ($actual) use ($url) { + $this->assertEquals(0, stristr($url, $actual['url'])); + return true; + }), + Mockery::on(function (Closure $closure) use ($user) { + $mock = Mockery::mock(Illuminate\Mail\Message::class); + + $mock->shouldReceive('from') + ->once() + ->with(config('mail.username'), option_localized('site_name')); + + $mock->shouldReceive('to') + ->once() + ->with($user->email) + ->andReturnSelf(); + + $mock->shouldReceive('subject') + ->once() + ->with(trans('user.verification.mail.title', ['sitename' => option_localized('site_name')])); + $closure($mock); + return true; + }) + ); + + // Success + $this->actAs($user) + ->post('/user/email-verification') + ->seeJson([ + 'errno' => 0, + 'msg' => trans('user.verification.success') + ])->assertSessionHas('last_mail_time'); + } + public function testProfile() { $this->visit('/user/profile') @@ -329,6 +410,7 @@ class UserControllerTest extends TestCase 'msg' => trans('user.profile.email.success') ]); $this->assertEquals('a@b.c', User::find($user->uid)->email); + $this->assertEquals(0, User::find($user->uid)->verified); // After changed email, user should re-login. $this->visit('/user')->seePageIs('/auth/login');