UNPKG

vue-tweet-embed

Version:
492 lines (429 loc) 16.3 kB
import test from 'ava' import jsdom from 'jsdom' import decache from 'decache' import fs from 'fs' import { spy } from 'simple-spy' const { JSDOM } = jsdom const loadVueIntoWindow = (window) => { const vueFilePath = require.resolve('vue/dist/vue') window.eval(String(fs.readFileSync(vueFilePath))) return window.Vue } const createEnv = (scripts = []) => { return new Promise((resolve, reject) => { const dom = new JSDOM('<!doctype html><html><body></body></html>', { runScripts: "outside-only", // will not log browser events to console virtualConsole: new jsdom.VirtualConsole() }) const window = dom.window const document = window.document const Vue = loadVueIntoWindow(window) // require a new instance of Tweet every time to avoid side-effects const { Tweet, Moment, Timeline } = require('./dist') // window and document needs to be global // (required by vue's Ctor.$mount) global.window = window global.document = document resolve({ Vue, Tweet, Moment, Timeline, window, document }) }) } test.beforeEach(t => { return createEnv().then(data => { t.context = data }) }) test.afterEach(() => { // remove old cached every time to avoid side-effects decache('./dist') decache('./dist/tweet') decache('./dist/moment') decache('./dist/timeline') }) // CORE TESTS (injecting platform script) test('Tweet Should be available on module level as well as per-component level', t => { const { Tweet } = require('./dist') t.truthy(Tweet) t.truthy(Tweet.data) const TweetL = require('./dist/tweet').default t.truthy(TweetL) t.is(Tweet, TweetL) }) test('Moment Should be available on module level as well as per-component level', t => { const { Moment } = require('./dist') t.truthy(Moment) t.truthy(Moment.data) const MomentL = require('./dist/moment').default t.truthy(MomentL) t.is(Moment, MomentL) }) test('Timeline Should be available on module level as well as per-component level', t => { const { Timeline } = require('./dist') t.truthy(Timeline) t.truthy(Timeline.data) const TimelineL = require('./dist/timeline').default t.truthy(TimelineL) t.is(Timeline, TimelineL) }) test('Should inject twitter embed script if none is given', t => { const { Tweet, Vue, document } = t.context const Ctor = Vue.extend(Tweet) new Ctor().$mount() const $script = document.querySelector('script[src="//platform.twitter.com/widgets.js"]') t.true($script !== null) }) test('Should not inject more than one script par page', t => { const { Tweet, Vue, document } = t.context const TweetPage = { components: { Tweet }, template: '<div><Tweet v-for="i in [1, 2, 3]"/></div>' } const Ctor = Vue.extend(TweetPage) new Ctor().$mount() const $scripts = document.querySelectorAll('script[src="//platform.twitter.com/widgets.js"]') t.is($scripts.length, 1) }) // TWEET COMPONENT TEST test.cb('Should show a newly created Tweet element as tweet\'s immeditate child', t => { const { Tweet, Vue, window, document } = t.context const mockTwttr = { widgets: { createTweetEmbed: spy((tweetId, parent) => { const $mockTweet = document.createElement('div') $mockTweet.setAttribute('id', 'loadedTweet') $mockTweet.innerText = 'tweet text' parent.appendChild($mockTweet) return Promise.resolve($mockTweet) }) } } window.twttr = mockTwttr const Ctor = Vue.extend({ template: '<Tweet id="123" :options="{foo:\'bar\'}"></Tweet>', components: { Tweet } }) const vm = new Ctor().$mount() setTimeout(() => { // check that library was called with correct options t.is(mockTwttr.widgets.createTweetEmbed.callCount, 1) t.is(mockTwttr.widgets.createTweetEmbed.args[0].length, 3) t.is(mockTwttr.widgets.createTweetEmbed.args[0][0], '123') t.is(mockTwttr.widgets.createTweetEmbed.args[0][1], vm.$el) t.deepEqual(mockTwttr.widgets.createTweetEmbed.args[0][2], { foo: 'bar' }) // check that the element was indeed injected const $loadedTweet = vm.$el.querySelector('#loadedTweet') t.is($loadedTweet.id, 'loadedTweet') t.is($loadedTweet.innerText, 'tweet text') t.end() }, 0) }) test.cb('Should show an error message when tweet cannot be fetched', t => { const { Tweet, Vue, window } = t.context const mockTwttr = { widgets: { createTweetEmbed: (tweetId, parent) => { const $mockTweet = undefined // tweet not found return Promise.resolve($mockTweet) } } } window.twttr = mockTwttr const Ctor = Vue.extend({ template: '<Tweet id="14"></Tweet>', components: { Tweet } }) const vm = new Ctor().$mount() setTimeout(() => { const $tweetContents = vm.$el.firstChild t.is($tweetContents.innerHTML, 'Whoops! We couldn\'t access this Tweet.') t.is($tweetContents.className, '') t.end() }, 0) }) test.cb('Should show a custom error message when tweet cannot be fetched and params are given', t => { const { Tweet, Vue, window } = t.context const mockTwttr = { widgets: { createTweetEmbed: (tweetId, parent) => { const $mockTweet = undefined // tweet not found return Promise.resolve($mockTweet) } } } window.twttr = mockTwttr const Ctor = Vue.extend({ template: '<Tweet error-message="why you no work" error-message-class="tweet-error" id="14"></Tweet>', components: { Tweet } }) const vm = new Ctor().$mount() setTimeout(() => { const $tweetContents = vm.$el.firstChild t.is($tweetContents.innerHTML, 'why you no work') t.is($tweetContents.className, 'tweet-error') t.end() }, 0) }) test.cb('Should show children while tweet is not loaded', t => { const { Tweet, Vue, window } = t.context const mockTwttr = { widgets: { createTweetEmbed: () => { // emulate tweet being loaded return Promise.resolve() } } } window.twttr = mockTwttr const Ctor = Vue.extend({ template: '<Tweet id="123"><div id="foo">hi</div></Tweet>', components: { Tweet } }) const vm = new Ctor().$mount() t.truthy(vm.$el.querySelector('#foo')) setTimeout(() => { t.falsy(vm.$el.querySelector('#foo')) t.end() }, 0) }) // Tests for Moment component test.cb('Should show a newly created Moment element as tweet\'s immeditate child', t => { const { Moment, Vue, window, document } = t.context const mockTwttr = { widgets: { createMoment: spy((tweetId, parent) => { const $mockTweet = document.createElement('div') $mockTweet.setAttribute('id', 'loadedTweet') $mockTweet.innerText = 'tweet text' parent.appendChild($mockTweet) return Promise.resolve($mockTweet) }) } } window.twttr = mockTwttr const Ctor = Vue.extend({ template: '<Moment id="123" :options="{foo:\'bar\'}"></Moment>', components: { Moment } }) const vm = new Ctor().$mount() setTimeout(() => { // check that library was called with correct options t.is(mockTwttr.widgets.createMoment.callCount, 1) t.is(mockTwttr.widgets.createMoment.args[0].length, 3) t.is(mockTwttr.widgets.createMoment.args[0][0], '123') t.is(mockTwttr.widgets.createMoment.args[0][1], vm.$el) t.deepEqual(mockTwttr.widgets.createMoment.args[0][2], { foo: 'bar' }) // check that the element was indeed injected const $loadedTweet = vm.$el.querySelector('#loadedTweet') t.is($loadedTweet.id, 'loadedTweet') t.is($loadedTweet.innerText, 'tweet text') t.end() }, 0) }) test.cb('Should show an error message when moment cannot be fetched', t => { const { Moment, Vue, window } = t.context const mockTwttr = { widgets: { createMoment: (tweetId, parent) => { const $mockTweet = undefined // tweet not found return Promise.resolve($mockTweet) } } } window.twttr = mockTwttr const Ctor = Vue.extend({ template: '<Moment id="14"></Moment>', components: { Moment } }) const vm = new Ctor().$mount() setTimeout(() => { const $tweetContents = vm.$el.firstChild t.is($tweetContents.innerHTML, 'Whoops! We couldn\'t access this Moment.') t.is($tweetContents.className, '') t.end() }, 0) }) test.cb('Should show a custom error message when moment cannot be fetched and params are given', t => { const { Moment, Vue, window } = t.context const mockTwttr = { widgets: { createMoment: (tweetId, parent) => { const $mockTweet = undefined // tweet not found return Promise.resolve($mockTweet) } } } window.twttr = mockTwttr const Ctor = Vue.extend({ template: '<Moment error-message="why you no work" error-message-class="moment-error" id="14"></Moment>', components: { Moment } }) const vm = new Ctor().$mount() setTimeout(() => { const $tweetContents = vm.$el.firstChild t.is($tweetContents.innerHTML, 'why you no work') t.is($tweetContents.className, 'moment-error') t.end() }, 0) }) test.cb('Should show children while moment is not loaded', t => { const { Moment, Vue, window } = t.context const mockTwttr = { widgets: { createMoment: () => { // emulate tweet being loaded return Promise.resolve() } } } window.twttr = mockTwttr const Ctor = Vue.extend({ template: '<Moment id="123"><div id="foo">hi</div></Moment>', components: { Moment } }) const vm = new Ctor().$mount() t.truthy(vm.$el.querySelector('#foo')) setTimeout(() => { t.falsy(vm.$el.querySelector('#foo')) t.end() }, 0) }) // Tests for Timeline component test.cb('Should show a newly created Timeline element as tweet\'s immeditate child', t => { const { Timeline, Vue, window, document } = t.context const mockTwttr = { widgets: { createTimeline: spy((userId, parent) => { const $mockTweet = document.createElement('div') $mockTweet.setAttribute('id', 'loadedTweet') $mockTweet.setAttribute('sourceType', 'loadedSouceType') $mockTweet.innerText = 'tweet text' parent.appendChild($mockTweet) return Promise.resolve($mockTweet) }) } } window.twttr = mockTwttr const Ctor = Vue.extend({ template: '<Timeline id="123" sourceType="profile" :options="{foo:\'bar\'}"></Timeline>', components: { Timeline } }) const vm = new Ctor().$mount() setTimeout(() => { // check that library was called with correct options t.is(mockTwttr.widgets.createTimeline.callCount, 1) t.is(mockTwttr.widgets.createTimeline.args[0].length, 3) t.is(mockTwttr.widgets.createTimeline.args[0][0]['screenName'], '123') t.is(mockTwttr.widgets.createTimeline.args[0][0]['sourceType'], 'profile') t.is(mockTwttr.widgets.createTimeline.args[0][1], vm.$el) t.deepEqual(mockTwttr.widgets.createTimeline.args[0][2], { foo: 'bar' }) // check that the element was indeed injected const $loadedTweet = vm.$el.querySelector('#loadedTweet') t.is($loadedTweet.id, 'loadedTweet') t.is($loadedTweet.innerText, 'tweet text') t.end() }, 0) }) test.cb('Should show a newly created Timeline element as list\'s immeditate child', t => { const { Timeline, Vue, window, document } = t.context const mockTwttr = { widgets: { createTimeline: spy((userId, parent) => { const $mockTweet = document.createElement('div') $mockTweet.setAttribute('id', 'loadedTweet') $mockTweet.setAttribute('sourceType', 'loadedSouceType') $mockTweet.innerText = 'tweet text' parent.appendChild($mockTweet) return Promise.resolve($mockTweet) }) } } window.twttr = mockTwttr const Ctor = Vue.extend({ template: '<Timeline sourceType="list" id="TwitterDev" slug="national-parks" :options="{foo:\'bar\'}"></Timeline>', components: { Timeline } }) const vm = new Ctor().$mount() setTimeout(() => { // check that library was called with correct options t.is(mockTwttr.widgets.createTimeline.callCount, 1) t.is(mockTwttr.widgets.createTimeline.args[0].length, 3) t.is(mockTwttr.widgets.createTimeline.args[0][0]['sourceType'], 'list') t.is(mockTwttr.widgets.createTimeline.args[0][0]['ownerScreenName'], 'TwitterDev') t.is(mockTwttr.widgets.createTimeline.args[0][0]['slug'], 'national-parks') t.is(mockTwttr.widgets.createTimeline.args[0][1], vm.$el) t.deepEqual(mockTwttr.widgets.createTimeline.args[0][2], { foo: 'bar' }) // check that the element was indeed injected const $loadedTweet = vm.$el.querySelector('#loadedTweet') t.is($loadedTweet.id, 'loadedTweet') t.is($loadedTweet.innerText, 'tweet text') t.end() }, 0) }) test.cb('Should show an error message when timeline cannot be fetched', t => { const { Timeline, Vue, window } = t.context const mockTwttr = { widgets: { createTimeline: (tweetId, parent) => { const $mockTweet = undefined // tweet not found return Promise.resolve($mockTweet) } } } window.twttr = mockTwttr const Ctor = Vue.extend({ template: '<Timeline id="14" sourceType="profile"></Timeline>', components: { Timeline } }) const vm = new Ctor().$mount() setTimeout(() => { const $tweetContents = vm.$el.firstChild t.is($tweetContents.innerHTML, 'Whoops! We couldn\'t access this Timeline.') t.is($tweetContents.className, '') t.end() }, 0) }) test.cb('Should show a custom error message when timeline cannot be fetched and params are given', t => { const { Timeline, Vue, window } = t.context const mockTwttr = { widgets: { createTimeline: (tweetId, parent) => { const $mockTweet = undefined // tweet not found return Promise.resolve($mockTweet) } } } window.twttr = mockTwttr const Ctor = Vue.extend({ template: '<Timeline error-message="why you no work" error-message-class="timeline-error" id="14"></Timeline>', components: { Timeline } }) const vm = new Ctor().$mount() setTimeout(() => { const $tweetContents = vm.$el.firstChild t.is($tweetContents.innerHTML, 'why you no work') t.is($tweetContents.className, 'timeline-error') t.end() }, 0) }) test.cb('Should show children timeline tweet is not loaded', t => { const { Timeline, Vue, window } = t.context const mockTwttr = { widgets: { createTimeline: () => { // emulate tweet being loaded return Promise.resolve() } } } window.twttr = mockTwttr const Ctor = Vue.extend({ template: '<Timeline id="123" sourceType="profile"><div id="foo">hi</div></Timeline>', components: { Timeline } }) const vm = new Ctor().$mount() t.truthy(vm.$el.querySelector('#foo')) setTimeout(() => { t.falsy(vm.$el.querySelector('#foo')) t.end() }, 0) })