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
+ Sending
+ `;
+
+ 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');