diff --git a/app/common/constants/settingsEnums.js b/app/common/constants/settingsEnums.js index 0b6dcc61961..86b70e8decb 100644 --- a/app/common/constants/settingsEnums.js +++ b/app/common/constants/settingsEnums.js @@ -21,6 +21,12 @@ const bookmarksToolbarMode = { FAVICONS_ONLY: 'faviconsOnly' } +const tabPreviewTiming = { + LONG: 2000, + NORMAL: 1000, + SHORT: 500 +} + const tabCloseAction = { LAST_ACTIVE: 'lastActive', NEXT: 'next', @@ -41,6 +47,7 @@ module.exports = { startsWithOption, newTabMode, bookmarksToolbarMode, + tabPreviewTiming, tabCloseAction, fullscreenOption, autoplayOption diff --git a/app/extensions/brave/locales/en-US/preferences.properties b/app/extensions/brave/locales/en-US/preferences.properties index bd33d411ca7..65b8f40a44a 100644 --- a/app/extensions/brave/locales/en-US/preferences.properties +++ b/app/extensions/brave/locales/en-US/preferences.properties @@ -380,3 +380,7 @@ urlBarOptions=URL Bar Options disableTitleMode=Always show the URL bar wideURLbar=Use wide URL bar autoplay=Autoplay Media +tabPreviewTiming=Time to wait before previewing a tab +long=Long +normal=Normal +short=Short diff --git a/app/renderer/components/preferences/tabsTab.js b/app/renderer/components/preferences/tabsTab.js index b8663d469ff..b4959a32f7f 100644 --- a/app/renderer/components/preferences/tabsTab.js +++ b/app/renderer/components/preferences/tabsTab.js @@ -10,7 +10,7 @@ const {SettingsList, SettingItem, SettingCheckbox} = require('../common/settings const {SettingDropdown} = require('../common/dropdown') -const {tabCloseAction} = require('../../../common/constants/settingsEnums') +const {tabCloseAction, tabPreviewTiming} = require('../../../common/constants/settingsEnums') const {changeSetting} = require('../../lib/settingsUtil') const getSetting = require('../../../../js/settings').getSetting const settings = require('../../../../js/constants/settings') @@ -36,6 +36,22 @@ class TabsTab extends ImmutableComponent { } ] } + get tabPreviewTimingOptions () { + return [ + { + id: 'long', + action: tabPreviewTiming.LONG + }, + { + id: 'normal', + action: tabPreviewTiming.NORMAL + }, + { + id: 'short', + action: tabPreviewTiming.SHORT + } + ] + } render () { return (
@@ -107,6 +123,26 @@ class TabsTab extends ImmutableComponent { settings={this.props.settings} onChangeSetting={this.props.onChangeSetting} /> + { + getSetting(settings.SHOW_TAB_PREVIEWS, this.props.settings) + ? + + {this.tabPreviewTimingOptions.map(option => + + + : null + } { @@ -292,6 +310,7 @@ class Tab extends React.Component { : null }
{ this.tabNode = node }} className={css( styles.tab, diff --git a/app/renderer/lib/tabUtil.js b/app/renderer/lib/tabUtil.js index ada265e82a0..dede6ffe2c7 100644 --- a/app/renderer/lib/tabUtil.js +++ b/app/renderer/lib/tabUtil.js @@ -36,3 +36,17 @@ module.exports.hasBreakpoint = (breakpoint, arr) => { arr = Array.isArray(arr) ? arr : [arr] return arr.includes(breakpoint) } + +/** + * Check whether or not the related target is a tab + * by checking the parentNode dataset + * @param {Object} event - The mouse event + * @returns {Boolean} Whether or not the related target is a tab + */ +module.exports.hasTabAsRelatedTarget = (event) => { + const relatedTarget = event.relatedTarget + const hasDataset = relatedTarget.parentNode && relatedTarget.parentNode.dataset + const tabAsRelatedTarget = hasDataset.tab || hasDataset.tabArea + + return hasDataset && tabAsRelatedTarget +} diff --git a/app/renderer/reducers/frameReducer.js b/app/renderer/reducers/frameReducer.js index 2893ae47612..768b3f2761f 100644 --- a/app/renderer/reducers/frameReducer.js +++ b/app/renderer/reducers/frameReducer.js @@ -37,6 +37,7 @@ const closeFrame = (state, action) => { const frameProps = frameStateUtil.getFrameByKey(state, action.frameKey) const hoverState = frameStateUtil.getTabHoverState(state, action.frameKey) + const previewMode = state.getIn(['ui', 'tabs', 'previewMode']) state = state.merge(frameStateUtil.removeFrame( state, @@ -57,7 +58,8 @@ const closeFrame = (state, action) => { // This allow us to have closeTab button visible for sequential frames closing, // until onMouseLeave event happens. if (hoverState) { - state = frameStateUtil.setTabHoverState(state, nextFrame.get('key'), hoverState) + state = frameStateUtil + .setTabHoverState(state, nextFrame.get('key'), hoverState, previewMode) } } else if (hoverState && frameStateUtil.getPreviewFrameKey(state) === action.frameKey) { state = frameStateUtil.setPreviewFrameKey(state, null) diff --git a/docs/state.md b/docs/state.md index 2a9ec286d26..87920066a14 100644 --- a/docs/state.md +++ b/docs/state.md @@ -709,8 +709,9 @@ WindowStore size: array, // last known window size [x, y] tabs: { hoverTabIndex: number, // index of the current hovered tab - tabPageIndex: number, // index of the current tab page - previewTabPageIndex: number // index of the tab being previewed + previewMode: boolean, // whether or not tab preview should be fired based on mouse idle time + previewTabPageIndex: number, // index of the tab being previewed + tabPageIndex: number // index of the current tab page }, }, widevinePanelDetail: { diff --git a/js/actions/windowActions.js b/js/actions/windowActions.js index 58b5d5f1139..4dc258fc3a2 100644 --- a/js/actions/windowActions.js +++ b/js/actions/windowActions.js @@ -244,20 +244,6 @@ const windowActions = { }) }, - /** - * Dispatches a message to the store to set a preview frame. - * This should only be called internally by `WINDOW_SET_TAB_HOVER_STATE` - * when we need to delay updating the preview frame value - * - * @param {Object} frameKey - the frame key for the webview in question. - */ - setPreviewFrame: function (frameKey) { - dispatch({ - actionType: windowConstants.WINDOW_SET_PREVIEW_FRAME, - frameKey - }) - }, - /** * Dispatches a message to the store to set the tab page index. * @@ -289,12 +275,15 @@ const windowActions = { * * @param {Object} frameKey - the frame key for the webview in question. * @param {boolean} hoverState - whether or not mouse is over tab + * @param {boolean} previewMode - whether or not the next tab should be previewed + * based on mouse idle time */ - setTabHoverState: function (frameKey, hoverState) { + setTabHoverState: function (frameKey, hoverState, previewMode) { dispatch({ actionType: windowConstants.WINDOW_SET_TAB_HOVER_STATE, frameKey, - hoverState + hoverState, + previewMode }) }, @@ -1058,6 +1047,13 @@ const windowActions = { }) }, + onTabMouseMove: function (data) { + dispatch({ + actionType: windowConstants.WINDOW_TAB_MOUSE_MOVE, + data + }) + }, + onTabMouseLeave: function (data) { dispatch({ actionType: windowConstants.WINDOW_TAB_MOUSE_LEAVE, diff --git a/js/constants/appConfig.js b/js/constants/appConfig.js index 268ff21d9d6..871b69c2065 100644 --- a/js/constants/appConfig.js +++ b/js/constants/appConfig.js @@ -149,6 +149,7 @@ module.exports = { 'tabs.tabs-per-page': 20, 'tabs.close-action': 'parent', 'tabs.show-tab-previews': true, + 'tabs.preview-timing': 2000, 'tabs.show-dashboard-images': true, 'privacy.history-suggestions': true, 'privacy.bookmark-suggestions': true, diff --git a/js/constants/settings.js b/js/constants/settings.js index b1950dc0dd5..f69f3bedfb5 100644 --- a/js/constants/settings.js +++ b/js/constants/settings.js @@ -28,6 +28,7 @@ const settings = { PAINT_TABS: 'tabs.paint-tabs', TABS_PER_PAGE: 'tabs.tabs-per-page', SHOW_TAB_PREVIEWS: 'tabs.show-tab-previews', + TAB_PREVIEW_TIMING: 'tabs.preview-timing', SHOW_DASHBOARD_IMAGES: 'tabs.show-dashboard-images', // Privacy Tab HISTORY_SUGGESTIONS: 'privacy.history-suggestions', diff --git a/js/constants/windowConstants.js b/js/constants/windowConstants.js index 7186fbf9f51..850a5a95565 100644 --- a/js/constants/windowConstants.js +++ b/js/constants/windowConstants.js @@ -10,7 +10,6 @@ const windowConstants = { WINDOW_CLOSE_FRAME: _, WINDOW_CLOSE_FRAMES: _, WINDOW_SET_FOCUSED_FRAME: _, - WINDOW_SET_PREVIEW_FRAME: _, WINDOW_SET_PREVIEW_TAB_PAGE_INDEX: _, WINDOW_SET_TAB_PAGE_INDEX: _, WINDOW_SET_TAB_BREAKPOINT: _, @@ -82,6 +81,7 @@ const windowConstants = { WINDOW_AUTOFILL_SELECTION_CLICKED: _, WINDOW_AUTOFILL_POPUP_HIDDEN: _, WINDOW_TAB_CLOSED_WITH_MOUSE: _, + WINDOW_TAB_MOUSE_MOVE: _, WINDOW_TAB_MOUSE_LEAVE: _, WINDOW_FRAME_MOUSE_ENTER: _, WINDOW_FRAME_MOUSE_LEAVE: _, diff --git a/js/state/frameStateUtil.js b/js/state/frameStateUtil.js index b71f871694e..723baab46e4 100644 --- a/js/state/frameStateUtil.js +++ b/js/state/frameStateUtil.js @@ -590,38 +590,42 @@ const setPreviewTabPageIndex = (state, index, immediate = false) => { return state.setIn(['ui', 'tabs', 'previewTabPageIndex'], newTabPageIndex) } -const setPreviewFrameKey = (state, frameKey, immediate = false) => { +/** + * Defines whether or not a tab should be allowed to preview its content + * based on mouse idle time defined by mouse move in tab.js + * @see windowConstants.WINDOW_TAB_MOUSE_MOVE for information + * on how the data is handled in the store. + * @param state {Object} - Application state + * @param previewMode {Boolean} - Whether or not minimium idle time + * has match the criteria + */ +const setPreviewMode = (state, previewMode) => { + return state.setIn(['ui', 'tabs', 'previewMode'], previewMode) +} + +/** + * Gets the previewMode application state + * @param state {Object} - Application state + * @return Immutable top level application state for previewMode + */ +const getPreviewMode = (state) => { + return state.getIn(['ui', 'tabs', 'previewMode']) +} + +const setPreviewFrameKey = (state, frameKey) => { clearTimeout(tabHoverTimeout) const frame = getFrameByKey(state, frameKey) const isActive = isFrameKeyActive(state, frameKey) const previewTabs = getSetting(settings.SHOW_TAB_PREVIEWS) const hoverState = getTabHoverState(state, frameKey) + const previewMode = getPreviewMode(state) let newPreviewFrameKey = frameKey - if (!previewTabs || frame == null || !hoverState || isActive) { + if (!previewTabs || !previewMode || frame == null || !hoverState || isActive) { newPreviewFrameKey = null } - if (!immediate) { - // if there is an existing preview frame key then we're already in preview mode - // we use actions here because that is the only way to delay updating the state - const previewMode = getPreviewFrameKey(state) != null - if (previewMode && newPreviewFrameKey == null) { - // add a small delay when we are clearing the preview frame key so we don't lose - // previewMode if the user mouses over another tab - see below - tabHoverTimeout = setTimeout(windowActions.setPreviewFrame.bind(null, null), 200) - return state - } - - if (!previewMode) { - // If user isn't in previewMode so we add a bit of delay to avoid tab from flashing out - // as reported here: https://github.com/brave/browser-laptop/issues/1434 - // using an action here because that is the only way we can do a delayed state update - tabHoverTimeout = setTimeout(windowActions.setPreviewFrame.bind(null, newPreviewFrameKey), 200) - return state - } - } - + // TODO: remove this method to a tabPageIndex-related one const index = frame ? getFrameTabPageIndex(state, frame.get('tabId')) : -1 if (index !== -1) { if (index !== state.getIn(['ui', 'tabs', 'tabPageIndex'])) { @@ -692,11 +696,11 @@ const setHoverTabIndex = (state, frameKey, hoverState) => { * @param hoverState {Boolean} - True if the current tab is being hovered. * @return Immutable top level application state for hoverTabIndex */ - -const setTabHoverState = (state, frameKey, hoverState) => { +const setTabHoverState = (state, frameKey, hoverState, enablePreviewMode) => { const frameIndex = getFrameIndex(state, frameKey) if (frameIndex !== -1) { state = setHoverTabIndex(state, frameKey, hoverState) + state = setPreviewMode(state, enablePreviewMode) state = setPreviewFrameKey(state, frameKey) } return state diff --git a/js/stores/windowStore.js b/js/stores/windowStore.js index 1e916ec4249..56ad7b7c691 100644 --- a/js/stores/windowStore.js +++ b/js/stores/windowStore.js @@ -41,6 +41,7 @@ let windowState = Immutable.fromJS({ } }) let lastEmittedState +let mouseTimeout const CHANGE_EVENT = 'change' @@ -375,9 +376,6 @@ const doAction = (action) => { windowState.get('closedFrames').filterNot((frame) => frame.get('location') === action.location)) } break - case windowConstants.WINDOW_SET_PREVIEW_FRAME: - windowState = frameStateUtil.setPreviewFrameKey(windowState, action.frameKey, true) - break case windowConstants.WINDOW_SET_PREVIEW_TAB_PAGE_INDEX: windowState = frameStateUtil.setPreviewTabPageIndex(windowState, action.previewTabPageIndex, true) break @@ -400,9 +398,23 @@ const doAction = (action) => { } break } + case windowConstants.WINDOW_TAB_MOUSE_MOVE: + { + // previewMode is only triggered if mouse is idle over a tab + // for a given amount of time based on timing defined in prefs->tabs + // we use actions here because that is the only way to delay updating the state + clearTimeout(mouseTimeout) + mouseTimeout = setTimeout( + () => windowActions.setTabHoverState(action.data, true, true), + getSetting(settings.TAB_PREVIEW_TIMING) + ) + break + } case windowConstants.WINDOW_SET_TAB_HOVER_STATE: { - windowState = frameStateUtil.setTabHoverState(windowState, action.frameKey, action.hoverState) + clearTimeout(mouseTimeout) + windowState = frameStateUtil + .setTabHoverState(windowState, action.frameKey, action.hoverState, action.previewMode) break } case windowConstants.WINDOW_SET_TAB_PAGE_HOVER_STATE: diff --git a/test/lib/selectors.js b/test/lib/selectors.js index 2ea9e2357bb..a6646e80628 100644 --- a/test/lib/selectors.js +++ b/test/lib/selectors.js @@ -137,5 +137,6 @@ module.exports = { // about:preferences#tabs tabsPerTabPageActiveOption: '[data-test-id="tabsPerTabPageOption"][data-test-active="true"]', - tabCloseActionActiveOption: '[data-test-id="tabCloseActionActiveOption"][data-test-active="true"]' + tabCloseActionActiveOption: '[data-test-id="tabCloseActionActiveOption"][data-test-active="true"]', + tabPreviewTimingActiveOption: '[data-test-id="tabPreviewTimingOption"][data-test-active="true"]' } diff --git a/test/tab-components/tabTest.js b/test/tab-components/tabTest.js index 4664f13aad6..3a35e8fff75 100644 --- a/test/tab-components/tabTest.js +++ b/test/tab-components/tabTest.js @@ -3,6 +3,7 @@ const Brave = require('../lib/brave') const messages = require('../../js/constants/messages') const settings = require('../../js/constants/settings') +const {tabPreviewTiming} = require('../../app/common/constants/settingsEnums') const {urlInput, backButton, forwardButton, activeTab, activeTabTitle, activeTabFavicon, newFrameButton, notificationBar, contextMenu, pinnedTabsTabs, tabsTabs} = require('../lib/selectors') const newTabUrl = 'chrome-extension://mnojpmjdmbbfmejpflffifhffcmidifd/about-newtab.html' @@ -373,6 +374,8 @@ describe('tab tests', function () { const page1 = Brave.server.url('page1.html') const page2 = Brave.server.url('page2.html') yield setup(this.app.client) + yield this.app.client + .changeSetting(settings.TAB_PREVIEW_TIMING, tabPreviewTiming.SHORT) yield this.app.client .newTab({ url: page1 }) .waitForUrl(page1) @@ -383,11 +386,11 @@ describe('tab tests', function () { .windowByUrl(Brave.browserWindowUrl) .waitForExist('[data-test-id="tab"][data-frame-key="3"]') }) - it('shows a tab preview', function * () { + it('shows a tab preview when TAB_PREVIEW_TIMING is set as SHORT', function * () { yield this.app.client .moveToObject('[data-test-id="tab"][data-frame-key="2"]') .moveToObject('[data-test-id="tab"][data-frame-key="2"]', 3, 3) - .waitForExist('.frameWrapper.isPreview webview[data-frame-key="2"]') + .waitForElementCount('.frameWrapper.isPreview webview[data-frame-key="2"]', 1) .moveToObject(urlInput) }) it('does not show preview in the active tab', function * () { @@ -424,6 +427,8 @@ describe('tab tests', function () { const page5 = Brave.server.url('page1.html') const page6 = Brave.server.url('page2.html') yield setup(this.app.client) + // Set to minimum preview timing to avoid timeout + yield this.app.client.changeSetting(settings.TAB_PREVIEW_TIMING, tabPreviewTiming.SHORT) yield this.app.client .newTab({ url: page1 }) .waitForUrl(page1) @@ -454,30 +459,36 @@ describe('tab tests', function () { it('show active tab content if next tab does not exist', function * () { yield this.app.client .moveToObject('[data-test-id="tab"][data-frame-key="2"]') + .moveToObject('[data-test-id="tab"][data-frame-key="2"]', 3, 3) .click('[data-test-id="tab"][data-frame-key="2"]') .moveToObject('[data-test-id="tab"][data-frame-key="7"]') + .moveToObject('[data-test-id="tab"][data-frame-key="7"]', 3, 3) .middleClick('[data-test-id="tab"][data-frame-key="7"]') // no preview should be shown - .waitForVisible('.frameWrapper.isPreview webview', 500, true) + .waitForElementCount('.frameWrapper.isPreview webview', 0) }) it('preview the next tab if preview option is on', function * () { yield this.app.client .moveToObject('[data-test-id="tab"][data-frame-key="2"]') + .moveToObject('[data-test-id="tab"][data-frame-key="2"]', 3, 3) .click('[data-test-id="tab"][data-frame-key="2"]') .moveToObject('[data-test-id="tab"][data-frame-key="4"]') + .moveToObject('[data-test-id="tab"][data-frame-key="4"]', 3, 3) .middleClick('[data-test-id="tab"][data-frame-key="4"]') .waitForExist('.frameWrapper.isPreview webview[data-frame-key="5"]') - .waitForVisible('.frameWrapper.isPreview webview[data-frame-key="5"]') + .waitForElementCount('.frameWrapper.isPreview webview[data-frame-key="5"]', 1) }) it('do not preview the next tab if preview option is off', function * () { yield this.app.client.changeSetting(settings.SHOW_TAB_PREVIEWS, false) yield this.app.client + .moveToObject('[data-test-id="tab"][data-frame-key="2"]', 3, 3) .moveToObject('[data-test-id="tab"][data-frame-key="2"]') .click('[data-test-id="tab"][data-frame-key="2"]') + .moveToObject('[data-test-id="tab"][data-frame-key="5"]', 3, 3) .moveToObject('[data-test-id="tab"][data-frame-key="5"]') .middleClick('[data-test-id="tab"][data-frame-key="5"]') // no preview should be shown - .waitForVisible('.frameWrapper.isPreview webview', 500, true) + .waitForElementCount('.frameWrapper.isPreview webview', 0) }) }) diff --git a/test/unit/app/renderer/components/preferences/tabsTabTest.js b/test/unit/app/renderer/components/preferences/tabsTabTest.js index 1ada4c3ffac..89cdd1795e1 100644 --- a/test/unit/app/renderer/components/preferences/tabsTabTest.js +++ b/test/unit/app/renderer/components/preferences/tabsTabTest.js @@ -5,8 +5,12 @@ const mockery = require('mockery') const {mount, shallow} = require('enzyme') -const {tabCloseAction} = require('../../../../../../app/common/constants/settingsEnums') -const {tabsPerTabPageActiveOption, tabCloseActionActiveOption} = require('../../../../../lib/selectors') +const {tabCloseAction, tabPreviewTiming} = require('../../../../../../app/common/constants/settingsEnums') +const { + tabsPerTabPageActiveOption, + tabCloseActionActiveOption, + tabPreviewTimingActiveOption +} = require('../../../../../lib/selectors') const assert = require('assert') const fakeElectron = require('../../../../lib/fakeElectron') require('../../../../braveUnit') @@ -164,26 +168,87 @@ describe('TabsTab component', function () { }) }) - describe('basic functionality', function () { - it('can switch to new tabs immediately', function () { + describe('tab preview functionality', function () { + it('can show tab previews on hover', function () { settingDefaultValue = true const wrapper = shallow() assert.notEqual( wrapper - .find('[dataTestId="switchToNewTabs"]') + .find('[dataTestId="showTabPreviews"]') .map(option => option.props().value) .includes(settingDefaultValue), true ) }) - it('can show tab previews on hover', function () { + it('show all 3 tab preview timing options if tab preview is on', function () { + settingDefaultValue = true + const wrapper = shallow() + assert.equal(wrapper.find(tabPreviewTimingActiveOption).length, 3) + }) + it('does not show tab preview timing options if tab preview is off', function () { + settingDefaultValue = false + const wrapper = shallow() + assert.equal(wrapper.find(tabPreviewTimingActiveOption).length, 0) + }) + it('can switch tab previews time to activate previews to LONG', function () { + settingDefaultValue = tabPreviewTiming.LONG + const wrapper = shallow() + + assert.equal( + wrapper + .find(tabPreviewTimingActiveOption) + .map(option => option.props().value) + .includes(settingDefaultValue), + true + ) + }) + it('can switch tab previews time to activate previews to NORMAL', function () { + settingDefaultValue = tabPreviewTiming.NORMAL + const wrapper = shallow() + + assert.equal( + wrapper + .find(tabPreviewTimingActiveOption) + .map(option => option.props().value) + .includes(settingDefaultValue), + true + ) + }) + it('can switch tab previews time to activate previews to SHORT', function () { + settingDefaultValue = tabPreviewTiming.SHORT + const wrapper = shallow() + + assert.equal( + wrapper + .find(tabPreviewTimingActiveOption) + .map(option => option.props().value) + .includes(settingDefaultValue), + true + ) + }) + it('can not switch to other values', function () { + settingDefaultValue = tabPreviewTiming.ONLY_ONCE_IN_A_LIFETIME + const wrapper = shallow() + + assert.notEqual( + wrapper + .find(tabPreviewTimingActiveOption) + .map(option => option.props().value) + .includes(settingDefaultValue), + true + ) + }) + }) + + describe('basic functionality', function () { + it('can switch to new tabs immediately', function () { settingDefaultValue = true const wrapper = shallow() assert.notEqual( wrapper - .find('[dataTestId="paintTabs"]') + .find('[dataTestId="switchToNewTabs"]') .map(option => option.props().value) .includes(settingDefaultValue), true @@ -195,7 +260,7 @@ describe('TabsTab component', function () { assert.notEqual( wrapper - .find('[dataTestId="showTabPreviews"]') + .find('[dataTestId="paintTabs"]') .map(option => option.props().value) .includes(settingDefaultValue), true diff --git a/test/unit/app/renderer/lib/tabUtilTest.js b/test/unit/app/renderer/lib/tabUtilTest.js new file mode 100644 index 00000000000..ab04895255b --- /dev/null +++ b/test/unit/app/renderer/lib/tabUtilTest.js @@ -0,0 +1,29 @@ +/* global describe, it */ +const tabUtil = require('../../../../../app/renderer/lib/tabUtil') +const assert = require('assert') + +require('../../../braveUnit') + +describe('tabUtil', function () { + describe('hasTabAsRelatedTarget', function () { + const fakeEvent = (fakeDataset) => ({ + relatedTarget: { + parentNode: { + dataset: { [fakeDataset]: true } + } + } + }) + it('returns true if dataset is tab', function () { + const fakeDataset = fakeEvent('tab') + assert.equal(tabUtil.hasTabAsRelatedTarget(fakeDataset), true) + }) + it('returns true if dataset is tabArea', function () { + const fakeDataset = fakeEvent('tabArea') + assert.equal(tabUtil.hasTabAsRelatedTarget(fakeDataset), true) + }) + it('returns false if dataset is neither tab nor tabArea', function () { + const fakeDataset = fakeEvent('badCoffee') + assert.notEqual(tabUtil.hasTabAsRelatedTarget(fakeDataset), true) + }) + }) +})