Skip to content

Commit

Permalink
Merge pull request #6 from spaceagetv/feat/download+updater
Browse files Browse the repository at this point in the history
Feat: DownloadItem + AutoUpdater
  • Loading branch information
jjeff authored Aug 25, 2023
2 parents fe54dca + 07a8dd5 commit 215af72
Show file tree
Hide file tree
Showing 15 changed files with 2,997 additions and 1,474 deletions.
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# electron-mocks

[![npm](https://img.shields.io/npm/v/electron-mocks.svg)](https://www.npmjs.com/package/electron-mocks)
[![GitHub release](https://img.shields.io/github/release/spaceagetv/electron-mocks.svg)](https://github.com/spaceagetv/electron-mocks/releases)
[![npm](https://img.shields.io/npm/dm/electron-mocks)](https://www.npmjs.com/package/electron-mocks)
Expand Down Expand Up @@ -27,6 +28,8 @@ Currently implemented:
- [MockDialog](src/MockDialog.ts)
- [MockScreen](src/MockScreen.ts)
- [MockDisplay](src/MockDisplay.ts)
- [MockAutoUpdater](src/MockAutoUpdater.ts)
- [MockDownloadItem](src/MockDownloadItem.ts)

All methods are implemented and should return logical values. Additionally, methods are wrapped in [sinon.spy()]([url](https://sinonjs.org/releases/latest/spies/)) so calls can be queried. All logical events should be emitted.

Expand Down Expand Up @@ -100,6 +103,5 @@ MIT

### Alternatives

- [electron-mocha](https://github.com/jprichardson/electron-mocha) - Allows you to run your tests in Electron, but you still need to mock out IPC calls and spin up a real Electron instance
- [electron-mock-ipc](https://github.com/h3poteto/electron-mock-ipc) - Mocks out Electron ipc calls. Does not rely on sinon. ipcMain and ipcRenderer communicate with one another.

- [electron-mocha](https://github.com/jprichardson/electron-mocha) - Allows you to run your tests in Electron, but you still need to mock out IPC calls and spin up a real Electron instance. FWIW, you can use `electron-mocks` with `electron-mocha` to get the best of both worlds.
- [electron-mock-ipc](https://github.com/h3poteto/electron-mock-ipc) - Mocks out Electron ipc calls. Does not rely on sinon. ipcMain and ipcRenderer communicate with one another. Again, you can mix and match this with `electron-mocks` if you want.
4,224 changes: 2,794 additions & 1,430 deletions package-lock.json

Large diffs are not rendered by default.

34 changes: 18 additions & 16 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,33 +33,35 @@
"author": "Jeff Robbins",
"license": "MIT",
"devDependencies": {
"@microsoft/api-documenter": "^7.22.7",
"@microsoft/api-documenter": "^7.22.33",
"@semantic-release/changelog": "^6.0.3",
"@semantic-release/git": "^10.0.1",
"@semantic-release/github": "^8.1.0",
"@semantic-release/npm": "^10.0.3",
"@semantic-release/github": "^9.0.4",
"@semantic-release/npm": "^10.0.5",
"@types/chai": "^4.3.5",
"@types/chai-as-promised": "^7.1.5",
"@types/mocha": "^10.0.1",
"@types/sinon": "^10.0.15",
"@typescript-eslint/eslint-plugin": "^5.59.8",
"@typescript-eslint/parser": "^5.59.8",
"chai": "^4.3.7",
"@types/sinon": "^10.0.16",
"@types/sinon-chai": "^3.2.9",
"@typescript-eslint/eslint-plugin": "^6.4.1",
"@typescript-eslint/parser": "^6.4.1",
"chai": "^4.3.8",
"chai-as-promised": "^7.1.1",
"conventional-changelog-conventionalcommits": "^5.0.0",
"electron": "^25.0.1",
"conventional-changelog-conventionalcommits": "^6.1.0",
"electron": "^26.1.0",
"electron-mocks": "file:./",
"eslint": "^8.41.0",
"eslint-config-prettier": "^8.8.0",
"eslint-plugin-prettier": "^4.2.1",
"eslint": "^8.47.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-prettier": "^5.0.0",
"mocha": "^10.2.0",
"prettier": "^2.8.8",
"semantic-release": "^21.0.2",
"prettier": "^3.0.2",
"semantic-release": "^21.1.1",
"sinon-chai": "^3.7.0",
"ts-node": "^10.9.1",
"typescript": "^5.1.3"
"typescript": "^5.2.2"
},
"dependencies": {
"sinon": "^15.1.0"
"sinon": "^15.2.0"
},
"publishConfig": {
"access": "public"
Expand Down
22 changes: 22 additions & 0 deletions src/MockAutoUpdater.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { AutoUpdater } from 'electron'
import * as sinon from 'sinon'
import { EventEmitter } from 'events'

export class MockAutoUpdater extends EventEmitter implements AutoUpdater {
_feedURL: string
_headers: Record<string, string>
_serverType: string

checkForUpdates = sinon.spy()
quitAndInstall = sinon.spy()

setFeedURL = sinon.spy(({ url, headers, serverType }) => {
this._feedURL = url
this._headers = headers
this._serverType = serverType
})

getFeedURL() {
return this._feedURL
}
}
4 changes: 2 additions & 2 deletions src/MockBrowserWindow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ export class MockBrowserWindow extends EventEmitter implements BrowserWindow {
})
isSimpleFullScreen = sinon.spy(() => this._simpleFullscreen)
isNormal = sinon.spy(
() => !this._fullscreen && !this._minimized && !this._maximized
() => !this._fullscreen && !this._minimized && !this._maximized,
)
setAspectRatio = sinon.spy()
setBackgroundColor = sinon.spy((color: string) => {
Expand Down Expand Up @@ -395,7 +395,7 @@ export class MockBrowserWindow extends EventEmitter implements BrowserWindow {
constructor(options: Electron.BrowserWindowConstructorOptions = {}) {
super()
this.webContents = new MockWebContents(
options.webPreferences
options.webPreferences,
) as unknown as WebContents

this.webContents.on('did-finish-load', () => {
Expand Down
51 changes: 51 additions & 0 deletions src/MockDownloadItem.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { DownloadItem } from 'electron'
import * as sinon from 'sinon'
import { EventEmitter } from 'events'

// https://www.electronjs.org/docs/latest/api/download-item

export class MockDownloadItem extends EventEmitter implements DownloadItem {
_filename: string
_URL: string
savePath = ''
_receivedBytes = 0
_totalBytes = 1000
_state: 'completed' | 'cancelled' | 'interrupted' | 'progressing' =
'completed'
_canResume = true
_isPaused = false

cancel = sinon.spy()
canResume = sinon.spy()
getContentDisposition = sinon.spy(() => '')
getETag = sinon.spy(() => '')
getFilename = sinon.spy(() => this._filename)
getLastModifiedTime = sinon.spy(() => '')
getMimeType = sinon.spy(() => '')
getReceivedBytes = sinon.spy(() => this._receivedBytes)
getSavePath = sinon.spy(() => this.savePath)
getStartTime = sinon.spy(() => Date.now())
getState = sinon.spy(() => this._state)
getTotalBytes = sinon.spy(() => this._totalBytes)
getURL = sinon.spy(() => this._URL)
getURLChain = sinon.spy(() => [this._URL])
hasUserGesture = sinon.spy(() => false)
isPaused = sinon.spy(() => this._isPaused)
pause = sinon.spy(() => {
this._isPaused = true
})
resume = sinon.spy(() => {
this._isPaused = false
})
setSaveDialogOptions = sinon.spy()
getSaveDialogOptions = sinon.spy(() => ({}))
setSavePath = sinon.spy((path: string) => {
this.savePath = path
})

constructor(url: string) {
super()
this._URL = url
this._filename = url.split('?')[0].split('/').pop() || ''
}
}
4 changes: 2 additions & 2 deletions src/MockScreen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,15 @@ export class MockScreen extends EventEmitter implements Electron.Screen {
dipToScreenRect = sinon.spy(
(window: Electron.BrowserWindow | null, rect: Electron.Rectangle) => {
return rect
}
},
)
screenToDipPoint = sinon.spy((point: Electron.Point) => {
return point
})
screenToDipRect = sinon.spy(
(window: Electron.BrowserWindow | null, rect: Electron.Rectangle) => {
return rect
}
},
)

constructor(displays: Electron.Display[] = []) {
Expand Down
2 changes: 1 addition & 1 deletion src/MockWebContents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ export class MockWebContents extends EventEmitter implements WebContents {
| 'browserView'
| 'remote'
| 'webview'
| 'offscreen'
| 'offscreen',
)
setImageAnimationPolicy = sinon.spy()

Expand Down
8 changes: 5 additions & 3 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
export * from './MockAutoUpdater'
export * from './MockBrowserWindow'
export * from './MockDialog'
export * from './MockDisplay'
export * from './MockDownloadItem'
export * from './MockIpcMain'
export * from './MockIpcRenderer'
export * from './MockWebContents'
export * from './MockScreen'
export * from './MockDisplay'
export * from './MockDialog'
export * from './MockWebContents'
44 changes: 44 additions & 0 deletions test/MockAutoUpdater.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { MockAutoUpdater } from '../src'
import chai, { expect } from 'chai'
import sinonChai from 'sinon-chai'

chai.use(sinonChai)

describe('MockAutoUpdater', () => {
it('should be defined', () => {
expect(MockAutoUpdater).to.be.not.undefined
})

it('should be a class', () => {
expect(typeof MockAutoUpdater).to.equal('function')
})

it('should instantiate', () => {
const mockAutoUpdater = new MockAutoUpdater()
expect(mockAutoUpdater).to.be.not.undefined
})

it('should be able to setUrl()', () => {
const mockAutoUpdater = new MockAutoUpdater() as Electron.AutoUpdater
mockAutoUpdater.setFeedURL({
url: 'https://example.com',
headers: {
'User-Agent': 'MockAutoUpdater',
},
serverType: 'json',
})
expect(mockAutoUpdater.getFeedURL()).to.equal('https://example.com')
})

it('should be able to call checkForUpdates()', () => {
const mockAutoUpdater = new MockAutoUpdater() as Electron.AutoUpdater
mockAutoUpdater.checkForUpdates()
expect(mockAutoUpdater.checkForUpdates).to.have.been.calledOnce
})

it('should be able to call quitAndInstall()', () => {
const mockAutoUpdater = new MockAutoUpdater() as Electron.AutoUpdater
mockAutoUpdater.quitAndInstall()
expect(mockAutoUpdater.quitAndInstall).to.have.been.calledOnce
})
})
8 changes: 4 additions & 4 deletions test/MockBrowserWindow.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,12 +96,12 @@ describe('MockBrowserWindow', () => {
sinon.assert.calledOnce(window.webContents.loadURL as sinon.SinonSpy)
sinon.assert.calledWithExactly(
window.webContents.loadURL as sinon.SinonSpy,
'https://example.com'
'https://example.com',
)
sinon.assert.calledOnce(window.webContents.loadURL as sinon.SinonSpy)
sinon.assert.calledWithExactly(
window.webContents.loadURL as sinon.SinonSpy,
'https://example.com'
'https://example.com',
)
expect(window.webContents.getURL()).to.equal('https://example.com')
})
Expand All @@ -116,10 +116,10 @@ describe('MockBrowserWindow', () => {
sinon.assert.calledOnce(window.webContents.loadFile as sinon.SinonSpy)
sinon.assert.calledWithExactly(
window.webContents.loadFile as sinon.SinonSpy,
'test/fixtures/index.html'
'test/fixtures/index.html',
)
expect(window.webContents.getURL()).to.equal(
'file://test/fixtures/index.html'
'file://test/fixtures/index.html',
)
})

Expand Down
12 changes: 6 additions & 6 deletions test/MockDialog.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ describe('MockDialog', () => {
mockDialog.showOpenDialog.calledWith({
title: 'title',
defaultPath: 'defaultPath',
})
}),
).to.be.true
expect(result).to.deep.equal(returnValue)
})
Expand All @@ -54,7 +54,7 @@ describe('MockDialog', () => {
mockDialog.showSaveDialog.calledWith({
title: 'title',
defaultPath: 'defaultPath',
})
}),
).to.be.true
expect(result).to.deep.equal(returnValue)
})
Expand All @@ -80,7 +80,7 @@ describe('MockDialog', () => {
mockDialog.showMessageBox.calledWith({
title: 'title',
message: 'message',
})
}),
).to.be.true
expect(result).to.deep.equal(returnValue)
})
Expand Down Expand Up @@ -114,7 +114,7 @@ describe('MockDialog', () => {
mockDialog.showMessageBoxSync.calledWith({
title: 'title',
message: 'message',
})
}),
).to.be.true
expect(result).to.equal(returnValue)
})
Expand All @@ -137,7 +137,7 @@ describe('MockDialog', () => {
mockDialog.showOpenDialogSync.calledWith({
title: 'title',
defaultPath: 'defaultPath',
})
}),
).to.be.true
expect(result).to.deep.equal(returnValue)
})
Expand All @@ -163,7 +163,7 @@ describe('MockDialog', () => {
mockDialog.showSaveDialogSync.calledWith({
title: 'title',
defaultPath: 'defaultPath',
})
}),
).to.be.true
expect(result).to.deep.equal(returnValue)
})
Expand Down
36 changes: 36 additions & 0 deletions test/MockDownloadItem.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { MockDownloadItem } from '../src'
import chai, { expect } from 'chai'
import sinonChai from 'sinon-chai'

chai.use(sinonChai)

describe('MockDownloadItem', () => {
it('should create an instance', () => {
const mockDownloadItem = new MockDownloadItem(
'https://example.com',
) as Electron.DownloadItem
expect(mockDownloadItem).to.be.not.undefined
})

it('should be able to get the URL', () => {
const mockDownloadItem = new MockDownloadItem(
'https://example.com',
) as Electron.DownloadItem
expect(mockDownloadItem.getURL()).to.equal('https://example.com')
})

it('should be able to get the filename', () => {
const mockDownloadItem = new MockDownloadItem(
'https://example.com/example.txt?foo=bar&baz=qux',
) as Electron.DownloadItem
expect(mockDownloadItem.getFilename()).to.equal('example.txt')
})

it('should be able to set/get the save path', () => {
const mockDownloadItem = new MockDownloadItem(
'https://example.com',
) as Electron.DownloadItem
mockDownloadItem.setSavePath('/tmp/example.txt')
expect(mockDownloadItem.getSavePath()).to.equal('/tmp/example.txt')
})
})
Loading

0 comments on commit 215af72

Please sign in to comment.