From acea881bb012cce0b59f3d5874a630b16d0caaef Mon Sep 17 00:00:00 2001 From: Jacob Schatz Date: Fri, 7 Apr 2017 23:38:43 -0400 Subject: Initial balsamiq support --- app/models/blob.rb | 6 ++++++ app/views/projects/blob/_bmpr.html.haml | 5 +++++ app/views/projects/blob/balsamiq_viewer.js | 3 +++ config/webpack.config.js | 1 + db/schema.rb | 2 +- 5 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 app/views/projects/blob/_bmpr.html.haml create mode 100644 app/views/projects/blob/balsamiq_viewer.js diff --git a/app/models/blob.rb b/app/models/blob.rb index 801d3442803..0c1730f6801 100644 --- a/app/models/blob.rb +++ b/app/models/blob.rb @@ -58,6 +58,10 @@ class Blob < SimpleDelegator binary? && extname.downcase.delete('.') == 'sketch' end + def balsamiq? + binary? && extname.downcase.delete('.') == 'bmpr' + end + def stl? extname.downcase.delete('.') == 'stl' end @@ -87,6 +91,8 @@ class Blob < SimpleDelegator 'sketch' elsif stl? 'stl' + elsif balsamiq? + 'bmpr' elsif text? 'text' else diff --git a/app/views/projects/blob/_bmpr.html.haml b/app/views/projects/blob/_bmpr.html.haml new file mode 100644 index 00000000000..048bdf9dcb5 --- /dev/null +++ b/app/views/projects/blob/_bmpr.html.haml @@ -0,0 +1,5 @@ +- content_for :page_specific_javascripts do + = page_specific_javascript_bundle_tag('common_vue') + = page_specific_javascript_bundle_tag('balsamiq_viewer') + +.file-content#js-balsamiq-viewer{ data: { endpoint: namespace_project_raw_path(@project.namespace, @project, @id) } } \ No newline at end of file diff --git a/app/views/projects/blob/balsamiq_viewer.js b/app/views/projects/blob/balsamiq_viewer.js new file mode 100644 index 00000000000..b60cfe165a4 --- /dev/null +++ b/app/views/projects/blob/balsamiq_viewer.js @@ -0,0 +1,3 @@ +import renderBalsamiq from './balsamiq'; + +document.addEventListener('DOMContentLoaded', renderBalsamiq); \ No newline at end of file diff --git a/config/webpack.config.js b/config/webpack.config.js index e3bc939d578..42638d47cfa 100644 --- a/config/webpack.config.js +++ b/config/webpack.config.js @@ -39,6 +39,7 @@ var config = { notebook_viewer: './blob/notebook_viewer.js', sketch_viewer: './blob/sketch_viewer.js', pdf_viewer: './blob/pdf_viewer.js', + balsamiq_viewer: './blob/balsamiq_viewer.js', profile: './profile/profile_bundle.js', protected_branches: './protected_branches/protected_branches_bundle.js', protected_tags: './protected_tags', diff --git a/db/schema.rb b/db/schema.rb index 3422847d729..ceae051db0c 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -950,9 +950,9 @@ ActiveRecord::Schema.define(version: 20170408033905) do t.boolean "lfs_enabled" t.text "description_html" t.boolean "only_allow_merge_if_all_discussions_are_resolved" - t.integer "auto_cancel_pending_pipelines", default: 0, null: false t.boolean "printing_merge_request_link_enabled", default: true, null: false t.string "import_jid" + t.integer "auto_cancel_pending_pipelines", default: 0, null: false end add_index "projects", ["ci_id"], name: "index_projects_on_ci_id", using: :btree -- cgit v1.2.1 From ef07200cd0f059a2e0493779263aa526a2ade2e3 Mon Sep 17 00:00:00 2001 From: Jacob Schatz Date: Sat, 8 Apr 2017 00:18:37 -0400 Subject: Get initial sql values back from file which is database --- app/assets/javascripts/blob/balsamiq/index.js | 27 ++++++++++++++++++++++++++ app/assets/javascripts/blob/balsamiq_viewer.js | 5 +++++ app/views/projects/blob/balsamiq_viewer.js | 3 --- config/webpack.config.js | 5 +++++ package.json | 1 + yarn.lock | 4 ++++ 6 files changed, 42 insertions(+), 3 deletions(-) create mode 100644 app/assets/javascripts/blob/balsamiq/index.js create mode 100644 app/assets/javascripts/blob/balsamiq_viewer.js delete mode 100644 app/views/projects/blob/balsamiq_viewer.js diff --git a/app/assets/javascripts/blob/balsamiq/index.js b/app/assets/javascripts/blob/balsamiq/index.js new file mode 100644 index 00000000000..04201366897 --- /dev/null +++ b/app/assets/javascripts/blob/balsamiq/index.js @@ -0,0 +1,27 @@ +import Vue from 'vue'; +import sqljs from 'sql.js'; + +export default class BalsamiqViewer { + constructor(el) { + this.el = el; + this.loadSqlFile(); + } + + + + loadSqlFile() { + var xhr = new XMLHttpRequest(); + console.log(this.el) + xhr.open('GET', this.el.dataset.endpoint, true); + xhr.responseType = 'arraybuffer'; + + xhr.onload = function(e) { + var uInt8Array = new Uint8Array(this.response); + var db = new SQL.Database(uInt8Array); + var contents = db.exec("SELECT * FROM thumbnails"); + console.log(contents) + // contents is now [{columns:['col1','col2',...], values:[[first row], [second row], ...]}] + }; + xhr.send(); + } +} \ No newline at end of file diff --git a/app/assets/javascripts/blob/balsamiq_viewer.js b/app/assets/javascripts/blob/balsamiq_viewer.js new file mode 100644 index 00000000000..b1493398099 --- /dev/null +++ b/app/assets/javascripts/blob/balsamiq_viewer.js @@ -0,0 +1,5 @@ +import BalsamiqViewer from './balsamiq'; + +document.addEventListener('DOMContentLoaded', () => { + new BalsamiqViewer(document.getElementById('js-balsamiq-viewer')); +}); \ No newline at end of file diff --git a/app/views/projects/blob/balsamiq_viewer.js b/app/views/projects/blob/balsamiq_viewer.js deleted file mode 100644 index b60cfe165a4..00000000000 --- a/app/views/projects/blob/balsamiq_viewer.js +++ /dev/null @@ -1,3 +0,0 @@ -import renderBalsamiq from './balsamiq'; - -document.addEventListener('DOMContentLoaded', renderBalsamiq); \ No newline at end of file diff --git a/config/webpack.config.js b/config/webpack.config.js index 42638d47cfa..bc4126bce02 100644 --- a/config/webpack.config.js +++ b/config/webpack.config.js @@ -15,6 +15,10 @@ var DEV_SERVER_LIVERELOAD = process.env.DEV_SERVER_LIVERELOAD !== 'false'; var WEBPACK_REPORT = process.env.WEBPACK_REPORT; var config = { + // because sqljs requires fs. + node: { + fs: "empty" + }, context: path.join(ROOT_PATH, 'app/assets/javascripts'), entry: { common: './commons/index.js', @@ -118,6 +122,7 @@ var config = { 'merge_conflicts', 'notebook_viewer', 'pdf_viewer', + 'balsamiq_viewer', 'vue_pipelines', ], minChunks: function(module, count) { diff --git a/package.json b/package.json index 6d4f99e33b3..eb680b0d76c 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "raphael": "^2.2.7", "raw-loader": "^0.5.1", "select2": "3.5.2-browserify", + "sql.js": "^0.4.0", "stats-webpack-plugin": "^0.4.3", "three": "^0.84.0", "three-orbit-controls": "^82.1.0", diff --git a/yarn.lock b/yarn.lock index 2434b3a8a48..d0175ca9f98 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4154,6 +4154,10 @@ sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" +sql.js@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/sql.js/-/sql.js-0.4.0.tgz#23be9635520eb0ff43a741e7e830397266e88445" + sshpk@^1.7.0: version "1.10.2" resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.10.2.tgz#d5a804ce22695515638e798dbe23273de070a5fa" -- cgit v1.2.1 From e1b0ed391fcbd1c622b6e3c866674b85ccd0edea Mon Sep 17 00:00:00 2001 From: Jacob Schatz Date: Sat, 8 Apr 2017 10:36:42 -0400 Subject: Show thumbnails and their titles. --- app/assets/javascripts/blob/balsamiq/index.js | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/app/assets/javascripts/blob/balsamiq/index.js b/app/assets/javascripts/blob/balsamiq/index.js index 04201366897..61f4631b423 100644 --- a/app/assets/javascripts/blob/balsamiq/index.js +++ b/app/assets/javascripts/blob/balsamiq/index.js @@ -8,19 +8,33 @@ export default class BalsamiqViewer { } - loadSqlFile() { var xhr = new XMLHttpRequest(); - console.log(this.el) + var self = this; xhr.open('GET', this.el.dataset.endpoint, true); xhr.responseType = 'arraybuffer'; xhr.onload = function(e) { + var list = document.createElement('ul'); var uInt8Array = new Uint8Array(this.response); var db = new SQL.Database(uInt8Array); var contents = db.exec("SELECT * FROM thumbnails"); - console.log(contents) - // contents is now [{columns:['col1','col2',...], values:[[first row], [second row], ...]}] + var previews = contents[0].values.map((i)=>{return JSON.parse(i[1])}); + previews.forEach((prev) => { + var li = document.createElement('li'); + var title = db.exec(`select * from resources where id = '${prev.resourceID}'`) + var template = + `
+
${JSON.parse(title[0].values[0][2]).name}
+
+ +
+
`; + li.innerHTML = template; + list.appendChild(li); + }); + list.classList += 'list-inline'; + self.el.appendChild(list); }; xhr.send(); } -- cgit v1.2.1 From 440ff838f7646ac7ea7a660280b51740ebeed70f Mon Sep 17 00:00:00 2001 From: "Luke \"Jared\" Bennett" Date: Mon, 10 Apr 2017 15:48:23 +0100 Subject: Tidy balsamiq viewer and remove unused Vue --- .../javascripts/blob/balsamiq/balsamiq_viewer.js | 89 ++++++++++++++++++++++ app/assets/javascripts/blob/balsamiq/index.js | 41 ---------- app/assets/javascripts/blob/balsamiq_viewer.js | 7 +- app/views/projects/blob/_bmpr.html.haml | 3 +- 4 files changed, 94 insertions(+), 46 deletions(-) create mode 100644 app/assets/javascripts/blob/balsamiq/balsamiq_viewer.js delete mode 100644 app/assets/javascripts/blob/balsamiq/index.js diff --git a/app/assets/javascripts/blob/balsamiq/balsamiq_viewer.js b/app/assets/javascripts/blob/balsamiq/balsamiq_viewer.js new file mode 100644 index 00000000000..e1c837cb09e --- /dev/null +++ b/app/assets/javascripts/blob/balsamiq/balsamiq_viewer.js @@ -0,0 +1,89 @@ +/* global Flash */ + +import sqljs from 'sql.js'; + +class BalsamiqViewer { + constructor(viewer) { + this.viewer = viewer; + this.endpoint = this.viewer.dataset.endpoint; + } + + loadFile() { + const xhr = new XMLHttpRequest(); + + xhr.open('GET', this.endpoint, true); + xhr.responseType = 'arraybuffer'; + + xhr.onload = this.renderFile.bind(this); + xhr.onerror = BalsamiqViewer.onError; + + xhr.send(); + } + + renderFile(loadEvent) { + const container = document.createElement('ul'); + + this.initDatabase(loadEvent.target.response); + + const previews = this.getPreviews(); + const renderedPreviews = previews.map(preview => this.renderPreview(preview, container)); + + container.innerHTML = renderedPreviews.join(''); + container.classList.add('list-inline'); + + this.viewer.appendChild(container); + } + + initDatabase(data) { + const previewBinary = new Uint8Array(data); + + this.database = new sqljs.Database(previewBinary); + } + + getPreviews() { + const thumnails = this.database.exec('SELECT * FROM thumbnails'); + + return thumnails[0].values.map(BalsamiqViewer.parsePreview); + } + + renderPreview(preview) { + const previewElement = document.createElement('li'); + + previewElement.innerHTML = this.renderTemplate(preview); + + return previewElement.outerHTML; + } + + renderTemplate(preview) { + let template = BalsamiqViewer.PREVIEW_TEMPLATE; + + const title = this.database.exec(`SELECT * FROM resources WHERE id = '${preview.resourceID}'`); + const name = JSON.parse(title[0].values[0][2]).name; + const image = preview.image; + + template = template.replace(/{{name}}/, name).replace(/{{image}}/, image); + + return template; + } + + static parsePreview(preview) { + return JSON.parse(preview[1]); + } + + static onError() { + const flash = new Flash('Balsamiq file could not be loaded.'); + + return flash; + } +} + +BalsamiqViewer.PREVIEW_TEMPLATE = ` +
+
{{name}}
+
+ +
+
+`; + +export default BalsamiqViewer; diff --git a/app/assets/javascripts/blob/balsamiq/index.js b/app/assets/javascripts/blob/balsamiq/index.js deleted file mode 100644 index 61f4631b423..00000000000 --- a/app/assets/javascripts/blob/balsamiq/index.js +++ /dev/null @@ -1,41 +0,0 @@ -import Vue from 'vue'; -import sqljs from 'sql.js'; - -export default class BalsamiqViewer { - constructor(el) { - this.el = el; - this.loadSqlFile(); - } - - - loadSqlFile() { - var xhr = new XMLHttpRequest(); - var self = this; - xhr.open('GET', this.el.dataset.endpoint, true); - xhr.responseType = 'arraybuffer'; - - xhr.onload = function(e) { - var list = document.createElement('ul'); - var uInt8Array = new Uint8Array(this.response); - var db = new SQL.Database(uInt8Array); - var contents = db.exec("SELECT * FROM thumbnails"); - var previews = contents[0].values.map((i)=>{return JSON.parse(i[1])}); - previews.forEach((prev) => { - var li = document.createElement('li'); - var title = db.exec(`select * from resources where id = '${prev.resourceID}'`) - var template = - `
-
${JSON.parse(title[0].values[0][2]).name}
-
- -
-
`; - li.innerHTML = template; - list.appendChild(li); - }); - list.classList += 'list-inline'; - self.el.appendChild(list); - }; - xhr.send(); - } -} \ No newline at end of file diff --git a/app/assets/javascripts/blob/balsamiq_viewer.js b/app/assets/javascripts/blob/balsamiq_viewer.js index b1493398099..1dacf84470f 100644 --- a/app/assets/javascripts/blob/balsamiq_viewer.js +++ b/app/assets/javascripts/blob/balsamiq_viewer.js @@ -1,5 +1,6 @@ -import BalsamiqViewer from './balsamiq'; +import BalsamiqViewer from './balsamiq/balsamiq_viewer'; document.addEventListener('DOMContentLoaded', () => { - new BalsamiqViewer(document.getElementById('js-balsamiq-viewer')); -}); \ No newline at end of file + const balsamiqViewer = new BalsamiqViewer(document.getElementById('js-balsamiq-viewer')); + balsamiqViewer.loadFile(); +}); diff --git a/app/views/projects/blob/_bmpr.html.haml b/app/views/projects/blob/_bmpr.html.haml index 048bdf9dcb5..36ec0cbcce8 100644 --- a/app/views/projects/blob/_bmpr.html.haml +++ b/app/views/projects/blob/_bmpr.html.haml @@ -1,5 +1,4 @@ - content_for :page_specific_javascripts do - = page_specific_javascript_bundle_tag('common_vue') = page_specific_javascript_bundle_tag('balsamiq_viewer') -.file-content#js-balsamiq-viewer{ data: { endpoint: namespace_project_raw_path(@project.namespace, @project, @id) } } \ No newline at end of file +.file-content#js-balsamiq-viewer{ data: { endpoint: namespace_project_raw_path(@project.namespace, @project, @id) } } -- cgit v1.2.1 From 80745bc81da089af088d99574088dc6bf8959e4d Mon Sep 17 00:00:00 2001 From: "Luke \"Jared\" Bennett" Date: Mon, 10 Apr 2017 16:16:09 +0100 Subject: Revert schema.db --- db/schema.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/schema.rb b/db/schema.rb index ceae051db0c..3422847d729 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -950,9 +950,9 @@ ActiveRecord::Schema.define(version: 20170408033905) do t.boolean "lfs_enabled" t.text "description_html" t.boolean "only_allow_merge_if_all_discussions_are_resolved" + t.integer "auto_cancel_pending_pipelines", default: 0, null: false t.boolean "printing_merge_request_link_enabled", default: true, null: false t.string "import_jid" - t.integer "auto_cancel_pending_pipelines", default: 0, null: false end add_index "projects", ["ci_id"], name: "index_projects_on_ci_id", using: :btree -- cgit v1.2.1 From 75d3ed9a43eceda6a375e1538945a20c97925841 Mon Sep 17 00:00:00 2001 From: "Luke \"Jared\" Bennett" Date: Mon, 10 Apr 2017 16:44:49 +0100 Subject: Added Spinner class --- .../javascripts/blob/balsamiq/balsamiq_viewer.js | 13 +++++++--- app/assets/javascripts/spinner.js | 28 ++++++++++++++++++++++ app/assets/stylesheets/framework/files.scss | 11 +++++++++ 3 files changed, 49 insertions(+), 3 deletions(-) create mode 100644 app/assets/javascripts/spinner.js diff --git a/app/assets/javascripts/blob/balsamiq/balsamiq_viewer.js b/app/assets/javascripts/blob/balsamiq/balsamiq_viewer.js index e1c837cb09e..0065584cec5 100644 --- a/app/assets/javascripts/blob/balsamiq/balsamiq_viewer.js +++ b/app/assets/javascripts/blob/balsamiq/balsamiq_viewer.js @@ -1,11 +1,13 @@ /* global Flash */ +import Spinner from '../../spinner'; import sqljs from 'sql.js'; class BalsamiqViewer { constructor(viewer) { this.viewer = viewer; this.endpoint = this.viewer.dataset.endpoint; + this.spinner = new Spinner(this.viewer); } loadFile() { @@ -17,10 +19,14 @@ class BalsamiqViewer { xhr.onload = this.renderFile.bind(this); xhr.onerror = BalsamiqViewer.onError; + this.spinner.start(); + xhr.send(); } renderFile(loadEvent) { + this.spinner.stop(); + const container = document.createElement('ul'); this.initDatabase(loadEvent.target.response); @@ -29,7 +35,7 @@ class BalsamiqViewer { const renderedPreviews = previews.map(preview => this.renderPreview(preview, container)); container.innerHTML = renderedPreviews.join(''); - container.classList.add('list-inline'); + container.classList.add('list-inline', 'previews'); this.viewer.appendChild(container); } @@ -41,14 +47,15 @@ class BalsamiqViewer { } getPreviews() { - const thumnails = this.database.exec('SELECT * FROM thumbnails'); + const thumbnails = this.database.exec('SELECT * FROM thumbnails'); - return thumnails[0].values.map(BalsamiqViewer.parsePreview); + return thumbnails[0].values.map(BalsamiqViewer.parsePreview); } renderPreview(preview) { const previewElement = document.createElement('li'); + previewElement.classList.add('preview'); previewElement.innerHTML = this.renderTemplate(preview); return previewElement.outerHTML; diff --git a/app/assets/javascripts/spinner.js b/app/assets/javascripts/spinner.js new file mode 100644 index 00000000000..b7bfe1a2572 --- /dev/null +++ b/app/assets/javascripts/spinner.js @@ -0,0 +1,28 @@ +class Spinner { + constructor(renderable) { + this.renderable = renderable; + + this.container = Spinner.createContainer(); + } + + start() { + this.renderable.prepend(this.container); + } + + stop() { + this.container.remove(); + } + + static createContainer() { + const container = document.createElement('div'); + container.classList.add('loading'); + + container.innerHTML = Spinner.TEMPLATE; + + return container; + } +} + +Spinner.TEMPLATE = ''; + +export default Spinner; diff --git a/app/assets/stylesheets/framework/files.scss b/app/assets/stylesheets/framework/files.scss index a5a8522739e..b8dab538fee 100644 --- a/app/assets/stylesheets/framework/files.scss +++ b/app/assets/stylesheets/framework/files.scss @@ -168,6 +168,17 @@ &.code { padding: 0; } + + .list-inline.previews { + display: flex; + flex-wrap: wrap; + justify-content: space-between; + padding: $gl-padding; + + .preview { + flex-shrink: 0; + } + } } } -- cgit v1.2.1 From 40e2be25c74aef14a94cf9dff4e80554958fd347 Mon Sep 17 00:00:00 2001 From: "Luke \"Jared\" Bennett" Date: Mon, 10 Apr 2017 18:20:54 +0100 Subject: Added unit tests --- .../javascripts/blob/balsamiq/balsamiq_viewer.js | 6 +- .../blob/balsamiq/balsamiq_viewer_spec.js | 357 +++++++++++++++++++++ spec/javascripts/spinner_spec.js | 97 ++++++ 3 files changed, 457 insertions(+), 3 deletions(-) create mode 100644 spec/javascripts/blob/balsamiq/balsamiq_viewer_spec.js create mode 100644 spec/javascripts/spinner_spec.js diff --git a/app/assets/javascripts/blob/balsamiq/balsamiq_viewer.js b/app/assets/javascripts/blob/balsamiq/balsamiq_viewer.js index 0065584cec5..d0c161f2aad 100644 --- a/app/assets/javascripts/blob/balsamiq/balsamiq_viewer.js +++ b/app/assets/javascripts/blob/balsamiq/balsamiq_viewer.js @@ -1,7 +1,7 @@ /* global Flash */ -import Spinner from '../../spinner'; import sqljs from 'sql.js'; +import Spinner from '../../spinner'; class BalsamiqViewer { constructor(viewer) { @@ -32,7 +32,7 @@ class BalsamiqViewer { this.initDatabase(loadEvent.target.response); const previews = this.getPreviews(); - const renderedPreviews = previews.map(preview => this.renderPreview(preview, container)); + const renderedPreviews = previews.map(preview => this.renderPreview(preview)); container.innerHTML = renderedPreviews.join(''); container.classList.add('list-inline', 'previews'); @@ -68,7 +68,7 @@ class BalsamiqViewer { const name = JSON.parse(title[0].values[0][2]).name; const image = preview.image; - template = template.replace(/{{name}}/, name).replace(/{{image}}/, image); + template = template.replace(/{{name}}/g, name).replace(/{{image}}/g, image); return template; } diff --git a/spec/javascripts/blob/balsamiq/balsamiq_viewer_spec.js b/spec/javascripts/blob/balsamiq/balsamiq_viewer_spec.js new file mode 100644 index 00000000000..10db4175ca4 --- /dev/null +++ b/spec/javascripts/blob/balsamiq/balsamiq_viewer_spec.js @@ -0,0 +1,357 @@ +import sqljs from 'sql.js'; +import BalsamiqViewer from '~/blob/balsamiq/balsamiq_viewer'; +import * as spinnerSrc from '~/spinner'; +import ClassSpecHelper from '../../helpers/class_spec_helper'; + +describe('BalsamiqViewer', () => { + let balsamiqViewer; + let endpoint; + let viewer; + + describe('class constructor', () => { + beforeEach(() => { + endpoint = 'endpoint'; + viewer = { + dataset: { + endpoint, + }, + }; + + spyOn(spinnerSrc, 'default'); + + balsamiqViewer = new BalsamiqViewer(viewer); + }); + + it('should set .viewer', () => { + expect(balsamiqViewer.viewer).toBe(viewer); + }); + + it('should set .endpoint', () => { + expect(balsamiqViewer.endpoint).toBe(endpoint); + }); + + it('should instantiate Spinner', () => { + expect(spinnerSrc.default).toHaveBeenCalledWith(viewer); + }); + + it('should set .spinner', () => { + expect(balsamiqViewer.spinner).toEqual(jasmine.any(spinnerSrc.default)); + }); + }); + + describe('loadFile', () => { + let xhr; + let spinner; + + beforeEach(() => { + endpoint = 'endpoint'; + xhr = jasmine.createSpyObj('xhr', ['open', 'send']); + spinner = jasmine.createSpyObj('spinner', ['start']); + + balsamiqViewer = jasmine.createSpyObj('balsamiqViewer', ['renderFile']); + balsamiqViewer.endpoint = endpoint; + balsamiqViewer.spinner = spinner; + + spyOn(window, 'XMLHttpRequest').and.returnValue(xhr); + + BalsamiqViewer.prototype.loadFile.call(balsamiqViewer); + }); + + it('should instantiate XMLHttpRequest', () => { + expect(window.XMLHttpRequest).toHaveBeenCalled(); + }); + + it('should call .open', () => { + expect(xhr.open).toHaveBeenCalledWith('GET', endpoint, true); + }); + + it('should set .responseType', () => { + expect(xhr.responseType).toBe('arraybuffer'); + }); + + it('should set .onload', () => { + expect(xhr.onload).toEqual(jasmine.any(Function)); + }); + + it('should set .onerror', () => { + expect(xhr.onerror).toBe(BalsamiqViewer.onError); + }); + + it('should call spinner.start', () => { + expect(spinner.start).toHaveBeenCalled(); + }); + + it('should call .send', () => { + expect(xhr.send).toHaveBeenCalled(); + }); + }); + + describe('renderFile', () => { + let spinner; + let container; + let loadEvent; + let previews; + + beforeEach(() => { + loadEvent = { target: { response: {} } }; + viewer = jasmine.createSpyObj('viewer', ['appendChild']); + spinner = jasmine.createSpyObj('spinner', ['stop']); + previews = [0, 1, 2]; + + balsamiqViewer = jasmine.createSpyObj('balsamiqViewer', ['initDatabase', 'getPreviews', 'renderPreview']); + balsamiqViewer.viewer = viewer; + balsamiqViewer.spinner = spinner; + + balsamiqViewer.getPreviews.and.returnValue(previews); + balsamiqViewer.renderPreview.and.callFake(preview => preview); + viewer.appendChild.and.callFake((containerElement) => { + container = containerElement; + }); + + BalsamiqViewer.prototype.renderFile.call(balsamiqViewer, loadEvent); + }); + + it('should call spinner.stop', () => { + expect(spinner.stop).toHaveBeenCalled(); + }); + + it('should call .initDatabase', () => { + expect(balsamiqViewer.initDatabase).toHaveBeenCalledWith(loadEvent.target.response); + }); + + it('should call .getPreviews', () => { + expect(balsamiqViewer.getPreviews).toHaveBeenCalled(); + }); + + it('should call .renderPreview for each preview', () => { + const allArgs = balsamiqViewer.renderPreview.calls.allArgs(); + + expect(allArgs.length).toBe(3); + + previews.forEach((preview, i) => { + expect(allArgs[i][0]).toBe(preview); + }); + }); + + it('should set .innerHTML', () => { + expect(container.innerHTML).toBe('012'); + }); + + it('should add inline preview classes', () => { + expect(container.classList[0]).toBe('list-inline'); + expect(container.classList[1]).toBe('previews'); + }); + + it('should call viewer.appendChild', () => { + expect(viewer.appendChild).toHaveBeenCalledWith(container); + }); + }); + + describe('initDatabase', () => { + let database; + let uint8Array; + let data; + + beforeEach(() => { + uint8Array = {}; + database = {}; + data = 'data'; + + balsamiqViewer = {}; + + spyOn(window, 'Uint8Array').and.returnValue(uint8Array); + spyOn(sqljs, 'Database').and.returnValue(database); + + BalsamiqViewer.prototype.initDatabase.call(balsamiqViewer, data); + }); + + it('should instantiate Uint8Array', () => { + expect(window.Uint8Array).toHaveBeenCalledWith(data); + }); + + it('should call sqljs.Database', () => { + expect(sqljs.Database).toHaveBeenCalledWith(uint8Array); + }); + + it('should set .database', () => { + expect(balsamiqViewer.database).toBe(database); + }); + }); + + describe('getPreviews', () => { + let database; + let thumbnails; + let getPreviews; + + beforeEach(() => { + database = jasmine.createSpyObj('database', ['exec']); + thumbnails = [{ values: [0, 1, 2] }]; + + balsamiqViewer = { + database, + }; + + spyOn(BalsamiqViewer, 'parsePreview').and.callFake(preview => preview.toString()); + database.exec.and.returnValue(thumbnails); + + getPreviews = BalsamiqViewer.prototype.getPreviews.call(balsamiqViewer); + }); + + it('should call database.exec', () => { + expect(database.exec).toHaveBeenCalledWith('SELECT * FROM thumbnails'); + }); + + it('should call .parsePreview for each value', () => { + const allArgs = BalsamiqViewer.parsePreview.calls.allArgs(); + + expect(allArgs.length).toBe(3); + + thumbnails[0].values.forEach((value, i) => { + expect(allArgs[i][0]).toBe(value); + }); + }); + + it('should return an array of parsed values', () => { + expect(getPreviews).toEqual(['0', '1', '2']); + }); + }); + + describe('renderPreview', () => { + let previewElement; + let innerHTML; + let preview; + let renderPreview; + + beforeEach(() => { + innerHTML = 'innerHTML'; + previewElement = { + outerHTML: '

outerHTML

', + classList: jasmine.createSpyObj('classList', ['add']), + }; + preview = {}; + + balsamiqViewer = jasmine.createSpyObj('balsamiqViewer', ['renderTemplate']); + + spyOn(document, 'createElement').and.returnValue(previewElement); + balsamiqViewer.renderTemplate.and.returnValue(innerHTML); + + renderPreview = BalsamiqViewer.prototype.renderPreview.call(balsamiqViewer, preview); + }); + + it('should call document.createElement', () => { + expect(document.createElement).toHaveBeenCalledWith('li'); + }); + + it('should call classList.add', () => { + expect(previewElement.classList.add).toHaveBeenCalledWith('preview'); + }); + + it('should call .renderTemplate', () => { + expect(balsamiqViewer.renderTemplate).toHaveBeenCalledWith(preview); + }); + + it('should set .innerHTML', () => { + expect(previewElement.innerHTML).toBe(innerHTML); + }); + + it('should return .outerHTML', () => { + expect(renderPreview).toBe(previewElement.outerHTML); + }); + }); + + describe('renderTemplate', () => { + let preview; + let database; + let title; + let renderTemplate; + + beforeEach(() => { + preview = { reosourceID: 1, image: 'image' }; + title = [{ values: [['{}', '{}', '{ "name": "name" }']] }]; + database = jasmine.createSpyObj('database', ['exec']); + + database.exec.and.returnValue(title); + + balsamiqViewer = { + database, + }; + + spyOn(JSON, 'parse').and.callThrough(); + spyOn(String.prototype, 'replace').and.callThrough(); + + renderTemplate = BalsamiqViewer.prototype.renderTemplate.call(balsamiqViewer, preview); + }); + + it('should call database.exec', () => { + expect(database.exec).toHaveBeenCalledWith(`SELECT * FROM resources WHERE id = '${preview.resourceID}'`); + }); + + it('should call JSON.parse', () => { + expect(JSON.parse).toHaveBeenCalledWith(title[0].values[0][2]); + }); + + it('should call String.prototype.replace', () => { + const allArgs = String.prototype.replace.calls.allArgs(); + + expect(allArgs.length).toBe(2); + expect(allArgs[0]).toEqual([/{{name}}/g, 'name']); + expect(allArgs[1]).toEqual([/{{image}}/g, 'image']); + }); + + it('should return the template string', function () { + const template = ` +
+
name
+
+ +
+
+ `; + + expect(renderTemplate.replace(/\s/g, '')).toEqual(template.replace(/\s/g, '')); + }); + }); + + describe('parsePreview', () => { + let preview; + let parsePreview; + + beforeEach(() => { + preview = ['{}', '{ "id": 1 }']; + + spyOn(JSON, 'parse').and.callThrough(); + + parsePreview = BalsamiqViewer.parsePreview(preview); + }); + + ClassSpecHelper.itShouldBeAStaticMethod(BalsamiqViewer, 'parsePreview'); + + it('should call JSON.parse', () => { + expect(JSON.parse).toHaveBeenCalledWith(preview[1]); + }); + + it('should return the parsed JSON', () => { + expect(parsePreview).toEqual(JSON.parse('{ "id": 1 }')); + }); + }); + + describe('onError', () => { + let onError; + + beforeEach(() => { + spyOn(window, 'Flash'); + + onError = BalsamiqViewer.onError(); + }); + + ClassSpecHelper.itShouldBeAStaticMethod(BalsamiqViewer, 'onError'); + + it('should instantiate Flash', () => { + expect(window.Flash).toHaveBeenCalledWith('Balsamiq file could not be loaded.'); + }); + + it('should return Flash', () => { + expect(onError).toEqual(jasmine.any(window.Flash)); + }); + }); +}); diff --git a/spec/javascripts/spinner_spec.js b/spec/javascripts/spinner_spec.js new file mode 100644 index 00000000000..ec62503659c --- /dev/null +++ b/spec/javascripts/spinner_spec.js @@ -0,0 +1,97 @@ +import Spinner from '~/spinner'; +import ClassSpecHelper from './helpers/class_spec_helper'; + +describe('Spinner', () => { + let renderable; + let container; + let spinner; + + describe('class constructor', () => { + beforeEach(() => { + renderable = {}; + container = {}; + + spyOn(Spinner, 'createContainer').and.returnValue(container); + + spinner = new Spinner(renderable); + }); + + it('should set .renderable', () => { + expect(spinner.renderable).toBe(renderable); + }); + + it('should call Spinner.createContainer', () => { + expect(Spinner.createContainer).toHaveBeenCalled(); + }); + + it('should set .container', () => { + expect(spinner.container).toBe(container); + }); + }); + + describe('start', () => { + beforeEach(() => { + renderable = jasmine.createSpyObj('renderable', ['prepend']); + container = {}; + + spinner = { + renderable, + container, + }; + + Spinner.prototype.start.call(spinner); + }); + + it('should call .prepend', () => { + expect(renderable.prepend).toHaveBeenCalledWith(container); + }); + }); + + describe('stop', () => { + beforeEach(() => { + container = jasmine.createSpyObj('container', ['remove']); + + spinner = { + container, + }; + + Spinner.prototype.stop.call(spinner); + }); + + it('should call .remove', () => { + expect(container.remove).toHaveBeenCalled(); + }); + }); + + describe('createContainer', () => { + let createContainer; + + beforeEach(() => { + container = { + classList: jasmine.createSpyObj('classList', ['add']), + }; + + spyOn(document, 'createElement').and.returnValue(container); + + createContainer = Spinner.createContainer(); + }); + + ClassSpecHelper.itShouldBeAStaticMethod(Spinner, 'createContainer'); + + it('should call document.createElement', () => { + expect(document.createElement).toHaveBeenCalledWith('div'); + }); + + it('should call classList.add', () => { + expect(container.classList.add).toHaveBeenCalledWith('loading'); + }); + + it('should return the container element', () => { + expect(createContainer).toBe(container); + }); + + it('should set the container .innerHTML to Spinner.TEMPLATE', () => { + expect(container.innerHTML).toBe(Spinner.TEMPLATE); + }); + }); +}); -- cgit v1.2.1 From b72e18b04fb6a054368a065c4c612505e842b526 Mon Sep 17 00:00:00 2001 From: "Luke \"Jared\" Bennett" Date: Tue, 11 Apr 2017 10:09:47 +0100 Subject: Added feature test --- .../projects/blobs/balsamiq_preview_spec.rb | 24 ++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 spec/features/projects/blobs/balsamiq_preview_spec.rb diff --git a/spec/features/projects/blobs/balsamiq_preview_spec.rb b/spec/features/projects/blobs/balsamiq_preview_spec.rb new file mode 100644 index 00000000000..856ec3271a0 --- /dev/null +++ b/spec/features/projects/blobs/balsamiq_preview_spec.rb @@ -0,0 +1,24 @@ +require 'spec_helper' + +feature 'Balsamiq preview', :feature, :js do + include TreeHelper + + let(:user) { create(:user) } + let(:project) { create(:project) } + let(:branch) { 'add-balsamiq-file' } + let(:path) { 'files/images/balsamiq.bmpr' } + + before do + project.add_master(user) + + login_as user + + p namespace_project_blob_path(project.namespace, project, tree_join(branch, path)) + + visit namespace_project_blob_path(project.namespace, project, tree_join(branch, path)) + end + + it 'should' do + screenshot_and_open_image + end +end -- cgit v1.2.1 From 897d8d547c5888bc63d3c3ecc0d0dd971d70e6c0 Mon Sep 17 00:00:00 2001 From: "Luke \"Jared\" Bennett" Date: Tue, 11 Apr 2017 11:28:17 +0100 Subject: Further review changes --- .../javascripts/blob/balsamiq/balsamiq_viewer.js | 28 ++++-- .../blob/balsamiq/balsamiq_viewer_spec.js | 110 +++++++++++++++------ 2 files changed, 98 insertions(+), 40 deletions(-) diff --git a/app/assets/javascripts/blob/balsamiq/balsamiq_viewer.js b/app/assets/javascripts/blob/balsamiq/balsamiq_viewer.js index d0c161f2aad..3885b0f43b2 100644 --- a/app/assets/javascripts/blob/balsamiq/balsamiq_viewer.js +++ b/app/assets/javascripts/blob/balsamiq/balsamiq_viewer.js @@ -1,6 +1,7 @@ /* global Flash */ import sqljs from 'sql.js'; +import _ from 'underscore'; import Spinner from '../../spinner'; class BalsamiqViewer { @@ -52,6 +53,10 @@ class BalsamiqViewer { return thumbnails[0].values.map(BalsamiqViewer.parsePreview); } + getTitle(resourceID) { + return this.database.exec(`SELECT * FROM resources WHERE id = '${resourceID}'`); + } + renderPreview(preview) { const previewElement = document.createElement('li'); @@ -62,13 +67,14 @@ class BalsamiqViewer { } renderTemplate(preview) { - let template = BalsamiqViewer.PREVIEW_TEMPLATE; - - const title = this.database.exec(`SELECT * FROM resources WHERE id = '${preview.resourceID}'`); - const name = JSON.parse(title[0].values[0][2]).name; + const title = this.getTitle(preview.resourceID); + const name = BalsamiqViewer.parseTitle(title); const image = preview.image; - template = template.replace(/{{name}}/g, name).replace(/{{image}}/g, image); + const template = BalsamiqViewer.PREVIEW_TEMPLATE({ + name, + image, + }); return template; } @@ -77,6 +83,10 @@ class BalsamiqViewer { return JSON.parse(preview[1]); } + static parseTitle(title) { + return JSON.parse(title[0].values[0][2]).name; + } + static onError() { const flash = new Flash('Balsamiq file could not be loaded.'); @@ -84,13 +94,13 @@ class BalsamiqViewer { } } -BalsamiqViewer.PREVIEW_TEMPLATE = ` +BalsamiqViewer.PREVIEW_TEMPLATE = _.template(`
-
{{name}}
+
<%- name %>
- +
-`; +`); export default BalsamiqViewer; diff --git a/spec/javascripts/blob/balsamiq/balsamiq_viewer_spec.js b/spec/javascripts/blob/balsamiq/balsamiq_viewer_spec.js index 10db4175ca4..557eb721a2b 100644 --- a/spec/javascripts/blob/balsamiq/balsamiq_viewer_spec.js +++ b/spec/javascripts/blob/balsamiq/balsamiq_viewer_spec.js @@ -216,6 +216,35 @@ describe('BalsamiqViewer', () => { }); }); + describe('getTitle', () => { + let database; + let resourceID; + let resource; + let getTitle; + + beforeEach(() => { + database = jasmine.createSpyObj('database', ['exec']); + resourceID = 4; + resource = 'resource'; + + balsamiqViewer = { + database, + }; + + database.exec.and.returnValue(resource); + + getTitle = BalsamiqViewer.prototype.getTitle.call(balsamiqViewer, resourceID); + }); + + it('should call database.exec', () => { + expect(database.exec).toHaveBeenCalledWith(`SELECT * FROM resources WHERE id = '${resourceID}'`); + }); + + it('should return the selected resource', () => { + expect(getTitle).toBe(resource); + }); + }); + describe('renderPreview', () => { let previewElement; let innerHTML; @@ -261,54 +290,50 @@ describe('BalsamiqViewer', () => { describe('renderTemplate', () => { let preview; - let database; + let name; let title; + let template; let renderTemplate; beforeEach(() => { - preview = { reosourceID: 1, image: 'image' }; - title = [{ values: [['{}', '{}', '{ "name": "name" }']] }]; - database = jasmine.createSpyObj('database', ['exec']); - - database.exec.and.returnValue(title); + preview = { resourceID: 1, image: 'image' }; + name = 'name'; + title = 'title'; + template = ` +
+
name
+
+ +
+
+ `; - balsamiqViewer = { - database, - }; + balsamiqViewer = jasmine.createSpyObj('balsamiqViewer', ['getTitle']); - spyOn(JSON, 'parse').and.callThrough(); - spyOn(String.prototype, 'replace').and.callThrough(); + spyOn(BalsamiqViewer, 'parseTitle').and.returnValue(name); + spyOn(BalsamiqViewer, 'PREVIEW_TEMPLATE').and.returnValue(template); + balsamiqViewer.getTitle.and.returnValue(title); renderTemplate = BalsamiqViewer.prototype.renderTemplate.call(balsamiqViewer, preview); }); - it('should call database.exec', () => { - expect(database.exec).toHaveBeenCalledWith(`SELECT * FROM resources WHERE id = '${preview.resourceID}'`); + it('should call .getTitle', () => { + expect(balsamiqViewer.getTitle).toHaveBeenCalledWith(preview.resourceID); }); - it('should call JSON.parse', () => { - expect(JSON.parse).toHaveBeenCalledWith(title[0].values[0][2]); + it('should call .parseTitle', () => { + expect(BalsamiqViewer.parseTitle).toHaveBeenCalledWith(title); }); - it('should call String.prototype.replace', () => { - const allArgs = String.prototype.replace.calls.allArgs(); - - expect(allArgs.length).toBe(2); - expect(allArgs[0]).toEqual([/{{name}}/g, 'name']); - expect(allArgs[1]).toEqual([/{{image}}/g, 'image']); + it('should call .PREVIEW_TEMPLATE', () => { + expect(BalsamiqViewer.PREVIEW_TEMPLATE).toHaveBeenCalledWith({ + name, + image: preview.image, + }); }); it('should return the template string', function () { - const template = ` -
-
name
-
- -
-
- `; - - expect(renderTemplate.replace(/\s/g, '')).toEqual(template.replace(/\s/g, '')); + expect(renderTemplate.trim()).toBe(template.trim()); }); }); @@ -335,6 +360,29 @@ describe('BalsamiqViewer', () => { }); }); + describe('parseTitle', () => { + let title; + let parseTitle; + + beforeEach(() => { + title = [{ values: [['{}', '{}', '{"name":"name"}']] }]; + + spyOn(JSON, 'parse').and.callThrough(); + + parseTitle = BalsamiqViewer.parseTitle(title); + }); + + ClassSpecHelper.itShouldBeAStaticMethod(BalsamiqViewer, 'parsePreview'); + + it('should call JSON.parse', () => { + expect(JSON.parse).toHaveBeenCalledWith(title[0].values[0][2]); + }); + + it('should return the name value', () => { + expect(parseTitle).toBe('name'); + }); + }); + describe('onError', () => { let onError; -- cgit v1.2.1 From fbed2909091b98f614ae51c5d6503cdd40a74eb5 Mon Sep 17 00:00:00 2001 From: "Luke \"Jared\" Bennett" Date: Tue, 11 Apr 2017 11:42:47 +0100 Subject: Removed prepend in favour of clean and appendChild --- app/assets/javascripts/spinner.js | 3 ++- spec/javascripts/spinner_spec.js | 10 +++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/app/assets/javascripts/spinner.js b/app/assets/javascripts/spinner.js index b7bfe1a2572..6b5ac89a576 100644 --- a/app/assets/javascripts/spinner.js +++ b/app/assets/javascripts/spinner.js @@ -6,7 +6,8 @@ class Spinner { } start() { - this.renderable.prepend(this.container); + this.renderable.innerHTML = ''; + this.renderable.appendChild(this.container); } stop() { diff --git a/spec/javascripts/spinner_spec.js b/spec/javascripts/spinner_spec.js index ec62503659c..f550285e0f7 100644 --- a/spec/javascripts/spinner_spec.js +++ b/spec/javascripts/spinner_spec.js @@ -31,7 +31,7 @@ describe('Spinner', () => { describe('start', () => { beforeEach(() => { - renderable = jasmine.createSpyObj('renderable', ['prepend']); + renderable = jasmine.createSpyObj('renderable', ['appendChild']); container = {}; spinner = { @@ -42,8 +42,12 @@ describe('Spinner', () => { Spinner.prototype.start.call(spinner); }); - it('should call .prepend', () => { - expect(renderable.prepend).toHaveBeenCalledWith(container); + it('should set .innerHTML to an empty string', () => { + expect(renderable.innerHTML).toEqual(''); + }); + + it('should call .appendChild', () => { + expect(renderable.appendChild).toHaveBeenCalledWith(container); }); }); -- cgit v1.2.1 From 18751fb1356935f38bf1ff0f00350b276d77169d Mon Sep 17 00:00:00 2001 From: "Luke \"Jared\" Bennett" Date: Tue, 11 Apr 2017 13:06:32 +0100 Subject: Only import template function from underscore --- app/assets/javascripts/blob/balsamiq/balsamiq_viewer.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/blob/balsamiq/balsamiq_viewer.js b/app/assets/javascripts/blob/balsamiq/balsamiq_viewer.js index 3885b0f43b2..5bcd7d5eccc 100644 --- a/app/assets/javascripts/blob/balsamiq/balsamiq_viewer.js +++ b/app/assets/javascripts/blob/balsamiq/balsamiq_viewer.js @@ -1,7 +1,7 @@ /* global Flash */ import sqljs from 'sql.js'; -import _ from 'underscore'; +import { template as _template } from 'underscore'; import Spinner from '../../spinner'; class BalsamiqViewer { @@ -94,7 +94,7 @@ class BalsamiqViewer { } } -BalsamiqViewer.PREVIEW_TEMPLATE = _.template(` +BalsamiqViewer.PREVIEW_TEMPLATE = _template(`
<%- name %>
-- cgit v1.2.1 From cf678945044f657111997a92330420a0110d03fc Mon Sep 17 00:00:00 2001 From: "Luke \"Jared\" Bennett" Date: Tue, 11 Apr 2017 13:06:45 +0100 Subject: Finished feature specs --- app/views/projects/blob/_bmpr.html.haml | 2 +- spec/features/projects/blobs/balsamiq_preview_spec.rb | 11 +++++++---- spec/support/test_env.rb | 1 + 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/app/views/projects/blob/_bmpr.html.haml b/app/views/projects/blob/_bmpr.html.haml index 36ec0cbcce8..573b24ae44f 100644 --- a/app/views/projects/blob/_bmpr.html.haml +++ b/app/views/projects/blob/_bmpr.html.haml @@ -1,4 +1,4 @@ - content_for :page_specific_javascripts do = page_specific_javascript_bundle_tag('balsamiq_viewer') -.file-content#js-balsamiq-viewer{ data: { endpoint: namespace_project_raw_path(@project.namespace, @project, @id) } } +.file-content.balsamiq-viewer#js-balsamiq-viewer{ data: { endpoint: namespace_project_raw_path(@project.namespace, @project, @id) } } diff --git a/spec/features/projects/blobs/balsamiq_preview_spec.rb b/spec/features/projects/blobs/balsamiq_preview_spec.rb index 856ec3271a0..503f1f94915 100644 --- a/spec/features/projects/blobs/balsamiq_preview_spec.rb +++ b/spec/features/projects/blobs/balsamiq_preview_spec.rb @@ -7,18 +7,21 @@ feature 'Balsamiq preview', :feature, :js do let(:project) { create(:project) } let(:branch) { 'add-balsamiq-file' } let(:path) { 'files/images/balsamiq.bmpr' } + let(:file_content) { find('.file-content') } before do project.add_master(user) login_as user - p namespace_project_blob_path(project.namespace, project, tree_join(branch, path)) - visit namespace_project_blob_path(project.namespace, project, tree_join(branch, path)) end - it 'should' do - screenshot_and_open_image + it 'should show a loading icon' do + expect(file_content).to have_selector('.loading') + end + + it 'should show a viewer container' do + expect(page).to have_selector('.balsamiq-viewer') end end diff --git a/spec/support/test_env.rb b/spec/support/test_env.rb index 1b5cb71a6b0..5765a31ca18 100644 --- a/spec/support/test_env.rb +++ b/spec/support/test_env.rb @@ -27,6 +27,7 @@ module TestEnv 'expand-collapse-files' => '025db92', 'expand-collapse-lines' => '238e82d', 'video' => '8879059', + 'add-balsamiq-file' => 'b89b56d', 'crlf-diff' => '5938907', 'conflict-start' => '824be60', 'conflict-resolvable' => '1450cd6', -- cgit v1.2.1 From ccba2f445dc0efd670a492cef954169a117eb166 Mon Sep 17 00:00:00 2001 From: "Luke \"Jared\" Bennett" Date: Wed, 12 Apr 2017 07:22:52 +0100 Subject: Spec review changes --- spec/features/projects/blobs/balsamiq_preview_spec.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/spec/features/projects/blobs/balsamiq_preview_spec.rb b/spec/features/projects/blobs/balsamiq_preview_spec.rb index 503f1f94915..551d2045744 100644 --- a/spec/features/projects/blobs/balsamiq_preview_spec.rb +++ b/spec/features/projects/blobs/balsamiq_preview_spec.rb @@ -11,9 +11,7 @@ feature 'Balsamiq preview', :feature, :js do before do project.add_master(user) - login_as user - visit namespace_project_blob_path(project.namespace, project, tree_join(branch, path)) end -- cgit v1.2.1 From 0453d6d7ae72f0179d02ff5190122a86e304e1bb Mon Sep 17 00:00:00 2001 From: "Luke \"Jared\" Bennett" Date: Wed, 12 Apr 2017 09:50:54 +0100 Subject: BE review changes --- app/models/blob.rb | 2 +- spec/features/projects/blobs/balsamiq_preview_spec.rb | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/app/models/blob.rb b/app/models/blob.rb index 91cf171a688..82333b6f369 100644 --- a/app/models/blob.rb +++ b/app/models/blob.rb @@ -63,7 +63,7 @@ class Blob < SimpleDelegator end def balsamiq? - binary? && extname.downcase.delete('.') == 'bmpr' + binary? && extension == 'bmpr' end def stl? diff --git a/spec/features/projects/blobs/balsamiq_preview_spec.rb b/spec/features/projects/blobs/balsamiq_preview_spec.rb index 551d2045744..59475525b81 100644 --- a/spec/features/projects/blobs/balsamiq_preview_spec.rb +++ b/spec/features/projects/blobs/balsamiq_preview_spec.rb @@ -7,7 +7,6 @@ feature 'Balsamiq preview', :feature, :js do let(:project) { create(:project) } let(:branch) { 'add-balsamiq-file' } let(:path) { 'files/images/balsamiq.bmpr' } - let(:file_content) { find('.file-content') } before do project.add_master(user) @@ -16,7 +15,7 @@ feature 'Balsamiq preview', :feature, :js do end it 'should show a loading icon' do - expect(file_content).to have_selector('.loading') + expect(find('.file-content')).to have_selector('.loading') end it 'should show a viewer container' do -- cgit v1.2.1 From cc248f3e7be6ae3cfd0d9f24eae92500e14b9e98 Mon Sep 17 00:00:00 2001 From: "Luke \"Jared\" Bennett" Date: Wed, 26 Apr 2017 14:05:13 +0100 Subject: Swapped flex out for grid system --- app/assets/stylesheets/framework/files.scss | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/app/assets/stylesheets/framework/files.scss b/app/assets/stylesheets/framework/files.scss index 099187a5193..3737ae3e653 100644 --- a/app/assets/stylesheets/framework/files.scss +++ b/app/assets/stylesheets/framework/files.scss @@ -162,13 +162,31 @@ } .list-inline.previews { - display: flex; - flex-wrap: wrap; - justify-content: space-between; - padding: $gl-padding; + display: inline-block; .preview { - flex-shrink: 0; + display: inline-block; + height: 280px; + min-width: 260px; + margin: 0; + padding: 16px; + @include make-xs-column(3); + } + + .panel { + margin: 0; + height: 100%; + + .panel-body { + padding: $gl-padding 0 0; + text-align: center; + } + } + + .img-thumbnail { + max-height: 195px; + max-width: 195px; + padding: 0; } } } -- cgit v1.2.1 From f74d0ba195079260d3b3217cd72b2fd9b0483c00 Mon Sep 17 00:00:00 2001 From: "Luke \"Jared\" Bennett" Date: Thu, 27 Apr 2017 17:58:51 +0100 Subject: Fix webpack config conflicts bad resolution --- config/webpack.config.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/config/webpack.config.js b/config/webpack.config.js index f61e65c0e5b..8b5354809e7 100644 --- a/config/webpack.config.js +++ b/config/webpack.config.js @@ -46,6 +46,7 @@ var config = { network: './network/network_bundle.js', notebook_viewer: './blob/notebook_viewer.js', pdf_viewer: './blob/pdf_viewer.js', + pipelines: './pipelines/index.js', balsamiq_viewer: './blob/balsamiq_viewer.js', profile: './profile/profile_bundle.js', protected_branches: './protected_branches/protected_branches_bundle.js', @@ -129,6 +130,7 @@ var config = { 'merge_conflicts', 'notebook_viewer', 'pdf_viewer', + 'pipelines', 'balsamiq_viewer', ], minChunks: function(module, count) { -- cgit v1.2.1 From 817aa1b1160bcc1f3be828ad3c0c66fe403e2557 Mon Sep 17 00:00:00 2001 From: "Luke \"Jared\" Bennett" Date: Fri, 28 Apr 2017 11:32:56 +0100 Subject: added balsamiq blobviewer class --- app/models/blob_viewer/balsamiq.rb | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 app/models/blob_viewer/balsamiq.rb diff --git a/app/models/blob_viewer/balsamiq.rb b/app/models/blob_viewer/balsamiq.rb new file mode 100644 index 00000000000..8b0c0c049a1 --- /dev/null +++ b/app/models/blob_viewer/balsamiq.rb @@ -0,0 +1,12 @@ +module BalsamiqViewer + class Balsamiq < Base + include Rich + include ClientSide + + self.partial_name = 'balsamiq' + self.extensions = %w(bmpr) + self.binary = true + self.switcher_icon = 'file-image-o' + self.switcher_title = 'preview' + end +end -- cgit v1.2.1 From a67a648b0055334dbcfe0b20e01cd7b1401d2a0a Mon Sep 17 00:00:00 2001 From: "Luke \"Jared\" Bennett" Date: Fri, 28 Apr 2017 11:42:30 +0100 Subject: Moved partial --- app/models/blob_viewer/balsamiq.rb | 2 +- app/views/projects/blob/_bmpr.html.haml | 4 ---- app/views/projects/blob/viewers/_balsamiq.html.haml | 4 ++++ 3 files changed, 5 insertions(+), 5 deletions(-) delete mode 100644 app/views/projects/blob/_bmpr.html.haml create mode 100644 app/views/projects/blob/viewers/_balsamiq.html.haml diff --git a/app/models/blob_viewer/balsamiq.rb b/app/models/blob_viewer/balsamiq.rb index 8b0c0c049a1..f982521db99 100644 --- a/app/models/blob_viewer/balsamiq.rb +++ b/app/models/blob_viewer/balsamiq.rb @@ -1,4 +1,4 @@ -module BalsamiqViewer +module BlobViewer class Balsamiq < Base include Rich include ClientSide diff --git a/app/views/projects/blob/_bmpr.html.haml b/app/views/projects/blob/_bmpr.html.haml deleted file mode 100644 index 573b24ae44f..00000000000 --- a/app/views/projects/blob/_bmpr.html.haml +++ /dev/null @@ -1,4 +0,0 @@ -- content_for :page_specific_javascripts do - = page_specific_javascript_bundle_tag('balsamiq_viewer') - -.file-content.balsamiq-viewer#js-balsamiq-viewer{ data: { endpoint: namespace_project_raw_path(@project.namespace, @project, @id) } } diff --git a/app/views/projects/blob/viewers/_balsamiq.html.haml b/app/views/projects/blob/viewers/_balsamiq.html.haml new file mode 100644 index 00000000000..573b24ae44f --- /dev/null +++ b/app/views/projects/blob/viewers/_balsamiq.html.haml @@ -0,0 +1,4 @@ +- content_for :page_specific_javascripts do + = page_specific_javascript_bundle_tag('balsamiq_viewer') + +.file-content.balsamiq-viewer#js-balsamiq-viewer{ data: { endpoint: namespace_project_raw_path(@project.namespace, @project, @id) } } -- cgit v1.2.1 From fc954749eace61d448fe8cc04c7fc0126bdbcdee Mon Sep 17 00:00:00 2001 From: "Luke \"Jared\" Bennett" Date: Fri, 28 Apr 2017 12:15:07 +0100 Subject: Corrected styling, less hacky --- app/assets/stylesheets/framework/files.scss | 29 ++++++----------------------- 1 file changed, 6 insertions(+), 23 deletions(-) diff --git a/app/assets/stylesheets/framework/files.scss b/app/assets/stylesheets/framework/files.scss index 3737ae3e653..a95ce4cc963 100644 --- a/app/assets/stylesheets/framework/files.scss +++ b/app/assets/stylesheets/framework/files.scss @@ -162,31 +162,14 @@ } .list-inline.previews { - display: inline-block; + display: flex; + flex-wrap: wrap; + justify-content: center; + align-content: flex-start; + align-items: baseline; .preview { - display: inline-block; - height: 280px; - min-width: 260px; - margin: 0; - padding: 16px; - @include make-xs-column(3); - } - - .panel { - margin: 0; - height: 100%; - - .panel-body { - padding: $gl-padding 0 0; - text-align: center; - } - } - - .img-thumbnail { - max-height: 195px; - max-width: 195px; - padding: 0; + padding: $gl-padding; } } } -- cgit v1.2.1 From f6ebb90a7bcc984a958f2e5cf01f8f1716c7f1de Mon Sep 17 00:00:00 2001 From: "Luke \"Jared\" Bennett" Date: Fri, 28 Apr 2017 16:16:41 +0100 Subject: Deleted spinner and spec --- app/assets/javascripts/spinner.js | 29 ----------- spec/javascripts/spinner_spec.js | 101 -------------------------------------- 2 files changed, 130 deletions(-) delete mode 100644 app/assets/javascripts/spinner.js delete mode 100644 spec/javascripts/spinner_spec.js diff --git a/app/assets/javascripts/spinner.js b/app/assets/javascripts/spinner.js deleted file mode 100644 index 6b5ac89a576..00000000000 --- a/app/assets/javascripts/spinner.js +++ /dev/null @@ -1,29 +0,0 @@ -class Spinner { - constructor(renderable) { - this.renderable = renderable; - - this.container = Spinner.createContainer(); - } - - start() { - this.renderable.innerHTML = ''; - this.renderable.appendChild(this.container); - } - - stop() { - this.container.remove(); - } - - static createContainer() { - const container = document.createElement('div'); - container.classList.add('loading'); - - container.innerHTML = Spinner.TEMPLATE; - - return container; - } -} - -Spinner.TEMPLATE = ''; - -export default Spinner; diff --git a/spec/javascripts/spinner_spec.js b/spec/javascripts/spinner_spec.js deleted file mode 100644 index f550285e0f7..00000000000 --- a/spec/javascripts/spinner_spec.js +++ /dev/null @@ -1,101 +0,0 @@ -import Spinner from '~/spinner'; -import ClassSpecHelper from './helpers/class_spec_helper'; - -describe('Spinner', () => { - let renderable; - let container; - let spinner; - - describe('class constructor', () => { - beforeEach(() => { - renderable = {}; - container = {}; - - spyOn(Spinner, 'createContainer').and.returnValue(container); - - spinner = new Spinner(renderable); - }); - - it('should set .renderable', () => { - expect(spinner.renderable).toBe(renderable); - }); - - it('should call Spinner.createContainer', () => { - expect(Spinner.createContainer).toHaveBeenCalled(); - }); - - it('should set .container', () => { - expect(spinner.container).toBe(container); - }); - }); - - describe('start', () => { - beforeEach(() => { - renderable = jasmine.createSpyObj('renderable', ['appendChild']); - container = {}; - - spinner = { - renderable, - container, - }; - - Spinner.prototype.start.call(spinner); - }); - - it('should set .innerHTML to an empty string', () => { - expect(renderable.innerHTML).toEqual(''); - }); - - it('should call .appendChild', () => { - expect(renderable.appendChild).toHaveBeenCalledWith(container); - }); - }); - - describe('stop', () => { - beforeEach(() => { - container = jasmine.createSpyObj('container', ['remove']); - - spinner = { - container, - }; - - Spinner.prototype.stop.call(spinner); - }); - - it('should call .remove', () => { - expect(container.remove).toHaveBeenCalled(); - }); - }); - - describe('createContainer', () => { - let createContainer; - - beforeEach(() => { - container = { - classList: jasmine.createSpyObj('classList', ['add']), - }; - - spyOn(document, 'createElement').and.returnValue(container); - - createContainer = Spinner.createContainer(); - }); - - ClassSpecHelper.itShouldBeAStaticMethod(Spinner, 'createContainer'); - - it('should call document.createElement', () => { - expect(document.createElement).toHaveBeenCalledWith('div'); - }); - - it('should call classList.add', () => { - expect(container.classList.add).toHaveBeenCalledWith('loading'); - }); - - it('should return the container element', () => { - expect(createContainer).toBe(container); - }); - - it('should set the container .innerHTML to Spinner.TEMPLATE', () => { - expect(container.innerHTML).toBe(Spinner.TEMPLATE); - }); - }); -}); -- cgit v1.2.1 From 1e2246e73ed45de4161c47a842dd68616373898a Mon Sep 17 00:00:00 2001 From: "Luke \"Jared\" Bennett" Date: Fri, 28 Apr 2017 16:21:50 +0100 Subject: Removed Spinner usage --- app/assets/javascripts/blob/balsamiq/balsamiq_viewer.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/assets/javascripts/blob/balsamiq/balsamiq_viewer.js b/app/assets/javascripts/blob/balsamiq/balsamiq_viewer.js index 5bcd7d5eccc..1778f9cba51 100644 --- a/app/assets/javascripts/blob/balsamiq/balsamiq_viewer.js +++ b/app/assets/javascripts/blob/balsamiq/balsamiq_viewer.js @@ -2,13 +2,11 @@ import sqljs from 'sql.js'; import { template as _template } from 'underscore'; -import Spinner from '../../spinner'; class BalsamiqViewer { constructor(viewer) { this.viewer = viewer; this.endpoint = this.viewer.dataset.endpoint; - this.spinner = new Spinner(this.viewer); } loadFile() { -- cgit v1.2.1 From 2ef7c6f070e52f68b10cba09d10a4db9ab8b2537 Mon Sep 17 00:00:00 2001 From: "Luke \"Jared\" Bennett" Date: Fri, 28 Apr 2017 17:32:50 +0100 Subject: Fixed specs --- .../javascripts/blob/balsamiq/balsamiq_viewer.js | 58 +++++++----- .../projects/blob/viewers/_balsamiq.html.haml | 2 +- .../blob/balsamiq/balsamiq_viewer_spec.js | 105 +++++---------------- 3 files changed, 56 insertions(+), 109 deletions(-) diff --git a/app/assets/javascripts/blob/balsamiq/balsamiq_viewer.js b/app/assets/javascripts/blob/balsamiq/balsamiq_viewer.js index 1778f9cba51..cdbfe36ca1c 100644 --- a/app/assets/javascripts/blob/balsamiq/balsamiq_viewer.js +++ b/app/assets/javascripts/blob/balsamiq/balsamiq_viewer.js @@ -3,6 +3,15 @@ import sqljs from 'sql.js'; import { template as _template } from 'underscore'; +const PREVIEW_TEMPLATE = _template(` +
+
<%- name %>
+
+ +
+
+`); + class BalsamiqViewer { constructor(viewer) { this.viewer = viewer; @@ -18,23 +27,23 @@ class BalsamiqViewer { xhr.onload = this.renderFile.bind(this); xhr.onerror = BalsamiqViewer.onError; - this.spinner.start(); - xhr.send(); } renderFile(loadEvent) { - this.spinner.stop(); - const container = document.createElement('ul'); this.initDatabase(loadEvent.target.response); const previews = this.getPreviews(); - const renderedPreviews = previews.map(preview => this.renderPreview(preview)); + previews.forEach((preview) => { + const renderedPreview = this.renderPreview(preview); - container.innerHTML = renderedPreviews.join(''); - container.classList.add('list-inline', 'previews'); + container.appendChild(renderedPreview); + }); + + container.classList.add('list-inline'); + container.classList.add('previews'); this.viewer.appendChild(container); } @@ -51,8 +60,10 @@ class BalsamiqViewer { return thumbnails[0].values.map(BalsamiqViewer.parsePreview); } - getTitle(resourceID) { - return this.database.exec(`SELECT * FROM resources WHERE id = '${resourceID}'`); + getResource(resourceID) { + const resources = this.database.exec(`SELECT * FROM resources WHERE id = '${resourceID}'`); + + return resources[0]; } renderPreview(preview) { @@ -61,15 +72,15 @@ class BalsamiqViewer { previewElement.classList.add('preview'); previewElement.innerHTML = this.renderTemplate(preview); - return previewElement.outerHTML; + return previewElement; } renderTemplate(preview) { - const title = this.getTitle(preview.resourceID); - const name = BalsamiqViewer.parseTitle(title); + const resource = this.getResource(preview.resourceID); + const name = BalsamiqViewer.parseTitle(resource); const image = preview.image; - const template = BalsamiqViewer.PREVIEW_TEMPLATE({ + const template = PREVIEW_TEMPLATE({ name, image, }); @@ -81,8 +92,16 @@ class BalsamiqViewer { return JSON.parse(preview[1]); } - static parseTitle(title) { - return JSON.parse(title[0].values[0][2]).name; + /* + * resource = { + * columns: ['ID', 'BRANCHID', 'ATTRIBUTES', 'DATA'], + * values: [['id', 'branchId', 'attributes', 'data']], + * } + * + * 'attributes' being a JSON string containing the `name` property. + */ + static parseTitle(resource) { + return JSON.parse(resource.values[0][2]).name; } static onError() { @@ -92,13 +111,4 @@ class BalsamiqViewer { } } -BalsamiqViewer.PREVIEW_TEMPLATE = _template(` -
-
<%- name %>
-
- -
-
-`); - export default BalsamiqViewer; diff --git a/app/views/projects/blob/viewers/_balsamiq.html.haml b/app/views/projects/blob/viewers/_balsamiq.html.haml index 573b24ae44f..28670e7de97 100644 --- a/app/views/projects/blob/viewers/_balsamiq.html.haml +++ b/app/views/projects/blob/viewers/_balsamiq.html.haml @@ -1,4 +1,4 @@ - content_for :page_specific_javascripts do = page_specific_javascript_bundle_tag('balsamiq_viewer') -.file-content.balsamiq-viewer#js-balsamiq-viewer{ data: { endpoint: namespace_project_raw_path(@project.namespace, @project, @id) } } +.file-content.balsamiq-viewer#js-balsamiq-viewer{ data: { endpoint: blob_raw_url } } diff --git a/spec/javascripts/blob/balsamiq/balsamiq_viewer_spec.js b/spec/javascripts/blob/balsamiq/balsamiq_viewer_spec.js index 557eb721a2b..85816ee1f11 100644 --- a/spec/javascripts/blob/balsamiq/balsamiq_viewer_spec.js +++ b/spec/javascripts/blob/balsamiq/balsamiq_viewer_spec.js @@ -1,6 +1,5 @@ import sqljs from 'sql.js'; import BalsamiqViewer from '~/blob/balsamiq/balsamiq_viewer'; -import * as spinnerSrc from '~/spinner'; import ClassSpecHelper from '../../helpers/class_spec_helper'; describe('BalsamiqViewer', () => { @@ -17,8 +16,6 @@ describe('BalsamiqViewer', () => { }, }; - spyOn(spinnerSrc, 'default'); - balsamiqViewer = new BalsamiqViewer(viewer); }); @@ -29,38 +26,23 @@ describe('BalsamiqViewer', () => { it('should set .endpoint', () => { expect(balsamiqViewer.endpoint).toBe(endpoint); }); - - it('should instantiate Spinner', () => { - expect(spinnerSrc.default).toHaveBeenCalledWith(viewer); - }); - - it('should set .spinner', () => { - expect(balsamiqViewer.spinner).toEqual(jasmine.any(spinnerSrc.default)); - }); }); describe('loadFile', () => { let xhr; - let spinner; beforeEach(() => { endpoint = 'endpoint'; xhr = jasmine.createSpyObj('xhr', ['open', 'send']); - spinner = jasmine.createSpyObj('spinner', ['start']); balsamiqViewer = jasmine.createSpyObj('balsamiqViewer', ['renderFile']); balsamiqViewer.endpoint = endpoint; - balsamiqViewer.spinner = spinner; spyOn(window, 'XMLHttpRequest').and.returnValue(xhr); BalsamiqViewer.prototype.loadFile.call(balsamiqViewer); }); - it('should instantiate XMLHttpRequest', () => { - expect(window.XMLHttpRequest).toHaveBeenCalled(); - }); - it('should call .open', () => { expect(xhr.open).toHaveBeenCalledWith('GET', endpoint, true); }); @@ -69,25 +51,12 @@ describe('BalsamiqViewer', () => { expect(xhr.responseType).toBe('arraybuffer'); }); - it('should set .onload', () => { - expect(xhr.onload).toEqual(jasmine.any(Function)); - }); - - it('should set .onerror', () => { - expect(xhr.onerror).toBe(BalsamiqViewer.onError); - }); - - it('should call spinner.start', () => { - expect(spinner.start).toHaveBeenCalled(); - }); - it('should call .send', () => { expect(xhr.send).toHaveBeenCalled(); }); }); describe('renderFile', () => { - let spinner; let container; let loadEvent; let previews; @@ -95,12 +64,10 @@ describe('BalsamiqViewer', () => { beforeEach(() => { loadEvent = { target: { response: {} } }; viewer = jasmine.createSpyObj('viewer', ['appendChild']); - spinner = jasmine.createSpyObj('spinner', ['stop']); - previews = [0, 1, 2]; + previews = [document.createElement('ul'), document.createElement('ul')]; balsamiqViewer = jasmine.createSpyObj('balsamiqViewer', ['initDatabase', 'getPreviews', 'renderPreview']); balsamiqViewer.viewer = viewer; - balsamiqViewer.spinner = spinner; balsamiqViewer.getPreviews.and.returnValue(previews); balsamiqViewer.renderPreview.and.callFake(preview => preview); @@ -111,10 +78,6 @@ describe('BalsamiqViewer', () => { BalsamiqViewer.prototype.renderFile.call(balsamiqViewer, loadEvent); }); - it('should call spinner.stop', () => { - expect(spinner.stop).toHaveBeenCalled(); - }); - it('should call .initDatabase', () => { expect(balsamiqViewer.initDatabase).toHaveBeenCalledWith(loadEvent.target.response); }); @@ -126,15 +89,15 @@ describe('BalsamiqViewer', () => { it('should call .renderPreview for each preview', () => { const allArgs = balsamiqViewer.renderPreview.calls.allArgs(); - expect(allArgs.length).toBe(3); + expect(allArgs.length).toBe(2); previews.forEach((preview, i) => { expect(allArgs[i][0]).toBe(preview); }); }); - it('should set .innerHTML', () => { - expect(container.innerHTML).toBe('012'); + it('should set the container HTML', () => { + expect(container.innerHTML).toBe('
      '); }); it('should add inline preview classes', () => { @@ -216,16 +179,16 @@ describe('BalsamiqViewer', () => { }); }); - describe('getTitle', () => { + describe('getResource', () => { let database; let resourceID; let resource; - let getTitle; + let getResource; beforeEach(() => { database = jasmine.createSpyObj('database', ['exec']); resourceID = 4; - resource = 'resource'; + resource = ['resource']; balsamiqViewer = { database, @@ -233,7 +196,7 @@ describe('BalsamiqViewer', () => { database.exec.and.returnValue(resource); - getTitle = BalsamiqViewer.prototype.getTitle.call(balsamiqViewer, resourceID); + getResource = BalsamiqViewer.prototype.getResource.call(balsamiqViewer, resourceID); }); it('should call database.exec', () => { @@ -241,7 +204,7 @@ describe('BalsamiqViewer', () => { }); it('should return the selected resource', () => { - expect(getTitle).toBe(resource); + expect(getResource).toBe(resource[0]); }); }); @@ -267,10 +230,6 @@ describe('BalsamiqViewer', () => { renderPreview = BalsamiqViewer.prototype.renderPreview.call(balsamiqViewer, preview); }); - it('should call document.createElement', () => { - expect(document.createElement).toHaveBeenCalledWith('li'); - }); - it('should call classList.add', () => { expect(previewElement.classList.add).toHaveBeenCalledWith('preview'); }); @@ -283,22 +242,22 @@ describe('BalsamiqViewer', () => { expect(previewElement.innerHTML).toBe(innerHTML); }); - it('should return .outerHTML', () => { - expect(renderPreview).toBe(previewElement.outerHTML); + it('should return element', () => { + expect(renderPreview).toBe(previewElement); }); }); describe('renderTemplate', () => { let preview; let name; - let title; + let resource; let template; let renderTemplate; beforeEach(() => { preview = { resourceID: 1, image: 'image' }; name = 'name'; - title = 'title'; + resource = 'resource'; template = `
      name
      @@ -308,32 +267,24 @@ describe('BalsamiqViewer', () => {
      `; - balsamiqViewer = jasmine.createSpyObj('balsamiqViewer', ['getTitle']); + balsamiqViewer = jasmine.createSpyObj('balsamiqViewer', ['getResource']); spyOn(BalsamiqViewer, 'parseTitle').and.returnValue(name); - spyOn(BalsamiqViewer, 'PREVIEW_TEMPLATE').and.returnValue(template); - balsamiqViewer.getTitle.and.returnValue(title); + balsamiqViewer.getResource.and.returnValue(resource); renderTemplate = BalsamiqViewer.prototype.renderTemplate.call(balsamiqViewer, preview); }); - it('should call .getTitle', () => { - expect(balsamiqViewer.getTitle).toHaveBeenCalledWith(preview.resourceID); + it('should call .getResource', () => { + expect(balsamiqViewer.getResource).toHaveBeenCalledWith(preview.resourceID); }); it('should call .parseTitle', () => { - expect(BalsamiqViewer.parseTitle).toHaveBeenCalledWith(title); - }); - - it('should call .PREVIEW_TEMPLATE', () => { - expect(BalsamiqViewer.PREVIEW_TEMPLATE).toHaveBeenCalledWith({ - name, - image: preview.image, - }); + expect(BalsamiqViewer.parseTitle).toHaveBeenCalledWith(resource); }); it('should return the template string', function () { - expect(renderTemplate.trim()).toBe(template.trim()); + expect(renderTemplate.replace(/\s/g, '')).toEqual(template.replace(/\s/g, '')); }); }); @@ -351,10 +302,6 @@ describe('BalsamiqViewer', () => { ClassSpecHelper.itShouldBeAStaticMethod(BalsamiqViewer, 'parsePreview'); - it('should call JSON.parse', () => { - expect(JSON.parse).toHaveBeenCalledWith(preview[1]); - }); - it('should return the parsed JSON', () => { expect(parsePreview).toEqual(JSON.parse('{ "id": 1 }')); }); @@ -365,7 +312,7 @@ describe('BalsamiqViewer', () => { let parseTitle; beforeEach(() => { - title = [{ values: [['{}', '{}', '{"name":"name"}']] }]; + title = { values: [['{}', '{}', '{"name":"name"}']] }; spyOn(JSON, 'parse').and.callThrough(); @@ -374,22 +321,16 @@ describe('BalsamiqViewer', () => { ClassSpecHelper.itShouldBeAStaticMethod(BalsamiqViewer, 'parsePreview'); - it('should call JSON.parse', () => { - expect(JSON.parse).toHaveBeenCalledWith(title[0].values[0][2]); - }); - it('should return the name value', () => { expect(parseTitle).toBe('name'); }); }); describe('onError', () => { - let onError; - beforeEach(() => { spyOn(window, 'Flash'); - onError = BalsamiqViewer.onError(); + BalsamiqViewer.onError(); }); ClassSpecHelper.itShouldBeAStaticMethod(BalsamiqViewer, 'onError'); @@ -397,9 +338,5 @@ describe('BalsamiqViewer', () => { it('should instantiate Flash', () => { expect(window.Flash).toHaveBeenCalledWith('Balsamiq file could not be loaded.'); }); - - it('should return Flash', () => { - expect(onError).toEqual(jasmine.any(window.Flash)); - }); }); }); -- cgit v1.2.1 From adff7923ca3088df9e07ccd60d6f9fa24fbdbfc5 Mon Sep 17 00:00:00 2001 From: "Luke \"Jared\" Bennett" Date: Fri, 28 Apr 2017 19:08:39 +0100 Subject: Removed unneeded spec --- .../projects/blobs/balsamiq_preview_spec.rb | 24 ---------------------- 1 file changed, 24 deletions(-) delete mode 100644 spec/features/projects/blobs/balsamiq_preview_spec.rb diff --git a/spec/features/projects/blobs/balsamiq_preview_spec.rb b/spec/features/projects/blobs/balsamiq_preview_spec.rb deleted file mode 100644 index 59475525b81..00000000000 --- a/spec/features/projects/blobs/balsamiq_preview_spec.rb +++ /dev/null @@ -1,24 +0,0 @@ -require 'spec_helper' - -feature 'Balsamiq preview', :feature, :js do - include TreeHelper - - let(:user) { create(:user) } - let(:project) { create(:project) } - let(:branch) { 'add-balsamiq-file' } - let(:path) { 'files/images/balsamiq.bmpr' } - - before do - project.add_master(user) - login_as user - visit namespace_project_blob_path(project.namespace, project, tree_join(branch, path)) - end - - it 'should show a loading icon' do - expect(find('.file-content')).to have_selector('.loading') - end - - it 'should show a viewer container' do - expect(page).to have_selector('.balsamiq-viewer') - end -end -- cgit v1.2.1 From dc3d778fac4f9b0d4fbcb574f9ef3b0cd11ad640 Mon Sep 17 00:00:00 2001 From: "Luke \"Jared\" Bennett" Date: Sun, 30 Apr 2017 12:02:17 +0100 Subject: Fix failing specs --- spec/features/projects/commit/cherry_pick_spec.rb | 2 +- spec/features/projects/environments/environment_spec.rb | 2 +- spec/features/projects/merge_request_button_spec.rb | 12 ++++++------ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/spec/features/projects/commit/cherry_pick_spec.rb b/spec/features/projects/commit/cherry_pick_spec.rb index 5d64d42fd61..8c86a6742c0 100644 --- a/spec/features/projects/commit/cherry_pick_spec.rb +++ b/spec/features/projects/commit/cherry_pick_spec.rb @@ -75,7 +75,7 @@ describe 'Cherry-pick Commits' do wait_for_ajax page.within('#modal-cherry-pick-commit .dropdown-menu .dropdown-content') do - click_link 'feature' + click_link "'test'" end page.within('#modal-cherry-pick-commit') do diff --git a/spec/features/projects/environments/environment_spec.rb b/spec/features/projects/environments/environment_spec.rb index acc3efe04e6..49a4944e874 100644 --- a/spec/features/projects/environments/environment_spec.rb +++ b/spec/features/projects/environments/environment_spec.rb @@ -200,7 +200,7 @@ feature 'Environment', :feature do end scenario 'user deletes the branch with running environment' do - visit namespace_project_branches_path(project.namespace, project) + visit namespace_project_branches_path(project.namespace, project, page: 2) remove_branch_with_hooks(project, user, 'feature') do page.within('.js-branch-feature') { find('a.btn-remove').click } diff --git a/spec/features/projects/merge_request_button_spec.rb b/spec/features/projects/merge_request_button_spec.rb index 05f3162f13c..2c5642f9a8c 100644 --- a/spec/features/projects/merge_request_button_spec.rb +++ b/spec/features/projects/merge_request_button_spec.rb @@ -25,7 +25,7 @@ feature 'Merge Request button', feature: true do it 'shows Create merge request button' do href = new_namespace_project_merge_request_path(project.namespace, project, - merge_request: { source_branch: 'feature', + merge_request: { source_branch: "'test'", target_branch: 'master' }) visit url @@ -69,7 +69,7 @@ feature 'Merge Request button', feature: true do it 'shows Create merge request button' do href = new_namespace_project_merge_request_path(forked_project.namespace, forked_project, - merge_request: { source_branch: 'feature', + merge_request: { source_branch: "'test'", target_branch: 'master' }) visit fork_url @@ -93,16 +93,16 @@ feature 'Merge Request button', feature: true do context 'on compare page' do it_behaves_like 'Merge request button only shown when allowed' do let(:label) { 'Create merge request' } - let(:url) { namespace_project_compare_path(project.namespace, project, from: 'master', to: 'feature') } - let(:fork_url) { namespace_project_compare_path(forked_project.namespace, forked_project, from: 'master', to: 'feature') } + let(:url) { namespace_project_compare_path(project.namespace, project, from: 'master', to: "'test'") } + let(:fork_url) { namespace_project_compare_path(forked_project.namespace, forked_project, from: 'master', to: "'test'") } end end context 'on commits page' do it_behaves_like 'Merge request button only shown when allowed' do let(:label) { 'Create merge request' } - let(:url) { namespace_project_commits_path(project.namespace, project, 'feature') } - let(:fork_url) { namespace_project_commits_path(forked_project.namespace, forked_project, 'feature') } + let(:url) { namespace_project_commits_path(project.namespace, project, "'test'") } + let(:fork_url) { namespace_project_commits_path(forked_project.namespace, forked_project, "'test'") } end end end -- cgit v1.2.1 From 5ed2465c1af700bd32242d0f1f04a1968ee65039 Mon Sep 17 00:00:00 2001 From: "Luke \"Jared\" Bennett" Date: Tue, 2 May 2017 17:00:57 +0100 Subject: Fixed tests --- spec/features/projects/merge_request_button_spec.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/spec/features/projects/merge_request_button_spec.rb b/spec/features/projects/merge_request_button_spec.rb index 1a4d613ef94..1370ab1c521 100644 --- a/spec/features/projects/merge_request_button_spec.rb +++ b/spec/features/projects/merge_request_button_spec.rb @@ -25,7 +25,7 @@ feature 'Merge Request button', feature: true do it 'shows Create merge request button' do href = new_namespace_project_merge_request_path(project.namespace, project, - merge_request: { source_branch: "'test'", + merge_request: { source_branch: 'feature', target_branch: 'master' }) visit url @@ -69,7 +69,7 @@ feature 'Merge Request button', feature: true do it 'shows Create merge request button' do href = new_namespace_project_merge_request_path(forked_project.namespace, forked_project, - merge_request: { source_branch: "'test'", + merge_request: { source_branch: 'feature', target_branch: 'master' }) visit fork_url @@ -93,16 +93,16 @@ feature 'Merge Request button', feature: true do context 'on compare page' do it_behaves_like 'Merge request button only shown when allowed' do let(:label) { 'Create merge request' } - let(:url) { namespace_project_compare_path(project.namespace, project, from: 'master', to: "'test'") } - let(:fork_url) { namespace_project_compare_path(forked_project.namespace, forked_project, from: 'master', to: "'test'") } + let(:url) { namespace_project_compare_path(project.namespace, project, from: 'master', to: 'feature') } + let(:fork_url) { namespace_project_compare_path(forked_project.namespace, forked_project, from: 'master', to: 'feature') } end end context 'on commits page' do it_behaves_like 'Merge request button only shown when allowed' do let(:label) { 'Create merge request' } - let(:url) { namespace_project_commits_path(project.namespace, project, "'test'") } - let(:fork_url) { namespace_project_commits_path(forked_project.namespace, forked_project, "'test'") } + let(:url) { namespace_project_commits_path(project.namespace, project, 'feature') } + let(:fork_url) { namespace_project_commits_path(forked_project.namespace, forked_project, 'feature') } end end end -- cgit v1.2.1 From 165bcec30506380efa912b3dba25815aeea9c92f Mon Sep 17 00:00:00 2001 From: "Luke \"Jared\" Bennett" Date: Tue, 2 May 2017 20:17:45 +0100 Subject: [ci skip] added changelog entry --- changelogs/unreleased/balsalmiq-support.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 changelogs/unreleased/balsalmiq-support.yml diff --git a/changelogs/unreleased/balsalmiq-support.yml b/changelogs/unreleased/balsalmiq-support.yml new file mode 100644 index 00000000000..56a0b4c83fa --- /dev/null +++ b/changelogs/unreleased/balsalmiq-support.yml @@ -0,0 +1,4 @@ +--- +title: Added balsamiq file viewer +merge_request: 10564 +author: -- cgit v1.2.1