Repository navigation
feat: 새 버전 다운로드 전에 사용자 승인을 받는다 #48
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,141 @@ | ||
| import { EventEmitter } from 'events' | ||
| import type { BrowserWindow } from 'electron' | ||
| import { autoUpdater } from 'electron-updater' | ||
| import { beforeEach, describe, expect, it, vi } from 'vitest' | ||
| import { downloadUpdate, initAutoUpdate } from './autoUpdate' | ||
|
|
||
| vi.mock('electron', () => ({ app: { isPackaged: true, getVersion: () => '0.9.0' } })) | ||
|
|
||
| vi.mock('electron-updater', async () => { | ||
| const { EventEmitter } = await import('events') | ||
| const fake = Object.assign(new EventEmitter(), { | ||
| autoDownload: true, // 기본값 — initAutoUpdate 가 이걸 끄는지 본다 | ||
| autoInstallOnAppQuit: true, | ||
| // 회귀 방지용 — 진행률이 이 값으로 되돌아가면 아래 버전 테스트가 깨진다 | ||
| currentVersion: { version: '0.9.0' }, | ||
| // 실제 downloadUpdate 는 Promise<string[]> 를 돌려주고 실패하면 거부한다 | ||
| downloadUpdate: vi.fn(() => Promise.resolve([])), | ||
| quitAndInstall: vi.fn() | ||
| }) | ||
| return { autoUpdater: fake } | ||
| }) | ||
|
|
||
| /** initAutoUpdate 가 쓰는 만큼만 갖춘 창 대역 */ | ||
| function fakeWindow(destroyed = false): { window: BrowserWindow; sent: unknown[] } { | ||
| const sent: unknown[] = [] | ||
| const window = { | ||
| isDestroyed: () => destroyed, | ||
| webContents: { | ||
| send: (channel: string, payload: unknown) => { | ||
| if (channel === 'update:event') sent.push(payload) | ||
| } | ||
| } | ||
| } | ||
| return { window: window as unknown as BrowserWindow, sent } | ||
| } | ||
|
|
||
| const updater = autoUpdater as unknown as EventEmitter & { | ||
| autoDownload: boolean | ||
| downloadUpdate: ReturnType<typeof vi.fn> | ||
| } | ||
|
|
||
| beforeEach(() => { | ||
| updater.removeAllListeners() | ||
| updater.autoDownload = true | ||
| vi.clearAllMocks() | ||
| updater.downloadUpdate.mockImplementation(() => Promise.resolve([])) | ||
| }) | ||
|
|
||
| describe('initAutoUpdate', () => { | ||
| it('새 버전을 찾으면 다운로드하지 않고 확인 이벤트를 보낸다', () => { | ||
| const { window, sent } = fakeWindow() | ||
| initAutoUpdate(window) | ||
|
|
||
| updater.emit('update-available', { version: '0.9.1' }) | ||
|
|
||
| expect(sent).toEqual([{ type: 'available', version: '0.9.1' }]) | ||
| expect(updater.downloadUpdate).not.toHaveBeenCalled() | ||
| }) | ||
|
|
||
| it('autoDownload 를 꺼서 라이브러리가 스스로 받지 않게 한다', () => { | ||
| initAutoUpdate(fakeWindow().window) | ||
|
|
||
| expect(updater.autoDownload).toBe(false) | ||
| }) | ||
|
|
||
| it('진행률은 설치된 버전이 아니라 받고 있는 버전을 알린다', () => { | ||
| const { window, sent } = fakeWindow() | ||
| initAutoUpdate(window) | ||
|
|
||
| updater.emit('update-available', { version: '0.9.1' }) | ||
| updater.emit('download-progress', { percent: 41.6 }) | ||
|
|
||
| expect(sent.at(-1)).toEqual({ type: 'downloading', version: '0.9.1', percent: 42 }) | ||
| }) | ||
|
|
||
| it('어느 버전을 받는지 모르면 진행률을 알리지 않는다', () => { | ||
| const { window, sent } = fakeWindow() | ||
| initAutoUpdate(window) | ||
|
|
||
| updater.emit('download-progress', { percent: 30 }) | ||
|
|
||
| expect(sent).toEqual([]) | ||
| }) | ||
|
|
||
| it('다 받으면 설치 준비를 알린다', () => { | ||
| const { window, sent } = fakeWindow() | ||
| initAutoUpdate(window) | ||
|
|
||
| updater.emit('update-downloaded', { version: '0.9.1' }) | ||
|
|
||
| expect(sent).toEqual([{ type: 'ready', version: '0.9.1' }]) | ||
| }) | ||
|
|
||
| it('오류를 렌더러까지 올린다', () => { | ||
| const { window, sent } = fakeWindow() | ||
| initAutoUpdate(window) | ||
|
|
||
| // electron-updater 는 error 를 (에러, 스택) 두 인자로 발행한다 | ||
| updater.emit('error', new Error('ENOTFOUND'), 'stack') | ||
|
|
||
| expect(sent).toEqual([{ type: 'error', message: 'ENOTFOUND' }]) | ||
| }) | ||
|
|
||
| it('창이 이미 닫혔으면 아무것도 보내지 않는다', () => { | ||
| const { window, sent } = fakeWindow(true) | ||
| initAutoUpdate(window) | ||
|
|
||
| updater.emit('update-available', { version: '0.9.1' }) | ||
|
|
||
| expect(sent).toEqual([]) | ||
| }) | ||
|
|
||
| // autoUpdater 는 모듈 싱글턴이라, 창을 닫았다 다시 열면 리스너가 쌓이고 죽은 창의 클로저가 남는다 | ||
| it('창을 다시 열면 이전 창의 리스너를 남기지 않는다', () => { | ||
| const first = fakeWindow() | ||
| initAutoUpdate(first.window) | ||
| const second = fakeWindow() | ||
| initAutoUpdate(second.window) | ||
|
|
||
| updater.emit('update-available', { version: '0.9.1' }) | ||
|
|
||
| expect(first.sent).toEqual([]) | ||
| expect(second.sent).toEqual([{ type: 'available', version: '0.9.1' }]) | ||
| }) | ||
| }) | ||
|
|
||
| describe('downloadUpdate', () => { | ||
| it('라이브러리에 다운로드를 시작시킨다', async () => { | ||
| await downloadUpdate() | ||
|
|
||
| expect(updater.downloadUpdate).toHaveBeenCalledOnce() | ||
| }) | ||
|
|
||
| // 취소는 electron-updater 가 'error' 이벤트를 내지 않는다. 여기서 삼키면 배너가 승인 상태에 | ||
| // 갇혀 다시 누를 수도 없다 | ||
| it('실패를 삼키지 않고 호출자에게 올린다', async () => { | ||
| updater.downloadUpdate.mockImplementation(() => Promise.reject(new Error('cancelled'))) | ||
|
|
||
| await expect(downloadUpdate()).rejects.toThrow('cancelled') | ||
| }) | ||
| }) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.