diff --git a/app/browser/api/ledger.js b/app/browser/api/ledger.js index aacd49bc98b..9afe3fdf6c9 100644 --- a/app/browser/api/ledger.js +++ b/app/browser/api/ledger.js @@ -44,7 +44,7 @@ const request = require('../../../js/lib/request') const ledgerUtil = require('../../common/lib/ledgerUtil') const tabState = require('../../common/state/tabState') const pageDataUtil = require('../../common/lib/pageDataUtil') -const ledgerVideoCahce = require('../../common/cache/ledgerVideoCache') +const ledgerVideoCache = require('../../common/cache/ledgerVideoCache') // Caching let locationDefault = 'NOOP' @@ -567,7 +567,7 @@ const getPublisherData = (result, scorekeeper) => { weight: result.pinPercentage } - data.publisherURL = result.publisherURL || (result.protocol || 'http:') + '//' + result.publisherKey + data.publisherURL = result.publisherURL || ((result.protocol || 'http:') + '//' + result.publisherKey) // media publisher if (result.faviconName) { @@ -940,7 +940,7 @@ const saveVisit = (state, publisherKey, duration, revisited) => { synopsis.addPublisher(publisherKey, {duration: duration, revisitP: revisited}) state = ledgerState.setPublisher(state, publisherKey, synopsis.publishers[publisherKey]) state = updatePublisherInfo(state) - state = verifiedP(state, publisherKey, (error, result) => { + state = module.exports.verifiedP(state, publisherKey, (error, result) => { if (!error) { appActions.onPublisherOptionUpdate(publisherKey, 'verified', result) savePublisherOption(publisherKey, 'verified', result) @@ -2345,7 +2345,7 @@ const saveOptionSynopsis = (prop, value) => { } const savePublisherOption = (publisherKey, prop, value) => { - if (synopsis.publishers && synopsis.publishers[publisherKey]) { + if (synopsis.publishers && synopsis.publishers[publisherKey] && synopsis.publishers[publisherKey].options) { synopsis.publishers[publisherKey].options[prop] = value } } @@ -2476,7 +2476,7 @@ const transitionWalletToBat = () => { let currentMediaKey = null const onMediaRequest = (state, xhr, type) => { - if (!xhr) { + if (!xhr || type == null) { return state } @@ -2499,10 +2499,10 @@ const onMediaRequest = (state, xhr, type) => { currentMediaKey = mediaKey } - const cache = ledgerVideoCahce.getDataByVideoId(state, mediaKey) + const cache = ledgerVideoCache.getDataByVideoId(state, mediaKey) if (!cache.isEmpty()) { - return saveVisit(state, cache.get('publisher'), duration, revisited) + return module.exports.saveVisit(state, cache.get('publisher'), duration, revisited) } const options = underscore.extend({roundtrip: roundtrip}, clientOptions) @@ -2529,7 +2529,7 @@ const onMediaRequest = (state, xhr, type) => { } const onMediaPublisher = (state, mediaKey, response, duration, revisited) => { - const publisherKey = response.get('publisher') + const publisherKey = response ? response.get('publisher') : null if (publisherKey == null) { return state } @@ -2569,14 +2569,16 @@ const onMediaPublisher = (state, mediaKey, response, duration, revisited) => { } else { synopsis.publishers[publisherKey].faviconName = faviconName synopsis.publishers[publisherKey].faviconURL = faviconURL + synopsis.publishers[publisherKey].publisherURL = publisherURL state = ledgerState.setPublishersProp(state, publisherKey, 'faviconName', faviconName) state = ledgerState.setPublishersProp(state, publisherKey, 'faviconURL', faviconURL) + state = ledgerState.setPublishersProp(state, publisherKey, 'publisherURL', publisherURL) } // Add to cache - state = ledgerVideoCahce.setCacheByVideoId(state, mediaKey, response) + state = ledgerVideoCache.setCacheByVideoId(state, mediaKey, response) - state = saveVisit(state, publisherKey, duration, revisited) + state = module.exports.saveVisit(state, publisherKey, duration, revisited) return state } @@ -2616,7 +2618,8 @@ const getMethods = () => { getNewClient, savePublisherData, onMediaRequest, - onMediaPublisher + onMediaPublisher, + saveVisit } let privateMethods = {} @@ -2636,6 +2639,10 @@ const getMethods = () => { setSynopsis: (data) => { synopsis = data }, + setCurrentMediaKey: (key) => { + currentMediaKey = key + }, + getCurrentMediaKey: (key) => currentMediaKey, synopsisNormalizer, pruneSynopsis } diff --git a/app/common/cache/ledgerVideoCache.js b/app/common/cache/ledgerVideoCache.js index 63c22f1ae3c..c7f0ae3fcb1 100644 --- a/app/common/cache/ledgerVideoCache.js +++ b/app/common/cache/ledgerVideoCache.js @@ -2,8 +2,19 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ const Immutable = require('immutable') +const assert = require('assert') + +const { makeImmutable, isMap } = require('../state/immutableUtil') + +const validateState = function (state) { + state = makeImmutable(state) + assert.ok(isMap(state), 'state must be an Immutable.Map') + assert.ok(isMap(state.getIn(['cache', 'ledgerVideos'])), 'state must contain ledgerVideos as Immutable.Map') + return state +} const getDataByVideoId = (state, key) => { + state = validateState(state) if (key == null) { return Immutable.Map() } @@ -12,10 +23,13 @@ const getDataByVideoId = (state, key) => { } const setCacheByVideoId = (state, key, data) => { + state = validateState(state) if (key == null) { return state } + data = makeImmutable(data) + return state.setIn(['cache', 'ledgerVideos', key], data) } diff --git a/app/common/lib/ledgerUtil.js b/app/common/lib/ledgerUtil.js index 6e7b4c12b74..48238677fa4 100644 --- a/app/common/lib/ledgerUtil.js +++ b/app/common/lib/ledgerUtil.js @@ -188,6 +188,11 @@ const stickyP = (state, publisherKey) => { const getMediaId = (data, type) => { let id = null + + if (type == null || data == null) { + return id + } + switch (type) { case ledgerMediaProviders.YOUTUBE: { @@ -204,11 +209,16 @@ const getMediaKey = (id, type) => { return null } - return `${type.toLowerCase()}_${id.toLowerCase()}` + return `${type.toLowerCase()}_${id}` } const getMediaData = (xhr, type) => { let result = null + + if (xhr == null || type == null) { + return result + } + switch (type) { case ledgerMediaProviders.YOUTUBE: { @@ -253,18 +263,22 @@ const getYoutubeDuration = (data) => { } for (let i = 0; i < startTime.length; i++) { - time += parseInt(endTime[i]) - parseInt(startTime[i]) + time += parseFloat(endTime[i]) - parseFloat(startTime[i]) } // we get seconds back, so we need to convert it into ms time = time * 1000 - return time + return parseInt(time) } const isMediaProvider = (url) => { let provider = null + if (url == null) { + return provider + } + // Youtube if (url.startsWith('https://www.youtube.com/api/stats/watchtime?')) { provider = ledgerMediaProviders.YOUTUBE @@ -273,21 +287,35 @@ const isMediaProvider = (url) => { return provider } -module.exports = { - shouldTrackView, - batToCurrencyString, - formattedTimeFromNow, - formattedDateFromTimestamp, - walletStatus, - blockedP, - contributeP, - visibleP, - eligibleP, - stickyP, - formatCurrentBalance, - getMediaId, - getMediaDuration, - isMediaProvider, - getMediaData, - getMediaKey +const getMethods = () => { + const publicMethods = { + shouldTrackView, + batToCurrencyString, + formattedTimeFromNow, + formattedDateFromTimestamp, + walletStatus, + blockedP, + contributeP, + visibleP, + eligibleP, + stickyP, + formatCurrentBalance, + getMediaId, + getMediaDuration, + isMediaProvider, + getMediaData, + getMediaKey + } + + let privateMethods = {} + + if (process.env.NODE_ENV === 'test') { + privateMethods = { + getYoutubeDuration + } + } + + return Object.assign({}, publicMethods, privateMethods) } + +module.exports = getMethods() diff --git a/app/sessionStore.js b/app/sessionStore.js index 1f0afa9a156..04030735f81 100644 --- a/app/sessionStore.js +++ b/app/sessionStore.js @@ -926,7 +926,8 @@ module.exports.defaultAppState = () => { }, cache: { bookmarkLocation: undefined, - bookmarkOrder: {} + bookmarkOrder: {}, + ledgerVideos: {} }, pinnedSites: {}, bookmarks: {}, diff --git a/docs/state.md b/docs/state.md index dcb7198ed1b..6c4b63a01e1 100644 --- a/docs/state.md +++ b/docs/state.md @@ -89,6 +89,9 @@ AppStore order: number, type: string // siteTags.BOOKMARK or siteTags.BOOKMARK_FOLDER }] + }, + ledgerVideos: { + [mediaKey]: string // publisher key } } clearBrowsingDataDefaults: { diff --git a/test/unit/app/browser/api/ledgerTest.js b/test/unit/app/browser/api/ledgerTest.js index 6109415f6a0..cc53c4d2b86 100644 --- a/test/unit/app/browser/api/ledgerTest.js +++ b/test/unit/app/browser/api/ledgerTest.js @@ -7,12 +7,16 @@ const Immutable = require('immutable') const assert = require('assert') const sinon = require('sinon') const mockery = require('mockery') +const batPublisher = require('bat-publisher') const settings = require('../../../../../js/constants/settings') const appActions = require('../../../../../js/actions/appActions') const migrationState = require('../../../../../app/common/state/migrationState') -const batPublisher = require('bat-publisher') +const ledgerMediaProviders = require('../../../../../app/common/constants/ledgerMediaProviders') const defaultAppState = Immutable.fromJS({ + cache: { + ledgerVideos: {} + }, ledger: {}, migrations: {} }) @@ -21,6 +25,12 @@ describe('ledger api unit tests', function () { let ledgerApi let isBusy = false let ledgerClient + let ledgerPublisher + + // constants + const xhr = 'https://www.youtube.com/api/stats/watchtime?docid=kLiLOkzLetE&st=11.338&et=21.339' + const videoId = 'youtube_kLiLOkzLetE' + const publisherKey = 'youtube#channel:UCFNTTISby1c_H-rm5Ww5rZg' // settings let paymentsEnabled @@ -138,14 +148,17 @@ describe('ledger api unit tests', function () { mockery.registerMock('bat-client', ledgerClient) // ledger publisher stubbing - const lp = { + ledgerPublisher = { ruleset: [], - getPublisherProps: function (publisher) { + getPublisherProps: function () { return null }, - Synopsis: batPublisher.Synopsis + Synopsis: batPublisher.Synopsis, + getMedia: { + getPublisherFromMediaProps: () => {} + } } - mockery.registerMock('bat-publisher', lp) + mockery.registerMock('bat-publisher', ledgerPublisher) // once everything is stubbed, load the ledger ledgerApi = require('../../../../../app/browser/api/ledger') @@ -164,9 +177,15 @@ describe('ledger api unit tests', function () { beforeEach(function () { notificationsInitStub = sinon.stub(ledgerApi.notifications, 'init') }) + afterEach(function () { notificationsInitStub.restore() }) + + after(function () { + ledgerApi.setSynopsis(undefined) + }) + it('calls notifications.init', function () { ledgerApi.initialize(defaultAppState, true) assert(notificationsInitStub.calledOnce) @@ -405,6 +424,10 @@ describe('ledger api unit tests', function () { }) describe('transitionWalletToBat', function () { + after(function () { + ledgerApi.setSynopsis(undefined) + }) + describe('when client is not busy', function () { before(function () { const batState = ledgerApi.onBootStateFile(defaultAppState) @@ -712,6 +735,10 @@ describe('ledger api unit tests', function () { }) describe('synopsisNormalizer', function () { + after(function () { + ledgerApi.setSynopsis(undefined) + }) + describe('prune synopsis', function () { let pruneSynopsisSpy @@ -736,6 +763,10 @@ describe('ledger api unit tests', function () { }) describe('pruneSynopsis', function () { + after(function () { + ledgerApi.setSynopsis(undefined) + }) + it('null case', function () { const result = ledgerApi.pruneSynopsis(defaultAppState) assert.deepEqual(result.toJS(), defaultAppState.toJS()) @@ -763,6 +794,9 @@ describe('ledger api unit tests', function () { }) const expectedResult = { + cache: { + ledgerVideos: {} + }, ledger: { synopsis: { publishers: { @@ -779,4 +813,209 @@ describe('ledger api unit tests', function () { assert.deepEqual(result.toJS(), expectedResult) }) }) + + describe('onMediaRequest', function () { + let publisherFromMediaPropsStub, saveVisitSpy + + beforeEach(function () { + publisherFromMediaPropsStub = sinon.stub(ledgerPublisher.getMedia, 'getPublisherFromMediaProps') + saveVisitSpy = sinon.spy(ledgerApi, 'saveVisit') + }) + + afterEach(function () { + publisherFromMediaPropsStub.restore() + saveVisitSpy.restore() + ledgerApi.setCurrentMediaKey(null) + }) + + after(function () { + ledgerApi.setSynopsis(undefined) + }) + + it('null case', function () { + const result = ledgerApi.onMediaRequest(defaultAppState) + assert.deepEqual(result.toJS(), defaultAppState.toJS()) + assert(publisherFromMediaPropsStub.notCalled) + assert(saveVisitSpy.notCalled) + }) + + it('set currentMediaKey when it is different than saved', function () { + ledgerApi.onMediaRequest(defaultAppState, xhr, ledgerMediaProviders.YOUTUBE) + assert.equal(ledgerApi.getCurrentMediaKey(), videoId) + assert(publisherFromMediaPropsStub.calledOnce) + assert(saveVisitSpy.notCalled) + }) + + it('get data from cache', function () { + const state = defaultAppState.setIn(['cache', 'ledgerVideos', videoId], Immutable.fromJS({ + publisher: publisherKey + })) + ledgerApi.onMediaRequest(state, xhr, ledgerMediaProviders.YOUTUBE) + assert(publisherFromMediaPropsStub.notCalled) + assert(saveVisitSpy.withArgs(state, publisherKey, 10001, false).calledOnce) + }) + + it('revisited if visiting the same media', function () { + const state = defaultAppState.setIn(['cache', 'ledgerVideos', videoId], Immutable.fromJS({ + publisher: publisherKey + })) + // first call, revisit false + ledgerApi.onMediaRequest(state, xhr, ledgerMediaProviders.YOUTUBE) + assert.equal(ledgerApi.getCurrentMediaKey(), videoId) + assert(saveVisitSpy.withArgs(state, publisherKey, 10001, false).calledOnce) + + // second call, revisit true + ledgerApi.onMediaRequest(state, xhr, ledgerMediaProviders.YOUTUBE) + assert(publisherFromMediaPropsStub.notCalled) + assert(saveVisitSpy.withArgs(state, publisherKey, 10001, true).calledOnce) + }) + }) + + describe('onMediaPublisher', function () { + let saveVisitSpy, verifiedPStub + + const expectedState = Immutable.fromJS({ + cache: { + ledgerVideos: { + 'youtube_kLiLOkzLetE': { + publisher: 'youtube#channel:UCFNTTISby1c_H-rm5Ww5rZg', + faviconName: 'Brave', + providerName: 'Youtube', + faviconURL: 'data:image/jpeg;base64,...', + publisherURL: 'https://brave.com' + } + } + }, + ledger: { + about: { + synopsis: [], + synopsisOptions: {} + }, + synopsis: { + publishers: { + 'youtube#channel:UCFNTTISby1c_H-rm5Ww5rZg': { + exclude: false, + options: { + exclude: true + }, + providerName: 'Youtube', + faviconName: 'Brave', + faviconURL: 'data:image/jpeg;base64,...', + publisherURL: 'https://brave.com' + } + } + } + }, + migrations: {} + }) + + before(function () { + verifiedPStub = sinon.stub(ledgerApi, 'verifiedP', (state, publisherKey, fn) => state) + }) + + after(function () { + verifiedPStub.restore() + }) + + beforeEach(function () { + ledgerApi.setSynopsis({ + initPublisher: () => {}, + addPublisher: () => {}, + publishers: { + [publisherKey]: { + exclude: false, + options: { + exclude: true + }, + providerName: 'Youtube' + } + } + }) + saveVisitSpy = sinon.spy(ledgerApi, 'saveVisit') + }) + + afterEach(function () { + ledgerApi.setSynopsis(undefined) + saveVisitSpy.restore() + }) + + it('null case', function () { + const result = ledgerApi.onMediaPublisher(defaultAppState) + assert.deepEqual(result.toJS(), defaultAppState.toJS()) + }) + + it('min duration is set to minimum visit time if below that threshold', function () { + const response = Immutable.fromJS({ + publisher: publisherKey, + faviconName: 'Brave', + faviconURL: 'data:image/jpeg;base64,...', + publisherURL: 'https://brave.com', + providerName: 'Youtube' + }) + + ledgerApi.onMediaPublisher(defaultAppState, videoId, response, 1000, false) + assert(saveVisitSpy.calledOnce) + assert.equal(saveVisitSpy.args[0][1], publisherKey) + assert.equal(saveVisitSpy.args[0][2], paymentsMinVisitTime) + assert.equal(saveVisitSpy.args[0][3], false) + }) + + it('create publisher if new and add cache', function () { + const response = Immutable.fromJS({ + publisher: publisherKey, + faviconName: 'Brave', + faviconURL: 'data:image/jpeg;base64,...', + publisherURL: 'https://brave.com', + providerName: 'Youtube' + }) + + const state = ledgerApi.onMediaPublisher(defaultAppState, videoId, response, 1000, false) + assert(saveVisitSpy.calledOnce) + assert.deepEqual(state.toJS(), expectedState.toJS()) + }) + + it('update publisher if exists', function () { + const newState = Immutable.fromJS({ + cache: { + ledgerVideos: { + 'youtube_kLiLOkzLetE': { + publisher: 'youtube#channel:UCFNTTISby1c_H-rm5Ww5rZg', + faviconName: 'Brave', + providerName: 'Youtube', + faviconURL: 'data:image/jpeg;base64,...', + publisherURL: 'https://brave.com' + } + } + }, + ledger: { + synopsis: { + publishers: { + 'youtube#channel:UCFNTTISby1c_H-rm5Ww5rZg': { + options: { + exclude: true + }, + faviconName: 'old Brave', + faviconURL: 'data:image/jpeg;base64,...', + publisherURL: 'https://brave.io', + providerName: 'Youtube' + } + } + } + }, + migrations: {} + }) + + const response = Immutable.fromJS({ + publisher: publisherKey, + faviconName: 'Brave', + faviconURL: 'data:image/jpeg;base64,...', + publisherURL: 'https://brave.com', + providerName: 'Youtube' + }) + + const state = ledgerApi.onMediaPublisher(newState, videoId, response, 1000, false) + assert(saveVisitSpy.calledOnce) + assert.deepEqual(state.toJS(), expectedState.toJS()) + }) + }) }) diff --git a/test/unit/app/common/cache/ledgerVideoCacheTest.js b/test/unit/app/common/cache/ledgerVideoCacheTest.js new file mode 100644 index 00000000000..feadd27153c --- /dev/null +++ b/test/unit/app/common/cache/ledgerVideoCacheTest.js @@ -0,0 +1,61 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ +/* global describe, it */ + +const Immutable = require('immutable') +const assert = require('assert') +const ledgerVideoCache = require('../../../../../app/common/cache/ledgerVideoCache') + +const baseState = Immutable.fromJS({ + cache: { + ledgerVideos: {} + } +}) +const stateWithData = Immutable.fromJS({ + cache: { + ledgerVideos: { + 'youtube_kLiLOkzLetE': { + publisher: 'youtube#channel:UCFNTTISby1c_H-rm5Ww5rZg' + } + } + } +}) + +describe('ledgerVideoCache unit test', function () { + describe('getDataByVideoId', function () { + it('key is not provided', function () { + const result = ledgerVideoCache.getDataByVideoId(baseState) + assert.deepEqual(result.toJS(), {}) + }) + + it('key does not exist in the cache', function () { + const result = ledgerVideoCache.getDataByVideoId(baseState, 'key') + assert.deepEqual(result.toJS(), {}) + }) + + it('data is ok', function () { + const result = ledgerVideoCache.getDataByVideoId(stateWithData, 'youtube_kLiLOkzLetE') + assert.deepEqual(result.toJS(), { + publisher: 'youtube#channel:UCFNTTISby1c_H-rm5Ww5rZg' + }) + }) + }) + + describe('setCacheByVideoId', function () { + it('key is not provided', function () { + const state = ledgerVideoCache.setCacheByVideoId(baseState) + assert.deepEqual(state.toJS(), baseState.toJS()) + }) + + it('data is ok', function () { + const state = ledgerVideoCache.setCacheByVideoId(baseState, 'youtube_kLiLOkzLetE', Immutable.fromJS({ + publisher: 'youtube#channel:UCFNTTISby1c_H-rm5Ww5rZg' + })) + const expectedState = state.setIn(['cache', 'ledgerVideos', 'youtube_kLiLOkzLetE'], Immutable.fromJS({ + publisher: 'youtube#channel:UCFNTTISby1c_H-rm5Ww5rZg' + })) + assert.deepEqual(state.toJS(), expectedState.toJS()) + }) + }) +}) diff --git a/test/unit/app/common/lib/ledgerUtilTest.js b/test/unit/app/common/lib/ledgerUtilTest.js index 3e068702fa6..22c1459e4cc 100644 --- a/test/unit/app/common/lib/ledgerUtilTest.js +++ b/test/unit/app/common/lib/ledgerUtilTest.js @@ -3,6 +3,7 @@ const mockery = require('mockery') const assert = require('assert') const Immutable = require('immutable') require('../../../braveUnit') +const ledgerMediaProviders = require('../../../../../app/common/constants/ledgerMediaProviders') describe('ledgerUtil test', function () { let ledgerUtil @@ -171,4 +172,123 @@ describe('ledgerUtil test', function () { describe('walletStatus', function () { }) + + describe('getMediaId', function () { + it('null case', function () { + const result = ledgerUtil.getMediaId() + assert.equal(result, null) + }) + + it('unknown type', function () { + const result = ledgerUtil.getMediaData({}, 'test') + assert.equal(result, null) + }) + + describe('Youtube', function () { + it('null case', function () { + const result = ledgerUtil.getMediaId(null, ledgerMediaProviders.YOUTUBE) + assert.equal(result, null) + }) + + it('id is provided', function () { + const result = ledgerUtil.getMediaId({docid: 'kLiLOkzLetE'}, ledgerMediaProviders.YOUTUBE) + assert.equal(result, 'kLiLOkzLetE') + }) + }) + }) + + describe('getMediaKey', function () { + it('null case', function () { + const result = ledgerUtil.getMediaKey() + assert.equal(result, null) + }) + + it('type is missing', function () { + const result = ledgerUtil.getMediaKey('kLiLOkzLetE') + assert.equal(result, null) + }) + + it('id is null', function () { + const result = ledgerUtil.getMediaKey(null, ledgerMediaProviders.YOUTUBE) + assert.equal(result, null) + }) + + it('data is ok', function () { + const result = ledgerUtil.getMediaKey('kLiLOkzLetE', ledgerMediaProviders.YOUTUBE) + assert.equal(result, 'youtube_kLiLOkzLetE') + }) + }) + + describe('getMediaData', function () { + it('null case', function () { + const result = ledgerUtil.getMediaData() + assert.equal(result, null) + }) + + it('unknown type', function () { + const result = ledgerUtil.getMediaData('https://youtube.com', 'test') + assert.equal(result, null) + }) + + describe('Youtube', function () { + it('null case', function () { + const result = ledgerUtil.getMediaData(null, ledgerMediaProviders.YOUTUBE) + assert.equal(result, null) + }) + + it('query is not present', function () { + const result = ledgerUtil.getMediaData('https://youtube.com', ledgerMediaProviders.YOUTUBE) + assert.equal(result, null) + }) + + it('query is present', function () { + const result = ledgerUtil.getMediaData('https://www.youtube.com/api/stats/watchtime?docid=kLiLOkzLetE&st=11.338&et=21.339', ledgerMediaProviders.YOUTUBE) + assert.deepEqual(result, { + docid: 'kLiLOkzLetE', + st: '11.338', + et: '21.339' + }) + }) + }) + }) + + describe('getYoutubeDuration', function () { + it('null case', function () { + const result = ledgerUtil.getYoutubeDuration() + assert.equal(result, 0) + }) + + it('multiple times', function () { + const result = ledgerUtil.getYoutubeDuration({ + st: '11.338,21.339,25.000', + et: '21.339,25.000,26.100' + }) + assert.equal(result, 14762) + }) + + it('single time', function () { + const result = ledgerUtil.getYoutubeDuration({ + st: '11.338', + et: '21.339' + }) + assert.equal(result, 10001) + }) + }) + + describe('isMediaProvider', function () { + it('null case', function () { + const result = ledgerUtil.isMediaProvider() + assert.equal(result, null) + }) + + it('unknown provider', function () { + const result = ledgerUtil.isMediaProvider('https://www.brave.com') + assert.equal(result, null) + }) + + it('youtube', function () { + const result = ledgerUtil.isMediaProvider('https://www.youtube.com/api/stats/watchtime?docid=kLiLOkzLetE&st=11.338&et=21.339') + assert.equal(result, ledgerMediaProviders.YOUTUBE) + }) + }) })