From e953a1c3a87a6a4d737c764ee6d2997a23dff6f6 Mon Sep 17 00:00:00 2001 From: Kevin Ansfield Date: Fri, 4 May 2018 14:59:39 +0100 Subject: [PATCH] Koenig - Versioned renderer (#9606) refs https://github.com/TryGhost/Ghost/issues/9505 - updates mobiledoc converter's `render` method to accept a `version` argument - `1` === Ghost 1.0's markdown-only renderer output - `2` === Koenig's full mobiledoc renderer output - switch between mobiledoc renderer versions in Post model's `onSaving` hook - version 1 by default - version 2 if Koenig is enabled (currently behind dev experiments config + labs flag) - version 2 if the post's mobiledoc is not compatible with the markdown-only renderer - "version 2" full-Koenig mobiledoc renderer output - wraps content in a `.kg-post` div - removes wrapper around markdown and html card output - adds classes to image card output including selected image size/style - standardises es6 usage across mobiledoc related files --- core/server/lib/mobiledoc/atoms/index.js | 4 +- .../server/lib/mobiledoc/atoms/soft-return.js | 2 + core/server/lib/mobiledoc/cards/hr.js | 2 + core/server/lib/mobiledoc/cards/html.js | 14 +- core/server/lib/mobiledoc/cards/image.js | 10 +- core/server/lib/mobiledoc/cards/index.js | 12 +- core/server/lib/mobiledoc/cards/markdown.js | 21 +-- .../converters/mobiledoc-converter.js | 37 ++++- core/server/models/post.js | 34 ++++- .../lib/mobiledoc/atoms/soft-return_spec.js | 17 ++- core/test/unit/lib/mobiledoc/cards/hr_spec.js | 15 +- .../unit/lib/mobiledoc/cards/html_spec.js | 40 ++--- .../unit/lib/mobiledoc/cards/image_spec.js | 64 ++++++-- .../unit/lib/mobiledoc/cards/markdown_spec.js | 98 ++++++++---- .../converters/mobiledoc-converter_spec.js | 141 ++++++++++++++---- core/test/unit/models/post_spec.js | 65 ++++++++ 16 files changed, 427 insertions(+), 149 deletions(-) diff --git a/core/server/lib/mobiledoc/atoms/index.js b/core/server/lib/mobiledoc/atoms/index.js index 98508df9d0a..630d0a975d5 100644 --- a/core/server/lib/mobiledoc/atoms/index.js +++ b/core/server/lib/mobiledoc/atoms/index.js @@ -1,3 +1,5 @@ -var softReturn = require('./soft-return'); +'use strict'; + +const softReturn = require('./soft-return'); module.exports = [softReturn]; diff --git a/core/server/lib/mobiledoc/atoms/soft-return.js b/core/server/lib/mobiledoc/atoms/soft-return.js index 38419400816..74ee4bdd93a 100644 --- a/core/server/lib/mobiledoc/atoms/soft-return.js +++ b/core/server/lib/mobiledoc/atoms/soft-return.js @@ -1,3 +1,5 @@ +'use strict'; + module.exports = { name: 'soft-return', type: 'dom', diff --git a/core/server/lib/mobiledoc/cards/hr.js b/core/server/lib/mobiledoc/cards/hr.js index 3df54808ec9..8ff7e9e0a9e 100644 --- a/core/server/lib/mobiledoc/cards/hr.js +++ b/core/server/lib/mobiledoc/cards/hr.js @@ -1,3 +1,5 @@ +'use strict'; + module.exports = { name: 'hr', type: 'dom', diff --git a/core/server/lib/mobiledoc/cards/html.js b/core/server/lib/mobiledoc/cards/html.js index aec08b2571c..31a93863579 100644 --- a/core/server/lib/mobiledoc/cards/html.js +++ b/core/server/lib/mobiledoc/cards/html.js @@ -2,20 +2,8 @@ module.exports = { name: 'html', type: 'dom', render(opts) { - let payload = opts.payload; - let dom = opts.env.dom; - let caption = ''; - - if (payload.caption) { - caption = `

${payload.caption}

`; - } - - let html = `
${payload.html}${caption}
`; - // use the SimpleDOM document to create a raw HTML section. // avoids parsing/rendering of potentially broken or unsupported HTML - let element = dom.createRawHTMLSection(html); - - return element; + return opts.env.dom.createRawHTMLSection(opts.payload.html); } }; diff --git a/core/server/lib/mobiledoc/cards/image.js b/core/server/lib/mobiledoc/cards/image.js index 2ed12d29587..18e17972795 100644 --- a/core/server/lib/mobiledoc/cards/image.js +++ b/core/server/lib/mobiledoc/cards/image.js @@ -3,12 +3,20 @@ module.exports = { type: 'dom', render(opts) { let payload = opts.payload; + // let version = opts.options.version; let dom = opts.env.dom; + let figure = dom.createElement('figure'); + figure.setAttribute('class', 'kg-image-card'); let img = dom.createElement('img'); - img.className = 'kg-card-image'; + let imgClass = 'kg-image'; + if (payload.imageStyle) { + imgClass = `${imgClass} kg-image--${payload.imageStyle}`; + } img.setAttribute('src', payload.src); + img.setAttribute('class', imgClass); + figure.appendChild(img); if (payload.caption) { diff --git a/core/server/lib/mobiledoc/cards/index.js b/core/server/lib/mobiledoc/cards/index.js index 045726d1cea..1cbca3a3ba3 100644 --- a/core/server/lib/mobiledoc/cards/index.js +++ b/core/server/lib/mobiledoc/cards/index.js @@ -1,7 +1,9 @@ -var hr = require('./hr'), - html = require('./html'), - image = require('./image'), - markdown = require('./markdown'), - cardMarkdown = require('./card-markdown'); +'use strict'; + +const hr = require('./hr'); +const html = require('./html'); +const image = require('./image'); +const markdown = require('./markdown'); +const cardMarkdown = require('./card-markdown'); module.exports = [hr, html, image, markdown, cardMarkdown]; diff --git a/core/server/lib/mobiledoc/cards/markdown.js b/core/server/lib/mobiledoc/cards/markdown.js index 459dc4c92e4..cc965a76d35 100644 --- a/core/server/lib/mobiledoc/cards/markdown.js +++ b/core/server/lib/mobiledoc/cards/markdown.js @@ -1,19 +1,22 @@ +'use strict'; + module.exports = { name: 'markdown', type: 'dom', render: function (opts) { - var converters = require('../converters'), - html, element; - + let converters = require('../converters'); + let payload = opts.payload; + let version = opts.options.version; // convert markdown to HTML ready for insertion into dom - html = '
' - + converters.markdownConverter.render(opts.payload.markdown || '') - + '
'; + let html = converters.markdownConverter.render(payload.markdown || ''); + + // Ghost 1.0's markdown-only renderer wrapped cards + if (version === 1) { + html = `
${html}
`; + } // use the SimpleDOM document to create a raw HTML section. // avoids parsing/rendering of potentially broken or unsupported HTML - element = opts.env.dom.createRawHTMLSection(html); - - return element; + return opts.env.dom.createRawHTMLSection(html); } }; diff --git a/core/server/lib/mobiledoc/converters/mobiledoc-converter.js b/core/server/lib/mobiledoc/converters/mobiledoc-converter.js index 50a36349dc4..7e0bdff98de 100644 --- a/core/server/lib/mobiledoc/converters/mobiledoc-converter.js +++ b/core/server/lib/mobiledoc/converters/mobiledoc-converter.js @@ -36,11 +36,38 @@ var SimpleDom = require('simple-dom'), // } module.exports = { - render: function (mobiledoc) { - var renderer = new Renderer(options), - rendered = renderer.render(mobiledoc), - serializer = new SimpleDom.HTMLSerializer(SimpleDom.voidMap), - html = serializer.serializeChildren(rendered.result); + // version 1 === Ghost 1.0 markdown-only mobiledoc + // version 2 === Ghost 2.0 full mobiledoc + render: function (mobiledoc, version) { + version = version || 1; + + // pass the version through to the card renderers. + // create a new object here to avoid modifying the default options + // object because the version can change per-render until 2.0 is released + let versionedOptions = Object.assign({}, options, { + cardOptions: {version} + }); + + let renderer = new Renderer(versionedOptions); + let rendered = renderer.render(mobiledoc); + let serializer = new SimpleDom.HTMLSerializer(SimpleDom.voidMap); + + // Koenig keeps a blank paragraph at the end of a doc but we want to + // make sure it doesn't get rendered + let lastChild = rendered.result.lastChild; + if (lastChild && lastChild.tagName === 'P' && !lastChild.firstChild) { + rendered.result.removeChild(lastChild); + } + + let html = serializer.serializeChildren(rendered.result); + + // full version of Koenig wraps the content with a specific class to + // be targetted with our default stylesheet for vertical rhythm and + // card-specific styles + if (version === 2) { + html = `
\n${html}\n
`; + } + return html; } }; diff --git a/core/server/models/post.js b/core/server/models/post.js index acd0d50e893..8c7b84aa76e 100644 --- a/core/server/models/post.js +++ b/core/server/models/post.js @@ -9,6 +9,7 @@ var _ = require('lodash'), htmlToText = require('html-to-text'), ghostBookshelf = require('./base'), config = require('../config'), + labs = require('../services/labs'), converters = require('../lib/mobiledoc/converters'), urlService = require('../services/url'), relations = require('./relations'), @@ -185,7 +186,7 @@ Post = ghostBookshelf.Model.extend({ prevSlug = this.previous('slug'), publishedAt = this.get('published_at'), publishedAtHasChanged = this.hasDateChanged('published_at', {beforeWrite: true}), - mobiledoc = this.get('mobiledoc'), + mobiledoc = JSON.parse(this.get('mobiledoc') || null), generatedFields = ['html', 'plaintext'], tagsToSave, ops = []; @@ -249,8 +250,37 @@ Post = ghostBookshelf.Model.extend({ } }); + // render mobiledoc to HTML. Switch render version if Koenig is enabled + // or has been edited with Koenig and is no longer compatible with the + // Ghost 1.0 markdown-only renderer + // TODO: re-render all content and remove the version toggle for Ghost 2.0 if (mobiledoc) { - this.set('html', converters.mobiledocConverter.render(JSON.parse(mobiledoc))); + let version = 1; + let devExperimentsEnabled = config.get('enableDeveloperExperiments'); + let koenigEnabled = labs.isSet('koenigEditor') === true; + + let mobiledocIsCompatibleWithV1 = function mobiledocIsCompatibleWithV1(doc) { + if (doc + && doc.markups.length === 0 + && doc.cards.length === 1 + && doc.cards[0][0].match(/(?:card-)?markdown/) + && doc.sections.length === 1 + && doc.sections[0].length === 2 + && doc.sections[0][0] === 10 + && doc.sections[0][1] === 0 + ) { + return true; + } + + return false; + }; + + if ((devExperimentsEnabled && koenigEnabled) || !mobiledocIsCompatibleWithV1(mobiledoc)) { + version = 2; + } + + let html = converters.mobiledocConverter.render(mobiledoc, version); + this.set('html', html); } if (this.hasChanged('html') || !this.get('plaintext')) { diff --git a/core/test/unit/lib/mobiledoc/atoms/soft-return_spec.js b/core/test/unit/lib/mobiledoc/atoms/soft-return_spec.js index ff89b2d1ab0..055adb8b0e4 100644 --- a/core/test/unit/lib/mobiledoc/atoms/soft-return_spec.js +++ b/core/test/unit/lib/mobiledoc/atoms/soft-return_spec.js @@ -1,17 +1,18 @@ -var should = require('should'), // jshint ignore:line - card = require('../../../../../server/lib/mobiledoc/atoms/soft-return'), - SimpleDom = require('simple-dom'), - opts; +'use strict'; -describe('Soft return card', function () { +const should = require('should'); // jshint ignore:line +const atom = require('../../../../../server/lib/mobiledoc/atoms/soft-return'); +const SimpleDom = require('simple-dom'); +const serializer = new SimpleDom.HTMLSerializer(SimpleDom.voidMap); + +describe('Soft return atom', function () { it('generates a `br` tag', function () { - opts = { + let opts = { env: { dom: new SimpleDom.Document() } }; - var serializer = new SimpleDom.HTMLSerializer([]); - serializer.serialize(card.render(opts)).should.match('

'); + serializer.serialize(atom.render(opts)).should.match('
'); }); }); diff --git a/core/test/unit/lib/mobiledoc/cards/hr_spec.js b/core/test/unit/lib/mobiledoc/cards/hr_spec.js index f52a9e696bd..6814b35d823 100644 --- a/core/test/unit/lib/mobiledoc/cards/hr_spec.js +++ b/core/test/unit/lib/mobiledoc/cards/hr_spec.js @@ -1,17 +1,18 @@ -var should = require('should'), // jshint ignore:line - card = require('../../../../../server/lib/mobiledoc/cards/hr'), - SimpleDom = require('simple-dom'), - opts; +'use strict'; + +const should = require('should'); // jshint ignore:line +const card = require('../../../../../server/lib/mobiledoc/cards/hr'); +const SimpleDom = require('simple-dom'); +const serializer = new SimpleDom.HTMLSerializer(SimpleDom.voidMap); describe('HR card', function () { it('generates a horizontal rule', function () { - opts = { + let opts = { env: { dom: new SimpleDom.Document() } }; - var serializer = new SimpleDom.HTMLSerializer([]); - serializer.serialize(card.render(opts)).should.match('
'); + serializer.serialize(card.render(opts)).should.match('
'); }); }); diff --git a/core/test/unit/lib/mobiledoc/cards/html_spec.js b/core/test/unit/lib/mobiledoc/cards/html_spec.js index fc69be6245e..39c130f8209 100644 --- a/core/test/unit/lib/mobiledoc/cards/html_spec.js +++ b/core/test/unit/lib/mobiledoc/cards/html_spec.js @@ -1,11 +1,13 @@ -var should = require('should'), // jshint ignore:line - card = require('../../../../../server/lib/mobiledoc/cards/html'), - SimpleDom = require('simple-dom'), - opts; +'use strict'; + +const should = require('should'); // jshint ignore:line +const card = require('../../../../../server/lib/mobiledoc/cards/html'); +const SimpleDom = require('simple-dom'); +const serializer = new SimpleDom.HTMLSerializer(SimpleDom.voidMap); describe('HTML card', function () { it('HTML Card renders', function () { - opts = { + let opts = { env: { dom: new SimpleDom.Document() }, @@ -14,12 +16,11 @@ describe('HTML card', function () { } }; - var serializer = new SimpleDom.HTMLSerializer([]); - serializer.serialize(card.render(opts)).should.match('

HEADING

PARAGRAPH

'); + serializer.serialize(card.render(opts)).should.match('

HEADING

PARAGRAPH

'); }); it('Plain content renders', function () { - opts = { + let opts = { env: { dom: new SimpleDom.Document() }, @@ -28,12 +29,11 @@ describe('HTML card', function () { } }; - var serializer = new SimpleDom.HTMLSerializer([]); - serializer.serialize(card.render(opts)).should.match('
CONTENT
'); + serializer.serialize(card.render(opts)).should.match('CONTENT'); }); it('Invalid HTML returns', function () { - opts = { + let opts = { env: { dom: new SimpleDom.Document() }, @@ -42,22 +42,6 @@ describe('HTML card', function () { } }; - var serializer = new SimpleDom.HTMLSerializer([]); - serializer.serialize(card.render(opts)).should.match('

HEADING<

'); - }); - - it('Caption renders', function () { - opts = { - env: { - dom: new SimpleDom.Document() - }, - payload: { - html: '', - caption: 'Embed caption test' - } - }; - - var serializer = new SimpleDom.HTMLSerializer([]); - serializer.serialize(card.render(opts)).should.match('

Embed caption test

'); + serializer.serialize(card.render(opts)).should.match('

HEADING<'); }); }); diff --git a/core/test/unit/lib/mobiledoc/cards/image_spec.js b/core/test/unit/lib/mobiledoc/cards/image_spec.js index c3b5fc9031e..6b5e18b1caa 100644 --- a/core/test/unit/lib/mobiledoc/cards/image_spec.js +++ b/core/test/unit/lib/mobiledoc/cards/image_spec.js @@ -1,11 +1,13 @@ -var should = require('should'), // jshint ignore:line - card = require('../../../../../server/lib/mobiledoc/cards/image'), - SimpleDom = require('simple-dom'), - opts; +'use strict'; + +const should = require('should'); // jshint ignore:line +const card = require('../../../../../server/lib/mobiledoc/cards/image'); +const SimpleDom = require('simple-dom'); +const serializer = new SimpleDom.HTMLSerializer(SimpleDom.voidMap); describe('Image card', function () { it('generates an image', function () { - opts = { + let opts = { env: { dom: new SimpleDom.Document() }, @@ -14,12 +16,11 @@ describe('Image card', function () { } }; - var serializer = new SimpleDom.HTMLSerializer([]); - serializer.serialize(card.render(opts)).should.match('
'); + serializer.serialize(card.render(opts)).should.eql('
'); }); it('generates an image with caption', function () { - opts = { + let opts = { env: { dom: new SimpleDom.Document() }, @@ -29,7 +30,50 @@ describe('Image card', function () { } }; - var serializer = new SimpleDom.HTMLSerializer([]); - serializer.serialize(card.render(opts)).should.match('
Test caption
'); + serializer.serialize(card.render(opts)).should.eql('
Test caption
'); + }); + + describe('sizes', function () { + it('standard', function () { + let opts = { + env: { + dom: new SimpleDom.Document() + }, + payload: { + src: 'https://www.ghost.org/image.png', + imageStyle: '' + } + }; + + serializer.serialize(card.render(opts)).should.eql('
'); + }); + + it('wide', function () { + let opts = { + env: { + dom: new SimpleDom.Document() + }, + payload: { + src: 'https://www.ghost.org/image.png', + imageStyle: 'wide' + } + }; + + serializer.serialize(card.render(opts)).should.eql('
'); + }); + + it('full', function () { + let opts = { + env: { + dom: new SimpleDom.Document() + }, + payload: { + src: 'https://www.ghost.org/image.png', + imageStyle: 'full' + } + }; + + serializer.serialize(card.render(opts)).should.eql('
'); + }); }); }); diff --git a/core/test/unit/lib/mobiledoc/cards/markdown_spec.js b/core/test/unit/lib/mobiledoc/cards/markdown_spec.js index 64e025f8773..00cdeafe62d 100644 --- a/core/test/unit/lib/mobiledoc/cards/markdown_spec.js +++ b/core/test/unit/lib/mobiledoc/cards/markdown_spec.js @@ -1,34 +1,76 @@ -var should = require('should'), // jshint ignore:line - card = require('../../../../../server/lib/mobiledoc/cards/markdown'), - SimpleDom = require('simple-dom'), - opts; +'use strict'; + +const should = require('should'); // jshint ignore:line +const card = require('../../../../../server/lib/mobiledoc/cards/markdown'); +const SimpleDom = require('simple-dom'); +const serializer = new SimpleDom.HTMLSerializer(SimpleDom.voidMap); describe('Markdown card', function () { - it('Markdown Card renders', function () { - opts = { - env: { - dom: new SimpleDom.Document() - }, - payload: { - markdown: '#HEADING\r\n- list\r\n- items' - } - }; - - var serializer = new SimpleDom.HTMLSerializer([]); - serializer.serialize(card.render(opts)).should.match('

HEADING

\n
    \n
  • list
  • \n
  • items
  • \n
\n
'); + describe('version 1', function () { + it('Markdown Card renders', function () { + let opts = { + env: { + dom: new SimpleDom.Document() + }, + payload: { + markdown: '#HEADING\r\n- list\r\n- items' + }, + options: { + version: 1 + } + }; + + serializer.serialize(card.render(opts)).should.match('

HEADING

\n
    \n
  • list
  • \n
  • items
  • \n
\n
'); + }); + + it('Accepts invalid HTML in markdown', function () { + let opts = { + env: { + dom: new SimpleDom.Document() + }, + payload: { + markdown: '#HEADING\r\n

Heading 2>' + }, + options: { + version: 1 + } + }; + + serializer.serialize(card.render(opts)).should.match('

HEADING

\n

Heading 2>

'); + }); }); - it('Accepts invalid HTML in markdown', function () { - opts = { - env: { - dom: new SimpleDom.Document() - }, - payload: { - markdown: '#HEADING\r\n

Heading 2>' - } - }; - - var serializer = new SimpleDom.HTMLSerializer([]); - serializer.serialize(card.render(opts)).should.match('

HEADING

\n

Heading 2>

'); + describe('version 2', function () { + it('Markdown Card renders', function () { + let opts = { + env: { + dom: new SimpleDom.Document() + }, + payload: { + markdown: '#HEADING\r\n- list\r\n- items' + }, + options: { + version: 2 + } + }; + + serializer.serialize(card.render(opts)).should.match('

HEADING

\n\n'); + }); + + it('Accepts invalid HTML in markdown', function () { + let opts = { + env: { + dom: new SimpleDom.Document() + }, + payload: { + markdown: '#HEADING\r\n

Heading 2>' + }, + options: { + version: 2 + } + }; + + serializer.serialize(card.render(opts)).should.match('

HEADING

\n

Heading 2>'); + }); }); }); diff --git a/core/test/unit/lib/mobiledoc/converters/mobiledoc-converter_spec.js b/core/test/unit/lib/mobiledoc/converters/mobiledoc-converter_spec.js index bf16c85da56..1278ffd5c3d 100644 --- a/core/test/unit/lib/mobiledoc/converters/mobiledoc-converter_spec.js +++ b/core/test/unit/lib/mobiledoc/converters/mobiledoc-converter_spec.js @@ -1,34 +1,111 @@ -var should = require('should'), // jshint ignore:line - converter = require('../../../../../server/lib/mobiledoc/converters/mobiledoc-converter'); - -describe('Convert mobiledoc to HTML ', function () { - var mobiledoc = { - version: '0.3.1', - atoms: [], - cards: [ - ['markdown', - { - pos: 'top', - card_name: 'markdown', - markdown: '#heading\n\n- list one\n- list two\n- list three' - } - ], - ['html', { - pos: 'top', - card_name: 'html', - html: '

HTML CARD

' - }] - ], - markups: [], - sections: [ - [1, 'p', [ - [0, [], 0, 'test'] - ]], - [10, 0], - [10, 1] - ] - }; - it('Converts a mobiledoc to HTML', function () { - converter.render(mobiledoc).should.match('

test

heading

\n
    \n
  • list one
  • \n
  • list two
  • \n
  • list three
  • \n
\n

HTML CARD

'); +'use strict'; + +const should = require('should'); // jshint ignore:line +const converter = require('../../../../../server/lib/mobiledoc/converters/mobiledoc-converter'); + +describe('Mobiledoc converter', function () { + // version 1 === Ghost 1.0 markdown-only renderer + describe('version 1', function () { + it('renders correctly', function () { + let mobiledoc = { + version: '0.3.1', + atoms: [], + cards: [ + ['markdown', + { + pos: 'top', + card_name: 'markdown', + markdown: '#heading\n\n- list one\n- list two\n- list three' + } + ] + ], + markups: [], + sections: [ + [10, 0] + ] + }; + + converter.render(mobiledoc).should.eql('

heading

\n
    \n
  • list one
  • \n
  • list two
  • \n
  • list three
  • \n
\n
'); + }); + }); + + // version 2 === Ghost 2.0 full Koenig renderer + describe('version 2', function () { + it('renders all default cards and atoms', function () { + let mobiledoc = { + version: '0.3.1', + atoms: [ + ['soft-return', '', {}] + ], + cards: [ + ['markdown', { + markdown: '# Markdown card\nSome markdown' + }], + ['hr', {}], + ['image', { + imageStyle: 'wide', + src: '/content/images/2018/04/NatGeo06.jpg', + caption: 'Birdies' + }], + ['html', { + html: '

HTML card

\n

Some HTML

' + }] + ], + markups: [], + sections: [ + [1, 'p', [ + [0, [], 0, 'One'], + [1, [], 0, 0], + [0, [], 0, 'Two'] + ]], + [10, 0], + [1, 'p', [ + [0, [], 0, 'Three'] + ]], + [10, 1], + [10, 2], + [1, 'p', [ + [0, [], 0, 'Four'] + ]], + [10, 3], + [1, 'p', []] + ] + }; + + converter.render(mobiledoc, 2).should.eql('
\n

One
Two

Markdown card

\n

Some markdown

\n

Three


Birdies

Four

HTML card

\n

Some HTML

\n
'); + }); + + it('wraps output with a .kg-post div', function () { + let mobiledoc = { + version: '0.3.1', + atoms: [], + cards: [], + markups: [], + sections: [ + [1, 'p', [ + [0, [], 0, 'Test'] + ]] + ] + }; + + converter.render(mobiledoc, 2).should.eql('
\n

Test

\n
'); + }); + + it('removes final blank paragraph', function () { + let mobiledoc = { + version: '0.3.1', + atoms: [], + cards: [], + markups: [], + sections: [ + [1, 'p', [ + [0, [], 0, 'Test'] + ]], + [1, 'p', []] + ] + }; + + converter.render(mobiledoc, 2).should.eql('
\n

Test

\n
'); + }); }); }); diff --git a/core/test/unit/models/post_spec.js b/core/test/unit/models/post_spec.js index 0b2cf578cf0..3a39af21d31 100644 --- a/core/test/unit/models/post_spec.js +++ b/core/test/unit/models/post_spec.js @@ -1889,4 +1889,69 @@ describe('Unit: models/post', function () { }); }); }); + + describe('Mobiledoc conversion', function () { + let configUtils = require('../../utils/configUtils'); + let labs = require('../../../server/services/labs'); + let origLabs = _.cloneDeep(labs); + let events; + + beforeEach(function () { + events = { + post: [] + }; + + sandbox.stub(models.Post.prototype, 'emitChange').callsFake(function (event) { + events.post.push({event: event, data: this.toJSON()}); + }); + }); + + afterEach(configUtils.restore); + + it('uses v2 if Koenig is enabled', function () { + configUtils.set('enableDeveloperExperiments', true); + sandbox.stub(labs, 'isSet').callsFake(function (key) { + if (key === 'koenigEditor') { + return true; + } + return origLabs.get(key); + }); + + let newPost = testUtils.DataGenerator.forModel.posts[2]; + + return models.Post.add( + newPost, + testUtils.context.editor + ).then((post) => { + should.exist(post); + post.has('html').should.equal(true); + post.get('html').should.equal('
\n

testing

\n

mctesters

\n\n\n
'); + }); + }); + + it('uses v2 if Koenig is disabled but post is not v1 compatible', function () { + let newPost = testUtils.DataGenerator.forModel.posts[2]; + + newPost.mobiledoc = JSON.stringify({ + version: '0.3.1', + atoms: [], + cards: [], + markups: [], + sections: [ + [1, 'p', [ + [0, [], 0, 'Test'] + ]] + ] + }); + + return models.Post.add( + newPost, + testUtils.context.editor + ).then((post) => { + should.exist(post); + post.has('html').should.equal(true); + post.get('html').should.equal('
\n

Test

\n
'); + }); + }); + }); });