From b081d3ecf5451153bf296df94145818d28cc150f Mon Sep 17 00:00:00 2001 From: NejcZdovc Date: Mon, 31 Jul 2017 17:13:58 +0200 Subject: [PATCH] Adds more tests --- .../reducers/bookmarkFoldersReducer.js | 19 +- app/browser/reducers/historyReducer.js | 16 +- app/browser/reducers/pinnedSitesReducer.js | 9 +- app/common/cache/bookmarkLocationCache.js | 5 + app/common/cache/bookmarkOrderCache.js | 33 +- app/common/lib/bookmarkFoldersUtil.js | 25 +- app/common/lib/bookmarkUtil.js | 12 + app/common/lib/historyUtil.js | 7 +- app/common/lib/pinnedSitesUtil.js | 5 +- app/common/state/bookmarksState.js | 3 +- app/common/state/historyState.js | 2 - app/common/state/pinnedSitesState.js | 15 +- app/filtering.js | 2 +- .../bookmarksToolbarTest.js | 1 + .../reducers/bookmarkFoldersReducerTest.js | 15 +- .../browser/reducers/historyReducerTest.js | 444 ++++++++++++++++ .../reducers/pinnedSitesReducerTest.js | 232 +++++++++ .../common/cache/bookmarkLocationCacheTest.js | 76 ++- .../common/cache/bookmarkOrderCacheTest.js | 484 ++++++++++++++++++ .../app/common/lib/bookmarkFoldersUtilTest.js | 198 +++++++ test/unit/app/common/lib/bookmarkUtilTest.js | 134 ++++- test/unit/lib/urlutilTest.js | 218 ++++---- 22 files changed, 1809 insertions(+), 146 deletions(-) create mode 100644 test/unit/app/browser/reducers/historyReducerTest.js create mode 100644 test/unit/app/browser/reducers/pinnedSitesReducerTest.js create mode 100644 test/unit/app/common/cache/bookmarkOrderCacheTest.js create mode 100644 test/unit/app/common/lib/bookmarkFoldersUtilTest.js diff --git a/app/browser/reducers/bookmarkFoldersReducer.js b/app/browser/reducers/bookmarkFoldersReducer.js index 5ef20c60836..0849fcd05f4 100644 --- a/app/browser/reducers/bookmarkFoldersReducer.js +++ b/app/browser/reducers/bookmarkFoldersReducer.js @@ -74,15 +74,24 @@ const bookmarkFoldersReducer = (state, action, immutableAction) => { } case appConstants.APP_REMOVE_BOOKMARK_FOLDER: { - const key = action.get('folderKey') + const folderKey = action.get('folderKey') - if (key == null) { + if (folderKey == null) { break } - const folder = state.getIn([STATE_SITES.BOOKMARK_FOLDERS, key]) - state = bookmarkFoldersState.removeFolder(state, key) - state = syncUtil.updateObjectCache(state, folder, STATE_SITES.BOOKMARK_FOLDERS) + if (Immutable.List.isList(folderKey)) { + action.get('folderKey', Immutable.List()).forEach((key) => { + const folder = state.getIn([STATE_SITES.BOOKMARK_FOLDERS, key]) + state = bookmarkFoldersState.removeFolder(state, key) + state = syncUtil.updateObjectCache(state, folder, STATE_SITES.BOOKMARK_FOLDERS) + }) + } else { + const folder = state.getIn([STATE_SITES.BOOKMARK_FOLDERS, folderKey]) + state = bookmarkFoldersState.removeFolder(state, folderKey) + state = syncUtil.updateObjectCache(state, folder, STATE_SITES.BOOKMARK_FOLDERS) + } + break } } diff --git a/app/browser/reducers/historyReducer.js b/app/browser/reducers/historyReducer.js index 875935d1816..67579796d79 100644 --- a/app/browser/reducers/historyReducer.js +++ b/app/browser/reducers/historyReducer.js @@ -28,13 +28,18 @@ const historyReducer = (state, action, immutableAction) => { const clearData = defaults ? defaults.merge(temp) : temp if (clearData.get('browserHistory')) { state = historyState.clearSites(state) + state = aboutHistoryState.clearHistory(state) filtering.clearHistory() } break } case appConstants.APP_ADD_HISTORY_SITE: { - const detail = action.get('siteDetail') + const detail = action.get('siteDetail', Immutable.Map()) + + if (detail.isEmpty()) { + break + } if (Immutable.List.isList(detail)) { detail.forEach((item) => { @@ -53,14 +58,19 @@ const historyReducer = (state, action, immutableAction) => { case appConstants.APP_REMOVE_HISTORY_SITE: { - if (Immutable.List.isList(action.get('historyKey'))) { + const historyKey = action.get('historyKey') + if (historyKey == null) { + break + } + + if (Immutable.List.isList(historyKey)) { action.get('historyKey', Immutable.List()).forEach((key) => { state = historyState.removeSite(state, key) // TODO: Implement Sync history site removal // state = syncUtil.updateObjectCache(state, action.get('siteDetail'), STATE_SITES.HISTORY_SITES) }) } else { - state = historyState.removeSite(state, action.get('historyKey')) + state = historyState.removeSite(state, historyKey) // TODO: Implement Sync history site removal // state = syncUtil.updateObjectCache(state, action.get('siteDetail'), STATE_SITES.HISTORY_SITES) } diff --git a/app/browser/reducers/pinnedSitesReducer.js b/app/browser/reducers/pinnedSitesReducer.js index ee735ad0203..e717dfe23eb 100644 --- a/app/browser/reducers/pinnedSitesReducer.js +++ b/app/browser/reducers/pinnedSitesReducer.js @@ -2,6 +2,8 @@ * 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/. */ +const Immutable = require('immutable') + // State const pinnedSitesState = require('../../common/state/pinnedSitesState') const tabState = require('../../common/state/tabState') @@ -41,7 +43,7 @@ const pinnedSitesReducer = (state, action, immutableAction) => { } case appConstants.APP_CREATE_TAB_REQUESTED: { - const createProperties = action.get('createProperties') + const createProperties = action.get('createProperties', Immutable.Map()) if (createProperties.get('pinned')) { state = pinnedSitesState.addPinnedSite(state, pinnedSitesUtil.getDetailFromProperties(createProperties)) } @@ -50,6 +52,11 @@ const pinnedSitesReducer = (state, action, immutableAction) => { case appConstants.APP_ON_PINNED_TAB_REORDER: { const siteKey = action.get('siteKey') + + if (siteKey == null) { + break + } + state = pinnedSitesState.reOrderSite( state, siteKey, diff --git a/app/common/cache/bookmarkLocationCache.js b/app/common/cache/bookmarkLocationCache.js index 5fc6837fa0a..ddb20c0c413 100644 --- a/app/common/cache/bookmarkLocationCache.js +++ b/app/common/cache/bookmarkLocationCache.js @@ -59,6 +59,11 @@ const generateCache = (state) => { */ const getCacheKey = (state, location) => { const normalLocation = normalizeLocation(location) + + if (normalLocation == null) { + return Immutable.List() + } + return state.getIn(['cache', 'bookmarkLocation', normalLocation], Immutable.List()) } diff --git a/app/common/cache/bookmarkOrderCache.js b/app/common/cache/bookmarkOrderCache.js index 62dd73eae0e..462ba2b6de5 100644 --- a/app/common/cache/bookmarkOrderCache.js +++ b/app/common/cache/bookmarkOrderCache.js @@ -37,8 +37,13 @@ const setOrder = (cache, key, tag, destinationKey, append = true) => { } const addCacheItem = (state, parentId = 0, key, destinationKey, tag, append) => { + if (key == null) { + return state + } + parentId = parentId.toString() key = key.toString() + // cache with this parentId doesn't exist yet if (!state.hasIn(['cache', 'bookmarkOrder', parentId])) { return state.setIn(['cache', 'bookmarkOrder', parentId], Immutable.fromJS([ @@ -53,6 +58,11 @@ const addCacheItem = (state, parentId = 0, key, destinationKey, tag, append) => const cache = state.getIn(['cache', 'bookmarkOrder', parentId]) // destination key is not provided if (destinationKey == null) { + const keyExist = cache.some(item => item.get('key') === key) + if (keyExist) { + return state + } + return state.setIn(['cache', 'bookmarkOrder', parentId], cache.push(Immutable.fromJS( { key: key, @@ -75,21 +85,26 @@ const addFolderToCache = (state, parentId, key, destinationKey, append) => { return addCacheItem(state, parentId, key, destinationKey, siteTags.BOOKMARK_FOLDER, append) } -const getFoldersByParentId = (state, parentId) => { +const getFoldersByParentId = (state, parentId = 0) => { return state.getIn(['cache', 'bookmarkOrder', parentId.toString()], Immutable.List()) .filter(item => bookmarkFoldersUtil.isFolder(item)) } const getBookmarksByParentId = (state, parentId = 0) => { + const bookmarkUtil = require('../lib/bookmarkUtil') return state.getIn(['cache', 'bookmarkOrder', parentId.toString()], Immutable.List()) - .filter(item => bookmarkFoldersUtil.isFolder(item)) + .filter(item => bookmarkUtil.isBookmark(item)) } -const getBookmarksWithFolders = (state, parentId) => { +const getBookmarksWithFolders = (state, parentId = 0) => { return state.getIn(['cache', 'bookmarkOrder', parentId.toString()], Immutable.List()) } const removeCacheKey = (state, parentId, key) => { + if (parentId == null || key == null) { + return state + } + parentId = parentId.toString() key = key.toString() const cache = state.getIn(['cache', 'bookmarkOrder', parentId]) @@ -108,10 +123,20 @@ const removeCacheKey = (state, parentId, key) => { } } - return state.setIn(['cache', 'bookmarkOrder', parentId], newCache) + if (newCache.size > 0) { + state = state.setIn(['cache', 'bookmarkOrder', parentId], newCache) + } else { + state = state.deleteIn(['cache', 'bookmarkOrder', parentId]) + } + + return state } const removeCacheParent = (state, parentId) => { + if (parentId == null) { + return state + } + return state.deleteIn(['cache', 'bookmarkOrder', parentId.toString()]) } diff --git a/app/common/lib/bookmarkFoldersUtil.js b/app/common/lib/bookmarkFoldersUtil.js index b7de2a22ee4..be10e0357e6 100644 --- a/app/common/lib/bookmarkFoldersUtil.js +++ b/app/common/lib/bookmarkFoldersUtil.js @@ -7,8 +7,8 @@ const isFolderNameValid = (title) => { return title != null && title.trim().length > 0 } -const getNextFolderIdItem = (folders) => - folders.max((folderA, folderB) => { +const getNextFolderIdItem = (folders) => { + return folders.max((folderA, folderB) => { const folderIdA = folderA.get('folderId') const folderIdB = folderB.get('folderId') @@ -23,12 +23,14 @@ const getNextFolderIdItem = (folders) => } return folderIdA > folderIdB }) +} const getNextFolderId = (folders) => { const defaultFolderId = 0 if (!folders) { return defaultFolderId } + const maxIdItem = getNextFolderIdItem(folders) return (maxIdItem ? (maxIdItem.get('folderId') || 0) : 0) + 1 } @@ -37,8 +39,8 @@ const getNextFolderName = (folders, name) => { if (!folders) { return name } - const site = folders.find((site) => site.get('title') === name) - if (!site) { + const exist = folders.some((site) => site.get('title') === name) + if (!exist) { return name } const filenameFormat = /(.*) \((\d+)\)/ @@ -52,6 +54,10 @@ const getNextFolderName = (folders, name) => { } const isFolder = (folder) => { + if (folder == null) { + return false + } + return folder.get('type') === siteTags.BOOKMARK_FOLDER } @@ -85,11 +91,15 @@ const getAncestorFolderIds = (parentFolderIds, bookmarkFolder, allBookmarks) => /** * Determine if a proposed move is valid * - * @param sites The application state's Immutable sites list + * @param folderList The application state's Immutable folderList list * @param sourceDetail The site detail to move * @param destinationDetail The site detail to move to */ -const isMoveAllowed = (sites, sourceDetail, destinationDetail) => { +const isMoveAllowed = (folderList, sourceDetail, destinationDetail) => { + if (destinationDetail == null || sourceDetail == null) { + return false + } + if (typeof destinationDetail.get('parentFolderId') === 'number' && typeof sourceDetail.get('folderId') === 'number') { // Folder can't be its own parent if (sourceDetail.get('folderId') === destinationDetail.get('folderId')) { @@ -97,11 +107,12 @@ const isMoveAllowed = (sites, sourceDetail, destinationDetail) => { } // Ancestor folder can't be moved into a descendant let ancestorFolderIds = [] - getAncestorFolderIds(ancestorFolderIds, destinationDetail, sites) + getAncestorFolderIds(ancestorFolderIds, destinationDetail, folderList) if (ancestorFolderIds.includes(sourceDetail.get('folderId'))) { return false } } + return true } diff --git a/app/common/lib/bookmarkUtil.js b/app/common/lib/bookmarkUtil.js index 14dc35d8735..bb0d8e50eb1 100644 --- a/app/common/lib/bookmarkUtil.js +++ b/app/common/lib/bookmarkUtil.js @@ -171,6 +171,10 @@ const getToolbarBookmarks = (state) => { } const getDetailFromFrame = (frame) => { + if (frame == null) { + return null + } + return Immutable.fromJS({ location: frame.get('location'), title: frame.get('title'), @@ -204,6 +208,10 @@ const isLocationBookmarked = (state, location) => { * @return {Object} A createProperties plain JS object, not ImmutableJS */ const toCreateProperties = (bookmark) => { + if (bookmark == null) { + return null + } + return { url: bookmark.get('location'), partitionNumber: bookmark.get('partitionNumber') @@ -225,6 +233,10 @@ const getBookmarksByParentId = (state, folderKey) => { } const isBookmark = (bookmark) => { + if (bookmark == null) { + return false + } + return bookmark.get('type') === siteTags.BOOKMARK } diff --git a/app/common/lib/historyUtil.js b/app/common/lib/historyUtil.js index ebb70da00e5..297dc04d48f 100644 --- a/app/common/lib/historyUtil.js +++ b/app/common/lib/historyUtil.js @@ -78,9 +78,11 @@ const prepareHistoryEntry = (siteDetail) => { objectId: undefined, title: siteDetail.get('title'), location: siteDetail.get('location'), + partitionNumber: ~~siteDetail.get('partitionNumber', 0), count: 1, themeColor: siteDetail.get('themeColor'), - favicon: siteDetail.get('favicon', siteDetail.get('icon')) + favicon: siteDetail.get('favicon', siteDetail.get('icon')), + key: getKey(siteDetail) }) } @@ -95,6 +97,7 @@ const mergeSiteDetails = (oldDetail, newDetail) => { objectId, title: newDetail.get('title'), location: newDetail.get('location'), + partitionNumber: ~~newDetail.get('partitionNumber', 0), count: ~~oldDetail.get('count', 0) + 1 }) @@ -111,6 +114,8 @@ const mergeSiteDetails = (oldDetail, newDetail) => { site = site.set('favicon', favicon) } + site = site.set('key', getKey(site)) + return site } diff --git a/app/common/lib/pinnedSitesUtil.js b/app/common/lib/pinnedSitesUtil.js index ea03b22637d..f756a1ce77b 100644 --- a/app/common/lib/pinnedSitesUtil.js +++ b/app/common/lib/pinnedSitesUtil.js @@ -20,7 +20,7 @@ const getSitesBySubkey = (sites, siteKey) => { const getDetailsFromTab = (sites, tab) => { let location = tab.get('url') - const partitionNumber = tab.get('partitionNumber') + const partitionNumber = tab.get('partitionNumber', 0) let parentFolderId // TODO check if needed https://github.com/brave/browser-laptop/pull/8588 @@ -60,8 +60,7 @@ const getDetailsFromTab = (sites, tab) => { title: tab.get('title') } - // TODO I think that we don't need this one - if (partitionNumber) { + if (partitionNumber != null) { siteDetail.partitionNumber = partitionNumber } diff --git a/app/common/state/bookmarksState.js b/app/common/state/bookmarksState.js index a9272a441aa..3ff7a0b1e1f 100644 --- a/app/common/state/bookmarksState.js +++ b/app/common/state/bookmarksState.js @@ -93,8 +93,9 @@ const bookmarksState = { if (dataItem.isEmpty()) { // check if we have data in tabs const tab = tabState.getActiveTab(state) || Immutable.Map() + const activeLocation = tab.get('url') || tab.getIn(['frame', 'location']) - if (!tab.isEmpty() && bookmarkDetail.get('location') === tab.get('url')) { + if (!tab.isEmpty() && bookmarkDetail.get('location') === activeLocation) { dataItem = makeImmutable({ partitionNumber: tab.getIn(['frame', 'partitionNumber'], 0), favicon: tab.getIn(['frame', 'icon']), diff --git a/app/common/state/historyState.js b/app/common/state/historyState.js index 2211393a0df..3185c59d229 100644 --- a/app/common/state/historyState.js +++ b/app/common/state/historyState.js @@ -52,8 +52,6 @@ const historyState = { }, removeSite: (state, siteKey) => { - // TODO should we remove this only when a tab with this siteKey is not opened - // if not, we are deleting data that is use for bookmarking return state.deleteIn([STATE_SITES.HISTORY_SITES, siteKey]) }, diff --git a/app/common/state/pinnedSitesState.js b/app/common/state/pinnedSitesState.js index e2f47268894..0fd0285fdfc 100644 --- a/app/common/state/pinnedSitesState.js +++ b/app/common/state/pinnedSitesState.js @@ -15,6 +15,17 @@ const validateState = function (state) { return state } +const reorderSite = (sites, order) => { + sites = sites.map((site) => { + const siteOrder = site.get('order') + if (siteOrder > order) { + return site.set('order', siteOrder - 1) + } + return site + }) + return sites +} + const pinnedSiteState = { getSites: (state) => { state = validateState(state) @@ -73,7 +84,9 @@ const pinnedSiteState = { return state } - // TODO update order, so that is up to date + if (siteDetail.get('order') != null) { + state = state.set(STATE_SITES.PINNED_SITES, reorderSite(pinnedSiteState.getSites(state), siteDetail.get('order'))) + } return state.deleteIn(stateKey, site) }, diff --git a/app/filtering.js b/app/filtering.js index 553a540d5df..4f7b2921bde 100644 --- a/app/filtering.js +++ b/app/filtering.js @@ -32,7 +32,7 @@ const {adBlockResourceName} = require('./adBlock') const {updateElectronDownloadItem} = require('./browser/electronDownloadItem') const {fullscreenOption} = require('./common/constants/settingsEnums') const isThirdPartyHost = require('./browser/isThirdPartyHost') -var extensionState = require('./common/state/extensionState.js') +const extensionState = require('./common/state/extensionState') const {cookieExceptions, refererExceptions} = require('../js/data/siteHacks') let appStore = null diff --git a/test/bookmark-components/bookmarksToolbarTest.js b/test/bookmark-components/bookmarksToolbarTest.js index ec7293617ca..9c5b951e5e3 100644 --- a/test/bookmark-components/bookmarksToolbarTest.js +++ b/test/bookmark-components/bookmarksToolbarTest.js @@ -166,6 +166,7 @@ describe('bookmarksToolbar', function () { .waitForBookmarkDetail(pageWithFavicon, pageWithFavicon.replace(/http:\/\//, '')) .waitForEnabled(doneButton) .click(doneButton) + .click('[data-test-id="bookmarkToolbarButton"]') yield this.app.client.waitUntil(() => this.app.client.getCssProperty('[data-test-id="bookmarkFavicon"]', 'background-image').then((backgroundImage) => diff --git a/test/unit/app/browser/reducers/bookmarkFoldersReducerTest.js b/test/unit/app/browser/reducers/bookmarkFoldersReducerTest.js index 39f127c446c..b50a395051c 100644 --- a/test/unit/app/browser/reducers/bookmarkFoldersReducerTest.js +++ b/test/unit/app/browser/reducers/bookmarkFoldersReducerTest.js @@ -313,7 +313,20 @@ describe('bookmarkFoldersReducer unit test', function () { assert.deepEqual(state, newState) }) - it('check if delete is working', function () { + it('folder key is list (multiple folders)', function () { + spy = sinon.spy(bookmarkFoldersState, 'removeFolder') + const newState = bookmarkFoldersReducer(stateWithData, { + actionType: appConstants.APP_REMOVE_BOOKMARK_FOLDER, + folderKey: [ + '1', + '69' + ] + }) + assert.equal(spy.callCount, 2) + assert.deepEqual(newState.toJS(), state.toJS()) + }) + + it('folder key is map (single folder)', function () { spy = sinon.spy(bookmarkFoldersState, 'removeFolder') const newState = bookmarkFoldersReducer(stateWithData, { actionType: appConstants.APP_REMOVE_BOOKMARK_FOLDER, diff --git a/test/unit/app/browser/reducers/historyReducerTest.js b/test/unit/app/browser/reducers/historyReducerTest.js new file mode 100644 index 00000000000..5aad0fc93c4 --- /dev/null +++ b/test/unit/app/browser/reducers/historyReducerTest.js @@ -0,0 +1,444 @@ +/* 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 https://mozilla.org/MPL/2.0/. */ + +/* global describe, it, before, after, afterEach */ +const mockery = require('mockery') +const Immutable = require('immutable') +const assert = require('assert') +const sinon = require('sinon') + +const appConstants = require('../../../../../js/constants/appConstants') +require('../../../braveUnit') + +describe('historyReducer unit test', function () { + let historyReducer, historyState, aboutHistoryState + + const state = Immutable.fromJS({ + windows: [], + bookmarks: {}, + bookmarkFolders: {}, + cache: {}, + historySites: {}, + tabs: [], + about: { + history: { + entries: [], + updatedStamp: 0 + } + } + }) + + const stateWithData = Immutable.fromJS({ + windows: [], + bookmarks: {}, + bookmarkFolders: {}, + cache: {}, + historySites: { + 'https://clifton.io/|0': { + count: 1, + favicon: undefined, + key: 'https://clifton.io/|0', + lastAccessedTime: 0, + location: 'https://clifton.io/', + objectId: undefined, + partitionNumber: 0, + themeColor: undefined, + title: 'Clifton' + }, + 'https://brave.com/|0': { + count: 1, + favicon: undefined, + key: 'https://brave.com/|0', + lastAccessedTime: 0, + location: 'https://brave.com/', + objectId: undefined, + partitionNumber: 0, + themeColor: undefined, + title: 'Brave' + } + }, + tabs: [], + about: { + history: { + entries: [ + { + count: 1, + favicon: undefined, + key: 'https://clifton.io/|0', + lastAccessedTime: 0, + location: 'https://clifton.io/', + objectId: undefined, + partitionNumber: 0, + themeColor: undefined, + title: 'Clifton' + }, + { + count: 1, + favicon: undefined, + key: 'https://brave.com/|0', + lastAccessedTime: 0, + location: 'https://brave.com/', + objectId: undefined, + partitionNumber: 0, + themeColor: undefined, + title: 'Brave' + } + ] + } + } + }) + + before(function () { + this.clock = sinon.useFakeTimers() + this.clock.tick(0) + mockery.enable({ + warnOnReplace: false, + warnOnUnregistered: false, + useCleanCache: true + }) + mockery.registerMock('../../filtering', { + clearHistory: () => { + } + }) + historyReducer = require('../../../../../app/browser/reducers/historyReducer') + historyState = require('../../../../../app/common/state/historyState') + aboutHistoryState = require('../../../../../app/common/state/aboutHistoryState') + }) + + after(function () { + mockery.disable() + this.clock.restore() + }) + + describe('APP_ON_CLEAR_BROWSING_DATA', function () { + let spy + + afterEach(function () { + spy.restore() + }) + + it('null case', function () { + spy = sinon.spy(historyState, 'clearSites') + const newState = historyReducer(state, { + actionType: appConstants.APP_ON_CLEAR_BROWSING_DATA + }) + assert.equal(spy.notCalled, true) + assert.deepEqual(newState.toJS(), state.toJS()) + }) + + it('clearBrowsingDataDefaults is provided', function () { + spy = sinon.spy(historyState, 'clearSites') + const initState = stateWithData.set('clearBrowsingDataDefaults', Immutable.fromJS({ + browserHistory: true + })) + const newState = historyReducer(initState, { + actionType: appConstants.APP_ON_CLEAR_BROWSING_DATA + }) + const expectedState = state.set('clearBrowsingDataDefaults', Immutable.fromJS({ + browserHistory: true + })) + assert.equal(spy.calledOnce, true) + assert.deepEqual(newState.toJS(), expectedState.toJS()) + }) + + it('tempClearBrowsingData is provided', function () { + spy = sinon.spy(historyState, 'clearSites') + const initState = stateWithData.set('tempClearBrowsingData', Immutable.fromJS({ + browserHistory: true + })) + const newState = historyReducer(initState, { + actionType: appConstants.APP_ON_CLEAR_BROWSING_DATA + }) + const expectedState = state.set('tempClearBrowsingData', Immutable.fromJS({ + browserHistory: true + })) + assert.equal(spy.calledOnce, true) + assert.deepEqual(newState.toJS(), expectedState.toJS()) + }) + + it('both are provided', function () { + spy = sinon.spy(historyState, 'clearSites') + const initState = stateWithData + .set('tempClearBrowsingData', Immutable.fromJS({ + browserHistory: true + })) + .set('clearBrowsingDataDefaults', Immutable.fromJS({ + browserHistory: false + })) + const newState = historyReducer(initState, { + actionType: appConstants.APP_ON_CLEAR_BROWSING_DATA + }) + const expectedState = state + .set('tempClearBrowsingData', Immutable.fromJS({ + browserHistory: true + })) + .set('clearBrowsingDataDefaults', Immutable.fromJS({ + browserHistory: false + })) + assert.equal(spy.calledOnce, true) + assert.deepEqual(newState.toJS(), expectedState.toJS()) + }) + }) + + describe('APP_ADD_HISTORY_SITE', function () { + let spy, spyAbout + + afterEach(function () { + spy.restore() + spyAbout.restore() + }) + + it('null case', function () { + spy = sinon.spy(historyState, 'addSite') + spyAbout = sinon.spy(aboutHistoryState, 'setHistory') + const newState = historyReducer(state, { + actionType: appConstants.APP_ADD_HISTORY_SITE + }) + assert.equal(spy.notCalled, true) + assert.equal(spyAbout.notCalled, true) + assert.deepEqual(newState.toJS(), state.toJS()) + }) + + it('siteDetail is a list', function () { + spy = sinon.spy(historyState, 'addSite') + spyAbout = sinon.spy(aboutHistoryState, 'setHistory') + const newState = historyReducer(state, { + actionType: appConstants.APP_ADD_HISTORY_SITE, + siteDetail: [ + { + location: 'https://clifton.io/', + title: 'Clifton' + }, + { + location: 'https://brave.com/', + title: 'Brave' + } + ] + }) + const expectedState = state + .set('historySites', Immutable.fromJS({ + 'https://clifton.io/|0': { + count: 1, + favicon: undefined, + key: 'https://clifton.io/|0', + lastAccessedTime: 0, + location: 'https://clifton.io/', + objectId: undefined, + partitionNumber: 0, + themeColor: undefined, + title: 'Clifton' + }, + 'https://brave.com/|0': { + count: 1, + favicon: undefined, + key: 'https://brave.com/|0', + lastAccessedTime: 0, + location: 'https://brave.com/', + objectId: undefined, + partitionNumber: 0, + themeColor: undefined, + title: 'Brave' + } + })) + .setIn(['about', 'history'], Immutable.fromJS({ + entries: [ + { + count: 1, + favicon: undefined, + key: 'https://clifton.io/|0', + lastAccessedTime: 0, + location: 'https://clifton.io/', + objectId: undefined, + partitionNumber: 0, + themeColor: undefined, + title: 'Clifton' + }, + { + count: 1, + favicon: undefined, + key: 'https://brave.com/|0', + lastAccessedTime: 0, + location: 'https://brave.com/', + objectId: undefined, + partitionNumber: 0, + themeColor: undefined, + title: 'Brave' + } + ], + updatedStamp: 0 + })) + assert.equal(spy.callCount, 2) + assert.equal(spyAbout.calledOnce, true) + assert.deepEqual(newState.toJS(), expectedState.toJS()) + }) + + it('siteDetail is a map', function () { + spy = sinon.spy(historyState, 'addSite') + spyAbout = sinon.spy(aboutHistoryState, 'setHistory') + const newState = historyReducer(state, { + actionType: appConstants.APP_ADD_HISTORY_SITE, + siteDetail: { + location: 'https://clifton.io/', + title: 'Clifton' + } + }) + const expectedState = state + .set('historySites', Immutable.fromJS({ + 'https://clifton.io/|0': { + count: 1, + favicon: undefined, + key: 'https://clifton.io/|0', + lastAccessedTime: 0, + location: 'https://clifton.io/', + objectId: undefined, + partitionNumber: 0, + themeColor: undefined, + title: 'Clifton' + } + })) + .setIn(['about', 'history'], Immutable.fromJS({ + entries: [ + { + count: 1, + favicon: undefined, + key: 'https://clifton.io/|0', + lastAccessedTime: 0, + location: 'https://clifton.io/', + objectId: undefined, + partitionNumber: 0, + themeColor: undefined, + title: 'Clifton' + } + ], + updatedStamp: 0 + })) + assert.equal(spy.calledOnce, true) + assert.equal(spyAbout.calledOnce, true) + assert.deepEqual(newState.toJS(), expectedState.toJS()) + }) + }) + + describe('APP_REMOVE_HISTORY_SITE', function () { + let spy, spyAbout + + afterEach(function () { + spy.restore() + spyAbout.restore() + }) + + it('null case', function () { + spy = sinon.spy(historyState, 'removeSite') + spyAbout = sinon.spy(aboutHistoryState, 'setHistory') + const newState = historyReducer(state, { + actionType: appConstants.APP_REMOVE_HISTORY_SITE + }) + assert.equal(spy.notCalled, true) + assert.equal(spyAbout.notCalled, true) + assert.deepEqual(newState.toJS(), state.toJS()) + }) + + it('historyKey is a list', function () { + spy = sinon.spy(historyState, 'removeSite') + spyAbout = sinon.spy(aboutHistoryState, 'setHistory') + const newState = historyReducer(stateWithData, { + actionType: appConstants.APP_REMOVE_HISTORY_SITE, + historyKey: [ + 'https://clifton.io/|0', + 'https://brave.com/|0' + ] + }) + assert.equal(spy.callCount, 2) + assert.equal(spyAbout.calledOnce, true) + assert.deepEqual(newState.toJS(), state.toJS()) + }) + + it('historyKey is a map', function () { + spy = sinon.spy(historyState, 'removeSite') + spyAbout = sinon.spy(aboutHistoryState, 'setHistory') + const newState = historyReducer(stateWithData, { + actionType: appConstants.APP_REMOVE_HISTORY_SITE, + historyKey: 'https://brave.com/|0' + }) + const expectedState = state + .set('historySites', Immutable.fromJS({ + 'https://clifton.io/|0': { + count: 1, + favicon: undefined, + key: 'https://clifton.io/|0', + lastAccessedTime: 0, + location: 'https://clifton.io/', + objectId: undefined, + partitionNumber: 0, + themeColor: undefined, + title: 'Clifton' + } + })) + .setIn(['about', 'history'], Immutable.fromJS({ + entries: [ + { + count: 1, + favicon: undefined, + key: 'https://clifton.io/|0', + lastAccessedTime: 0, + location: 'https://clifton.io/', + objectId: undefined, + partitionNumber: 0, + themeColor: undefined, + title: 'Clifton' + } + ], + updatedStamp: 0 + })) + assert.equal(spy.calledOnce, true) + assert.equal(spyAbout.calledOnce, true) + assert.deepEqual(newState.toJS(), expectedState.toJS()) + }) + }) + + describe('APP_POPULATE_HISTORY', function () { + let spy + + afterEach(function () { + spy.restore() + }) + + it('is working', function () { + spy = sinon.spy(aboutHistoryState, 'setHistory') + const customState = stateWithData.delete('about') + let newState = historyReducer(customState, { + actionType: appConstants.APP_POPULATE_HISTORY + }) + const expectedState = stateWithData + .setIn(['about', 'history', 'entries'], Immutable.fromJS([ + { + lastAccessedTime: 0, + objectId: undefined, + count: 1, + favicon: undefined, + key: 'https://clifton.io/|0', + location: 'https://clifton.io/', + partitionNumber: 0, + themeColor: undefined, + title: 'Clifton' + }, + { + lastAccessedTime: 0, + objectId: undefined, + count: 1, + favicon: undefined, + key: 'https://brave.com/|0', + location: 'https://brave.com/', + partitionNumber: 0, + themeColor: undefined, + title: 'Brave' + } + ])) + .setIn(['about', 'history', 'updatedStamp'], 0) + + newState = newState.setIn(['about', 'history', 'updatedStamp'], 0) + assert.equal(spy.calledOnce, true) + assert.deepEqual(newState.toJS(), expectedState.toJS()) + }) + }) +}) diff --git a/test/unit/app/browser/reducers/pinnedSitesReducerTest.js b/test/unit/app/browser/reducers/pinnedSitesReducerTest.js new file mode 100644 index 00000000000..89dd49ac93c --- /dev/null +++ b/test/unit/app/browser/reducers/pinnedSitesReducerTest.js @@ -0,0 +1,232 @@ +/* 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 https://mozilla.org/MPL/2.0/. */ + +/* global describe, it, before, after, afterEach */ +const mockery = require('mockery') +const Immutable = require('immutable') +const assert = require('assert') +const sinon = require('sinon') + +const appConstants = require('../../../../../js/constants/appConstants') +require('../../../braveUnit') + +describe('pinnedSitesReducer unit test', function () { + let pinnedSitesReducer, pinnedSitesState + + const state = Immutable.fromJS({ + windows: [], + cache: {}, + tabs: [{ + tabId: 1, + windowId: 1, + windowUUID: 'uuid', + url: 'https://brave.com/', + title: 'Brave' + }], + tabsInternal: { + index: { + 1: 0 + } + }, + pinnedSites: {} + }) + + const stateWithData = Immutable.fromJS({ + windows: [], + cache: {}, + tabs: [{ + tabId: 1, + windowId: 1, + windowUUID: 'uuid', + url: 'https://brave.com/', + title: 'Brave' + }], + tabsInternal: { + index: { + 1: 0 + } + }, + pinnedSites: { + 'https://brave.com/|0': { + location: 'https://brave.com/', + order: 0, + title: 'Brave', + partitionNumber: 0 + }, + 'https://clifton.io/|0': { + location: 'https://clifton.io/', + order: 1, + title: 'Clifton', + partitionNumber: 0 + } + } + }) + + before(function () { + mockery.enable({ + warnOnReplace: false, + warnOnUnregistered: false, + useCleanCache: true + }) + pinnedSitesReducer = require('../../../../../app/browser/reducers/pinnedSitesReducer') + pinnedSitesState = require('../../../../../app/common/state/pinnedSitesState') + }) + + after(function () { + mockery.disable() + }) + + describe('APP_TAB_UPDATED', function () { + let spyAdd, spyRemove + + afterEach(function () { + spyAdd.restore() + spyRemove.restore() + }) + + it('null case', function () { + spyAdd = sinon.spy(pinnedSitesState, 'addPinnedSite') + spyRemove = sinon.spy(pinnedSitesState, 'removePinnedSite') + const newState = pinnedSitesReducer(state, { + actionType: appConstants.APP_TAB_UPDATED + }) + assert.equal(spyAdd.notCalled, true) + assert.equal(spyRemove.notCalled, true) + assert.deepEqual(newState.toJS(), state.toJS()) + }) + + it('tab doesnt exist', function () { + spyAdd = sinon.spy(pinnedSitesState, 'addPinnedSite') + spyRemove = sinon.spy(pinnedSitesState, 'removePinnedSite') + const newState = pinnedSitesReducer(state, { + actionType: appConstants.APP_TAB_UPDATED, + changeInfo: { + pinned: { + test: 0 + } + } + }) + assert.equal(spyAdd.notCalled, true) + assert.equal(spyRemove.notCalled, true) + assert.deepEqual(newState.toJS(), state.toJS()) + }) + + it('add pinned site', function () { + spyAdd = sinon.spy(pinnedSitesState, 'addPinnedSite') + spyRemove = sinon.spy(pinnedSitesState, 'removePinnedSite') + const newState = pinnedSitesReducer(state, { + actionType: appConstants.APP_TAB_UPDATED, + changeInfo: { + pinned: true + }, + tabValue: { + tabId: 1 + } + }) + const expectedState = state.setIn(['pinnedSites', 'https://brave.com/|0'], Immutable.fromJS({ + location: 'https://brave.com/', + order: 0, + title: 'Brave', + partitionNumber: 0 + })) + assert.equal(spyAdd.calledOnce, true) + assert.equal(spyRemove.notCalled, true) + assert.deepEqual(newState.toJS(), expectedState.toJS()) + }) + + it('remove pinned site', function () { + spyAdd = sinon.spy(pinnedSitesState, 'addPinnedSite') + spyRemove = sinon.spy(pinnedSitesState, 'removePinnedSite') + const newState = pinnedSitesReducer(state, { + actionType: appConstants.APP_TAB_UPDATED, + changeInfo: { + pinned: false + }, + tabValue: { + tabId: 1 + } + }) + assert.equal(spyAdd.notCalled, true) + assert.equal(spyRemove.calledOnce, true) + assert.deepEqual(newState.toJS(), state.toJS()) + }) + }) + + describe('APP_CREATE_TAB_REQUESTED', function () { + let spy + + afterEach(function () { + spy.restore() + }) + + it('null case', function () { + spy = sinon.spy(pinnedSitesState, 'addPinnedSite') + const newState = pinnedSitesReducer(state, { + actionType: appConstants.APP_CREATE_TAB_REQUESTED + }) + assert.equal(spy.notCalled, true) + assert.deepEqual(newState.toJS(), state.toJS()) + }) + + it('add pinned site', function () { + spy = sinon.spy(pinnedSitesState, 'addPinnedSite') + const newState = pinnedSitesReducer(state, { + actionType: appConstants.APP_CREATE_TAB_REQUESTED, + createProperties: { + pinned: true, + url: 'https://brave.com/' + } + }) + const expectedState = state.setIn(['pinnedSites', 'https://brave.com/|0'], Immutable.fromJS({ + location: 'https://brave.com/', + order: 0 + })) + assert.equal(spy.calledOnce, true) + assert.deepEqual(newState.toJS(), expectedState.toJS()) + }) + }) + + describe('APP_ON_PINNED_TAB_REORDER', function () { + let spy + + afterEach(function () { + spy.restore() + }) + + it('null case', function () { + spy = sinon.spy(pinnedSitesState, 'reOrderSite') + const newState = pinnedSitesReducer(state, { + actionType: appConstants.APP_ON_PINNED_TAB_REORDER + }) + assert.equal(spy.notCalled, true) + assert.deepEqual(newState.toJS(), state.toJS()) + }) + + it('check if works', function () { + spy = sinon.spy(pinnedSitesState, 'reOrderSite') + const newState = pinnedSitesReducer(stateWithData, { + actionType: appConstants.APP_ON_PINNED_TAB_REORDER, + siteKey: 'https://clifton.io/|0', + destinationKey: 'https://brave.com/|0', + prepend: true + }) + const expectedState = state.setIn(['pinnedSites'], Immutable.fromJS({ + 'https://clifton.io/|0': { + location: 'https://clifton.io/', + order: 0, + title: 'Clifton', + partitionNumber: 0 + }, + 'https://brave.com/|0': { + location: 'https://brave.com/', + order: 1, + title: 'Brave', + partitionNumber: 0 + } + })) + assert.equal(spy.calledOnce, true) + assert.deepEqual(newState.toJS(), expectedState.toJS()) + }) + }) +}) diff --git a/test/unit/app/common/cache/bookmarkLocationCacheTest.js b/test/unit/app/common/cache/bookmarkLocationCacheTest.js index cab3df5fbb9..a29cd6c8f81 100644 --- a/test/unit/app/common/cache/bookmarkLocationCacheTest.js +++ b/test/unit/app/common/cache/bookmarkLocationCacheTest.js @@ -1,7 +1,11 @@ +/* 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 siteTags = require('../../../../../js/constants/siteTags') -const siteCache = require('../../../../../app/common/cache/bookmarkLocationCache') +const bookmarkLocationCache = require('../../../../../app/common/cache/bookmarkLocationCache') const {STATE_SITES} = require('../../../../../js/constants/stateConstants') const assert = require('assert') const Immutable = require('immutable') @@ -31,25 +35,33 @@ describe('bookmarkLocationCache unit test', function () { const expectedCache = { [bookmark.get('location')]: [bookmarkKey] } - const state = siteCache.generateCache(baseState) + const state = bookmarkLocationCache.generateCache(baseState) assert.deepEqual(state.getIn(['cache', 'bookmarkLocation']).toJS(), expectedCache) }) + it('dont generate cache if already exists', function () { + const newCache = { + 'https://clifton.io': ['https://clifton.io|0|0'] + } + const newState = baseState.setIn(['cache', 'bookmarkLocation'], Immutable.fromJS(newCache)) + const state = bookmarkLocationCache.generateCache(newState) + assert.deepEqual(state.getIn(['cache', 'bookmarkLocation']).toJS(), newCache) + }) }) describe('getCacheKey', function () { it('returns cached siteKeys', function () { - const state = siteCache.generateCache(baseState) - const cachedKeys = siteCache.getCacheKey(state, bookmark.get('location')) + const state = bookmarkLocationCache.generateCache(baseState) + const cachedKeys = bookmarkLocationCache.getCacheKey(state, bookmark.get('location')) assert.deepEqual(cachedKeys.toJS(), [bookmarkKey]) }) it('returns null when location is not cached', function () { - const state = siteCache.generateCache(baseState) - const cachedKeys = siteCache.getCacheKey(state, 'https://archive.org') + const state = bookmarkLocationCache.generateCache(baseState) + const cachedKeys = bookmarkLocationCache.getCacheKey(state, 'https://archive.org') assert.equal(cachedKeys, Immutable.List()) }) it('returns null when location is undefined', function () { - const state = siteCache.generateCache(baseState) - const cachedKeys = siteCache.getCacheKey(state, undefined) + const state = bookmarkLocationCache.generateCache(baseState) + const cachedKeys = bookmarkLocationCache.getCacheKey(state, undefined) assert.equal(cachedKeys, Immutable.List()) }) }) @@ -64,9 +76,9 @@ describe('bookmarkLocationCache unit test', function () { type: siteTags.BOOKMARK }) const siteKey = bookmarkUtil.getKey(site) - let state = siteCache.generateCache(baseState) - state = siteCache.addCacheKey(state, bookmarkLocation, siteKey) - const cachedKeys = siteCache.getCacheKey(state, bookmarkLocation) + let state = bookmarkLocationCache.generateCache(baseState) + state = bookmarkLocationCache.addCacheKey(state, bookmarkLocation, siteKey) + const cachedKeys = bookmarkLocationCache.getCacheKey(state, bookmarkLocation) assert.deepEqual(cachedKeys.toJS(), [bookmarkKey, siteKey]) }) it('when location is new, it creates a list with the key', function () { @@ -78,19 +90,25 @@ describe('bookmarkLocationCache unit test', function () { type: siteTags.BOOKMARK }) const siteKey = bookmarkUtil.getKey(site) - let state = siteCache.generateCache(baseState) - state = siteCache.addCacheKey(state, location, siteKey) - const cachedKeys = siteCache.getCacheKey(state, location) + let state = bookmarkLocationCache.generateCache(baseState) + state = bookmarkLocationCache.addCacheKey(state, location, siteKey) + const cachedKeys = bookmarkLocationCache.getCacheKey(state, location) assert.deepEqual(cachedKeys.toJS(), [siteKey]) }) it('when location is undefined, it no-ops', function () { - const state = siteCache.addCacheKey(baseState, undefined, '1') + const state = bookmarkLocationCache.addCacheKey(baseState, undefined, '1') assert.deepEqual(state.toJS(), baseState.toJS()) }) it('when siteKey is undefined, it no-ops', function () { - const state = siteCache.addCacheKey(baseState, testUrl1, undefined) + const state = bookmarkLocationCache.addCacheKey(baseState, testUrl1, undefined) assert.deepEqual(state.toJS(), baseState.toJS()) }) + it('when location is already cached and key already exists', function () { + let state = bookmarkLocationCache.generateCache(baseState) + state = bookmarkLocationCache.addCacheKey(state, bookmarkLocation, bookmarkKey) + const cachedKeys = bookmarkLocationCache.getCacheKey(state, bookmarkLocation) + assert.deepEqual(cachedKeys.toJS(), [bookmarkKey]) + }) }) describe('removeCacheKey', function () { @@ -105,22 +123,34 @@ describe('bookmarkLocationCache unit test', function () { }) const siteKey = bookmarkUtil.getKey(site) let state = baseState.setIn([STATE_SITES.BOOKMARKS, siteKey], site) - state = siteCache.generateCache(state) + state = bookmarkLocationCache.generateCache(state) // Sanity - let cachedKeys = siteCache.getCacheKey(state, bookmarkLocation) + let cachedKeys = bookmarkLocationCache.getCacheKey(state, bookmarkLocation) assert.deepEqual(cachedKeys.toJS(), [bookmarkKey, siteKey]) - state = siteCache.removeCacheKey(state, bookmarkLocation, bookmarkKey) - cachedKeys = siteCache.getCacheKey(state, bookmarkLocation) + state = bookmarkLocationCache.removeCacheKey(state, bookmarkLocation, bookmarkKey) + cachedKeys = bookmarkLocationCache.getCacheKey(state, bookmarkLocation) assert.deepEqual(cachedKeys.toJS(), [siteKey]) }) it('when removing the last siteKey, removes location', function () { - let state = siteCache.generateCache(baseState) - state = siteCache.removeCacheKey(state, bookmarkLocation, bookmarkKey) - const cachedKeys = siteCache.getCacheKey(state, bookmarkLocation) + let state = bookmarkLocationCache.generateCache(baseState) + state = bookmarkLocationCache.removeCacheKey(state, bookmarkLocation, bookmarkKey) + const cachedKeys = bookmarkLocationCache.getCacheKey(state, bookmarkLocation) assert.deepEqual(cachedKeys, Immutable.List()) }) + it('when location is undefined, it no-ops', function () { + const state = bookmarkLocationCache.removeCacheKey(baseState, undefined, '1') + assert.deepEqual(state.toJS(), baseState.toJS()) + }) + it('when siteKey is undefined, it no-ops', function () { + const state = bookmarkLocationCache.removeCacheKey(baseState, testUrl1, undefined) + assert.deepEqual(state.toJS(), baseState.toJS()) + }) + it('when siteKey is not in the cache', function () { + const state = bookmarkLocationCache.removeCacheKey(baseState, testUrl1, '10') + assert.deepEqual(state.toJS(), baseState.toJS()) + }) }) }) diff --git a/test/unit/app/common/cache/bookmarkOrderCacheTest.js b/test/unit/app/common/cache/bookmarkOrderCacheTest.js new file mode 100644 index 00000000000..6f5a1a278c9 --- /dev/null +++ b/test/unit/app/common/cache/bookmarkOrderCacheTest.js @@ -0,0 +1,484 @@ +/* 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 bookmarkOrderCache = require('../../../../../app/common/cache/bookmarkOrderCache') +const siteTags = require('../../../../../js/constants/siteTags') + +const bookmark = Immutable.fromJS({ + lastAccessedTime: 123, + objectId: [210, 115], + type: siteTags.BOOKMARK, + location: 'https://brave.com/', + title: 'Brave', + parentFolderId: 1, + partitionNumber: 0, + key: 'https://brave.com/|0|1' +}) +const bookmark1 = Immutable.fromJS({ + lastAccessedTime: 123, + objectId: [210, 31], + type: siteTags.BOOKMARK, + location: 'https://clifton.io/', + title: 'Clifton', + parentFolderId: 1, + partitionNumber: 0, + key: 'https://clifton.io/|0|1' +}) +const bookmark2 = Immutable.fromJS({ + lastAccessedTime: 123, + objectId: [115, 31], + type: siteTags.BOOKMARK, + location: 'https://brianbondy.com/', + title: 'Bondy', + parentFolderId: 0, + partitionNumber: 0, + key: 'https://brianbondy.com/|0|0' +}) + +const folder = Immutable.fromJS({ + lastAccessedTime: 123, + objectId: [115, 31], + type: siteTags.BOOKMARK_FOLDER, + folderId: 1, + title: 'Folder', + parentFolderId: 1, + partitionNumber: 0, + key: '1' +}) +const folder1 = Immutable.fromJS({ + lastAccessedTime: 123, + objectId: [10, 31], + type: siteTags.BOOKMARK_FOLDER, + folderId: 2, + title: 'Folder 1', + parentFolderId: 0, + partitionNumber: 0, + key: '2' +}) +const bookmarkKey = bookmark.get('key') +const bookmarkKey1 = bookmark1.get('key') +const bookmarkKey2 = bookmark2.get('key') +const folderKey = folder.get('key') +const folderKey1 = folder1.get('key') +const baseState = Immutable.fromJS({ + bookmarks: { + [bookmarkKey]: bookmark, + [bookmarkKey1]: bookmark1, + [folderKey]: folder + }, + cache: { + bookmarkOrder: {} + } +}) +const stateWithData = Immutable.fromJS({ + bookmarks: { + [bookmarkKey]: bookmark, + [bookmarkKey1]: bookmark1, + [folderKey]: folder + }, + cache: { + bookmarkOrder: { + '1': [ + { + key: bookmarkKey, + order: 0, + type: siteTags.BOOKMARK + } + ] + } + } +}) +const stateLargeData = Immutable.fromJS({ + bookmarks: { + [bookmarkKey]: bookmark, + [bookmarkKey1]: bookmark1, + [bookmarkKey2]: bookmark2, + [folderKey]: folder, + [folderKey1]: folder1 + }, + cache: { + bookmarkOrder: { + '0': [ + { + key: bookmarkKey2, + order: 0, + type: siteTags.BOOKMARK + }, + { + key: folderKey1, + order: 1, + type: siteTags.BOOKMARK_FOLDER + } + ], + '1': [ + { + key: bookmarkKey, + order: 0, + type: siteTags.BOOKMARK + }, + { + key: bookmarkKey1, + order: 1, + type: siteTags.BOOKMARK + }, + { + key: folderKey, + order: 2, + type: siteTags.BOOKMARK_FOLDER + } + ] + } + } +}) + +describe('bookmarkOrderCache unit test', function () { + describe('addBookmarkToCache', function () { + it('key is not provided', function () { + const state = bookmarkOrderCache.addBookmarkToCache(baseState) + assert.deepEqual(state.toJS(), baseState.toJS()) + }) + + it('parentId doesnt exist in the list', function () { + const state = bookmarkOrderCache.addBookmarkToCache(baseState, 1, bookmarkKey) + const expectedState = baseState.setIn(['cache', 'bookmarkOrder', '1'], Immutable.fromJS([ + { + key: bookmarkKey, + order: 0, + type: siteTags.BOOKMARK + } + ])) + assert.deepEqual(state.toJS(), expectedState.toJS()) + }) + + it('destination key is not provided, but parentId exist in the list', function () { + const state = bookmarkOrderCache.addBookmarkToCache(stateWithData, 1, bookmarkKey1) + const expectedState = stateWithData.setIn(['cache', 'bookmarkOrder', '1'], Immutable.fromJS([ + { + key: bookmarkKey, + order: 0, + type: siteTags.BOOKMARK + }, + { + key: bookmarkKey1, + order: 1, + type: siteTags.BOOKMARK + } + ])) + assert.deepEqual(state.toJS(), expectedState.toJS()) + }) + + it('dont add cache if key already exist, but destination is not provided', function () { + const state = bookmarkOrderCache.addBookmarkToCache(stateWithData, 1, bookmarkKey) + assert.deepEqual(state.toJS(), stateWithData.toJS()) + }) + + it('destination key is provided', function () { + const state = bookmarkOrderCache.addBookmarkToCache(stateWithData, 1, bookmarkKey1, bookmarkKey) + const expectedState = stateWithData.setIn(['cache', 'bookmarkOrder', '1'], Immutable.fromJS([ + { + key: bookmarkKey, + order: 0, + type: siteTags.BOOKMARK + }, + { + key: bookmarkKey1, + order: 1, + type: siteTags.BOOKMARK + } + ])) + assert.deepEqual(state.toJS(), expectedState.toJS()) + }) + + it('destination key is provided, item should be prepend', function () { + const state = bookmarkOrderCache.addBookmarkToCache(stateWithData, 1, bookmarkKey1, bookmarkKey, false) + const expectedState = stateWithData.setIn(['cache', 'bookmarkOrder', '1'], Immutable.fromJS([ + { + key: bookmarkKey1, + order: 0, + type: siteTags.BOOKMARK + }, + { + key: bookmarkKey, + order: 1, + type: siteTags.BOOKMARK + } + ])) + assert.deepEqual(state.toJS(), expectedState.toJS()) + }) + }) + + describe('addFolderToCache', function () { + it('null case', function () { + const state = bookmarkOrderCache.addFolderToCache(baseState) + assert.deepEqual(state.toJS(), baseState.toJS()) + }) + + it('parentId doesnt exist in the list', function () { + const state = bookmarkOrderCache.addFolderToCache(baseState, 1, folderKey) + const expectedState = baseState.setIn(['cache', 'bookmarkOrder', '1'], Immutable.fromJS([ + { + key: folderKey, + order: 0, + type: siteTags.BOOKMARK_FOLDER + } + ])) + assert.deepEqual(state.toJS(), expectedState.toJS()) + }) + + it('destination key is not provided, but parentId exist in the list', function () { + const state = bookmarkOrderCache.addFolderToCache(stateWithData, 1, folderKey) + const expectedState = stateWithData.setIn(['cache', 'bookmarkOrder', '1'], Immutable.fromJS([ + { + key: bookmarkKey, + order: 0, + type: siteTags.BOOKMARK + }, + { + key: folderKey, + order: 1, + type: siteTags.BOOKMARK_FOLDER + } + ])) + assert.deepEqual(state.toJS(), expectedState.toJS()) + }) + + it('dont add cache if key already exist, but destination is not provided', function () { + const stateWithFolder = Immutable.fromJS({ + bookmarks: { + [bookmarkKey]: bookmark, + [bookmarkKey1]: bookmark1, + [folderKey]: folder + }, + cache: { + bookmarkOrder: { + '1': [ + { + key: folderKey, + order: 0, + type: siteTags.BOOKMARK_FOLDER + } + ] + } + } + }) + const state = bookmarkOrderCache.addFolderToCache(stateWithFolder, 1, folderKey) + assert.deepEqual(state.toJS(), stateWithFolder.toJS()) + }) + + it('destination key is provided', function () { + const state = bookmarkOrderCache.addFolderToCache(stateWithData, 1, folderKey, bookmarkKey) + const expectedState = stateWithData.setIn(['cache', 'bookmarkOrder', '1'], Immutable.fromJS([ + { + key: bookmarkKey, + order: 0, + type: siteTags.BOOKMARK + }, + { + key: folderKey, + order: 1, + type: siteTags.BOOKMARK_FOLDER + } + ])) + assert.deepEqual(state.toJS(), expectedState.toJS()) + }) + + it('destination key is provided, item should be prepend', function () { + const state = bookmarkOrderCache.addFolderToCache(stateWithData, 1, folderKey, bookmarkKey, false) + const expectedState = stateWithData.setIn(['cache', 'bookmarkOrder', '1'], Immutable.fromJS([ + { + key: folderKey, + order: 0, + type: siteTags.BOOKMARK_FOLDER + }, + { + key: bookmarkKey, + order: 1, + type: siteTags.BOOKMARK + } + ])) + assert.deepEqual(state.toJS(), expectedState.toJS()) + }) + }) + + describe('removeCacheKey', function () { + it('key is not provided', function () { + const state = bookmarkOrderCache.removeCacheKey(baseState) + assert.deepEqual(state.toJS(), baseState.toJS()) + }) + + it('cache is empty', function () { + const state = bookmarkOrderCache.removeCacheKey(baseState, 0, bookmarkKey) + assert.deepEqual(state.toJS(), baseState.toJS()) + }) + + it('cache has only one key', function () { + const state = bookmarkOrderCache.removeCacheKey(stateWithData, 1, bookmarkKey) + assert.deepEqual(state.toJS(), baseState.toJS()) + }) + + it('cache has multiple keys', function () { + const initState = stateWithData.setIn(['cache', 'bookmarkOrder', '1'], Immutable.fromJS([ + { + key: bookmarkKey, + order: 0, + type: siteTags.BOOKMARK + }, + { + key: folderKey, + order: 1, + type: siteTags.BOOKMARK_FOLDER + } + ])) + const state = bookmarkOrderCache.removeCacheKey(initState, 1, bookmarkKey) + const expectedState = baseState.setIn(['cache', 'bookmarkOrder', '1'], Immutable.fromJS([ + { + key: folderKey, + order: 0, + type: siteTags.BOOKMARK_FOLDER + } + ])) + assert.deepEqual(state.toJS(), expectedState.toJS()) + }) + }) + + describe('getFoldersByParentId', function () { + it('parentId is not provided (default is used)', function () { + const cache = bookmarkOrderCache.getFoldersByParentId(stateLargeData) + const expectedCache = [ + { + key: folderKey1, + order: 1, + type: siteTags.BOOKMARK_FOLDER + } + ] + assert.deepEqual(cache.toJS(), expectedCache) + }) + + it('if not found return empty list', function () { + const cache = bookmarkOrderCache.getFoldersByParentId(stateLargeData, 2) + assert.deepEqual(cache, Immutable.List()) + }) + + it('parentId is provided', function () { + const cache = bookmarkOrderCache.getFoldersByParentId(stateLargeData, 1) + const expectedCache = [ + { + key: folderKey, + order: 2, + type: siteTags.BOOKMARK_FOLDER + } + ] + assert.deepEqual(cache.toJS(), expectedCache) + }) + }) + + describe('getBookmarksByParentId', function () { + it('parentId is not provided (default is used)', function () { + const cache = bookmarkOrderCache.getBookmarksByParentId(stateLargeData) + const expectedCache = [ + { + key: bookmarkKey2, + order: 0, + type: siteTags.BOOKMARK + } + ] + assert.deepEqual(cache.toJS(), expectedCache) + }) + + it('if not found return empty list', function () { + const cache = bookmarkOrderCache.getBookmarksByParentId(stateLargeData, 2) + assert.deepEqual(cache, Immutable.List()) + }) + + it('parentId is provided', function () { + const cache = bookmarkOrderCache.getBookmarksByParentId(stateLargeData, 1) + const expectedCache = [ + { + key: bookmarkKey, + order: 0, + type: siteTags.BOOKMARK + }, + { + key: bookmarkKey1, + order: 1, + type: siteTags.BOOKMARK + } + ] + assert.deepEqual(cache.toJS(), expectedCache) + }) + }) + + describe('getBookmarksWithFolders', function () { + it('parentId is not provided (default is used)', function () { + const cache = bookmarkOrderCache.getBookmarksWithFolders(stateLargeData) + const expectedCache = [ + { + key: bookmarkKey2, + order: 0, + type: siteTags.BOOKMARK + }, + { + key: folderKey1, + order: 1, + type: siteTags.BOOKMARK_FOLDER + } + ] + assert.deepEqual(cache.toJS(), expectedCache) + }) + + it('if not found return empty list', function () { + const cache = bookmarkOrderCache.getBookmarksByParentId(stateLargeData, 2) + assert.deepEqual(cache, Immutable.List()) + }) + + it('parentId is provided', function () { + const cache = bookmarkOrderCache.getBookmarksWithFolders(stateLargeData, 1) + const expectedCache = [ + { + key: bookmarkKey, + order: 0, + type: siteTags.BOOKMARK + }, + { + key: bookmarkKey1, + order: 1, + type: siteTags.BOOKMARK + }, + { + key: folderKey, + order: 2, + type: siteTags.BOOKMARK_FOLDER + } + ] + assert.deepEqual(cache.toJS(), expectedCache) + }) + }) + + describe('removeCacheParent', function () { + it('null case', function () { + const state = bookmarkOrderCache.removeCacheParent(baseState) + assert.deepEqual(state.toJS(), baseState.toJS()) + }) + + it('parentId is provided', function () { + const state = bookmarkOrderCache.removeCacheParent(stateWithData, 1) + assert.deepEqual(state.toJS(), baseState.toJS()) + }) + }) + + describe('getOrderCache', function () { + it('cache doesnt exist', function () { + const cache = bookmarkOrderCache.getOrderCache(baseState) + assert.deepEqual(cache, Immutable.Map()) + }) + + it('cache exists', function () { + const state = bookmarkOrderCache.getOrderCache(stateWithData) + assert.deepEqual(state.toJS(), stateWithData.getIn(['cache', 'bookmarkOrder']).toJS()) + }) + }) +}) diff --git a/test/unit/app/common/lib/bookmarkFoldersUtilTest.js b/test/unit/app/common/lib/bookmarkFoldersUtilTest.js new file mode 100644 index 00000000000..d8f3167ac29 --- /dev/null +++ b/test/unit/app/common/lib/bookmarkFoldersUtilTest.js @@ -0,0 +1,198 @@ +/* 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 bookmarkFoldersUtil = require('../../../../../app/common/lib/bookmarkFoldersUtil') +const siteTags = require('../../../../../js/constants/siteTags') + +describe('bookmarkFoldersUtil unit test', function () { + describe('isFolderNameValid', function () { + it('null check', function () { + const valid = bookmarkFoldersUtil.isFolderNameValid() + assert.equal(valid, false) + }) + + it('title is an empty string', function () { + const valid = bookmarkFoldersUtil.isFolderNameValid('') + assert.equal(valid, false) + }) + + it('title is correct', function () { + const valid = bookmarkFoldersUtil.isFolderNameValid('folder') + assert.equal(valid, true) + }) + }) + + describe('getNextFolderId', function () { + it('null check', function () { + const id = bookmarkFoldersUtil.getNextFolderId() + assert.equal(id, 0) + }) + + it('folders list is empty', function () { + const id = bookmarkFoldersUtil.getNextFolderId(Immutable.List()) + assert.equal(id, 1) + }) + + it('folder list is ok', function () { + const id = bookmarkFoldersUtil.getNextFolderId(Immutable.fromJS([ + { folderId: 0 }, + { folderId: 1 }, + { folderId: 2 } + ])) + assert.equal(id, 3) + }) + }) + + describe('getNextFolderName', function () { + it('null check', function () { + const name = bookmarkFoldersUtil.getNextFolderName(null, 'name') + assert.equal(name, 'name') + }) + + it('name doesnt exist', function () { + const name = bookmarkFoldersUtil.getNextFolderName(Immutable.fromJS([ + { title: 'name' }, + { title: 'name 1' }, + { title: 'name 2' } + ]), 'name 3') + assert.equal(name, 'name 3') + }) + + it('name already exist', function () { + const name = bookmarkFoldersUtil.getNextFolderName(Immutable.fromJS([ + { title: 'name' }, + { title: 'newName' } + ]), 'newName') + assert.equal(name, 'newName (1)') + }) + + it('returns non first duplicate name from duplicate name', function () { + const name = bookmarkFoldersUtil.getNextFolderName(Immutable.fromJS([ + { title: 'name' }, + { title: 'name (1)' } + ]), 'name (1)') + assert.equal(name, 'name (2)') + }) + + it('multiple names exist', function () { + const name = bookmarkFoldersUtil.getNextFolderName(Immutable.fromJS([ + { title: 'name' }, + { title: 'newName' }, + { title: 'newName (1)' } + ]), 'newName') + assert.equal(name, 'newName (2)') + }) + }) + + describe('isFolder', function () { + it('null check', function () { + const valid = bookmarkFoldersUtil.isFolder() + assert.equal(valid, false) + }) + + it('type is bookmark', function () { + const valid = bookmarkFoldersUtil.isFolder(Immutable.fromJS({type: siteTags.BOOKMARK})) + assert.equal(valid, false) + }) + + it('type is bookmark folder', function () { + const valid = bookmarkFoldersUtil.isFolder(Immutable.fromJS({type: siteTags.BOOKMARK_FOLDER})) + assert.equal(valid, true) + }) + }) + + describe('getKey', function () { + it('null check', function () { + const valid = bookmarkFoldersUtil.getKey() + assert.equal(valid, null) + }) + + it('returns key if folderId matches', function () { + const siteDetail = Immutable.fromJS({ + folderId: 1 + }) + const key = bookmarkFoldersUtil.getKey(siteDetail) + assert.equal(key, 1) + }) + + it('returns null if folderId is missing', function () { + const siteDetail = new Immutable.Map() + const key = bookmarkFoldersUtil.getKey(siteDetail) + assert.equal(key, null) + }) + }) + + describe('isMoveAllowed', function () { + it('null check', function () { + const valid = bookmarkFoldersUtil.isMoveAllowed() + assert.equal(valid, false) + }) + + it('folder cant be its own parent', function () { + const folder = Immutable.fromJS({ + parentFolderId: 0, + folderId: 1 + }) + const valid = bookmarkFoldersUtil.isMoveAllowed(null, folder, folder) + assert.equal(valid, false) + }) + + it('ancestor folder cant be moved into a descendant', function () { + const source = Immutable.fromJS({ + parentFolderId: 0, + folderId: 1 + }) + const destination = Immutable.fromJS({ + parentFolderId: 3, + folderId: 4 + }) + const sites = Immutable.fromJS({ + '1': { + parentFolderId: 0, + folderId: 1 + }, + '2': { + parentFolderId: 1, + folderId: 2 + }, + '3': { + parentFolderId: 2, + folderId: 3 + }, + '4': { + parentFolderId: 3, + folderId: 4 + } + }) + const valid = bookmarkFoldersUtil.isMoveAllowed(sites, source, destination) + assert.equal(valid, false) + }) + + it('move is allow', function () { + const source = Immutable.fromJS({ + parentFolderId: 0, + folderId: 1 + }) + const destination = Immutable.fromJS({ + parentFolderId: 0, + folderId: 2 + }) + const sites = Immutable.fromJS({ + '1': { + parentFolderId: 0, + folderId: 1 + }, + '2': { + parentFolderId: 0, + folderId: 2 + } + }) + const valid = bookmarkFoldersUtil.isMoveAllowed(sites, source, destination) + assert.equal(valid, true) + }) + }) +}) diff --git a/test/unit/app/common/lib/bookmarkUtilTest.js b/test/unit/app/common/lib/bookmarkUtilTest.js index e02eeceeb21..a9f958edcf2 100644 --- a/test/unit/app/common/lib/bookmarkUtilTest.js +++ b/test/unit/app/common/lib/bookmarkUtilTest.js @@ -10,10 +10,11 @@ const bookmarkUtil = require('../../../../../app/common/lib/bookmarkUtil') const {makeImmutable} = require('../../../../../app/common/state/immutableUtil') const {bookmarksToolbarMode} = require('../../../../../app/common/constants/settingsEnums') const dragTypes = require('../../../../../js/constants/dragTypes') +const siteTags = require('../../../../../js/constants/siteTags') require('../../../braveUnit') -describe('bookmarkUtil test', function () { +describe('bookmarkUtil unit test', function () { describe('bookmarkHangerHeading', function () { it('returns default if isFolder and editKey are not provided', function () { assert.equal(bookmarkUtil.bookmarkHangerHeading(), 'bookmarkCreateNew') @@ -187,4 +188,135 @@ describe('bookmarkUtil test', function () { assert.deepEqual(result, Immutable.Map()) }) }) + + describe('getDetailFromFrame', function () { + it('null check', function () { + const siteDetail = bookmarkUtil.getDetailFromFrame() + assert.equal(siteDetail, null) + }) + + it('returns an Immutable object with all expected properties', function () { + const frame = Immutable.fromJS({ + location: 'https://brave.com', + title: 'test123', + partitionNumber: 8, + type: siteTags.BOOKMARK, + favicon: 'https://brave.comfavicon.ico' + }) + const siteDetail = bookmarkUtil.getDetailFromFrame(frame) + assert.equal(siteDetail.get('location'), frame.get('location')) + assert.equal(siteDetail.get('title'), frame.get('title')) + assert.equal(siteDetail.get('partitionNumber'), frame.get('partitionNumber')) + assert.equal(siteDetail.get('icon'), frame.get('icon')) + }) + }) + + describe('isLocationBookmarked', function () { + + }) + + describe('toCreateProperties', function () { + it('null check', function () { + const result = bookmarkUtil.toCreateProperties() + assert.equal(result, null) + }) + + it('returns a plain javascript object with location and partitionNumber', function () { + const siteDetail = Immutable.fromJS({ + location: 'https://brave.com', + partitionNumber: 5 + }) + const result = bookmarkUtil.toCreateProperties(siteDetail) + assert.equal(result.url, siteDetail.get('location')) + assert.equal(result.partitionNumber, siteDetail.get('partitionNumber')) + }) + }) + + describe('getBookmarksByParentId', function () { + + }) + + describe('isBookmark', function () { + it('null check', function () { + const valid = bookmarkUtil.isBookmark() + assert.equal(valid, false) + }) + + it('type is bookmark', function () { + const valid = bookmarkUtil.isBookmark(Immutable.fromJS({type: siteTags.BOOKMARK})) + assert.equal(valid, true) + }) + + it('type is bookmark folder', function () { + const valid = bookmarkUtil.isBookmark(Immutable.fromJS({type: siteTags.BOOKMARK_FOLDER})) + assert.equal(valid, false) + }) + }) + + describe('updateTabBookmarked', function () { + + }) + + describe('updateActiveTabBookmarked', function () { + + }) + + describe('getKey', function () { + const testUrl1 = 'https://brave.com' + + it('returns key if location and partitionNumber match', function () { + const siteDetail = Immutable.fromJS({ + location: testUrl1, + partitionNumber: 0 + }) + const key = bookmarkUtil.getKey(siteDetail) + assert.equal(key, testUrl1 + '|0|0') + }) + + it('returns key if location matches and partitionNumber is NOT present', function () { + const siteDetail = Immutable.fromJS({ + location: testUrl1 + }) + const key = bookmarkUtil.getKey(siteDetail) + assert.equal(key, testUrl1 + '|0|0') + }) + + it('returns null if location is missing', function () { + const siteDetail = new Immutable.Map() + const key = bookmarkUtil.getKey(siteDetail) + assert.equal(key, null) + }) + + describe('prevent collision', function () { + it('partition number', function () { + const siteA = Immutable.fromJS({ + location: testUrl1 + '1', + partitionNumber: 0 + }) + const siteB = Immutable.fromJS({ + location: testUrl1, + partitionNumber: 10 + }) + const keyA = bookmarkUtil.getKey(siteA) + const keyB = bookmarkUtil.getKey(siteB) + assert.notEqual(keyA, keyB) + }) + + it('parent folder id', function () { + const siteA = Immutable.fromJS({ + location: testUrl1 + '1', + partitionNumber: 0, + parentFolderId: 0 + }) + const siteB = Immutable.fromJS({ + location: testUrl1, + partitionNumber: 10, + parentFolderId: 0 + }) + const keyA = bookmarkUtil.getKey(siteA) + const keyB = bookmarkUtil.getKey(siteB) + assert.notEqual(keyA, keyB) + }) + }) + }) }) diff --git a/test/unit/lib/urlutilTest.js b/test/unit/lib/urlutilTest.js index 35ca6ee8bde..2bfa780a0bb 100644 --- a/test/unit/lib/urlutilTest.js +++ b/test/unit/lib/urlutilTest.js @@ -1,5 +1,5 @@ /* global describe, it */ -const UrlUtil = require('../../../js/lib/urlutil') +const urlUtil = require('../../../js/lib/urlutil') const assert = require('assert') require('../braveUnit') @@ -7,232 +7,232 @@ require('../braveUnit') describe('urlutil', function () { describe('getScheme', function () { it('null for empty', function () { - assert.equal(UrlUtil.getScheme('/file/path/to/file'), null) + assert.equal(urlUtil.getScheme('/file/path/to/file'), null) }) it('localhost: for localhost', function () { - assert.equal(UrlUtil.getScheme('localhost://127.0.0.1'), 'localhost:') + assert.equal(urlUtil.getScheme('localhost://127.0.0.1'), 'localhost:') }) it('gets scheme with :', function () { - assert.equal(UrlUtil.getScheme('data:datauri'), 'data:') + assert.equal(urlUtil.getScheme('data:datauri'), 'data:') }) it('host:port is not recognized as a scheme', function () { - assert.equal(UrlUtil.getScheme('localhost:8089'), null) + assert.equal(urlUtil.getScheme('localhost:8089'), null) }) it('gets scheme with ://', function () { - assert.equal(UrlUtil.getScheme('http://www.brave.com'), 'http://') + assert.equal(urlUtil.getScheme('http://www.brave.com'), 'http://') }) }) describe('prependScheme', function () { it('returns null when input is null', function () { - assert.equal(UrlUtil.prependScheme(null), null) + assert.equal(urlUtil.prependScheme(null), null) }) it('returns undefined when input is undefined', function () { - assert.equal(UrlUtil.prependScheme(), undefined) + assert.equal(urlUtil.prependScheme(), undefined) }) it('prepends file:// to absolute file path', function () { - assert.equal(UrlUtil.prependScheme('/file/path/to/file'), 'file:///file/path/to/file') + assert.equal(urlUtil.prependScheme('/file/path/to/file'), 'file:///file/path/to/file') }) it('defaults to http://', function () { - assert.equal(UrlUtil.prependScheme('www.brave.com'), 'http://www.brave.com') + assert.equal(urlUtil.prependScheme('www.brave.com'), 'http://www.brave.com') }) it('keeps schema if already exists', function () { - assert.equal(UrlUtil.prependScheme('https://www.brave.com'), 'https://www.brave.com') + assert.equal(urlUtil.prependScheme('https://www.brave.com'), 'https://www.brave.com') }) }) describe('isNotURL', function () { describe('returns false when input:', function () { it('is a valid URL', function () { - assert.equal(UrlUtil.isNotURL('brave.com'), false) + assert.equal(urlUtil.isNotURL('brave.com'), false) }) it('is an absolute file path without scheme', function () { - assert.equal(UrlUtil.isNotURL('/file/path/to/file'), false) + assert.equal(urlUtil.isNotURL('/file/path/to/file'), false) }) it('is an absolute file path with scheme', function () { - assert.equal(UrlUtil.isNotURL('file:///file/path/to/file'), false) + assert.equal(urlUtil.isNotURL('file:///file/path/to/file'), false) }) describe('for special pages', function () { it('is a data URI', function () { - assert.equal(UrlUtil.isNotURL('data:text/html,hi'), false) + assert.equal(urlUtil.isNotURL('data:text/html,hi'), false) }) it('is a view source URL', function () { - assert.equal(UrlUtil.isNotURL('view-source://url-here'), false) + assert.equal(urlUtil.isNotURL('view-source://url-here'), false) }) it('is a mailto link', function () { - assert.equal(UrlUtil.isNotURL('mailto:brian@brave.com'), false) + assert.equal(urlUtil.isNotURL('mailto:brian@brave.com'), false) }) it('is an about page', function () { - assert.equal(UrlUtil.isNotURL('about:preferences'), false) + assert.equal(urlUtil.isNotURL('about:preferences'), false) }) it('is a chrome-extension page', function () { - assert.equal(UrlUtil.isNotURL('chrome-extension://fmfcbgogabcbclcofgocippekhfcmgfj/cast_sender.js'), false) + assert.equal(urlUtil.isNotURL('chrome-extension://fmfcbgogabcbclcofgocippekhfcmgfj/cast_sender.js'), false) }) it('is a magnet URL', function () { - assert.equal(UrlUtil.isNotURL('chrome://gpu'), false) + assert.equal(urlUtil.isNotURL('chrome://gpu'), false) }) it('is a chrome page', function () { - assert.equal(UrlUtil.isNotURL('magnet:?xt=urn:sha1:YNCKHTQCWBTRNJIV4WNAE52SJUQCZO5C'), false) + assert.equal(urlUtil.isNotURL('magnet:?xt=urn:sha1:YNCKHTQCWBTRNJIV4WNAE52SJUQCZO5C'), false) }) }) it('contains a hostname and port number', function () { - assert.equal(UrlUtil.isNotURL('someBraveServer:8089'), false) + assert.equal(urlUtil.isNotURL('someBraveServer:8089'), false) }) it('starts or ends with whitespace', function () { - assert.equal(UrlUtil.isNotURL(' http://brave.com '), false) - assert.equal(UrlUtil.isNotURL('\n\nhttp://brave.com\n\n'), false) - assert.equal(UrlUtil.isNotURL('\t\thttp://brave.com\t\t'), false) + assert.equal(urlUtil.isNotURL(' http://brave.com '), false) + assert.equal(urlUtil.isNotURL('\n\nhttp://brave.com\n\n'), false) + assert.equal(urlUtil.isNotURL('\t\thttp://brave.com\t\t'), false) }) it('is a URL which contains basic auth user/pass', function () { - assert.equal(UrlUtil.isNotURL('http://username:password@example.com'), false) + assert.equal(urlUtil.isNotURL('http://username:password@example.com'), false) }) it('is localhost (case-insensitive)', function () { - assert.equal(UrlUtil.isNotURL('LoCaLhOsT'), false) + assert.equal(urlUtil.isNotURL('LoCaLhOsT'), false) }) it('is a hostname (not a domain)', function () { - assert.equal(UrlUtil.isNotURL('http://computer001/phpMyAdmin'), false) + assert.equal(urlUtil.isNotURL('http://computer001/phpMyAdmin'), false) }) it('ends with period (input contains a forward slash and domain)', function () { - assert.equal(UrlUtil.isNotURL('brave.com/test/cc?_ri_=3vv-8-e.'), false) + assert.equal(urlUtil.isNotURL('brave.com/test/cc?_ri_=3vv-8-e.'), false) }) it('is a string with whitespace but has schema', function () { - assert.equal(UrlUtil.isNotURL('https://wwww.brave.com/test space.jpg'), false) + assert.equal(urlUtil.isNotURL('https://wwww.brave.com/test space.jpg'), false) }) it('has custom protocol', function () { - assert.equal(UrlUtil.isNotURL('brave://test'), false) + assert.equal(urlUtil.isNotURL('brave://test'), false) }) }) describe('returns true when input:', function () { it('is null or undefined', function () { - assert.equal(UrlUtil.isNotURL(), true) - assert.equal(UrlUtil.isNotURL(null), true) + assert.equal(urlUtil.isNotURL(), true) + assert.equal(urlUtil.isNotURL(null), true) }) it('is not a string', function () { - assert.equal(UrlUtil.isNotURL(false), true) - assert.equal(UrlUtil.isNotURL(333.449), true) + assert.equal(urlUtil.isNotURL(false), true) + assert.equal(urlUtil.isNotURL(333.449), true) }) it('is a quoted string', function () { - assert.equal(UrlUtil.isNotURL('"search term here"'), true) + assert.equal(urlUtil.isNotURL('"search term here"'), true) }) it('is a pure string (no TLD)', function () { - assert.equal(UrlUtil.isNotURL('brave'), true) + assert.equal(urlUtil.isNotURL('brave'), true) }) describe('search query', function () { it('starts with ?', function () { - assert.equal(UrlUtil.isNotURL('?brave'), true) + assert.equal(urlUtil.isNotURL('?brave'), true) }) it('has a question mark followed by a space', function () { - assert.equal(UrlUtil.isNotURL('? brave'), true) + assert.equal(urlUtil.isNotURL('? brave'), true) }) it('starts with .', function () { - assert.equal(UrlUtil.isNotURL('.brave'), true) + assert.equal(urlUtil.isNotURL('.brave'), true) }) it('ends with . (input does NOT contain a forward slash)', function () { - assert.equal(UrlUtil.isNotURL('brave.'), true) + assert.equal(urlUtil.isNotURL('brave.'), true) }) it('ends with period (input contains only a forward slash)', function () { - assert.equal(UrlUtil.isNotURL('brave/com/test/cc?_ri_=3vv-8-e.'), true) + assert.equal(urlUtil.isNotURL('brave/com/test/cc?_ri_=3vv-8-e.'), true) }) }) it('is a string with schema but invalid domain name', function () { - assert.equal(UrlUtil.isNotURL('https://www.bra ve.com/test space.jpg'), true) + assert.equal(urlUtil.isNotURL('https://www.bra ve.com/test space.jpg'), true) }) it('contains more than one word', function () { - assert.equal(UrlUtil.isNotURL('brave is cool'), true) + assert.equal(urlUtil.isNotURL('brave is cool'), true) }) it('is not an about page / view source / data URI / mailto / etc', function () { - assert.equal(UrlUtil.isNotURL('not-a-chrome-extension:'), true) - assert.equal(UrlUtil.isNotURL('mailtoo:'), true) + assert.equal(urlUtil.isNotURL('not-a-chrome-extension:'), true) + assert.equal(urlUtil.isNotURL('mailtoo:'), true) }) it('is a URL (without protocol) which contains basic auth user/pass', function () { - assert.equal(UrlUtil.isNotURL('username:password@example.com'), true) + assert.equal(urlUtil.isNotURL('username:password@example.com'), true) }) it('has space in schema', function () { - assert.equal(UrlUtil.isNotURL('https ://brave.com'), true) + assert.equal(urlUtil.isNotURL('https ://brave.com'), true) }) }) }) describe('getUrlFromInput', function () { it('returns empty string when input is null', function () { - assert.equal(UrlUtil.getUrlFromInput(null), '') + assert.equal(urlUtil.getUrlFromInput(null), '') }) it('returns empty string when input is undefined', function () { - assert.equal(UrlUtil.getUrlFromInput(), '') + assert.equal(urlUtil.getUrlFromInput(), '') }) it('calls prependScheme', function () { - assert.equal(UrlUtil.getUrlFromInput('/file/path/to/file'), 'file:///file/path/to/file') + assert.equal(urlUtil.getUrlFromInput('/file/path/to/file'), 'file:///file/path/to/file') }) }) describe('isURL', function () { it('returns !isNotURL', function () { - assert.equal(UrlUtil.isURL('brave.com'), !UrlUtil.isNotURL('brave.com')) - assert.equal(UrlUtil.isURL('brave is cool'), !UrlUtil.isNotURL('brave is cool')) - assert.equal(UrlUtil.isURL('mailto:brian@brave.com'), !UrlUtil.isNotURL('mailto:brian@brave.com')) + assert.equal(urlUtil.isURL('brave.com'), !urlUtil.isNotURL('brave.com')) + assert.equal(urlUtil.isURL('brave is cool'), !urlUtil.isNotURL('brave is cool')) + assert.equal(urlUtil.isURL('mailto:brian@brave.com'), !urlUtil.isNotURL('mailto:brian@brave.com')) }) }) describe('isFileType', function () { it('relative file', function () { - assert.equal(UrlUtil.isFileType('/file/abc/test.pdf', 'pdf'), true) + assert.equal(urlUtil.isFileType('/file/abc/test.pdf', 'pdf'), true) }) it('relative path', function () { - assert.equal(UrlUtil.isFileType('/file/abc/test', 'pdf'), false) + assert.equal(urlUtil.isFileType('/file/abc/test', 'pdf'), false) }) it('JPG URL', function () { - assert.equal(UrlUtil.isFileType('http://example.com/test/ABC.JPG?a=b#test', 'jpg'), true) + assert.equal(urlUtil.isFileType('http://example.com/test/ABC.JPG?a=b#test', 'jpg'), true) }) it('non-JPG URL', function () { - assert.equal(UrlUtil.isFileType('http://example.com/test/jpg', 'jpg'), false) + assert.equal(urlUtil.isFileType('http://example.com/test/jpg', 'jpg'), false) }) it('invalid URL', function () { - assert.equal(UrlUtil.isFileType('foo', 'jpg'), false) + assert.equal(urlUtil.isFileType('foo', 'jpg'), false) }) }) describe('toPDFJSLocation', function () { const baseUrl = 'chrome-extension://jdbefljfgobbmcidnmpjamcbhnbphjnb/' it('pdf', function () { - assert.equal(UrlUtil.toPDFJSLocation('http://abc.com/test.pdf'), baseUrl + 'http://abc.com/test.pdf') + assert.equal(urlUtil.toPDFJSLocation('http://abc.com/test.pdf'), baseUrl + 'http://abc.com/test.pdf') }) it('non-pdf', function () { - assert.equal(UrlUtil.toPDFJSLocation('http://abc.com/test.pdf.txt'), 'http://abc.com/test.pdf.txt') + assert.equal(urlUtil.toPDFJSLocation('http://abc.com/test.pdf.txt'), 'http://abc.com/test.pdf.txt') }) it('file url', function () { - assert.equal(UrlUtil.toPDFJSLocation('file://abc.com/test.pdf.txt'), 'file://abc.com/test.pdf.txt') + assert.equal(urlUtil.toPDFJSLocation('file://abc.com/test.pdf.txt'), 'file://abc.com/test.pdf.txt') }) it('empty', function () { - assert.equal(UrlUtil.toPDFJSLocation(''), '') + assert.equal(urlUtil.toPDFJSLocation(''), '') }) }) describe('getHostname', function () { it('returns undefined if the URL is invalid', function () { - assert.equal(UrlUtil.getHostname(null), undefined) + assert.equal(urlUtil.getHostname(null), undefined) }) it('returns the host field (including port number)', function () { - assert.equal(UrlUtil.getHostname('https://brave.com:8080/test/'), 'brave.com:8080') + assert.equal(urlUtil.getHostname('https://brave.com:8080/test/'), 'brave.com:8080') }) it('allows you to exclude the port number', function () { - assert.equal(UrlUtil.getHostname('https://brave.com:8080/test/', true), 'brave.com') + assert.equal(urlUtil.getHostname('https://brave.com:8080/test/', true), 'brave.com') }) }) describe('getHostnamePatterns', function () { it('gets bare domain hostname patterns', function () { // XXX: *.com probably should be excluded - assert.deepEqual(UrlUtil.getHostnamePatterns('http://brave.com'), + assert.deepEqual(urlUtil.getHostnamePatterns('http://brave.com'), ['brave.com', '*.com', 'brave.*']) }) it('gets subdomain hostname patterns', function () { - assert.deepEqual(UrlUtil.getHostnamePatterns('https://bar.brave.com'), + assert.deepEqual(urlUtil.getHostnamePatterns('https://bar.brave.com'), ['bar.brave.com', '*.brave.com', 'bar.*.com', 'bar.brave.*']) - assert.deepEqual(UrlUtil.getHostnamePatterns('https://foo.bar.brave.com'), + assert.deepEqual(urlUtil.getHostnamePatterns('https://foo.bar.brave.com'), ['foo.bar.brave.com', '*.bar.brave.com', 'foo.*.brave.com', @@ -244,98 +244,132 @@ describe('urlutil', function () { describe('getLocationIfPDF', function () { it('gets location for PDF JS URL', function () { - assert.equal(UrlUtil.getLocationIfPDF('chrome-extension://jdbefljfgobbmcidnmpjamcbhnbphjnb/https://www.blackhat.co…king-Kernel-Address-Space-Layout-Randomization-KASLR-With-Intel-TSX-wp.pdf'), + assert.equal(urlUtil.getLocationIfPDF('chrome-extension://jdbefljfgobbmcidnmpjamcbhnbphjnb/https://www.blackhat.co…king-Kernel-Address-Space-Layout-Randomization-KASLR-With-Intel-TSX-wp.pdf'), 'https://www.blackhat.co…king-Kernel-Address-Space-Layout-Randomization-KASLR-With-Intel-TSX-wp.pdf') }) it('gets location for PDF JS viewer URL', function () { - assert.equal(UrlUtil.getLocationIfPDF('chrome-extension://jdbefljfgobbmcidnmpjamcbhnbphjnb/content/web/viewer.html?file=http%3A%2F%2Funec.edu.az%2Fapplication%2Fuploads%2F2014%2F12%2Fpdf-sample.pdf'), + assert.equal(urlUtil.getLocationIfPDF('chrome-extension://jdbefljfgobbmcidnmpjamcbhnbphjnb/content/web/viewer.html?file=http%3A%2F%2Funec.edu.az%2Fapplication%2Fuploads%2F2014%2F12%2Fpdf-sample.pdf'), 'http://unec.edu.az/application/uploads/2014/12/pdf-sample.pdf') }) it('does not remove wayback machine url location for PDF JS URL', function () { - assert.equal(UrlUtil.getLocationIfPDF('chrome-extension://jdbefljfgobbmcidnmpjamcbhnbphjnb/https://web.archive.org/web/20160106152308/http://stlab.adobe.com/wiki/images/d/d3/Test.pdf'), + assert.equal(urlUtil.getLocationIfPDF('chrome-extension://jdbefljfgobbmcidnmpjamcbhnbphjnb/https://web.archive.org/web/20160106152308/http://stlab.adobe.com/wiki/images/d/d3/Test.pdf'), 'https://web.archive.org/web/20160106152308/http://stlab.adobe.com/wiki/images/d/d3/Test.pdf') }) it('does not modify location for non-pdf URL', function () { - assert.equal(UrlUtil.getLocationIfPDF('https://www.blackhat.co…king-Kernel-Address-Space-Layout-Randomization-KASLR-With-Intel-TSX-wp.pdf'), + assert.equal(urlUtil.getLocationIfPDF('https://www.blackhat.co…king-Kernel-Address-Space-Layout-Randomization-KASLR-With-Intel-TSX-wp.pdf'), 'https://www.blackhat.co…king-Kernel-Address-Space-Layout-Randomization-KASLR-With-Intel-TSX-wp.pdf') - assert.equal(UrlUtil.getLocationIfPDF('chrome-extension://blank'), 'chrome-extension://blank') - assert.equal(UrlUtil.getLocationIfPDF(null), null) + assert.equal(urlUtil.getLocationIfPDF('chrome-extension://blank'), 'chrome-extension://blank') + assert.equal(urlUtil.getLocationIfPDF(null), null) }) it('gets location for file: PDF URL', function () { let url = 'chrome-extension://jdbefljfgobbmcidnmpjamcbhnbphjnb/file:///Users/yan/Downloads/test.pdf' - assert.equal(UrlUtil.getLocationIfPDF(url), 'file:///Users/yan/Downloads/test.pdf') + assert.equal(urlUtil.getLocationIfPDF(url), 'file:///Users/yan/Downloads/test.pdf') }) }) describe('getDefaultFaviconUrl', function () { it('returns empty string if input is not a URL', function () { - assert.equal(UrlUtil.getDefaultFaviconUrl('invalid-url-goes-here'), '') + assert.equal(urlUtil.getDefaultFaviconUrl('invalid-url-goes-here'), '') }) it('returns the default favicon URL when given a valid URL', function () { - assert.equal(UrlUtil.getDefaultFaviconUrl('https://brave.com'), 'https://brave.com/favicon.ico') + assert.equal(urlUtil.getDefaultFaviconUrl('https://brave.com'), 'https://brave.com/favicon.ico') }) it('includes the port in the response when given a valid URL with a port number', function () { - assert.equal(UrlUtil.getDefaultFaviconUrl('https://brave.com:8080'), 'https://brave.com:8080/favicon.ico') + assert.equal(urlUtil.getDefaultFaviconUrl('https://brave.com:8080'), 'https://brave.com:8080/favicon.ico') }) }) describe('getPunycodeUrl', function () { it('returns empty string if input is not a URL', function () { - assert.equal(UrlUtil.getPunycodeUrl('invalid-url-goes-here'), 'invalid-url-goes-here') + assert.equal(urlUtil.getPunycodeUrl('invalid-url-goes-here'), 'invalid-url-goes-here') }) it('returns the punycode URL when given a valid URL', function () { - assert.equal(UrlUtil.getPunycodeUrl('http://brave:brave@ebаy.com:1234/brave#brave'), 'http://brave:brave@xn--eby-7cd.com:1234/brave#brave') + assert.equal(urlUtil.getPunycodeUrl('http://brave:brave@ebаy.com:1234/brave#brave'), 'http://brave:brave@xn--eby-7cd.com:1234/brave#brave') }) }) describe('isPotentialPhishingUrl', function () { it('returns false if input is not a URL', function () { - assert.equal(UrlUtil.isPotentialPhishingUrl(null), false) + assert.equal(urlUtil.isPotentialPhishingUrl(null), false) }) it('returns false if input is a regular URL', function () { - assert.equal(UrlUtil.isPotentialPhishingUrl(' https://google.com'), false) + assert.equal(urlUtil.isPotentialPhishingUrl(' https://google.com'), false) }) it('returns true if input is a data URL', function () { - assert.equal(UrlUtil.isPotentialPhishingUrl('data:text/html,'), true) + assert.equal(urlUtil.isPotentialPhishingUrl('data:text/html,'), true) }) it('returns true if input is a blob URL', function () { - assert.equal(UrlUtil.isPotentialPhishingUrl(' BLOB:foo '), true) + assert.equal(urlUtil.isPotentialPhishingUrl(' BLOB:foo '), true) }) }) describe('isFileScheme', function () { describe('returns true when input:', function () { it('is an absolute file path with scheme', function () { - assert.equal(UrlUtil.isFileScheme('file:///file/path/to/file'), true) + assert.equal(urlUtil.isFileScheme('file:///file/path/to/file'), true) }) }) describe('returns false when input:', function () { it('is an absolute file path without scheme', function () { - assert.equal(UrlUtil.isFileScheme('/file/path/to/file'), false) + assert.equal(urlUtil.isFileScheme('/file/path/to/file'), false) }) it('is a URL', function () { - assert.equal(UrlUtil.isFileScheme('http://brave.com'), false) + assert.equal(urlUtil.isFileScheme('http://brave.com'), false) }) it('has custom protocol', function () { - assert.equal(UrlUtil.isFileScheme('brave://test'), false) + assert.equal(urlUtil.isFileScheme('brave://test'), false) }) }) }) describe('getDisplayHost', function () { it('url is http', function () { - const result = UrlUtil.getDisplayHost('http://brave.com') + const result = urlUtil.getDisplayHost('http://brave.com') assert.equal(result, 'brave.com') }) it('url is https', function () { - const result = UrlUtil.getDisplayHost('https://brave.com') + const result = urlUtil.getDisplayHost('https://brave.com') assert.equal(result, 'brave.com') }) it('url is file', function () { - const result = UrlUtil.getDisplayHost('file://brave.text') + const result = urlUtil.getDisplayHost('file://brave.text') assert.equal(result, 'file://brave.text') }) }) + + describe('getOrigin', function () { + it('returns file:/// for any file url', function () { + assert.strictEqual(urlUtil.getOrigin('file://'), 'file:///') + assert.strictEqual(urlUtil.getOrigin('file:///'), 'file:///') + assert.strictEqual(urlUtil.getOrigin('file:///some'), 'file:///') + assert.strictEqual(urlUtil.getOrigin('file:///some/'), 'file:///') + assert.strictEqual(urlUtil.getOrigin('file:///some/path'), 'file:///') + }) + it('gets URL origin for simple url', function () { + assert.strictEqual(urlUtil.getOrigin('https://abc.bing.com'), 'https://abc.bing.com') + }) + it('gets URL origin for url with port', function () { + assert.strictEqual(urlUtil.getOrigin('https://bing.com:443/?test=1#abc'), 'https://bing.com:443') + }) + it('gets URL origin for IP host', function () { + assert.strictEqual(urlUtil.getOrigin('http://127.0.0.1:443/?test=1#abc'), 'http://127.0.0.1:443') + }) + it('gets URL origin for slashless protocol URL', function () { + assert.strictEqual(urlUtil.getOrigin('about:test/foo'), 'about:test') + }) + it('returns null for invalid URL', function () { + assert.strictEqual(urlUtil.getOrigin('abc'), null) + }) + it('returns null for empty URL', function () { + assert.strictEqual(urlUtil.getOrigin(''), null) + }) + it('returns null for null URL', function () { + assert.strictEqual(urlUtil.getOrigin(null), null) + }) + it('returns correct result for URL with hostname that is a scheme', function () { + assert.strictEqual(urlUtil.getOrigin('http://http/test'), 'http://http') + }) + }) })