From 9d333533ac47975d931d70f6a44b99b63a7d2e3a Mon Sep 17 00:00:00 2001 From: Austin Burdine Date: Thu, 3 Dec 2015 16:37:23 -0600 Subject: [PATCH] fix 404 error handling in editor, tags, and team routes closes #6094 - adds 404-handler mixin - applies mixin to settings/tags/tag, editor/edit and team/user routes - adds adapter-error test helper to override the default adapter error --- ghost/admin/app/mixins/404-handler.js | 23 +++++++++++++++++ ghost/admin/app/routes/editor/edit.js | 3 ++- ghost/admin/app/routes/settings/tags/tag.js | 4 +-- ghost/admin/app/routes/team/user.js | 3 ++- .../tests/acceptance/settings/tags-test.js | 25 +++++++++++++++++++ ghost/admin/tests/helpers/adapter-error.js | 19 ++++++++++++++ 6 files changed, 73 insertions(+), 4 deletions(-) create mode 100644 ghost/admin/app/mixins/404-handler.js create mode 100644 ghost/admin/tests/helpers/adapter-error.js diff --git a/ghost/admin/app/mixins/404-handler.js b/ghost/admin/app/mixins/404-handler.js new file mode 100644 index 0000000000..361e13ae21 --- /dev/null +++ b/ghost/admin/app/mixins/404-handler.js @@ -0,0 +1,23 @@ +import Ember from 'ember'; + +export default Ember.Mixin.create({ + actions: { + error(error, transition) { + if (error.errors[0].errorType === 'NotFoundError') { + transition.abort(); + + let routeInfo = transition.handlerInfos[transition.handlerInfos.length - 1]; + let router = this.get('router'); + let params = []; + + for (const key of Object.keys(routeInfo.params)) { + params.push(routeInfo.params[key]); + } + + return this.transitionTo('error404', router.generate(routeInfo.name, ...params).replace('/ghost/', '').replace(/^\//g, '')); + } + + return this._super(...arguments); + } + } +}); diff --git a/ghost/admin/app/routes/editor/edit.js b/ghost/admin/app/routes/editor/edit.js index 8f5bcbecd6..6e8e68ab0b 100644 --- a/ghost/admin/app/routes/editor/edit.js +++ b/ghost/admin/app/routes/editor/edit.js @@ -1,10 +1,11 @@ /* jscs:disable requireCamelCaseOrUpperCaseIdentifiers */ import AuthenticatedRoute from 'ghost/routes/authenticated'; import base from 'ghost/mixins/editor-base-route'; +import NotFoundHandler from 'ghost/mixins/404-handler'; import isNumber from 'ghost/utils/isNumber'; import isFinite from 'ghost/utils/isFinite'; -export default AuthenticatedRoute.extend(base, { +export default AuthenticatedRoute.extend(base, NotFoundHandler, { titleToken: 'Editor', beforeModel(transition) { diff --git a/ghost/admin/app/routes/settings/tags/tag.js b/ghost/admin/app/routes/settings/tags/tag.js index 5224d04049..6bfba4cb06 100644 --- a/ghost/admin/app/routes/settings/tags/tag.js +++ b/ghost/admin/app/routes/settings/tags/tag.js @@ -1,7 +1,8 @@ /* jscs:disable requireCamelCaseOrUpperCaseIdentifiers */ import AuthenticatedRoute from 'ghost/routes/authenticated'; +import NotFoundHandler from 'ghost/mixins/404-handler'; -export default AuthenticatedRoute.extend({ +export default AuthenticatedRoute.extend(NotFoundHandler, { model(params) { return this.store.queryRecord('tag', {slug: params.tag_slug}); @@ -16,5 +17,4 @@ export default AuthenticatedRoute.extend({ this._super(...arguments); this.set('controller.model', null); } - }); diff --git a/ghost/admin/app/routes/team/user.js b/ghost/admin/app/routes/team/user.js index 76fdab32f9..41950a4491 100644 --- a/ghost/admin/app/routes/team/user.js +++ b/ghost/admin/app/routes/team/user.js @@ -2,8 +2,9 @@ import AuthenticatedRoute from 'ghost/routes/authenticated'; import CurrentUserSettings from 'ghost/mixins/current-user-settings'; import styleBody from 'ghost/mixins/style-body'; +import NotFoundHandler from 'ghost/mixins/404-handler'; -export default AuthenticatedRoute.extend(styleBody, CurrentUserSettings, { +export default AuthenticatedRoute.extend(styleBody, CurrentUserSettings, NotFoundHandler, { titleToken: 'Team - User', classNames: ['team-view-user'], diff --git a/ghost/admin/tests/acceptance/settings/tags-test.js b/ghost/admin/tests/acceptance/settings/tags-test.js index 9ea1504e21..0f36454375 100644 --- a/ghost/admin/tests/acceptance/settings/tags-test.js +++ b/ghost/admin/tests/acceptance/settings/tags-test.js @@ -11,6 +11,8 @@ import Ember from 'ember'; import startApp from '../../helpers/start-app'; import destroyApp from '../../helpers/destroy-app'; import { invalidateSession, authenticateSession } from 'ghost/tests/helpers/ember-simple-auth'; +import { errorOverride, errorReset } from 'ghost/tests/helpers/adapter-error'; +import Mirage from 'ember-cli-mirage'; const {run} = Ember; @@ -279,5 +281,28 @@ describe('Acceptance: Settings - Tags', function () { .to.equal(32); }); }); + + describe('with 404', function () { + beforeEach(function () { + errorOverride(); + }); + + afterEach(function () { + errorReset(); + }); + + it('redirects to 404 when tag does not exist', function () { + server.get('/tags/slug/unknown/', function () { + return new Mirage.Response(404, {'Content-Type': 'application/json'}, {errors: [{message: 'Tag not found.', errorType: 'NotFoundError'}]}); + }); + + visit('settings/tags/unknown'); + + andThen(() => { + expect(currentPath()).to.equal('error404'); + expect(currentURL()).to.equal('/settings/tags/unknown'); + }); + }); + }); }); }); diff --git a/ghost/admin/tests/helpers/adapter-error.js b/ghost/admin/tests/helpers/adapter-error.js new file mode 100644 index 0000000000..567938f1ee --- /dev/null +++ b/ghost/admin/tests/helpers/adapter-error.js @@ -0,0 +1,19 @@ +import Ember from 'ember'; + +// This is needed for testing error responses in acceptance tests +// See http://williamsbdev.com/posts/testing-rsvp-errors-handled-globally/ + +let originalException; +let originalLoggerError; + +export function errorOverride() { + originalException = Ember.Test.adapter.exception; + originalLoggerError = Ember.Logger.error; + Ember.Test.adapter.exception = function () {}; + Ember.Logger.error = function () {}; +} + +export function errorReset() { + Ember.Test.adapter.exception = originalException; + Ember.Logger.error = originalLoggerError; +}