Skip to content

Commit

Permalink
Refactor to add config watching and test coverage (#27)
Browse files Browse the repository at this point in the history
* Add StandardJS linter

* Add windows tests

* Add more tests

* [WIP] Convert windows to class; plugin broken

* Add dispose

* Refactor modules; add tests

* Add tests for setup

* Fix broken paths and listeners

* Refactor with more tests

* Change hideOnBlur default to false

Update default behavior for blurring a window, to reduce confusion for
users.

* Add debounce to properly manage blur event

Prevent hiding Hyper during new window creation.

* Fix setup tests

* Fix missing parameter in setup

* Add coverage; event handler removal with DI

Use dependency injection to allow for additional test coverage and
proper removal of event handlers.

* Repair handler provided for blur event

* Repair last focused window tracking

* Fix activate handler

* Update README

* Update Jest coverage config

* Align callback generation
  • Loading branch information
dcalhoun authored Sep 28, 2017
1 parent 44860c9 commit 0393ca1
Show file tree
Hide file tree
Showing 18 changed files with 1,374 additions and 195 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
coverage/
node_modules/
11 changes: 8 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
# hyperterm-summon
[![Build Status](https://travis-ci.org/soutar/hyperterm-summon.svg?branch=master)](https://travis-ci.org/soutar/hyperterm-summon)
[![JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://standardjs.com)

Summon your Hyper windows with a system-wide hotkey. In a multi-window situation, hyperterm-summon will remember which window was active last and restore focus to it. If Hyper is already active when the hotkey is pressed, your terminal windows will be hidden and (on macOS only) your previously-active application will regain focus.
Summon your Hyper windows with a system-wide hotkey.

In a multi-window situation, hyperterm-summon will remember the last active window and restore focus to it.

If Hyper is already active when the hotkey is pressed, your terminal windows will be hidden and (on macOS only) your previously-active application will regain focus.

## Installation
1. Open your Hyper config file (i.e. `~/.hyper.js`) in your preferred text editor.
Expand All @@ -11,7 +16,7 @@ Summon your Hyper windows with a system-wide hotkey. In a multi-window situation
| Key | Description | Default |
| --- | ----------- | ------- |
| `hideDock` | Hide the Hyper icon in the dock and app switcher. | `false` |
| `hideOnBlur` | Hide the Hyper when window loses focus. | `true` |
| `hideOnBlur` | Hide the Hyper when window loses focus. | `false` |
| `hotkey` | Shortcut to toggle Hyper window visibility. | `Ctrl+;` |

*NOTE:* For a list of valid shortcuts, see [Electron Accelerators](https://github.com/electron/electron/blob/master/docs/api/accelerator.md).
Expand All @@ -22,7 +27,7 @@ module.exports = {
config: {
summon: {
hideDock: true,
hideOnBlur: false,
hideOnBlur: true,
hotkey: 'Alt+Super+O'
}
},
Expand Down
6 changes: 3 additions & 3 deletions __mocks__/hyperterm-register-shortcut.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
registerShortcut = () => jest.fn()

module.exports = registerShortcut
module.exports = jest.fn(
() => jest.fn()
)
17 changes: 17 additions & 0 deletions fixtures/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
exports.generateApp = () => ({
config: {
getConfig: jest.fn(() => ({})),
subscribe: jest.fn()
},
createWindow: jest.fn(),
dock: {
hide: jest.fn(),
show: jest.fn()
},
getLastFocusedWindow: jest.fn(),
getWindows: jest.fn(() => new Set()),
hide: jest.fn(),
on: jest.fn(),
removeListener: jest.fn(),
show: jest.fn()
})
25 changes: 25 additions & 0 deletions fixtures/windowSet.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
const generateWindowSet = (count, opts = {}) => {
const windows = []

for (var i = 0; i < count; i++) {
windows.push(generateWindow(opts))
}

return new Set(windows)
}

const generateWindow = ({ focused = false, fullScreen = false, visible = true } = {}) => ({
focus: jest.fn(),
hide: jest.fn(),
isFocused: jest.fn(() => focused),
isFullScreen: jest.fn(() => fullScreen),
isVisible: jest.fn(() => visible),
minimize: jest.fn(),
on: jest.fn(),
show: jest.fn()
})

module.exports = {
generateWindow,
generateWindowSet
}
16 changes: 10 additions & 6 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
const addWindow = require('./modules/windows').addWindow;
const setup = require('./modules/setup');
const dispose = require('./modules/dispose')
const { generateActivateCallback, onApp } = require('./modules/app')
const { generateBlurCallback, hideWindows, showWindows } = require('./modules/windows')

const windowSet = new Set([]);
let handleActivate, handleBlur

module.exports = {
onApp: app => setup(app, windowSet),
onWindow: window => addWindow(window, windowSet)
exports.onApp = app => {
handleBlur = generateBlurCallback(hideWindows)(app)
handleActivate = generateActivateCallback(showWindows)(app)

onApp(app, handleActivate, handleBlur)
}
exports.onUnload = app => dispose(app, handleActivate, handleBlur)
95 changes: 95 additions & 0 deletions modules/__tests__/app.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
const registerShortcut = require('hyperterm-register-shortcut')
const toggle = require('../toggle')
const { applyConfig, generateActivateCallback, onApp } = require('../app')
const { generateApp } = require('../../fixtures/app')

jest.mock('../toggle')
jest.mock('../windows')
jest.mock('hyperterm-register-shortcut')

let app = generateApp()
let callback
const handleBlurMock = jest.fn()
const generateActivateCallbackMock = jest.fn()

describe('applyConfig', () => {
describe('with default config', () => {
beforeEach(() => {
applyConfig(app, handleBlurMock)
})

it('registers the default hot key', () => {
expect(registerShortcut).toHaveBeenCalledWith('summon', toggle, 'Ctrl+;')
})

it('shows the dock', () => {
expect(app.dock.show).toHaveBeenCalled()
})

it('does not handle blur events', () => {
expect(app.removeListener).toHaveBeenCalledWith('browser-window-blur', handleBlurMock)
})
})

describe('with hideDock config enabled', () => {
beforeEach(() => {
app.config.getConfig.mockReturnValue({
summon: {
hideDock: true
}
})
applyConfig(app, handleBlurMock)
})

it('hides the dock', () => {
expect(app.dock.hide).toHaveBeenCalled()
})
})

describe('with hideOnBlur config enabled', () => {
beforeEach(() => {
app.config.getConfig.mockReturnValue({
summon: {
hideOnBlur: true
}
})
applyConfig(app, handleBlurMock)
})

it('handles blur events', () => {
expect(app.on).toHaveBeenCalledWith('browser-window-blur', handleBlurMock)
})
})
})

describe('generateActivateCallback', () => {
beforeAll(() => {
callback = jest.fn()
})

it('returns a function', () => {
expect(generateActivateCallback(callback)(app)).toBeInstanceOf(Function)
})

it('resulting callback shows the windows', () => {
generateActivateCallback(callback)(app)()

expect(callback).toHaveBeenCalledTimes(1)
})
})

describe('onApp', () => {
describe('with default config', () => {
beforeEach(() => {
onApp(app, handleBlurMock, generateActivateCallbackMock)
})

it('handles the activate event', () => {
expect(app.on).toHaveBeenCalledWith('activate', expect.any(Function))
})

it('subscribes to config change', () => {
expect(app.config.subscribe).toHaveBeenCalledTimes(1)
})
})
})
31 changes: 31 additions & 0 deletions modules/__tests__/dispose.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
const dispose = require('../dispose')
const { generateApp } = require('../../fixtures/app')
// const { unregisterShortcut } = require('hyperterm-register-shortcut')

// jest.mock('hyperterm-register-shortcut')

const app = generateApp()
const handleActivateMock = jest.fn()
const handleBlurMock = jest.fn()

describe('dispose', () => {
beforeEach(() => {
dispose(app, handleActivateMock, handleBlurMock)
})

it('shows the dock icon', () => {
expect(app.dock.show).toHaveBeenCalledTimes(1)
})

xit('unregisters the shortcut', () => {
// expect(unregisterShortcut).toHaveBeenCalledTimes(1)
})

it('removes the activate listener', () => {
expect(app.removeListener).toHaveBeenCalledWith('activate', handleActivateMock)
})

it('removes the blur listener', () => {
expect(app.removeListener).toHaveBeenCalledWith('browser-window-blur', handleBlurMock)
})
})
46 changes: 46 additions & 0 deletions modules/__tests__/toggle.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
const toggle = require('../toggle')
const { generateApp } = require('../../fixtures/app')
const { generateWindow, generateWindowSet } = require('../../fixtures/windowSet')
const { hideWindows, showWindows } = require('../windows')

jest.mock('../windows')

const app = generateApp()
let win
let set = generateWindowSet(2)

describe('toggle', () => {
beforeAll(() => {
app.getWindows.mockReturnValue(set)
})

describe('when windows blurred', () => {
beforeEach(() => {
toggle(app)
})

it('shows the windows', () => {
expect(showWindows).toHaveBeenCalledTimes(1)
})
})

describe('when windows focused', () => {
beforeAll(() => {
win = generateWindow()
win.isFocused.mockReturnValue(true)
set.add(win)
})

beforeEach(() => {
toggle(app)
})

afterAll(() => {
set.delete(win)
})

it('hides windows', () => {
expect(hideWindows).toHaveBeenCalledTimes(1)
})
})
})
Loading

0 comments on commit 0393ca1

Please sign in to comment.