Moved NotebookLab assets into repo
Moved all the notebooklab assets into the GitLab repo
This commit is contained in:
parent
3082a1195c
commit
4755f6066b
19 changed files with 841 additions and 2 deletions
|
@ -1,10 +1,9 @@
|
|||
/* eslint-disable no-new */
|
||||
import Vue from 'vue';
|
||||
import VueResource from 'vue-resource';
|
||||
import NotebookLab from 'vendor/notebooklab';
|
||||
import notebookLab from '../../notebook/index.vue';
|
||||
|
||||
Vue.use(VueResource);
|
||||
Vue.use(NotebookLab);
|
||||
|
||||
export default () => {
|
||||
const el = document.getElementById('js-notebook-viewer');
|
||||
|
@ -19,6 +18,9 @@ export default () => {
|
|||
json: {},
|
||||
};
|
||||
},
|
||||
components: {
|
||||
notebookLab,
|
||||
},
|
||||
template: `
|
||||
<div class="container-fluid md prepend-top-default append-bottom-default">
|
||||
<div
|
||||
|
|
58
app/assets/javascripts/notebook/cells/code.vue
Normal file
58
app/assets/javascripts/notebook/cells/code.vue
Normal file
|
@ -0,0 +1,58 @@
|
|||
<template>
|
||||
<div class="cell">
|
||||
<code-cell
|
||||
type="input"
|
||||
:raw-code="rawInputCode"
|
||||
:count="cell.execution_count"
|
||||
:code-css-class="codeCssClass" />
|
||||
<output-cell
|
||||
v-if="hasOutput"
|
||||
:count="cell.execution_count"
|
||||
:output="output"
|
||||
:code-css-class="codeCssClass" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CodeCell from './code/index.vue';
|
||||
import OutputCell from './output/index.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
'code-cell': CodeCell,
|
||||
'output-cell': OutputCell,
|
||||
},
|
||||
props: {
|
||||
cell: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
codeCssClass: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
rawInputCode() {
|
||||
if (this.cell.source) {
|
||||
return this.cell.source.join('');
|
||||
}
|
||||
|
||||
return '';
|
||||
},
|
||||
hasOutput() {
|
||||
return this.cell.outputs.length;
|
||||
},
|
||||
output() {
|
||||
return this.cell.outputs[0];
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.cell {
|
||||
flex-direction: column;
|
||||
}
|
||||
</style>
|
57
app/assets/javascripts/notebook/cells/code/index.vue
Normal file
57
app/assets/javascripts/notebook/cells/code/index.vue
Normal file
|
@ -0,0 +1,57 @@
|
|||
<template>
|
||||
<div :class="type">
|
||||
<prompt
|
||||
:type="promptType"
|
||||
:count="count" />
|
||||
<pre
|
||||
class="language-python"
|
||||
:class="codeCssClass"
|
||||
ref="code"
|
||||
v-text="code">
|
||||
</pre>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Prism from '../../lib/highlight';
|
||||
import Prompt from '../prompt.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
prompt: Prompt,
|
||||
},
|
||||
props: {
|
||||
count: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: 0,
|
||||
},
|
||||
codeCssClass: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
rawCode: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
code() {
|
||||
return this.rawCode;
|
||||
},
|
||||
promptType() {
|
||||
const type = this.type.split('put')[0];
|
||||
|
||||
return type.charAt(0).toUpperCase() + type.slice(1);
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
Prism.highlightElement(this.$refs.code);
|
||||
},
|
||||
};
|
||||
</script>
|
2
app/assets/javascripts/notebook/cells/index.js
Normal file
2
app/assets/javascripts/notebook/cells/index.js
Normal file
|
@ -0,0 +1,2 @@
|
|||
export { default as MarkdownCell } from './markdown.vue';
|
||||
export { default as CodeCell } from './code.vue';
|
49
app/assets/javascripts/notebook/cells/markdown.vue
Normal file
49
app/assets/javascripts/notebook/cells/markdown.vue
Normal file
|
@ -0,0 +1,49 @@
|
|||
<template>
|
||||
<div class="cell text-cell">
|
||||
<prompt />
|
||||
<div class="markdown" v-html="markdown"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/* global katex */
|
||||
import marked from 'marked';
|
||||
import Prompt from './prompt.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
prompt: Prompt,
|
||||
},
|
||||
props: {
|
||||
cell: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
markdown() {
|
||||
const regex = new RegExp('^\\$\\$(.*)\\$\\$$', 'g');
|
||||
|
||||
const source = this.cell.source.map((line) => {
|
||||
const matches = regex.exec(line.trim());
|
||||
|
||||
// Only render use the Katex library if it is actually loaded
|
||||
if (matches && matches.length > 0 && typeof katex !== 'undefined') {
|
||||
return katex.renderToString(matches[1]);
|
||||
}
|
||||
|
||||
return line;
|
||||
});
|
||||
|
||||
return marked(source.join(''));
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.markdown .katex {
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
22
app/assets/javascripts/notebook/cells/output/html.vue
Normal file
22
app/assets/javascripts/notebook/cells/output/html.vue
Normal file
|
@ -0,0 +1,22 @@
|
|||
<template>
|
||||
<div class="output">
|
||||
<prompt />
|
||||
<div v-html="rawCode"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Prompt from '../prompt.vue';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
rawCode: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
components: {
|
||||
prompt: Prompt,
|
||||
},
|
||||
};
|
||||
</script>
|
27
app/assets/javascripts/notebook/cells/output/image.vue
Normal file
27
app/assets/javascripts/notebook/cells/output/image.vue
Normal file
|
@ -0,0 +1,27 @@
|
|||
<template>
|
||||
<div class="output">
|
||||
<prompt />
|
||||
<img
|
||||
:src="'data:' + outputType + ';base64,' + rawCode" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Prompt from '../prompt.vue';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
outputType: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
rawCode: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
components: {
|
||||
prompt: Prompt,
|
||||
},
|
||||
};
|
||||
</script>
|
83
app/assets/javascripts/notebook/cells/output/index.vue
Normal file
83
app/assets/javascripts/notebook/cells/output/index.vue
Normal file
|
@ -0,0 +1,83 @@
|
|||
<template>
|
||||
<component :is="componentName"
|
||||
type="output"
|
||||
:outputType="outputType"
|
||||
:count="count"
|
||||
:raw-code="rawCode"
|
||||
:code-css-class="codeCssClass" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CodeCell from '../code/index.vue';
|
||||
import Html from './html.vue';
|
||||
import Image from './image.vue';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
codeCssClass: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
count: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: 0,
|
||||
},
|
||||
output: {
|
||||
type: Object,
|
||||
requred: true,
|
||||
},
|
||||
},
|
||||
components: {
|
||||
'code-cell': CodeCell,
|
||||
'html-output': Html,
|
||||
'image-output': Image,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
outputType: '',
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
componentName() {
|
||||
if (this.output.text) {
|
||||
return 'code-cell';
|
||||
} else if (this.output.data['image/png']) {
|
||||
this.outputType = 'image/png';
|
||||
|
||||
return 'image-output';
|
||||
} else if (this.output.data['text/html']) {
|
||||
this.outputType = 'text/html';
|
||||
|
||||
return 'html-output';
|
||||
} else if (this.output.data['image/svg+xml']) {
|
||||
this.outputType = 'image/svg+xml';
|
||||
|
||||
return 'html-output';
|
||||
}
|
||||
|
||||
this.outputType = 'text/plain';
|
||||
return 'code-cell';
|
||||
},
|
||||
rawCode() {
|
||||
if (this.output.text) {
|
||||
return this.output.text.join('');
|
||||
}
|
||||
|
||||
return this.dataForType(this.outputType);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
dataForType(type) {
|
||||
let data = this.output.data[type];
|
||||
|
||||
if (typeof data === 'object') {
|
||||
data = data.join('');
|
||||
}
|
||||
|
||||
return data;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
30
app/assets/javascripts/notebook/cells/prompt.vue
Normal file
30
app/assets/javascripts/notebook/cells/prompt.vue
Normal file
|
@ -0,0 +1,30 @@
|
|||
<template>
|
||||
<div class="prompt">
|
||||
<span v-if="type && count">
|
||||
{{ type }} [{{ count }}]:
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
type: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
count: {
|
||||
type: Number,
|
||||
required: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.prompt {
|
||||
padding: 0 10px;
|
||||
min-width: 7em;
|
||||
font-family: monospace;
|
||||
}
|
||||
</style>
|
75
app/assets/javascripts/notebook/index.vue
Normal file
75
app/assets/javascripts/notebook/index.vue
Normal file
|
@ -0,0 +1,75 @@
|
|||
<template>
|
||||
<div v-if="hasNotebook">
|
||||
<component
|
||||
v-for="(cell, index) in cells"
|
||||
:is="cellType(cell.cell_type)"
|
||||
:cell="cell"
|
||||
:key="index"
|
||||
:code-css-class="codeCssClass" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
MarkdownCell,
|
||||
CodeCell,
|
||||
} from './cells';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
'code-cell': CodeCell,
|
||||
'markdown-cell': MarkdownCell,
|
||||
},
|
||||
props: {
|
||||
notebook: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
codeCssClass: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
cellType(type) {
|
||||
return `${type}-cell`;
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
cells() {
|
||||
if (this.notebook.worksheets) {
|
||||
const data = {
|
||||
cells: [],
|
||||
};
|
||||
|
||||
return this.notebook.worksheets.reduce((cellData, sheet) => {
|
||||
const cellDataCopy = cellData;
|
||||
cellDataCopy.cells = cellDataCopy.cells.concat(sheet.cells);
|
||||
return cellDataCopy;
|
||||
}, data).cells;
|
||||
}
|
||||
|
||||
return this.notebook.cells;
|
||||
},
|
||||
hasNotebook() {
|
||||
return Object.keys(this.notebook).length;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.cell,
|
||||
.input,
|
||||
.output {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.cell pre {
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
22
app/assets/javascripts/notebook/lib/highlight.js
Normal file
22
app/assets/javascripts/notebook/lib/highlight.js
Normal file
|
@ -0,0 +1,22 @@
|
|||
import Prism from 'prismjs';
|
||||
import 'prismjs/components/prism-python';
|
||||
import 'prismjs/plugins/custom-class/prism-custom-class';
|
||||
|
||||
Prism.plugins.customClass.map({
|
||||
comment: 'c',
|
||||
error: 'err',
|
||||
operator: 'o',
|
||||
constant: 'kc',
|
||||
namespace: 'kn',
|
||||
keyword: 'k',
|
||||
string: 's',
|
||||
number: 'm',
|
||||
'attr-name': 'na',
|
||||
builtin: 'nb',
|
||||
entity: 'ni',
|
||||
function: 'nf',
|
||||
tag: 'nt',
|
||||
variable: 'nv',
|
||||
});
|
||||
|
||||
export default Prism;
|
|
@ -32,8 +32,10 @@
|
|||
"js-cookie": "^2.1.3",
|
||||
"jszip": "^3.1.3",
|
||||
"jszip-utils": "^0.0.2",
|
||||
"marked": "^0.3.6",
|
||||
"mousetrap": "^1.4.6",
|
||||
"pikaday": "^1.5.1",
|
||||
"prismjs": "^1.6.0",
|
||||
"raphael": "^2.2.7",
|
||||
"raw-loader": "^0.5.1",
|
||||
"react-dev-utils": "^0.5.2",
|
||||
|
|
51
spec/javascripts/notebook/cells/code_spec.js
Normal file
51
spec/javascripts/notebook/cells/code_spec.js
Normal file
|
@ -0,0 +1,51 @@
|
|||
import Vue from 'vue';
|
||||
import CodeComponent from '~/notebook/cells/code.vue';
|
||||
import json from '../../fixtures/notebook/file.json';
|
||||
|
||||
const Component = Vue.extend(CodeComponent);
|
||||
|
||||
describe('Code component', () => {
|
||||
let vm;
|
||||
|
||||
describe('without output', () => {
|
||||
beforeEach((done) => {
|
||||
vm = new Component({
|
||||
propsData: {
|
||||
cell: json.cells[0],
|
||||
},
|
||||
});
|
||||
vm.$mount();
|
||||
|
||||
setTimeout(() => {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('does not render output prompt', () => {
|
||||
expect(vm.$el.querySelectorAll('.prompt').length).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('with output', () => {
|
||||
beforeEach((done) => {
|
||||
vm = new Component({
|
||||
propsData: {
|
||||
cell: json.cells[2],
|
||||
},
|
||||
});
|
||||
vm.$mount();
|
||||
|
||||
setTimeout(() => {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('does not render output prompt', () => {
|
||||
expect(vm.$el.querySelectorAll('.prompt').length).toBe(2);
|
||||
});
|
||||
|
||||
it('renders output cell', () => {
|
||||
expect(vm.$el.querySelector('.output')).toBeDefined();
|
||||
});
|
||||
});
|
||||
});
|
37
spec/javascripts/notebook/cells/markdown_spec.js
Normal file
37
spec/javascripts/notebook/cells/markdown_spec.js
Normal file
|
@ -0,0 +1,37 @@
|
|||
import Vue from 'vue';
|
||||
import MarkdownComponent from '~/notebook/cells/markdown.vue';
|
||||
import json from '../../fixtures/notebook/file.json';
|
||||
|
||||
const cell = json.cells[1];
|
||||
const Component = Vue.extend(MarkdownComponent);
|
||||
|
||||
describe('Markdown component', () => {
|
||||
let vm;
|
||||
|
||||
beforeEach((done) => {
|
||||
vm = new Component({
|
||||
propsData: {
|
||||
cell,
|
||||
},
|
||||
});
|
||||
vm.$mount();
|
||||
|
||||
setTimeout(() => {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('does not render promot', () => {
|
||||
expect(vm.$el.querySelector('.prompt span')).toBeNull();
|
||||
});
|
||||
|
||||
it('does not render the markdown text', () => {
|
||||
expect(
|
||||
vm.$el.querySelector('.markdown').innerHTML.trim(),
|
||||
).not.toEqual(cell.source.join(''));
|
||||
});
|
||||
|
||||
it('renders the markdown HTML', () => {
|
||||
expect(vm.$el.querySelector('.markdown h1')).not.toBeNull();
|
||||
});
|
||||
});
|
122
spec/javascripts/notebook/cells/output/index_spec.js
Normal file
122
spec/javascripts/notebook/cells/output/index_spec.js
Normal file
|
@ -0,0 +1,122 @@
|
|||
import Vue from 'vue';
|
||||
import CodeComponent from '~/notebook/cells/output/index.vue';
|
||||
import json from '../../../fixtures/notebook/file.json';
|
||||
|
||||
const Component = Vue.extend(CodeComponent);
|
||||
|
||||
describe('Output component', () => {
|
||||
let vm;
|
||||
|
||||
const createComponent = (output) => {
|
||||
vm = new Component({
|
||||
propsData: {
|
||||
output,
|
||||
count: 1,
|
||||
},
|
||||
});
|
||||
vm.$mount();
|
||||
};
|
||||
|
||||
describe('text output', () => {
|
||||
beforeEach((done) => {
|
||||
createComponent(json.cells[2].outputs[0]);
|
||||
|
||||
setTimeout(() => {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('renders as plain text', () => {
|
||||
expect(vm.$el.querySelector('pre')).not.toBeNull();
|
||||
});
|
||||
|
||||
it('renders promot', () => {
|
||||
expect(vm.$el.querySelector('.prompt span')).not.toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('image output', () => {
|
||||
beforeEach((done) => {
|
||||
createComponent(json.cells[3].outputs[0]);
|
||||
|
||||
setTimeout(() => {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('renders as an image', () => {
|
||||
expect(vm.$el.querySelector('img')).not.toBeNull();
|
||||
});
|
||||
|
||||
it('does not render the prompt', () => {
|
||||
expect(vm.$el.querySelector('.prompt span')).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('html output', () => {
|
||||
beforeEach((done) => {
|
||||
createComponent(json.cells[4].outputs[0]);
|
||||
|
||||
setTimeout(() => {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('renders raw HTML', () => {
|
||||
expect(vm.$el.querySelector('p')).not.toBeNull();
|
||||
expect(vm.$el.textContent.trim()).toBe('test');
|
||||
});
|
||||
|
||||
it('does not render the prompt', () => {
|
||||
expect(vm.$el.querySelector('.prompt span')).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('svg output', () => {
|
||||
beforeEach((done) => {
|
||||
createComponent(json.cells[5].outputs[0]);
|
||||
|
||||
setTimeout(() => {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('renders as an svg', () => {
|
||||
expect(vm.$el.querySelector('svg')).not.toBeNull();
|
||||
});
|
||||
|
||||
it('does not render the prompt', () => {
|
||||
expect(vm.$el.querySelector('.prompt span')).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('default to plain text', () => {
|
||||
beforeEach((done) => {
|
||||
createComponent(json.cells[6].outputs[0]);
|
||||
|
||||
setTimeout(() => {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('renders as plain text', () => {
|
||||
expect(vm.$el.querySelector('pre')).not.toBeNull();
|
||||
expect(vm.$el.textContent.trim()).toContain('testing');
|
||||
});
|
||||
|
||||
it('renders promot', () => {
|
||||
expect(vm.$el.querySelector('.prompt span')).not.toBeNull();
|
||||
});
|
||||
|
||||
it('renders as plain text when doesn\'t recognise other types', (done) => {
|
||||
createComponent(json.cells[7].outputs[0]);
|
||||
|
||||
setTimeout(() => {
|
||||
expect(vm.$el.querySelector('pre')).not.toBeNull();
|
||||
expect(vm.$el.textContent.trim()).toContain('testing');
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
56
spec/javascripts/notebook/cells/prompt_spec.js
Normal file
56
spec/javascripts/notebook/cells/prompt_spec.js
Normal file
|
@ -0,0 +1,56 @@
|
|||
import Vue from 'vue';
|
||||
import PromptComponent from '~/notebook/cells/prompt.vue';
|
||||
|
||||
const Component = Vue.extend(PromptComponent);
|
||||
|
||||
describe('Prompt component', () => {
|
||||
let vm;
|
||||
|
||||
describe('input', () => {
|
||||
beforeEach((done) => {
|
||||
vm = new Component({
|
||||
propsData: {
|
||||
type: 'In',
|
||||
count: 1,
|
||||
},
|
||||
});
|
||||
vm.$mount();
|
||||
|
||||
setTimeout(() => {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('renders in label', () => {
|
||||
expect(vm.$el.textContent.trim()).toContain('In');
|
||||
});
|
||||
|
||||
it('renders count', () => {
|
||||
expect(vm.$el.textContent.trim()).toContain('1');
|
||||
});
|
||||
});
|
||||
|
||||
describe('output', () => {
|
||||
beforeEach((done) => {
|
||||
vm = new Component({
|
||||
propsData: {
|
||||
type: 'Out',
|
||||
count: 1,
|
||||
},
|
||||
});
|
||||
vm.$mount();
|
||||
|
||||
setTimeout(() => {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('renders in label', () => {
|
||||
expect(vm.$el.textContent.trim()).toContain('Out');
|
||||
});
|
||||
|
||||
it('renders count', () => {
|
||||
expect(vm.$el.textContent.trim()).toContain('1');
|
||||
});
|
||||
});
|
||||
});
|
93
spec/javascripts/notebook/index_spec.js
Normal file
93
spec/javascripts/notebook/index_spec.js
Normal file
|
@ -0,0 +1,93 @@
|
|||
import Vue from 'vue';
|
||||
import Notebook from '~/notebook/index.vue';
|
||||
import json from '../fixtures/notebook/file.json';
|
||||
import jsonWithWorksheet from '../fixtures/notebook/worksheets.json';
|
||||
|
||||
const Component = Vue.extend(Notebook);
|
||||
|
||||
describe('Notebook component', () => {
|
||||
let vm;
|
||||
|
||||
describe('without JSON', () => {
|
||||
beforeEach((done) => {
|
||||
vm = new Component({
|
||||
propsData: {
|
||||
notebook: {},
|
||||
},
|
||||
});
|
||||
vm.$mount();
|
||||
|
||||
setTimeout(() => {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('does not render', () => {
|
||||
expect(vm.$el.tagName).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('with JSON', () => {
|
||||
beforeEach((done) => {
|
||||
vm = new Component({
|
||||
propsData: {
|
||||
notebook: json,
|
||||
codeCssClass: 'js-code-class',
|
||||
},
|
||||
});
|
||||
vm.$mount();
|
||||
|
||||
setTimeout(() => {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('renders cells', () => {
|
||||
expect(vm.$el.querySelectorAll('.cell').length).toBe(json.cells.length);
|
||||
});
|
||||
|
||||
it('renders markdown cell', () => {
|
||||
expect(vm.$el.querySelector('.markdown')).not.toBeNull();
|
||||
});
|
||||
|
||||
it('renders code cell', () => {
|
||||
expect(vm.$el.querySelector('pre')).not.toBeNull();
|
||||
});
|
||||
|
||||
it('add code class to code blocks', () => {
|
||||
expect(vm.$el.querySelector('.js-code-class')).not.toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('with worksheets', () => {
|
||||
beforeEach((done) => {
|
||||
vm = new Component({
|
||||
propsData: {
|
||||
notebook: jsonWithWorksheet,
|
||||
codeCssClass: 'js-code-class',
|
||||
},
|
||||
});
|
||||
vm.$mount();
|
||||
|
||||
setTimeout(() => {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('renders cells', () => {
|
||||
expect(vm.$el.querySelectorAll('.cell').length).toBe(jsonWithWorksheet.worksheets[0].cells.length);
|
||||
});
|
||||
|
||||
it('renders markdown cell', () => {
|
||||
expect(vm.$el.querySelector('.markdown')).not.toBeNull();
|
||||
});
|
||||
|
||||
it('renders code cell', () => {
|
||||
expect(vm.$el.querySelector('pre')).not.toBeNull();
|
||||
});
|
||||
|
||||
it('add code class to code blocks', () => {
|
||||
expect(vm.$el.querySelector('.js-code-class')).not.toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
15
spec/javascripts/notebook/lib/highlight_spec.js
Normal file
15
spec/javascripts/notebook/lib/highlight_spec.js
Normal file
|
@ -0,0 +1,15 @@
|
|||
import Prism from '~/notebook/lib/highlight';
|
||||
|
||||
describe('Highlight library', () => {
|
||||
it('imports python language', () => {
|
||||
expect(Prism.languages.python).toBeDefined();
|
||||
});
|
||||
|
||||
it('uses custom CSS classes', () => {
|
||||
const el = document.createElement('div');
|
||||
el.innerHTML = Prism.highlight('console.log("a");', Prism.languages.javascript);
|
||||
|
||||
expect(el.querySelector('.s')).not.toBeNull();
|
||||
expect(el.querySelector('.nf')).not.toBeNull();
|
||||
});
|
||||
});
|
36
yarn.lock
36
yarn.lock
|
@ -1119,6 +1119,14 @@ cli-width@^2.0.0:
|
|||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.1.0.tgz#b234ca209b29ef66fc518d9b98d5847b00edf00a"
|
||||
|
||||
clipboard@^1.5.5:
|
||||
version "1.6.1"
|
||||
resolved "https://registry.yarnpkg.com/clipboard/-/clipboard-1.6.1.tgz#65c5b654812466b0faab82dc6ba0f1d2f8e4be53"
|
||||
dependencies:
|
||||
good-listener "^1.2.0"
|
||||
select "^1.1.2"
|
||||
tiny-emitter "^1.0.0"
|
||||
|
||||
cliui@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/cliui/-/cliui-2.1.0.tgz#4b475760ff80264c762c3a1719032e91c7fea0d1"
|
||||
|
@ -1596,6 +1604,10 @@ delayed-stream@~1.0.0:
|
|||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
|
||||
|
||||
delegate@^3.1.2:
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/delegate/-/delegate-3.1.2.tgz#1e1bc6f5cadda6cb6cbf7e6d05d0bcdd5712aebe"
|
||||
|
||||
delegates@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a"
|
||||
|
@ -2493,6 +2505,12 @@ globby@^5.0.0:
|
|||
pify "^2.0.0"
|
||||
pinkie-promise "^2.0.0"
|
||||
|
||||
good-listener@^1.2.0:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/good-listener/-/good-listener-1.2.2.tgz#d53b30cdf9313dffb7dc9a0d477096aa6d145c50"
|
||||
dependencies:
|
||||
delegate "^3.1.2"
|
||||
|
||||
got@^3.2.0:
|
||||
version "3.3.1"
|
||||
resolved "https://registry.yarnpkg.com/got/-/got-3.3.1.tgz#e5d0ed4af55fc3eef4d56007769d98192bcb2eca"
|
||||
|
@ -3550,6 +3568,10 @@ map-stream@~0.1.0:
|
|||
version "0.1.0"
|
||||
resolved "https://registry.yarnpkg.com/map-stream/-/map-stream-0.1.0.tgz#e56aa94c4c8055a16404a0674b78f215f7c8e194"
|
||||
|
||||
marked@^0.3.6:
|
||||
version "0.3.6"
|
||||
resolved "https://registry.yarnpkg.com/marked/-/marked-0.3.6.tgz#b2c6c618fccece4ef86c4fc6cb8a7cbf5aeda8d7"
|
||||
|
||||
math-expression-evaluator@^1.2.14:
|
||||
version "1.2.16"
|
||||
resolved "https://registry.yarnpkg.com/math-expression-evaluator/-/math-expression-evaluator-1.2.16.tgz#b357fa1ca9faefb8e48d10c14ef2bcb2d9f0a7c9"
|
||||
|
@ -4411,6 +4433,12 @@ preserve@^0.2.0:
|
|||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b"
|
||||
|
||||
prismjs@^1.6.0:
|
||||
version "1.6.0"
|
||||
resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.6.0.tgz#118d95fb7a66dba2272e343b345f5236659db365"
|
||||
optionalDependencies:
|
||||
clipboard "^1.5.5"
|
||||
|
||||
private@^0.1.6:
|
||||
version "0.1.7"
|
||||
resolved "https://registry.yarnpkg.com/private/-/private-0.1.7.tgz#68ce5e8a1ef0a23bb570cc28537b5332aba63ef1"
|
||||
|
@ -4869,6 +4897,10 @@ select2@3.5.2-browserify:
|
|||
version "3.5.2-browserify"
|
||||
resolved "https://registry.yarnpkg.com/select2/-/select2-3.5.2-browserify.tgz#dc4dafda38d67a734e8a97a46f0d3529ae05391d"
|
||||
|
||||
select@^1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/select/-/select-1.1.2.tgz#0e7350acdec80b1108528786ec1d4418d11b396d"
|
||||
|
||||
semver-diff@^2.0.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-2.1.0.tgz#4bbb8437c8d37e4b0cf1a68fd726ec6d645d6d36"
|
||||
|
@ -5364,6 +5396,10 @@ timers-browserify@^2.0.2:
|
|||
dependencies:
|
||||
setimmediate "^1.0.4"
|
||||
|
||||
tiny-emitter@^1.0.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-1.1.0.tgz#ab405a21ffed814a76c19739648093d70654fecb"
|
||||
|
||||
tmp@0.0.28, tmp@0.0.x:
|
||||
version "0.0.28"
|
||||
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.28.tgz#172735b7f614ea7af39664fa84cf0de4e515d120"
|
||||
|
|
Loading…
Reference in a new issue