Added tests

This commit is contained in:
Phil Hughes 2017-03-23 12:37:24 +00:00
parent e8949a1ee4
commit 0ef8585726
6 changed files with 265 additions and 78 deletions

View file

@ -0,0 +1,80 @@
/* eslint-disable no-new */
import Vue from 'vue';
import VueResource from 'vue-resource';
import NotebookLab from 'vendor/notebooklab';
Vue.use(VueResource);
Vue.use(NotebookLab);
export default () => {
const el = document.getElementById('js-notebook-viewer');
new Vue({
el,
data() {
return {
error: false,
loadError: false,
loading: true,
json: {},
};
},
template: `
<div class="container-fluid md prepend-top-default append-bottom-default">
<div
class="text-center loading"
v-if="loading && !error">
<i
class="fa fa-spinner fa-spin"
aria-hidden="true"
aria-label="iPython notebook loading">
</i>
</div>
<notebook-lab
v-if="!loading && !error"
:notebook="json" />
<p
class="text-center"
v-if="error">
<span v-if="loadError">
An error occured whilst loading the file. Please try again later.
</span>
<span v-else>
An error occured whilst parsing the file.
</span>
</p>
</div>
`,
methods: {
loadFile() {
this.$http.get(el.dataset.endpoint)
.then((res) => {
this.json = res.json();
this.loading = false;
})
.catch((e) => {
if (e.status) {
this.loadError = true;
}
this.error = true;
});
},
},
mounted() {
$('<link>', {
rel: 'stylesheet',
type: 'text/css',
href: gon.katex_css_url,
}).appendTo('head');
if (gon.katex_js_url) {
$.getScript(gon.katex_js_url, () => {
this.loadFile();
});
} else {
this.loadFile();
}
},
});
};

View file

@ -1,75 +1,3 @@
import Vue from 'vue';
import VueResource from 'vue-resource';
import NotebookLab from 'vendor/notebooklab';
import renderNotebook from './notebook';
Vue.use(VueResource);
Vue.use(NotebookLab);
document.addEventListener('DOMContentLoaded', () => {
const el = document.getElementById('js-notebook-viewer');
new Vue({
el,
data() {
return {
error: false,
loadError: false,
loading: true,
json: {},
};
},
template: `
<div class="container-fluid md prepend-top-default append-bottom-default">
<div
class="text-center loading"
v-if="loading && !error">
<i
class="fa fa-spinner fa-spin"
aria-hidden="true"
aria-label="iPython notebook loading">
</i>
</div>
<notebook-lab
v-if="!loading && !error"
:notebook="json" />
<p
class="text-center"
v-if="error">
<span v-if="loadError">
An error occured whilst loading the file. Please try again later.
</span>
<span v-else>
An error occured whilst parsing the file.
</span>
</p>
</div>
`,
methods: {
loadFile() {
this.$http.get(el.dataset.endpoint)
.then((res) => {
this.json = res.json();
this.loading = false;
})
.catch((e) => {
if (e.status) {
this.loadError = true;
}
this.error = true;
});
},
},
mounted() {
$('<link>', {
rel: 'stylesheet',
type: 'text/css',
href: gon.katex_css_url,
}).appendTo('head');
$.getScript(gon.katex_js_url, () => {
this.loadFile();
});
},
});
});
document.addEventListener('DOMContentLoaded', renderNotebook);

View file

@ -0,0 +1,159 @@
import Vue from 'vue';
import renderNotebook from '~/blob/notebook';
describe('iPython notebook renderer', () => {
preloadFixtures('static/notebook_viewer.html.raw');
beforeEach(() => {
loadFixtures('static/notebook_viewer.html.raw');
});
it('shows loading icon', () => {
renderNotebook();
expect(
document.querySelector('.loading'),
).not.toBeNull();
});
describe('successful response', () => {
const response = (request, next) => {
next(request.respondWith(JSON.stringify({
cells: [{
cell_type: 'markdown',
source: ['# test'],
}, {
cell_type: 'code',
execution_count: 1,
source: [
'def test(str)',
' return str',
],
outputs: [],
}],
}), {
status: 200,
}));
};
beforeEach((done) => {
Vue.http.interceptors.push(response);
renderNotebook();
setTimeout(() => {
done();
});
});
afterEach(() => {
Vue.http.interceptors = _.without(
Vue.http.interceptors, response,
);
});
it('does not show loading icon', () => {
expect(
document.querySelector('.loading'),
).toBeNull();
});
it('renders the notebook', () => {
expect(
document.querySelector('.md'),
).not.toBeNull();
});
it('renders the markdown cell', () => {
expect(
document.querySelector('h1'),
).not.toBeNull();
expect(
document.querySelector('h1').textContent.trim(),
).toBe('test');
});
it('highlights code', () => {
expect(
document.querySelector('.hljs'),
).not.toBeNull();
expect(
document.querySelector('.python'),
).not.toBeNull();
});
});
describe('error in JSON response', () => {
const response = (request, next) => {
next(request.respondWith('{ "cells": [{"cell_type": "markdown"} }', {
status: 200,
}));
};
beforeEach((done) => {
Vue.http.interceptors.push(response);
renderNotebook();
setTimeout(() => {
done();
});
});
afterEach(() => {
Vue.http.interceptors = _.without(
Vue.http.interceptors, response,
);
});
it('does not show loading icon', () => {
expect(
document.querySelector('.loading'),
).toBeNull();
});
it('shows error message', () => {
expect(
document.querySelector('.md').textContent.trim(),
).toBe('An error occured whilst parsing the file.');
});
});
describe('error getting file', () => {
const response = (request, next) => {
next(request.respondWith('', {
status: 500,
}));
};
beforeEach((done) => {
Vue.http.interceptors.push(response);
renderNotebook();
setTimeout(() => {
done();
});
});
afterEach(() => {
Vue.http.interceptors = _.without(
Vue.http.interceptors, response,
);
});
it('does not show loading icon', () => {
expect(
document.querySelector('.loading'),
).toBeNull();
});
it('shows error message', () => {
expect(
document.querySelector('.md').textContent.trim(),
).toBe('An error occured whilst loading the file. Please try again later.');
});
});
});

View file

@ -0,0 +1 @@
.file-content#js-notebook-viewer{ data: { endpoint: '/test' } }

View file

@ -53,6 +53,20 @@ describe Blob do
end
end
describe '#ipython_notebook?' do
it 'is falsey when language is not Jupyter Notebook' do
git_blob = double(text?: true, language: double(name: 'JSON'))
expect(described_class.decorate(git_blob)).not_to be_ipython_notebook
end
it 'is truthy when language is Jupyter Notebook' do
git_blob = double(text?: true, language: double(name: 'Jupyter Notebook'))
expect(described_class.decorate(git_blob)).to be_ipython_notebook
end
end
describe '#video?' do
it 'is falsey with image extension' do
git_blob = Gitlab::Git::Blob.new(name: 'image.png')
@ -116,6 +130,11 @@ describe Blob do
blob = stubbed_blob
expect(blob.to_partial_path(project)).to eq 'download'
end
it 'handles iPython notebooks' do
blob = stubbed_blob(text?: true, ipython_notebook?: true)
expect(blob.to_partial_path(project)).to eq 'notebook'
end
end
describe '#size_within_svg_limits?' do

View file

@ -414,13 +414,13 @@ exports.default = {
},
computed: {
markdown: function markdown() {
var regex = new RegExp(/^\$\$(.*)\$\$$/, 'g');
var regex = new RegExp('^\$\$(.*)\$\$$', 'g');
var source = this.cell.source.map(function (line) {
var matches = regex.exec(line.trim());
// Only render use the Katex library if it is actually loaded
if (matches && matches.length > 0 && katex) {
if (matches && matches.length > 0 && typeof katex !== 'undefined') {
return katex.renderToString(matches[1]);
} else {
return line;
@ -3047,7 +3047,7 @@ function escape(html, encode) {
}
function unescape(html) {
// explicitly match decimal, hex, and named HTML entities
// explicitly match decimal, hex, and named HTML entities
return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
n = n.toLowerCase();
if (n === 'colon') return ':';
@ -3636,4 +3636,4 @@ module.exports = {
/***/ })
/******/ ]);
});
});