UNPKG

paradigm-core

Version:
878 lines (736 loc) 30.8 kB
const moment = require('moment') const { Migrations, OrganizationsPlugin, ApplicationsPlugin, UsersPlugin, DigitalAssetsPlugin, TestHelpers: {MockHTTPServer}, } = require('structure-core') const pluginsList = require('../../helpers/plugins') const DocTestAPI = require('../../helpers/test-api-documents') const DocumentModel = require('../../../models/document') const server = new MockHTTPServer(pluginsList) const orgTestApi = new OrganizationsPlugin.TestAPI(server) const appTestApi = new ApplicationsPlugin.TestAPI(server) const userTestApi = new UsersPlugin.TestAPI(server) const digitalAssetTestApi = new DigitalAssetsPlugin.TestAPI(server) const docTestApi = new DocTestAPI(server) function formatDateString(dateString) { return new Date(dateString).toLocaleDateString( 'en-us', {year: 'numeric', month: 'long', day: 'numeric'} ) } function formatDate(date) { return moment(date).utc().format('YYYY-MM-DDTHH:mm:ss[Z]') } /** @test {DocumentModel} */ describe('DocumentModel', function() { before(function() { this.timeout(10000) this.migration = new Migrations({ db: 'test', items: {tables: [{ action: 'create', table: 'digital_assets', indexes: [ 'applicationId', 'updatedAt' ] }]}, plugins: pluginsList }) return this.migration.process() }) beforeEach(async function() { const orgRes = await orgTestApi.create({ title: 'Forbidden Children' }) this.orgId = orgRes.body.pkg.id const appRes = await appTestApi.create(this.orgId, { desc: '', title: 'App 45', host: 'www.app45.com' }) this.appId = appRes.body.pkg.id const userRes = await userTestApi.create(this.orgId, this.appId, { organizationId: this.orgId, username: 'jsmith', firstName: 'John', lastName: 'Smith', facebook: 'www.facebook.com/john.smith', email: 'jsmith@example.com', password: 'c4ntf1ndmyc4v3br0', timezone: 'Europe/Madrid' }) this.user = userRes.body.pkg }) afterEach(function() { return this.migration.purge() }) describe('basic article', function() { beforeEach(async function() { const docRes = await docTestApi.create(this.orgId, this.appId, { title: 'Basic Article', slug: 'basic-article', fields: [ { type: 'text', props: { value: '<p>Text 1</p>' } }, { type: 'text', props: { value: '<p>Text 2</p>' } } ], categoryIds: [], status: 'published', organizationId: this.orgId, userId: this.user.id }) this.doc = docRes.body.pkg }) /** @test {DocumentModel#basicArticle} */ it('should get apple news format for a basic article', async function() { this.timeout(30000) const documentModel = new DocumentModel({ organizationId: this.orgId, applicationId: this.appId, logger: this.logger }) const res = await documentModel.getAppleNewsFormat(this.doc.id) expect(res.article.version).to.equal('1.0') expect(res.article.title).to.equal(this.doc.title) expect(res.article.identifier).to.equal(this.doc.slug) expect(res.article.language).to.equal('en') expect(res.article.metadata.authors).to.deep.equal([`${this.user.firstName} ${this.user.lastName}`]) expect(res.article.metadata.dateModified).to.equal(formatDate(this.doc.updatedAt)) expect(res.article.metadata.datePublished).to.equal(formatDate(this.doc.publishedAt)) expect(res.article.metadata.dateCreated).to.equal(formatDate(this.doc.publishedAt)) expect(res.article.components).to.deep.equal([ { role: 'header', layout: 'headerLayout', style: 'headerStyle', components: [ { role: 'title', layout: 'titleLayout', text: this.doc.title }, { text: `By ${this.user.firstName} ${this.user.lastName} ${formatDateString(this.doc.publishedAt)}`, additions: [], inlineTextStyles: [], role: 'byline', layout: 'bylineLayout' } ] }, { text: 'Text 1\n', additions: [], inlineTextStyles: [], role: 'body', layout: 'bodyLayout' }, { text: 'Text 2\n', additions: [], inlineTextStyles: [], role: 'body', layout: 'bodyLayout' } ]) }) /** @test {DocumentModel#basicArticle} */ it('should get facebook instant article format for a basic article', async function() { this.timeout(30000) const documentModel = new DocumentModel({ organizationId: this.orgId, applicationId: this.appId, logger: this.logger }) const res = await documentModel.getFacebookIAFormat(this.doc.id) expect(res.canonicalUrl).to.equal(`http://www.app45.com${this.doc.permalink}`) expect(res.markup).to.equal(`<!DOCTYPE html><html lang="en" prefix="op: http://media.facebook.com/op#"><head><meta charset="utf-8"/><meta property="op:markup_version" content="v1.0"><meta property="fb:use_automatic_ad_placement" content="enable=true ad_density=default"><title>${this.doc.title}</title><link rel="canonical" href="http://www.app45.com${this.doc.permalink}"></head><body><article><header><h1>${this.doc.title}</h1><time class="op-published" datetime="${this.doc.publishedAt}">${formatDateString(this.doc.publishedAt)}</time><time class="op-modified" datetime="${this.doc.updatedAt}">${formatDateString(this.doc.updatedAt)}</time><address><a rel="facebook" href="${this.user.facebook}">${this.user.firstName} ${this.user.lastName}</a></address></header><p>Text 1</p><p>Text 2</p></article></body></html>`) }) }) describe('article with markup', function() { beforeEach(async function() { const docRes = await docTestApi.create(this.orgId, this.appId, { title: 'Article With Markup', slug: 'article-with-markup', fields: [ { type: 'text', props: { value: '<p>Plain & <em>Italic</em> <strong>Bold</strong> <a href="https://www.example.com">Link</a></p>' } }, ], categoryIds: [], status: 'published', organizationId: this.orgId, userId: this.user.id }) this.doc = docRes.body.pkg }) /** @test {DocumentModel#create} */ it('should get apple news format for article with markup', async function() { this.timeout(30000) const documentModel = new DocumentModel({ organizationId: this.orgId, applicationId: this.appId, logger: this.logger }) const res = await documentModel.getAppleNewsFormat(this.doc.id) expect(res.article.components).to.deep.equal([ { role: 'header', layout: 'headerLayout', style: 'headerStyle', components: [ { role: 'title', layout: 'titleLayout', text: this.doc.title }, { text: `By ${this.user.firstName} ${this.user.lastName} ${formatDateString(this.doc.publishedAt)}`, additions: [], inlineTextStyles: [], role: 'byline', layout: 'bylineLayout' } ] }, { text: 'Plain & Italic Bold Link\n', additions: [{ type: 'link', rangeStart: 20, rangeLength: 4, URL: 'https://www.example.com' }], inlineTextStyles: [ {rangeStart: 8, rangeLength: 6, textStyle: 'bodyItalicStyle'}, {rangeStart: 15, rangeLength: 4, textStyle: 'bodyBoldStyle'}, {rangeStart: 20, rangeLength: 4, textStyle: 'bodyLinkTextStyle'} ], role: 'body', layout: 'bodyLayout' } ]) }) /** @test {DocumentModel#basicArticle} */ it('should get facebook instant article format for article with markup', async function() { this.timeout(30000) const documentModel = new DocumentModel({ organizationId: this.orgId, applicationId: this.appId, logger: this.logger }) const res = await documentModel.getFacebookIAFormat(this.doc.id) expect(res.canonicalUrl).to.equal(`http://www.app45.com${this.doc.permalink}`) expect(res.markup).to.equal(`<!DOCTYPE html><html lang="en" prefix="op: http://media.facebook.com/op#"><head><meta charset="utf-8"/><meta property="op:markup_version" content="v1.0"><meta property="fb:use_automatic_ad_placement" content="enable=true ad_density=default"><title>${this.doc.title}</title><link rel="canonical" href="http://www.app45.com${this.doc.permalink}"></head><body><article><header><h1>${this.doc.title}</h1><time class="op-published" datetime="${this.doc.publishedAt}">${formatDateString(this.doc.publishedAt)}</time><time class="op-modified" datetime="${this.doc.updatedAt}">${formatDateString(this.doc.updatedAt)}</time><address><a rel="facebook" href="${this.user.facebook}">${this.user.firstName} ${this.user.lastName}</a></address></header><p>Plain &amp; <i>Italic</i> <b>Bold</b> <a href="https://www.example.com">Link</a></p></article></body></html>`) }) }) describe('article with excerpt', function() { beforeEach(async function() { const docRes = await docTestApi.create(this.orgId, this.appId, { title: 'Article With Excerpt', slug: 'article-with-excerpt', excerpt: '<p>Excerpt</p>', fields: [ { type: 'text', props: { value: '<p>Text 1</p>' } } ], categoryIds: [], status: 'published', organizationId: this.orgId, userId: this.user.id }) this.doc = docRes.body.pkg }) /** @test {DocumentModel#create} */ it('should get apple news format for an article with excerpt', async function() { this.timeout(30000) const documentModel = new DocumentModel({ organizationId: this.orgId, applicationId: this.appId, logger: this.logger }) const res = await documentModel.getAppleNewsFormat(this.doc.id) expect(res.article.metadata.excerpt).to.equal('Excerpt') expect(res.article.components).to.deep.equal([ { role: 'header', layout: 'headerLayout', style: 'headerStyle', components: [ { role: 'title', layout: 'titleLayout', text: this.doc.title }, { text: `By ${this.user.firstName} ${this.user.lastName} ${formatDateString(this.doc.publishedAt)}`, additions: [], inlineTextStyles: [], role: 'byline', layout: 'bylineLayout' } ] }, { text: 'Excerpt\n', additions: [], inlineTextStyles: [], role: 'body', layout: 'bodyLayout' }, { text: 'Text 1\n', additions: [], inlineTextStyles: [], role: 'body', layout: 'bodyLayout' }, ]) }) }) describe('article with featured image', function() { beforeEach(async function() { const daRes = await digitalAssetTestApi.getByUrl( this.orgId, this.appId, 'https://upload.wikimedia.org/wikipedia/commons/thumb/0/0b/Cat_poster_1.jpg/1280px-Cat_poster_1.jpg' ) this.da = daRes.body.pkg const docRes = await docTestApi.create(this.orgId, this.appId, { title: 'Article With Featured Image', slug: 'article-with-featured-image', featuredImageId: this.da.id, fields: [ { type: 'text', props: { value: '<p>Text 1</p>' } }, { type: 'text', props: { value: '<p>Text 2</p>' } } ], categoryIds: [], status: 'published', organizationId: this.orgId, userId: this.user.id }) this.doc = docRes.body.pkg }) /** @test {DocumentModel#create} */ it('should get apple news format for article with thumbnail image', async function() { this.timeout(30000) const documentModel = new DocumentModel({ organizationId: this.orgId, applicationId: this.appId, logger: this.logger }) const res = await documentModel.getAppleNewsFormat(this.doc.id) expect(res.article.metadata.thumbnailURL).to.equal('bundle://image-0.jpg') expect(res.bundlesToUrls['image-0.jpg']).to.equal(`http:${this.da.url}`) }) /** @test {DocumentModel#basicArticle} */ it('should get facebook instant article format for article with featured image', async function() { this.timeout(30000) const documentModel = new DocumentModel({ organizationId: this.orgId, applicationId: this.appId, logger: this.logger }) const res = await documentModel.getFacebookIAFormat(this.doc.id) expect(res.canonicalUrl).to.equal(`http://www.app45.com${this.doc.permalink}`) expect(res.markup).to.equal(`<!DOCTYPE html><html lang="en" prefix="op: http://media.facebook.com/op#"><head><meta charset="utf-8"/><meta property="op:markup_version" content="v1.0"><meta property="fb:use_automatic_ad_placement" content="enable=true ad_density=default"><title>${this.doc.title}</title><link rel="canonical" href="http://www.app45.com${this.doc.permalink}"></head><body><article><header><h1>${this.doc.title}</h1><time class="op-published" datetime="${this.doc.publishedAt}">${formatDateString(this.doc.publishedAt)}</time><time class="op-modified" datetime="${this.doc.updatedAt}">${formatDateString(this.doc.updatedAt)}</time><address><a rel="facebook" href="${this.user.facebook}">${this.user.firstName} ${this.user.lastName}</a></address><figure><img src="http://upload.wikimedia.org/wikipedia/commons/thumb/0/0b/Cat_poster_1.jpg/1280px-Cat_poster_1.jpg"><figcaption>undefined</figcaption></figure></header><p>Text 1</p><p>Text 2</p></article></body></html>`) }) }) describe('article with image embed', function() { beforeEach(async function() { const daRes = await digitalAssetTestApi.getByUrl( this.orgId, this.appId, 'https://upload.wikimedia.org/wikipedia/commons/thumb/0/0b/Cat_poster_1.jpg/1280px-Cat_poster_1.jpg' ) this.da = daRes.body.pkg const docRes = await docTestApi.create(this.orgId, this.appId, { title: 'Article With Image Embed', slug: 'article-with-image-embed', fields: [ { type: 'text', props: { value: '<p>Text 1</p>' } }, { type: 'embed', props: { digitalAssetId: this.da.id } }, ], categoryIds: [], status: 'published', organizationId: this.orgId, userId: this.user.id }) this.doc = docRes.body.pkg }) /** @test {DocumentModel#create} */ it('should get apple news format for article with image embed', async function() { this.timeout(30000) const documentModel = new DocumentModel({ organizationId: this.orgId, applicationId: this.appId, logger: this.logger }) const res = await documentModel.getAppleNewsFormat(this.doc.id) expect(res.article.components).to.deep.equal([ { role: 'header', layout: 'headerLayout', style: 'headerStyle', components: [ { role: 'title', layout: 'titleLayout', text: this.doc.title }, { text: `By ${this.user.firstName} ${this.user.lastName} ${formatDateString(this.doc.publishedAt)}`, additions: [], inlineTextStyles: [], role: 'byline', layout: 'bylineLayout' } ] }, { text: 'Text 1\n', additions: [], inlineTextStyles: [], role: 'body', layout: 'bodyLayout' }, { role: 'container', components: [{ role: 'photo', URL: 'bundle://image-0.jpg', style: 'embedMediaStyle', layout: 'embedMediaLayout' }], layout: 'embedLayout', style: 'embedStyle' } ]) expect(res.bundlesToUrls['image-0.jpg']).to.equal(`http:${this.da.url}`) }) /** @test {DocumentModel#basicArticle} */ it('should get facebook instant article format for article with image embed', async function() { this.timeout(30000) const documentModel = new DocumentModel({ organizationId: this.orgId, applicationId: this.appId, logger: this.logger }) const res = await documentModel.getFacebookIAFormat(this.doc.id) expect(res.canonicalUrl).to.equal(`http://www.app45.com${this.doc.permalink}`) expect(res.markup).to.equal(`<!DOCTYPE html><html lang="en" prefix="op: http://media.facebook.com/op#"><head><meta charset="utf-8"/><meta property="op:markup_version" content="v1.0"><meta property="fb:use_automatic_ad_placement" content="enable=true ad_density=default"><title>${this.doc.title}</title><link rel="canonical" href="http://www.app45.com${this.doc.permalink}"></head><body><article><header><h1>${this.doc.title}</h1><time class="op-published" datetime="${this.doc.publishedAt}">${formatDateString(this.doc.publishedAt)}</time><time class="op-modified" datetime="${this.doc.updatedAt}">${formatDateString(this.doc.updatedAt)}</time><address><a rel="facebook" href="${this.user.facebook}">${this.user.firstName} ${this.user.lastName}</a></address></header><p>Text 1</p><figure data-feedback="fb:likes,fb:comments"><img src="http://upload.wikimedia.org/wikipedia/commons/thumb/0/0b/Cat_poster_1.jpg/1280px-Cat_poster_1.jpg"></figure></article></body></html>`) }) }) describe('article with twitter embed', function() { beforeEach(async function() { const daRes = await digitalAssetTestApi.getByUrl( this.orgId, this.appId, 'https://twitter.com/zdevin/status/960643083154132993' ) this.da = daRes.body.pkg const docRes = await docTestApi.create(this.orgId, this.appId, { title: 'Article With Twitter Embed', slug: 'article-with-twitter-embed', fields: [ { type: 'text', props: { value: '<p>Text 1</p>' } }, { type: 'embed', props: { digitalAssetId: this.da.id } }, ], categoryIds: [], status: 'published', organizationId: this.orgId, userId: this.user.id }) this.doc = docRes.body.pkg }) /** @test {DocumentModel#create} */ it('should get apple news format for article with twitter embed', async function() { this.timeout(30000) const documentModel = new DocumentModel({ organizationId: this.orgId, applicationId: this.appId, logger: this.logger }) const res = await documentModel.getAppleNewsFormat(this.doc.id) expect(res.article.components).to.deep.equal([ { role: 'header', layout: 'headerLayout', style: 'headerStyle', components: [ { role: 'title', layout: 'titleLayout', text: this.doc.title }, { text: `By ${this.user.firstName} ${this.user.lastName} ${formatDateString(this.doc.publishedAt)}`, additions: [], inlineTextStyles: [], role: 'byline', layout: 'bylineLayout' } ] }, { text: 'Text 1\n', additions: [], inlineTextStyles: [], role: 'body', layout: 'bodyLayout' }, { role: 'container', components: [{ role: 'tweet', URL: "https://twitter.com/zdevin/status/960643083154132993", style: 'embedMediaStyle', layout: 'embedMediaLayout' }], layout: 'embedLayout', style: 'embedStyle' } ]) }) /** @test {DocumentModel#basicArticle} */ it('should get facebook instant article format for article with twitter embed', async function() { this.timeout(30000) const documentModel = new DocumentModel({ organizationId: this.orgId, applicationId: this.appId, logger: this.logger }) const res = await documentModel.getFacebookIAFormat(this.doc.id) expect(res.canonicalUrl).to.equal(`http://www.app45.com${this.doc.permalink}`) expect(res.markup).to.equal(`<!DOCTYPE html><html lang="en" prefix="op: http://media.facebook.com/op#"><head><meta charset="utf-8"/><meta property="op:markup_version" content="v1.0"><meta property="fb:use_automatic_ad_placement" content="enable=true ad_density=default"><title>${this.doc.title}</title><link rel="canonical" href="http://www.app45.com${this.doc.permalink}"></head><body><article><header><h1>${this.doc.title}</h1><time class="op-published" datetime="${this.doc.publishedAt}">${formatDateString(this.doc.publishedAt)}</time><time class="op-modified" datetime="${this.doc.updatedAt}">${formatDateString(this.doc.updatedAt)}</time><address><a rel="facebook" href="${this.user.facebook}">${this.user.firstName} ${this.user.lastName}</a></address></header><p>Text 1</p><figure class="op-interactive"><iframe><blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">THANK YOU for the love <a href="https://twitter.com/taylorswift13?ref_src=twsrc%5Etfw">@taylorswift13</a> ! (Again...) 😳🖤 listen to “All On Me” &amp; “Asking For A Friend” on her ‘SONGS TAYLOR LOVES’ guest list playlist. <a href="https://t.co/0BW2Bdk5rJ">https://t.co/0BW2Bdk5rJ</a></p>&mdash; Devin Dawson (@zdevin) <a href="https://twitter.com/zdevin/status/960643083154132993?ref_src=twsrc%5Etfw">February 5, 2018</a></blockquote><script async="true" src="//platform.twitter.com/widgets.js" charset="utf-8"></script></iframe></figure></article></body></html>`) }) }) /** @test {DocumentModel#fbiaPath} */ it('should get facebook instant article format with custom fbiaPath', async function() { const appRes = await appTestApi.create(this.orgId, { desc: '', title: 'App Custom', host: 'www.appcustom.com', facebook: { fbiaPath: '/fbia' } }) const appId = appRes.body.pkg.id const docRes = await docTestApi.create(this.orgId, appId, { title: 'Article With Custom fbiaPath', slug: 'article-with-custom-fbiapath', fields: [ { type: 'text', props: { value: '<p>Text 1</p>' } }, ], categoryIds: [], status: 'published', organizationId: this.orgId, userId: this.user.id }) const doc = docRes.body.pkg const documentModel = new DocumentModel({ organizationId: this.orgId, applicationId: appId, logger: this.logger }) const res = await documentModel.getFacebookIAFormat(doc.id) expect(res.canonicalUrl).to.equal(`http://www.appcustom.com/fbia${doc.permalink}`) expect(res.markup).to.equal(`<!DOCTYPE html><html lang="en" prefix="op: http://media.facebook.com/op#"><head><meta charset="utf-8"/><meta property="op:markup_version" content="v1.0"><meta property="fb:use_automatic_ad_placement" content="enable=true ad_density=default"><title>${doc.title}</title><link rel="canonical" href="http://www.appcustom.com/fbia${doc.permalink}"></head><body><article><header><h1>${doc.title}</h1><time class="op-published" datetime="${doc.publishedAt}">${formatDateString(doc.publishedAt)}</time><time class="op-modified" datetime="${doc.updatedAt}">${formatDateString(doc.updatedAt)}</time><address><a rel="facebook" href="${this.user.facebook}">${this.user.firstName} ${this.user.lastName}</a></address></header><p>Text 1</p></article></body></html>`) }) describe('article with twitter embed with &s', function() { beforeEach(async function() { const daRes = await digitalAssetTestApi.getByUrl( this.orgId, this.appId, 'https://twitter.com/legendsoflucas/status/1001265626508288000' ) this.da = daRes.body.pkg const da2Res = await digitalAssetTestApi.getByUrl( this.orgId, this.appId, 'https://upload.wikimedia.org/wikipedia/commons/thumb/0/0b/Cat_poster_1.jpg/1280px-Cat_poster_1.jpg' ) this.da2 = da2Res.body.pkg await digitalAssetTestApi.updateById(this.orgId, this.appId, this.da2.id, { attribution: true, attributionName: 'Reeves & Mortimer' }) const docRes = await docTestApi.create(this.orgId, this.appId, { title: 'Article With Twitter Embed with & in user name', slug: 'article-with-twitter-embed-with-ampersand-in-user-name', featuredImageId: this.da2.id, fields: [ { type: 'text', props: { value: '<p>Text 1</p>' } }, { type: 'embed', props: { digitalAssetId: this.da.id } }, { props: { value: "<blockquote>is this a quote & thing</blockquote>" }, type: "text" } ], categoryIds: [], status: 'published', organizationId: this.orgId, userId: this.user.id }) this.doc = docRes.body.pkg }) /** @test {DocumentModel#create} */ it('should get apple news format for article with twitter embed with &s', async function() { this.timeout(30000) const documentModel = new DocumentModel({ organizationId: this.orgId, applicationId: this.appId, logger: this.logger }) const res = await documentModel.getAppleNewsFormat(this.doc.id) expect(res.article.components).to.deep.equal([ { role: 'header', layout: 'headerLayout', style: 'headerStyle', components: [ { components: [ { URL: "bundle://image-0.jpg", layout: "headerEmbedMediaLayout", role: "photo", style: "headerEmbedMediaStyle", } ], layout: "headerEmbedLayout", role: "container", style: "headerEmbedStyle", }, { role: 'title', layout: 'titleLayout', text: this.doc.title }, { text: `By ${this.user.firstName} ${this.user.lastName} ${formatDateString(this.doc.publishedAt)}`, additions: [], inlineTextStyles: [], role: 'byline', layout: 'bylineLayout' } ] }, { text: 'Text 1\n', additions: [], inlineTextStyles: [], role: 'body', layout: 'bodyLayout' }, { role: 'container', components: [{ role: 'tweet', URL: "https://twitter.com/legendsoflucas/status/1001265626508288000", style: 'embedMediaStyle', layout: 'embedMediaLayout' }], layout: 'embedLayout', style: 'embedStyle' }, { components: [ { additions: [], inlineTextStyles: [], layout: "quoteTextLayout", role: "quote", style: "quoteTextStyle", text: "is this a quote & thing", textStyle: "quoteTextStyle" } ], layout: "quoteLayout", role: "container", style: "quoteStyle" } ]) }) /** @test {DocumentModel#basicArticle} */ it('should get facebook instant article format for article with twitter embed with &s', async function() { this.timeout(30000) const documentModel = new DocumentModel({ organizationId: this.orgId, applicationId: this.appId, logger: this.logger }) const res = await documentModel.getFacebookIAFormat(this.doc.id) expect(res.canonicalUrl).to.equal(`http://www.app45.com${this.doc.permalink}`) expect(res.markup).to.equal(`<!DOCTYPE html><html lang="en" prefix="op: http://media.facebook.com/op#"><head><meta charset="utf-8"/><meta property="op:markup_version" content="v1.0"><meta property="fb:use_automatic_ad_placement" content="enable=true ad_density=default"><title>${this.doc.title}</title><link rel="canonical" href="http://www.app45.com${this.doc.permalink}"></head><body><article><header><h1>${this.doc.title}</h1><time class="op-published" datetime="${this.doc.publishedAt}">${formatDateString(this.doc.publishedAt)}</time><time class="op-modified" datetime="${this.doc.updatedAt}">${formatDateString(this.doc.updatedAt)}</time><address><a rel="facebook" href="${this.user.facebook}">${this.user.firstName} ${this.user.lastName}</a></address><figure><img src="http://upload.wikimedia.org/wikipedia/commons/thumb/0/0b/Cat_poster_1.jpg/1280px-Cat_poster_1.jpg"><figcaption>Reeves &amp; Mortimer</figcaption></figure></header><p>Text 1</p><figure class="op-interactive"><iframe><blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">Sorry for your loss but very happy for your grandma&#039;s hilarious sendoff</p>&mdash; Sloppy Galoshes (@legendsoflucas) <a href="https://twitter.com/legendsoflucas/status/1001265626508288000?ref_src=twsrc%5Etfw">May 29, 2018</a></blockquote><script async="true" src="//platform.twitter.com/widgets.js" charset="utf-8"></script></iframe></figure><blockquote><p>is this a quote &amp; thing</p></blockquote></article></body></html>`) }) }) })