Browse Source

updated config and tests suite

* renamed clock and toastings settings
* added utility for destroying modal after test
* tests for change avatar modal
* added max time toasting is visible for
Rafał Pitoń 10 years ago
parent
commit
39d86eff0d

+ 5 - 1
misago/emberapp/app/components/forms/avatar-gallery-form.js

@@ -29,7 +29,11 @@ export default Ember.Component.extend({
         self.set('activeForm', 'select-avatar-type-form');
       }, function(jqXHR) {
         if (self.isDestroyed) { return; }
-        self.toast.apiError(jqXHR);
+        if (jqXHR.status === 400) {
+          self.toast.error(jqXHR.responseJSON.detail);
+        } else {
+          self.toast.apiError(jqXHR);
+        }
       }).finally(function() {
         if (self.isDestroyed) { return; }
         self.setProperties({

+ 3 - 3
misago/emberapp/app/services/clock.js

@@ -1,16 +1,16 @@
 import Ember from 'ember';
-import ENV from '../config/environment';
+import config from '../config/environment';
 
 export default Ember.Service.extend({
   tick: Ember.computed.oneWay('_tick').readOnly(),
 
   doTick: function () {
     var self = this;
-    if (ENV.environment !== 'test') {
+    if (config.environment !== 'test') {
       // running this loop in tests will block promises resolution
       Ember.run.later(function () {
         self.toggleProperty('_tick');
-      }, ENV.APP.TICK_FREQUENCY);
+      }, config.APP.tickFrequency);
     }
   }.observes('_tick').on('init'),
 

+ 7 - 4
misago/emberapp/app/services/toast-message.js

@@ -1,5 +1,5 @@
 import Ember from 'ember';
-import ENV from '../config/environment';
+import config from '../config/environment';
 
 export default Ember.Service.extend({
   id: null,
@@ -20,8 +20,11 @@ export default Ember.Service.extend({
       'isVisible': true
     });
 
-    var displayTime = ENV.APP.TOAST_BASE_DISPLAY_TIME;
-    displayTime += message.length * ENV.APP.TOAST_LENGTH_FACTOR;
+    var displayTime = config.APP.toastBaseDisplayTime;
+    displayTime += message.length * config.APP.toastLengthFactor;
+    if (displayTime > config.APP.toastMaxDisplayTime) {
+      displayTime = config.APP.toastMaxDisplayTime;
+    }
 
     var self = this;
     Ember.run.later(function () {
@@ -38,7 +41,7 @@ export default Ember.Service.extend({
       this.set('isVisible', false);
       Ember.run.later(function () {
         self._showToast(type, message);
-      }, ENV.APP.TOAST_HIDE_ANIMATION_LENGTH);
+      }, config.APP.toastHideAnimationLength);
     } else {
       this._showToast(type, message);
     }

+ 1 - 1
misago/emberapp/app/templates/components/forms/avatar-gallery-form.hbs

@@ -7,7 +7,7 @@
     {{#each row as |image|}}
     <div class="col-sm-3">
       <button type="button" class="btn btn-default btn-outlined {{if isBusy "disabled"}}" {{if isBusy "disabled"}} {{action "setAvatar" image}}>
-        <img src="{{mediaUrl}}/{{image}}" alt="">
+        <img src="{{mediaUrl}}{{image}}" alt="">
         {{loading-overlay itemName=image activeItem=activeItem}}
       </button>
     </div>

+ 5 - 5
misago/emberapp/app/templates/components/forms/select-avatar-type-form.hbs

@@ -10,29 +10,29 @@
   <div class="col-md-8">
 
     {{#if options.gravatar}}
-    <button type="button" class="btn btn-outlined btn-default btn-block {{if isBusy "disabled"}}" {{if isBusy "disabled"}} {{action "setGravatar"}}>
+    <button type="button" class="btn btn-outlined btn-default btn-gravatar btn-block {{if isBusy "disabled"}}" {{if isBusy "disabled"}} {{action "setGravatar"}}>
       {{gettext "Download my Gravatar"}}
     </button>
     {{/if}}
 
-    <button type="button" class="btn btn-outlined btn-default btn-block {{if isBusy "disabled"}}" {{if isBusy "disabled"}} {{action "setGenerated"}}>
+    <button type="button" class="btn btn-outlined btn-default btn-generated btn-block {{if isBusy "disabled"}}" {{if isBusy "disabled"}} {{action "setGenerated"}}>
       {{gettext "Generate my individual avatar"}}
     </button>
 
     {{#if options.crop_org}}
-    <button type="button" class="btn btn-outlined btn-default btn-block {{if isBusy "disabled"}}" {{if isBusy "disabled"}} {{action "goTo" "avatar-crop-form"}}>
+    <button type="button" class="btn btn-outlined btn-default btn-crop btn-block {{if isBusy "disabled"}}" {{if isBusy "disabled"}} {{action "goTo" "avatar-crop-form"}}>
       {{gettext "Re-crop uploaded image"}}
     </button>
     {{/if}}
 
     {{#if options.upload}}
-    <button type="button" class="btn btn-outlined btn-default btn-block {{if isBusy "disabled"}}" {{if isBusy "disabled"}} {{action "goTo" "avatar-upload-form"}}>
+    <button type="button" class="btn btn-outlined btn-default btn-upload btn-block {{if isBusy "disabled"}}" {{if isBusy "disabled"}} {{action "goTo" "avatar-upload-form"}}>
       {{gettext "Upload new image"}}
     </button>
     {{/if}}
 
     {{#if options.galleries}}
-    <button type="button" class="btn btn-outlined btn-default btn-block {{if isBusy "disabled"}}" {{if isBusy "disabled"}} {{action "goTo" "avatar-gallery-form"}}>
+    <button type="button" class="btn btn-outlined btn-default btn-gallery btn-block {{if isBusy "disabled"}}" {{if isBusy "disabled"}} {{action "goTo" "avatar-gallery-form"}}>
       {{gettext "Pick avatar from gallery"}}
     </button>
     {{/if}}

+ 7 - 5
misago/emberapp/config/environment.js

@@ -38,9 +38,10 @@ module.exports = function(environment) {
       TICK_FREQUENCY: 15000,
 
       // Toastings time
-      TOAST_BASE_DISPLAY_TIME: 4000,
-      TOAST_LENGTH_FACTOR: 110,
-      TOAST_HIDE_ANIMATION_LENGTH: 200
+      toastBaseDisplayTime: 4000,
+      toastMaxDisplayTime: 9000,
+      toastLengthFactor: 110,
+      toastHideAnimationLength: 200
     }
   };
 
@@ -71,8 +72,9 @@ module.exports = function(environment) {
     ENV.APP.API_ADD_TRAILING_SLASHES = true;
 
     // Reduce toast display times for test runner
-    ENV.APP.TOAST_BASE_DISPLAY_TIME = 200;
-    ENV.APP.TOAST_LENGTH_FACTOR = 0;
+    ENV.APP.toastBaseDisplayTime = 200;
+    ENV.APP.toastMaxDisplayTime = 200;
+    ENV.APP.toastLengthFactor = 0;
   }
 
   if (environment === 'production') {

+ 2 - 2
misago/emberapp/tests/acceptance/activate-test.js

@@ -1,6 +1,7 @@
 import Ember from 'ember';
 import { module, test } from 'qunit';
 import startApp from '../helpers/start-app';
+import destroyModal from '../helpers/destroy-modal';
 import getToastMessage from '../helpers/toast-message';
 
 var application;
@@ -12,8 +13,7 @@ module('Acceptance: Account Activation', {
 
   afterEach: function() {
     Ember.$('#hidden-login-form').off('submit.stopInTest');
-    Ember.$('#appModal').off();
-    Ember.$('body').removeClass('modal-open');
+    destroyModal();
     Ember.run(application, 'destroy');
     Ember.$.mockjax.clear();
   }

+ 487 - 0
misago/emberapp/tests/acceptance/change-avatar-test.js

@@ -0,0 +1,487 @@
+import Ember from 'ember';
+import { module, test } from 'qunit';
+import startApp from '../helpers/start-app';
+import destroyModal from '../helpers/destroy-modal';
+import getToastMessage from '../helpers/toast-message';
+import createUser from '../helpers/create-user';
+
+var application, container, auth;
+
+module('Acceptance: Avatar Change Modal', {
+  beforeEach: function() {
+    application = startApp();
+    container = application.__container__;
+    auth = container.lookup('service:auth');
+  },
+
+  afterEach: function() {
+    destroyModal();
+    Ember.run(application, 'destroy');
+    Ember.$.mockjax.clear();
+  }
+});
+
+
+test('Change avatar modal handles no permission', function(assert) {
+  var user = createUser();
+  auth.setProperties({
+    'isAuthenticated': true,
+    'user': user
+  });
+
+  var done = assert.async();
+  assert.expect(1);
+
+  Ember.$.mockjax({
+    url: '/api/users/' + user.id + '/avatar/',
+    status: 403,
+    responseText: {
+      'detail': 'Your avatar is locked.',
+      'reason': ''
+    }
+  });
+
+  visit('/');
+  click('.navbar-user-nav .user-menu .dropdown-toggle');
+  click('.navbar-user-nav .user-menu .editable-avatar');
+
+  andThen(function() {
+    var message = Ember.$.trim(find('#appModal .modal-message .lead').text());
+    assert.equal(message, 'Your avatar is locked.');
+    done();
+  });
+});
+
+
+test('Minimal select avatar type form works', function(assert) {
+  var user = createUser();
+  auth.setProperties({
+    'isAuthenticated': true,
+    'user': user
+  });
+
+  var done = assert.async();
+  assert.expect(5);
+
+  Ember.$.mockjax({
+    url: '/api/users/' + user.id + '/avatar/',
+    status: 200,
+    responseText: {
+      'generated': true,
+      'gravatar': false,
+      'crop_org': false,
+      'crop_tmp': false,
+      'upload': false,
+      'galleries': false
+    }
+  });
+
+  visit('/');
+  click('.navbar-user-nav .user-menu .dropdown-toggle');
+  click('.navbar-user-nav .user-menu .editable-avatar');
+
+  andThen(function() {
+    assert.ok(!find('#appModal .btn-gravatar').length);
+    assert.ok(find('#appModal .btn-generated').length);
+    assert.ok(!find('#appModal .btn-crop').length);
+    assert.ok(!find('#appModal .btn-upload').length);
+    assert.ok(!find('#appModal .btn-gallery').length);
+    done();
+  });
+});
+
+
+test('Complete select avatar type form works', function(assert) {
+  var user = createUser();
+  auth.setProperties({
+    'isAuthenticated': true,
+    'user': user
+  });
+
+  var done = assert.async();
+  assert.expect(5);
+
+  Ember.$.mockjax({
+    url: '/api/users/' + user.id + '/avatar/',
+    status: 200,
+    responseText: {
+      'generated': true,
+      'gravatar': true,
+      'crop_org': true,
+      'crop_tmp': true,
+      'upload': true,
+      'galleries': true
+    }
+  });
+
+  visit('/');
+  click('.navbar-user-nav .user-menu .dropdown-toggle');
+  click('.navbar-user-nav .user-menu .editable-avatar');
+
+  andThen(function() {
+    assert.ok(find('#appModal .btn-gravatar').length);
+    assert.ok(find('#appModal .btn-generated').length);
+    assert.ok(find('#appModal .btn-crop').length);
+    assert.ok(find('#appModal .btn-upload').length);
+    assert.ok(find('#appModal .btn-gallery').length);
+    done();
+  });
+});
+
+
+test('Failed change to gravatar', function(assert) {
+  var user = createUser();
+  auth.setProperties({
+    'isAuthenticated': true,
+    'user': user
+  });
+
+  var done = assert.async();
+  assert.expect(1);
+
+  Ember.$.mockjax({
+    url: '/api/users/' + user.id + '/avatar/',
+    status: 200,
+    type: 'GET',
+    responseText: {
+      'generated': true,
+      'gravatar': true,
+      'crop_org': true,
+      'crop_tmp': true,
+      'upload': true,
+      'galleries': true
+    }
+  });
+
+  Ember.$.mockjax({
+    url: '/api/users/' + user.id + '/avatar/',
+    status: 400,
+    type: 'POST',
+    responseText: {
+      'detail': 'Avatar change failed.',
+    }
+  });
+
+  visit('/');
+  click('.navbar-user-nav .user-menu .dropdown-toggle');
+  click('.navbar-user-nav .user-menu .editable-avatar');
+  click('#appModal .btn-gravatar');
+
+  andThen(function() {
+    assert.equal(getToastMessage(), 'Avatar change failed.');
+    done();
+  });
+});
+
+
+test('Changed avatar to gravatar', function(assert) {
+  var user = createUser();
+  auth.setProperties({
+    'isAuthenticated': true,
+    'user': user
+  });
+
+  var done = assert.async();
+  assert.expect(2);
+
+  Ember.$.mockjax({
+    url: '/api/users/' + user.id + '/avatar/',
+    status: 200,
+    type: 'GET',
+    responseText: {
+      'generated': true,
+      'gravatar': true,
+      'crop_org': true,
+      'crop_tmp': true,
+      'upload': true,
+      'galleries': true
+    }
+  });
+
+  Ember.$.mockjax({
+    url: '/api/users/' + user.id + '/avatar/',
+    status: 200,
+    type: 'POST',
+    responseText: {
+      'detail': 'Avatar changed to Gravatar.',
+      'avatar_hash': 'eeeeeeee',
+      'options': {
+        'generated': true,
+        'gravatar': true,
+        'crop_org': true,
+        'crop_tmp': true,
+        'upload': true,
+        'galleries': true
+      }
+    }
+  });
+
+  visit('/');
+  click('.navbar-user-nav .user-menu .dropdown-toggle');
+  click('.navbar-user-nav .user-menu .editable-avatar');
+  click('#appModal .btn-gravatar');
+
+  andThen(function() {
+    assert.equal(getToastMessage(), 'Avatar changed to Gravatar.');
+    assert.equal(auth.get('user.avatar_hash'), 'eeeeeeee');
+    done();
+  });
+});
+
+
+test('Failed change to generated', function(assert) {
+  var user = createUser();
+  auth.setProperties({
+    'isAuthenticated': true,
+    'user': user
+  });
+
+  var done = assert.async();
+  assert.expect(1);
+
+  Ember.$.mockjax({
+    url: '/api/users/' + user.id + '/avatar/',
+    status: 200,
+    type: 'GET',
+    responseText: {
+      'generated': true,
+      'gravatar': true,
+      'crop_org': true,
+      'crop_tmp': true,
+      'upload': true,
+      'galleries': true
+    }
+  });
+
+  Ember.$.mockjax({
+    url: '/api/users/' + user.id + '/avatar/',
+    status: 400,
+    type: 'POST',
+    responseText: {
+      'detail': 'Avatar change failed.',
+    }
+  });
+
+  visit('/');
+  click('.navbar-user-nav .user-menu .dropdown-toggle');
+  click('.navbar-user-nav .user-menu .editable-avatar');
+  click('#appModal .btn-generated');
+
+  andThen(function() {
+    assert.equal(getToastMessage(), 'Avatar change failed.');
+    done();
+  });
+});
+
+
+test('Changed avatar to generated', function(assert) {
+  var user = createUser();
+  auth.setProperties({
+    'isAuthenticated': true,
+    'user': user
+  });
+
+  var done = assert.async();
+  assert.expect(2);
+
+  Ember.$.mockjax({
+    url: '/api/users/' + user.id + '/avatar/',
+    status: 200,
+    type: 'GET',
+    responseText: {
+      'generated': true,
+      'gravatar': true,
+      'crop_org': true,
+      'crop_tmp': true,
+      'upload': true,
+      'galleries': true
+    }
+  });
+
+  Ember.$.mockjax({
+    url: '/api/users/' + user.id + '/avatar/',
+    status: 200,
+    type: 'POST',
+    responseText: {
+      'detail': 'Avatar changed to generated.',
+      'avatar_hash': 'eeeeeeee',
+      'options': {
+        'generated': true,
+        'gravatar': true,
+        'crop_org': true,
+        'crop_tmp': true,
+        'upload': true,
+        'galleries': true
+      }
+    }
+  });
+
+  visit('/');
+  click('.navbar-user-nav .user-menu .dropdown-toggle');
+  click('.navbar-user-nav .user-menu .editable-avatar');
+  click('#appModal .btn-generated');
+
+  andThen(function() {
+    assert.equal(getToastMessage(), 'Avatar changed to generated.');
+    assert.equal(auth.get('user.avatar_hash'), 'eeeeeeee');
+    done();
+  });
+});
+
+
+test('Failed to pick avatar from gallery', function(assert) {
+  var user = createUser();
+  auth.setProperties({
+    'isAuthenticated': true,
+    'user': user
+  });
+
+  var done = assert.async();
+  assert.expect(1);
+
+  Ember.$.mockjax({
+    url: '/api/users/' + user.id + '/avatar/',
+    status: 200,
+    type: 'GET',
+    responseText: {
+      'generated': true,
+      'gravatar': true,
+      'crop_org': true,
+      'crop_tmp': true,
+      'upload': true,
+      'galleries': [
+        {
+          'name': 'TestGallery',
+          'images': [
+            'some-image.jpg'
+          ]
+        }
+      ]
+    }
+  });
+
+  Ember.$.mockjax({
+    url: '/api/users/' + user.id + '/avatar/',
+    status: 400,
+    type: 'POST',
+    responseText: {
+      'detail': 'Failed to pick gallery avatar.',
+    }
+  });
+
+  visit('/');
+  click('.navbar-user-nav .user-menu .dropdown-toggle');
+  click('.navbar-user-nav .user-menu .editable-avatar');
+  click('#appModal .btn-gallery');
+  click('#appModal .col-sm-3 .btn');
+
+  andThen(function() {
+    assert.equal(getToastMessage(), 'Failed to pick gallery avatar.');
+    done();
+  });
+});
+
+
+test('Picked avatar from gallery', function(assert) {
+  var user = createUser();
+  auth.setProperties({
+    'isAuthenticated': true,
+    'user': user
+  });
+
+  var done = assert.async();
+  assert.expect(2);
+
+  Ember.$.mockjax({
+    url: '/api/users/' + user.id + '/avatar/',
+    status: 200,
+    type: 'GET',
+    responseText: {
+      'generated': true,
+      'gravatar': true,
+      'crop_org': true,
+      'crop_tmp': true,
+      'upload': true,
+      'galleries': [
+        {
+          'name': 'TestGallery',
+          'images': [
+            'some-image.jpg'
+          ]
+        }
+      ]
+    }
+  });
+
+  Ember.$.mockjax({
+    url: '/api/users/' + user.id + '/avatar/',
+    status: 200,
+    type: 'POST',
+    responseText: {
+      'detail': 'Avatar changed to gallery.',
+      'avatar_hash': 'eeeeeeee',
+      'options': {
+        'generated': true,
+        'gravatar': true,
+        'crop_org': true,
+        'crop_tmp': true,
+        'upload': true,
+        'galleries': true
+      }
+    }
+  });
+
+  visit('/');
+  click('.navbar-user-nav .user-menu .dropdown-toggle');
+  click('.navbar-user-nav .user-menu .editable-avatar');
+  click('#appModal .btn-gallery');
+  click('#appModal .col-sm-3 .btn');
+
+  andThen(function() {
+    assert.equal(getToastMessage(), 'Avatar changed to gallery.');
+    assert.equal(auth.get('user.avatar_hash'), 'eeeeeeee');
+    done();
+  });
+});
+
+
+test('Canceled pick from gallery', function(assert) {
+  var user = createUser();
+  auth.setProperties({
+    'isAuthenticated': true,
+    'user': user
+  });
+
+  var done = assert.async();
+  assert.expect(1);
+
+  Ember.$.mockjax({
+    url: '/api/users/' + user.id + '/avatar/',
+    status: 200,
+    type: 'GET',
+    responseText: {
+      'generated': true,
+      'gravatar': true,
+      'crop_org': true,
+      'crop_tmp': true,
+      'upload': true,
+      'galleries': [
+        {
+          'name': 'TestGallery',
+          'images': ['some-image.png']
+        }
+      ]
+    }
+  });
+
+  visit('/');
+  click('.navbar-user-nav .user-menu .dropdown-toggle');
+  click('.navbar-user-nav .user-menu .editable-avatar');
+  click('#appModal .btn-gallery');
+  click('#appModal .btn-block');
+
+  andThen(function() {
+    assert.ok(find('#appModal .btn-gravatar').length);
+    done();
+  });
+});

+ 29 - 0
misago/emberapp/tests/acceptance/cropit-service-test.js

@@ -0,0 +1,29 @@
+/* global zxcvbn */
+import Ember from 'ember';
+import { module, test } from 'qunit';
+import startApp from '../helpers/start-app';
+
+var application, container, service;
+
+module('Acceptance: CropitService', {
+  beforeEach: function() {
+    application = startApp();
+    container = application.__container__;
+    service = container.lookup('service:cropit');
+  },
+  afterEach: function() {
+    Ember.run(application, 'destroy');
+  }
+});
+
+test('loading cropit jquery extension', function(assert) {
+  var done = assert.async();
+  assert.expect(1);
+
+  Ember.run(function() {
+    service.load().then(function() {
+      assert.ok(typeof Ember.$.fn.cropit !== 'undefined');
+      done();
+    });
+  });
+});

+ 2 - 2
misago/emberapp/tests/acceptance/error-toast-test.js

@@ -1,6 +1,7 @@
 import Ember from 'ember';
 import { module, test } from 'qunit';
 import startApp from '../helpers/start-app';
+import destroyModal from '../helpers/destroy-modal';
 import getToastMessage from '../helpers/toast-message';
 
 var application;
@@ -10,8 +11,7 @@ module('Acceptance: Toasting Error Handler', {
     application = startApp();
   },
   afterEach: function() {
-    Ember.$('#appModal').off();
-    Ember.$('body').removeClass('modal-open');
+    destroyModal();
     Ember.run(application, 'destroy');
     Ember.$.mockjax.clear();
   }

+ 2 - 3
misago/emberapp/tests/acceptance/forgotten-password-test.js

@@ -1,6 +1,7 @@
 import Ember from 'ember';
 import { module, test } from 'qunit';
 import startApp from '../helpers/start-app';
+import destroyModal from '../helpers/destroy-modal';
 import getToastMessage from '../helpers/toast-message';
 
 var application;
@@ -9,10 +10,8 @@ module('Acceptance: Forgotten Password Change', {
   beforeEach: function() {
     application = startApp();
   },
-
   afterEach: function() {
-    Ember.$('#appModal').off();
-    Ember.$('body').removeClass('modal-open');
+    destroyModal();
     Ember.run(application, 'destroy');
     Ember.$.mockjax.clear();
   }

+ 2 - 2
misago/emberapp/tests/acceptance/login-test.js

@@ -1,6 +1,7 @@
 import Ember from 'ember';
 import { module, test } from 'qunit';
 import startApp from '../helpers/start-app';
+import destroyModal from '../helpers/destroy-modal';
 import getToastMessage from '../helpers/toast-message';
 
 var application;
@@ -16,8 +17,7 @@ module('Acceptance: Login', {
 
   afterEach: function() {
     Ember.$('#hidden-login-form').off('submit.stopInTest');
-    Ember.$('#appModal').off();
-    Ember.$('body').removeClass('modal-open');
+    destroyModal();
     Ember.run(application, 'destroy');
     Ember.$.mockjax.clear();
   }

+ 2 - 2
misago/emberapp/tests/acceptance/registration-test.js

@@ -2,6 +2,7 @@ import Ember from 'ember';
 import { module, test } from 'qunit';
 import startApp from '../helpers/start-app';
 import PreloadStore from 'misago/services/preload-store';
+import destroyModal from '../helpers/destroy-modal';
 import getToastMessage from '../helpers/toast-message';
 
 var application;
@@ -13,8 +14,7 @@ module('Acceptance: Register', {
 
   afterEach: function() {
     window.MisagoData['misagoSettings']['account_activation'] = 'none';
-    Ember.$('#appModal').off();
-    Ember.$('body').removeClass('modal-open');
+    destroyModal();
     Ember.run(application, 'destroy');
     Ember.$.mockjax.clear();
   }

+ 1 - 1
misago/emberapp/tests/acceptance/zxcvbn-service-test.js

@@ -24,7 +24,7 @@ test('loading zxcvbn and testing password with it', function(assert) {
     service.load().then(function() {
       assert.ok(typeof zxcvbn !== 'undefined');
       assert.ok(service.scorePassword('L0r3m !p5um') > 0);
-        done();
+      done();
     });
   });
 });

+ 1 - 0
misago/emberapp/tests/helpers/create-user.js

@@ -20,6 +20,7 @@ export default function createUser(newProps) {
         "css_class": "team",
         "is_tab": true
     },
+    "avatar_hash": 'a0b9c8d7',
     "new_notifications": 0,
     "limits_private_thread_invites_to": 0,
     "unread_private_threads": 0,

+ 7 - 0
misago/emberapp/tests/helpers/destroy-modal.js

@@ -0,0 +1,7 @@
+import Ember from 'ember';
+
+export default function destroyModal() {
+  Ember.$('#appModal').off();
+  Ember.$('body').removeClass('modal-open');
+  Ember.$('.modal-backdrop').remove();
+}

BIN
misago/project_template/avatar_store/blank/400.png