diff --git a/ghost/admin/app/controllers/settings/labs.js b/ghost/admin/app/controllers/settings/labs.js index 3dcb4bcd26..956882d563 100644 --- a/ghost/admin/app/controllers/settings/labs.js +++ b/ghost/admin/app/controllers/settings/labs.js @@ -25,6 +25,14 @@ const IMPORT_MIME_TYPES = [ const JSON_EXTENSION = ['json']; const JSON_MIME_TYPE = ['application/json']; +const YAML_EXTENSION = ['yml', 'yaml']; +const YAML_MIME_TYPE = [ + 'text/vnd.yaml', + 'application/vnd.yaml', + 'text/x-yaml', + 'application/x-yaml' +]; + export default Controller.extend({ ajax: service(), config: service(), @@ -43,12 +51,16 @@ export default Controller.extend({ importMimeType: null, jsonExtension: null, jsonMimeType: null, + yamlExtension: null, + yamlMimeType: null, init() { this._super(...arguments); this.importMimeType = IMPORT_MIME_TYPES; this.jsonExtension = JSON_EXTENSION; this.jsonMimeType = JSON_MIME_TYPE; + this.yamlExtension = YAML_EXTENSION; + this.yamlMimeType = YAML_MIME_TYPE; }, actions: { @@ -214,6 +226,17 @@ export default Controller.extend({ return true; }).drop(), + routesUploadResult: task(function* (success) { + this.set('routesSuccess', success); + this.set('routesFailure', !success); + + yield timeout(config.environment === 'test' ? 100 : 5000); + + this.set('routesSuccess', null); + this.set('routesFailure', null); + return true; + }).drop(), + reset() { this.set('importErrors', null); this.set('importSuccessful', false); diff --git a/ghost/admin/app/templates/settings/labs.hbs b/ghost/admin/app/templates/settings/labs.hbs index 41482e891a..b9a83f4ef4 100644 --- a/ghost/admin/app/templates/settings/labs.hbs +++ b/ghost/admin/app/templates/settings/labs.hbs @@ -163,7 +163,51 @@ {{/gh-uploader}} +
+ {{#gh-uploader + extensions=yamlExtension + uploadUrl="/settings/routes/yaml/" + paramName="routes" + onUploadSuccess=(perform routesUploadResult true) + onUploadFailure=(perform routesUploadResult false) + as |uploader| + }} +
+
Routes
+
Configure dynamic routing by modifying the routes.yaml file
+ {{#each uploader.errors as |error|}} +
{{error.message}}
+ {{/each}} +
+
+ {{#if uploader.isUploading}} + {{uploader.progressBar}} + {{else}} + + Download current routes.yml + {{/if}} +
+ {{gh-file-input multiple=false action=uploader.setFiles accept=yamlMimeType data-test-file-input="routes"}} +
+
+ {{/gh-uploader}} +
diff --git a/ghost/admin/tests/acceptance/settings/labs-test.js b/ghost/admin/tests/acceptance/settings/labs-test.js index 3fa6a4e2bb..ac47a673fe 100644 --- a/ghost/admin/tests/acceptance/settings/labs-test.js +++ b/ghost/admin/tests/acceptance/settings/labs-test.js @@ -195,5 +195,118 @@ describe('Acceptance: Settings - Labs', function () { let iframe = $('#iframeDownload'); expect(iframe.attr('src')).to.have.string('/redirects/json/'); }); + + it('can upload/download routes.yaml', async function () { + await visit('/settings/labs'); + + // successful upload + server.post('/settings/routes/yaml/', {}, 200); + + await fileUpload( + '[data-test-file-input="routes"]', + ['test'], + {name: 'routes.yaml', type: 'application/x-yaml'} + ); + + // TODO: tests for the temporary success/failure state have been + // disabled because they were randomly failing + + // this should be half-way through button reset timeout + // await timeout(50); + // + // // shows success button + // let button = find('[data-test-button="upload-routes"]'); + // expect(button.length, 'no of success buttons').to.equal(1); + // expect( + // button.hasClass('gh-btn-green'), + // 'success button is green' + // ).to.be.true; + // expect( + // button.text().trim(), + // 'success button text' + // ).to.have.string('Uploaded'); + // + // await wait(); + + // returned to normal button + let button = find('[data-test-button="upload-routes"]'); + expect(button.length, 'no of post-success buttons').to.equal(1); + expect( + button.hasClass('gh-btn-green'), + 'routes post-success button doesn\'t have success class' + ).to.be.false; + expect( + button.text().trim(), + 'routes post-success button text' + ).to.have.string('Upload routes YAML'); + + // failed upload + server.post('/settings/routes/yaml/', { + errors: [{ + errorType: 'BadRequestError', + message: 'Test failure message' + }] + }, 400); + + await fileUpload( + '[data-test-file-input="routes"]', + ['test'], + {name: 'routes-bad.yaml', type: 'application/x-yaml'} + ); + + // TODO: tests for the temporary success/failure state have been + // disabled because they were randomly failing + + // this should be half-way through button reset timeout + // await timeout(50); + // + // shows failure button + // button = find('[data-test-button="upload-routes"]'); + // expect(button.length, 'no of failure buttons').to.equal(1); + // expect( + // button.hasClass('gh-btn-red'), + // 'failure button is red' + // ).to.be.true; + // expect( + // button.text().trim(), + // 'failure button text' + // ).to.have.string('Upload Failed'); + // + // await wait(); + + // shows error message + expect( + find('[data-test-error="routes"]').text().trim(), + 'routes upload error text' + ).to.have.string('Test failure message'); + + // returned to normal button + button = find('[data-test-button="upload-routes"]'); + expect(button.length, 'no of post-failure buttons').to.equal(1); + expect( + button.hasClass('gh-btn-red'), + 'routes post-failure button doesn\'t have failure class' + ).to.be.false; + expect( + button.text().trim(), + 'routes post-failure button text' + ).to.have.string('Upload routes YAML'); + + // successful upload clears error + server.post('/settings/routes/yaml/', {}, 200); + await fileUpload( + '[data-test-file-input="routes"]', + ['test'], + {name: 'routes-good.yaml', type: 'application/x-yaml'} + ); + + expect(find('[data-test-error="routes"]')).to.not.exist; + + // can download redirects.json + await click('[data-test-link="download-routes"]'); + + let iframe = $('#iframeDownload'); + expect(iframe.attr('src')).to.have.string('/settings/routes/yaml/'); + }); }); });