diff --git a/package-lock.json b/package-lock.json index cc5135f..83cb414 100644 --- a/package-lock.json +++ b/package-lock.json @@ -57,6 +57,7 @@ "electron-builder": "24.6.4", "electron-debug": "3.2.0", "electron-devtools-installer": "3.2.0", + "electron-json-storage": "4.6.0", "electron-log": "4.4.8", "electron-updater": "6.1.4", "eslint": "8.51.0", @@ -6741,6 +6742,41 @@ "integrity": "sha512-R1oD5gMBPS7PVU8gJwH6CtT0e6VSoD0+SzSnYpNm+dBkcijgA+K7VAMHDfnRq/lkKPZArpzplTW6jfiMYosdzw==", "dev": true }, + "node_modules/electron-json-storage": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/electron-json-storage/-/electron-json-storage-4.6.0.tgz", + "integrity": "sha512-gAgNsnA7tEtV9LzzOnZTyVIb3cQtCva+bEBVT5pbRGU8ZSZTVKPBrTxIAYjeVfdSjyNXgfb1mr/CZrOJgeHyqg==", + "dev": true, + "dependencies": { + "async": "^2.0.0", + "lockfile": "^1.0.4", + "lodash": "^4.0.1", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.1", + "write-file-atomic": "^2.4.2" + } + }, + "node_modules/electron-json-storage/node_modules/async": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "dev": true, + "dependencies": { + "lodash": "^4.17.14" + } + }, + "node_modules/electron-json-storage/node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, "node_modules/electron-localshortcut": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/electron-localshortcut/-/electron-localshortcut-3.2.1.tgz", @@ -10501,6 +10537,15 @@ "node": ">=8" } }, + "node_modules/lockfile": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/lockfile/-/lockfile-1.0.4.tgz", + "integrity": "sha512-cvbTwETRfsFh4nHsL1eGWapU1XFi5Ot9E85sWAwia7Y7EgB7vfqcZhTKZ+l7hCGxSPoushMv5GKhT5PdLv03WA==", + "dev": true, + "dependencies": { + "signal-exit": "^3.0.2" + } + }, "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", @@ -15321,6 +15366,17 @@ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true }, + "node_modules/write-file-atomic": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz", + "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.2" + } + }, "node_modules/ws": { "version": "8.13.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", @@ -20507,6 +20563,40 @@ "integrity": "sha512-R1oD5gMBPS7PVU8gJwH6CtT0e6VSoD0+SzSnYpNm+dBkcijgA+K7VAMHDfnRq/lkKPZArpzplTW6jfiMYosdzw==", "dev": true }, + "electron-json-storage": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/electron-json-storage/-/electron-json-storage-4.6.0.tgz", + "integrity": "sha512-gAgNsnA7tEtV9LzzOnZTyVIb3cQtCva+bEBVT5pbRGU8ZSZTVKPBrTxIAYjeVfdSjyNXgfb1mr/CZrOJgeHyqg==", + "dev": true, + "requires": { + "async": "^2.0.0", + "lockfile": "^1.0.4", + "lodash": "^4.0.1", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.1", + "write-file-atomic": "^2.4.2" + }, + "dependencies": { + "async": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "dev": true, + "requires": { + "lodash": "^4.17.14" + } + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } + } + }, "electron-localshortcut": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/electron-localshortcut/-/electron-localshortcut-3.2.1.tgz", @@ -23388,6 +23478,15 @@ "p-locate": "^4.1.0" } }, + "lockfile": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/lockfile/-/lockfile-1.0.4.tgz", + "integrity": "sha512-cvbTwETRfsFh4nHsL1eGWapU1XFi5Ot9E85sWAwia7Y7EgB7vfqcZhTKZ+l7hCGxSPoushMv5GKhT5PdLv03WA==", + "dev": true, + "requires": { + "signal-exit": "^3.0.2" + } + }, "lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", @@ -27078,6 +27177,17 @@ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true }, + "write-file-atomic": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz", + "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.2" + } + }, "ws": { "version": "8.13.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", diff --git a/package.json b/package.json index 2239c1e..7ad2842 100644 --- a/package.json +++ b/package.json @@ -119,6 +119,7 @@ "electron-builder": "24.6.4", "electron-debug": "3.2.0", "electron-devtools-installer": "3.2.0", + "electron-json-storage": "4.6.0", "electron-log": "4.4.8", "electron-updater": "6.1.4", "eslint": "8.51.0", diff --git a/src/main/index.js b/src/main/index.js index 9c1ce6c..3b320d3 100644 --- a/src/main/index.js +++ b/src/main/index.js @@ -83,7 +83,9 @@ const addSettingsCommandToDefaultMenus = () => { label: 'Settings', accelerator: 'CommandOrControl+,', click: () => { - BrowserWindow.getAllWindows().forEach(window => window.webContents.send('show-settings')); + BrowserWindow.getAllWindows().forEach(window => { + window.webContents.send('show-settings', app.getPath('userData')); + }); } }; diff --git a/src/renderer/components/settings-dialog/settings-dialog.html b/src/renderer/components/settings-dialog/settings-dialog.html index 4bd0c03..c11b3b3 100644 --- a/src/renderer/components/settings-dialog/settings-dialog.html +++ b/src/renderer/components/settings-dialog/settings-dialog.html @@ -4,12 +4,18 @@ v-on="{ 'dialog.close': () => { dialogOpened = false } }" headline="Settings" > - winter is coming +
+ + +
diff --git a/src/renderer/components/settings-dialog/settings-dialog.js b/src/renderer/components/settings-dialog/settings-dialog.js index 187a891..e371ceb 100644 --- a/src/renderer/components/settings-dialog/settings-dialog.js +++ b/src/renderer/components/settings-dialog/settings-dialog.js @@ -1,19 +1,34 @@ import { ipcRenderer } from 'electron'; +import { setDataPath, getSync, setSync } from 'electron-json-storage'; + +export const SETTING_FILE_NAME = 'secret-editor-settings'; export default { name: 'settings-dialog', template: require('./settings-dialog.html'), data: () => ({ - dialogOpened: false + dialogOpened: false, + gcloudPath: '' }), methods: { save() { - console.log('save'); + setSync(SETTING_FILE_NAME, { gcloudPath: this.gcloudPath }); + ipcRenderer.send('restart'); } }, mounted() { - ipcRenderer.on('show-settings', async () => { + ipcRenderer.on('show-settings', (event, path) => { + setDataPath(path); + this.gcloudPath = loadSettings().gcloudPath || ''; this.dialogOpened = true; }); } }; + +const loadSettings = () => { + try { + return getSync(SETTING_FILE_NAME); + } catch (error) { + return {}; + } +}; diff --git a/src/renderer/components/settings-dialog/settings-dialog.spec.js b/src/renderer/components/settings-dialog/settings-dialog.spec.js new file mode 100644 index 0000000..ff8cfb3 --- /dev/null +++ b/src/renderer/components/settings-dialog/settings-dialog.spec.js @@ -0,0 +1,93 @@ +import { mount } from '@vue/test-utils'; +import { rmSync, writeFileSync } from 'fs'; +import SettingsDialog, { SETTING_FILE_NAME } from './settings-dialog'; +import { ipcRenderer } from 'electron'; +import { getDataPath, getSync, setDataPath, setSync } from 'electron-json-storage'; + +describe('SettingsDialog', () => { + const settingsFilePath = '/tmp/secret-editor-test/'; + + context('when show-settings event arrives', () => { + it('should display dialog', async () => { + const wrapper = mount(SettingsDialog); + expect(wrapper.vm.dialogOpened).to.be.false; + + await emitShowSettings(wrapper); + + expect(wrapper.vm.dialogOpened).to.be.true; + }); + + it('should call setDataPath on electron-storage-json', async () => { + const wrapper = mount(SettingsDialog); + setDataPath('/oldTestPath'); + + await emitShowSettings(wrapper); + + expect(getDataPath()).to.eql(settingsFilePath); + }); + + it('should initialise settings when settings file does not exist', async () => { + rmSync(settingsFilePath, { force: true, recursive: true }); + const wrapper = mount(SettingsDialog); + + await emitShowSettings(wrapper); + + expect(wrapper.find('#gcloud-path').element.value).to.eql(''); + }); + + it('should initialise glcoud path when settings file exists but path is not set', async () => { + setSync(SETTING_FILE_NAME, {}, { dataPath: settingsFilePath }); + const wrapper = mount(SettingsDialog); + + await emitShowSettings(wrapper); + + expect(wrapper.find('#gcloud-path').element.value).to.eql(''); + }); + + it('should initialise glcoud path when settings file contains invalid json', async () => { + writeFileSync(`${settingsFilePath}/${SETTING_FILE_NAME}.json`, 'INVALID'); + const wrapper = mount(SettingsDialog); + + await emitShowSettings(wrapper); + + expect(wrapper.find('#gcloud-path').element.value).to.eql(''); + }); + + it('should initialise glcoud path when settings file exists and path is set', async () => { + setSync(SETTING_FILE_NAME, { gcloudPath: '/some-interesting-path' }, { dataPath: settingsFilePath }); + const wrapper = mount(SettingsDialog); + + await emitShowSettings(wrapper); + + expect(wrapper.find('#gcloud-path').element.value).to.eql('/some-interesting-path'); + }); + }); + + describe('#save', () => { + it('should update settings file', async () => { + setSync(SETTING_FILE_NAME, { gcloudPath: '/some-interesting-path' }, { dataPath: settingsFilePath }); + const wrapper = mount(SettingsDialog); + await emitShowSettings(wrapper); + + await wrapper.find('#gcloud-path').setValue('/an-even-more-interesting-path'); + await wrapper.find('#saveButton').trigger('click'); + + expect(getSync(SETTING_FILE_NAME)).to.eql({ gcloudPath: '/an-even-more-interesting-path' }); + }); + + it('should send restart message to main process when save button clicked', async () => { + sinon.stub(ipcRenderer, 'send'); + const wrapper = mount(SettingsDialog); + await emitShowSettings(wrapper); + + await wrapper.find('#saveButton').trigger('click'); + + expect(ipcRenderer.send).to.have.been.calledWith('restart'); + }); + }); + + const emitShowSettings = async (wrapper) => { + ipcRenderer.emit('show-settings', {}, settingsFilePath); + await wrapper.vm.$nextTick(); + }; +});