diff --git a/package.json b/package.json index 531bb2ea..9f393534 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yattie", - "version": "0.1.0", + "version": "0.1.1", "private": true, "main": "background.js", "engines": { @@ -27,7 +27,7 @@ "@fortawesome/fontawesome-free": "^6.2.1", "@johmun/vue-tags-input": "^2.1.0", "@tinymce/tinymce-vue": "^3.2.0", - "@toast-ui/vue-image-editor": "^3.15.2", + "tui-image-editor": "^3.15.3", "adm-zip": "^0.5.9", "core-js": "^3.8.3", "d3": "^5.14.2", @@ -39,6 +39,7 @@ "fluent-ffmpeg": "^2.1.2", "lodash": "^4.17.21", "simple-json-db": "^2.0.0", + "sinon": "^15.0.0", "tinymce": "^6.2.0", "uuid": "3.3.3", "v-mask": "^2.3.0", diff --git a/src/App.vue b/src/App.vue index b8c585c5..7f61d7ca 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,41 +1,28 @@ + diff --git a/src/assets/avatar.png b/src/assets/avatar.png new file mode 100644 index 00000000..737cf25b Binary files /dev/null and b/src/assets/avatar.png differ diff --git a/src/assets/icon/camera-white.svg b/src/assets/icon/camera-white.svg new file mode 100644 index 00000000..0f73ffb5 --- /dev/null +++ b/src/assets/icon/camera-white.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/icon/expand.svg b/src/assets/icon/expand.svg new file mode 100644 index 00000000..72262a40 --- /dev/null +++ b/src/assets/icon/expand.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/icon/jira.png b/src/assets/icon/jira.png new file mode 100644 index 00000000..6c5aa9f2 Binary files /dev/null and b/src/assets/icon/jira.png differ diff --git a/src/assets/icon/jira.svg b/src/assets/icon/jira.svg new file mode 100644 index 00000000..612e4c42 --- /dev/null +++ b/src/assets/icon/jira.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/assets/icon/microphone-slash-solid.svg b/src/assets/icon/microphone-slash-solid.svg new file mode 100644 index 00000000..b69e019e --- /dev/null +++ b/src/assets/icon/microphone-slash-solid.svg @@ -0,0 +1,10 @@ + + + +Created with Fabric.js 4.6.0 + + + + + + \ No newline at end of file diff --git a/src/assets/icon/microphone-solid.svg b/src/assets/icon/microphone-solid.svg new file mode 100644 index 00000000..8028507e --- /dev/null +++ b/src/assets/icon/microphone-solid.svg @@ -0,0 +1,10 @@ + + + +Created with Fabric.js 4.6.0 + + + + + + \ No newline at end of file diff --git a/src/assets/icon/microphone-white.svg b/src/assets/icon/microphone-white.svg new file mode 100644 index 00000000..eaccc705 --- /dev/null +++ b/src/assets/icon/microphone-white.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/icon/microphone.svg b/src/assets/icon/microphone.svg deleted file mode 100644 index 16c93e0a..00000000 --- a/src/assets/icon/microphone.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/src/assets/icon/pause-white.svg b/src/assets/icon/pause-white.svg new file mode 100644 index 00000000..850db3d6 --- /dev/null +++ b/src/assets/icon/pause-white.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/icon/pencil-white1.svg b/src/assets/icon/pencil-white1.svg new file mode 100644 index 00000000..7cb6e4a0 --- /dev/null +++ b/src/assets/icon/pencil-white1.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/icon/practitest.png b/src/assets/icon/practitest.png new file mode 100644 index 00000000..4b9f3e27 Binary files /dev/null and b/src/assets/icon/practitest.png differ diff --git a/src/assets/icon/practitest.svg b/src/assets/icon/practitest.svg new file mode 100644 index 00000000..76ef7b1d --- /dev/null +++ b/src/assets/icon/practitest.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/assets/icon/qtest.png b/src/assets/icon/qtest.png new file mode 100644 index 00000000..6c5e5f4b Binary files /dev/null and b/src/assets/icon/qtest.png differ diff --git a/src/assets/icon/qtest.svg b/src/assets/icon/qtest.svg new file mode 100644 index 00000000..f900d4b4 --- /dev/null +++ b/src/assets/icon/qtest.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/assets/icon/stop-white.svg b/src/assets/icon/stop-white.svg new file mode 100644 index 00000000..c1887907 --- /dev/null +++ b/src/assets/icon/stop-white.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/icon/testrail.png b/src/assets/icon/testrail.png new file mode 100644 index 00000000..dae6f408 Binary files /dev/null and b/src/assets/icon/testrail.png differ diff --git a/src/assets/icon/testrail.svg b/src/assets/icon/testrail.svg new file mode 100644 index 00000000..53772188 --- /dev/null +++ b/src/assets/icon/testrail.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/assets/icon/union.svg b/src/assets/icon/union.svg new file mode 100644 index 00000000..874e20e6 --- /dev/null +++ b/src/assets/icon/union.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/icon/video-slash-solid.svg b/src/assets/icon/video-slash-solid.svg new file mode 100644 index 00000000..c9c72329 --- /dev/null +++ b/src/assets/icon/video-slash-solid.svg @@ -0,0 +1,10 @@ + + + +Created with Fabric.js 4.6.0 + + + + + + \ No newline at end of file diff --git a/src/assets/icon/video-solid.svg b/src/assets/icon/video-solid.svg new file mode 100644 index 00000000..2050d262 --- /dev/null +++ b/src/assets/icon/video-solid.svg @@ -0,0 +1,10 @@ + + + +Created with Fabric.js 4.6.0 + + + + + + \ No newline at end of file diff --git a/src/assets/icon/video-white.svg b/src/assets/icon/video-white.svg new file mode 100644 index 00000000..3649798e --- /dev/null +++ b/src/assets/icon/video-white.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/icon/video.svg b/src/assets/icon/video.svg deleted file mode 100644 index b66284eb..00000000 --- a/src/assets/icon/video.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/src/assets/icon/yattie.png b/src/assets/icon/yattie.png new file mode 100644 index 00000000..19bce1b9 Binary files /dev/null and b/src/assets/icon/yattie.png differ diff --git a/src/assets/icon/yattie.svg b/src/assets/icon/yattie.svg new file mode 100644 index 00000000..84e76873 --- /dev/null +++ b/src/assets/icon/yattie.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/AudioWrapper.vue b/src/components/AudioWrapper.vue index a45ba8c0..459e3936 100644 --- a/src/components/AudioWrapper.vue +++ b/src/components/AudioWrapper.vue @@ -98,7 +98,7 @@ diff --git a/src/components/CheckTaskWrapper.vue b/src/components/CheckTaskWrapper.vue index 51e2d0b6..87f077de 100644 --- a/src/components/CheckTaskWrapper.vue +++ b/src/components/CheckTaskWrapper.vue @@ -9,7 +9,7 @@ -
+
@@ -201,7 +201,11 @@ :disabled="status === 'pause'" @click="stopRecordVideo()" > - mdi-video-off + Stop Video Record @@ -242,9 +246,9 @@ @click="startRecordAudio()" > @@ -263,7 +267,11 @@ :disabled="status === 'pause'" @click="stopRecordAudio()" > - mdi-microphone-off + Stop Audio Record @@ -311,6 +319,26 @@ Mind Map + - + + [], + }, selectedItems: { type: Array, default: () => [], }, + configItem: { + type: Object, + default: () => {}, + }, checkedStatusOfPreSessionTask: { type: Boolean, default: () => false, @@ -422,15 +475,30 @@ export default { default: () => {}, }, }, + created() { + try { + audioContext = new AudioContext(); + dest = audioContext.createMediaStreamDestination(); + } catch (e) { + console.log(e); + } + }, watch: { + items: function (newValue) { + this.itemLists = newValue; + }, selectedItems: function (newValue) { this.selected = newValue; }, + configItem: function (newValue) { + this.config = newValue; + }, }, data() { return { sourcePickerDialog: false, noteDialog: false, + summaryDialog: false, deleteConfirmDialog: false, resetConfirmDialog: false, @@ -441,6 +509,8 @@ export default { sources: [], sourceId: "", + itemLists: this.items, + config: this.configItem, audioDevices: [], loaded: false, status: this.$store.state.status, @@ -460,6 +530,10 @@ export default { mounted() { this.$root.$on("close-sourcepickerdialog", this.hideSourcePickerDialog); this.$root.$on("close-notedialog", this.hideNoteDialog); + this.$root.$on("close-summarydialog", () => { + this.summaryDialog = false; + this.endSession(); + }); }, beforeDestroy() { this.$root.$off("close-sourcepickerdialog", this.hideSourcePickerDialog); @@ -509,9 +583,6 @@ export default { this.updateStoreSession(); }, updateStoreSession() { - console.log(` - status: ${this.status}, timer: ${this.timer}, duration: ${this.duration} - `); this.$store.commit("updateSession", { status: this.status, timer: this.timer, @@ -531,6 +602,7 @@ export default { this.$store.commit("setStarted", this.started); this.startInterval(); + this.changeSessionStatus(SESSION_STATUSES.START); const currentPath = this.$router.history.current.path; if (currentPath !== "/main/timeline") { @@ -539,6 +611,7 @@ export default { }, pauseSession() { this.status = SESSION_STATUSES.PAUSE; + this.changeSessionStatus(SESSION_STATUSES.PAUSE); this.stopInterval(); }, resumeSession() { @@ -562,6 +635,7 @@ export default { this.$store.commit("setEnded", this.ended); this.status = SESSION_STATUSES.END; + this.changeSessionStatus(SESSION_STATUSES.END); this.stopInterval(); if (window.ipc) { @@ -580,14 +654,17 @@ export default { }, resume() { this.status = SESSION_STATUSES.PAUSE; + this.changeSessionStatus(SESSION_STATUSES.PAUSE); this.timer = this.$store.state.timer; this.updateStoreSession(); + this.removeSummary(); this.$router.push({ path: "/main/timeline" }); }, reset() { this.resetConfirmDialog = false; this.status = SESSION_STATUSES.PENDING; + this.changeSessionStatus(SESSION_STATUSES.PENDING); this.$store.commit("resetState"); try { @@ -610,11 +687,13 @@ export default { proceed() { this.durationConfirmDialog = false; this.status = SESSION_STATUSES.PROCEED; + this.changeSessionStatus(SESSION_STATUSES.PROCEED); this.startInterval(); }, updateStatus(value) { this.status = value; + this.changeSessionStatus(this.status); this.$store.commit("setStatus", this.status); }, async getSourceList() { @@ -653,7 +732,7 @@ export default { }) .then(({ fileName, filePath }) => { const data = { - sessionType: "screenshot", + sessionType: "Screenshot", fileType: "image", fileName: fileName, filePath: filePath, @@ -690,6 +769,9 @@ export default { }, async startRecordVideo() { this.handleStream = (stream) => { + if (this.config.audioCapture && this.audioDevices.length > 0) { + stream.addTrack(dest.stream.getAudioTracks()[0]); + } mediaRecorder = new MediaRecorder(stream, { mimeType: "video/webm;codecs=h264", }); @@ -739,7 +821,7 @@ export default { }) .then(({ fileName, filePath }) => { const data = { - sessionType: "video", + sessionType: "Video", fileType: "video", fileName: fileName, filePath: filePath, @@ -761,21 +843,39 @@ export default { }; try { - const stream = await navigator.mediaDevices.getUserMedia({ + const videoQuality = this.config.videoQuality; + let resolution; + VIDEO_RESOLUTION.map((item) => { + let temp = Object.assign({}, item); + if (temp.type === videoQuality) { + resolution = temp; + } + }); + const constraints = { audio: false, video: { mandatory: { chromeMediaSource: "desktop", chromeMediaSourceId: this.sourceId, - minWidth: 640, - maxWidth: 1920, - minHeight: 480, - maxHeight: 1080, + minWidth: resolution.width, + maxWidth: resolution.width, + minHeight: resolution.height, + maxHeight: resolution.height, }, }, - }); + }; + + if (this.config.audioCapture) { + this.audioDevices = await this.getAudioSources(); + if (this.audioDevices.length > 0) { + await this.setAudio(this.audioDevices); + } + } + + const stream = await navigator.mediaDevices.getUserMedia(constraints); stream.getVideoTracks()[0].applyConstraints({ frameRate: 30 }); + this.handleStream(stream); } catch (e) { console.log(e); @@ -788,6 +888,29 @@ export default { console.log(error); } }, + async getAudioSources() { + return await navigator.mediaDevices.enumerateDevices().then((devices) => { + const audioDevices = devices.filter( + (d) => + d.kind === "audioinput" && + d.deviceId != "communications" && + d.deviceId != "default" + ); + return audioDevices; + }); + }, + async setAudio(source) { + const audioStream = await navigator.mediaDevices.getUserMedia({ + audio: { + deviceId: source.deviceId, + autoGainControl: false, + latency: 0.0, + }, + }); + let audioIn_01 = audioContext.createMediaStreamSource(audioStream); + audioIn_01.connect(dest); + return audioIn_01; + }, async startRecordAudio() { this.setAudioSource = async () => { try { @@ -829,21 +952,23 @@ export default { const blob = new Blob(recordedChunks, { type: "audio/mpeg-3", }); + const buffer = await blob.arrayBuffer(); - const fileName = dayjs().format("YYYY-MM-DD_HH-mm-ss-ms") + ".mp3"; + if (window.ipc) { await window.ipc .invoke(IPC_HANDLERS.CAPTURE, { - func: IPC_FUNCTIONS.CREATE_TEMP_USER_MEDIA, - data: { buffer: buffer, fileName: fileName }, + func: IPC_FUNCTIONS.CREATE_AUDIO, + data: { buffer: buffer }, }) - .then((filePath) => { + .then(({ fileName, filePath }) => { const data = { - sessionType: "audio", + sessionType: "Audio", fileType: "audio", fileName: fileName, filePath: filePath, time: this.timer, + poster: "", }; this.openAddWindow(data); @@ -859,6 +984,18 @@ export default { console.log("Error:", error); }; + // try { + // this.audioDevices = await this.getAudioSources(); + // if (!this.audioDevices.length) { + // this.audioErrorDialog = true; + // return; + // } + // const stream = this.setAudio(this.audioDevices); + // this.handleStream(stream); + // } catch (e) { + // console.log(e); + // } + await navigator.mediaDevices.enumerateDevices().then((devices) => { this.audioDevices = devices.filter( (d) => @@ -888,7 +1025,7 @@ export default { data: { width: 700, height: 800, data: data }, }); }, - async addNote(comment) { + async addNote(value) { const date = dayjs().format("MM/DD/YYYY HH:mm:ss"); const fileName = dayjs().format("YYYY-MM-DD_HH-mm-ss-ms") + ".txt"; @@ -897,16 +1034,16 @@ export default { await window.ipc .invoke(IPC_HANDLERS.CAPTURE, { func: IPC_FUNCTIONS.SAVE_NOTE, - data: { fileName: fileName, comment: comment }, + data: { fileName: fileName, comment: value }, }) .then((filePath) => { let newItem = { id: Date.now(), - sessionType: "note", + sessionType: "Note", fileType: "text", fileName: fileName, filePath: filePath, - comment: comment, + comment: value, time: this.timer, createdAt: date, }; @@ -916,20 +1053,55 @@ export default { this.noteDialog = false; }, + async addSummary(value) { + const date = dayjs().format("MM/DD/YYYY HH:mm:ss"); + + const data = { + id: Date.now(), + sessionType: "Summary", + comment: value, + time: this.timer, + createdAt: date, + }; + + this.$emit("add-item", data); + this.summaryDialog = false; + this.endSession(); + }, + async removeSummary() { + let summary = []; + this.items.map((item) => { + if (item.sessionType === "Summary") { + summary.push(item.id); + } + }); + if (window.ipc) { + await window.ipc.invoke(IPC_HANDLERS.DATABASE, { + func: IPC_FUNCTIONS.DELETE_ITEMS, + data: summary, + }); + } + }, mindMap() { const data = { - sessionType: "mindmap", + sessionType: "Mindmap", fileType: "mindmap", fileName: "", filePath: "", content: { - nodes: MAP_NODES, - connections: MAP_CONNECTIONS, + nodes: DEFAULT_MAP_NODES, + connections: DEFAULT_MAP_CONNECTIONS, }, time: this.timer, }; this.openAddWindow(data); }, + async minimize() { + await window.ipc.invoke(IPC_HANDLERS.CAPTURE, { + func: IPC_FUNCTIONS.OPEN_MINIMIZE_WINDOW, + data: { width: 700, height: 800 }, + }); + }, async deleteItems() { if (window.ipc) { await window.ipc.invoke(IPC_HANDLERS.DATABASE, { @@ -973,6 +1145,7 @@ export default { }, discardSession() { this.$store.commit("resetState"); + this.changeSessionStatus(SESSION_STATUSES.PENDING); clearInterval(this.interval); this.$router.push({ path: "/" }); }, @@ -991,6 +1164,12 @@ export default { return currentDateTime; }, + changeSessionStatus(status) { + window.ipc.invoke(IPC_HANDLERS.MENU, { + func: IPC_FUNCTIONS.CHANGE_MENUITEM_STATUS, + data: { sessionStatus: status }, + }); + }, }, }; diff --git a/src/components/FileWrapper.vue b/src/components/FileWrapper.vue index 14db5b82..6e3c6555 100644 --- a/src/components/FileWrapper.vue +++ b/src/components/FileWrapper.vue @@ -27,7 +27,8 @@ export default { }, triggerSave: function (newValue) { if (newValue) { - this.$root.$emit("save-data", this.sessionItem); + this.$root.$emit("update-session", this.sessionItem); + this.$root.$emit("save-data"); } }, }, diff --git a/src/components/ImageEditor.vue b/src/components/ImageEditor.vue index 09e9ce30..350320fe 100644 --- a/src/components/ImageEditor.vue +++ b/src/components/ImageEditor.vue @@ -3,39 +3,20 @@ class="d-flex flex-column" style="width: 100%; height: 100%; row-gap: 10px" > - -
- - Cancel - - - Apply - -
+
+ diff --git a/src/components/authentication/Signup1Wrapper.vue b/src/components/authentication/Signup1Wrapper.vue new file mode 100644 index 00000000..6d797e33 --- /dev/null +++ b/src/components/authentication/Signup1Wrapper.vue @@ -0,0 +1,126 @@ + + + + + diff --git a/src/components/authentication/Signup2Wrapper.vue b/src/components/authentication/Signup2Wrapper.vue new file mode 100644 index 00000000..62dfbf79 --- /dev/null +++ b/src/components/authentication/Signup2Wrapper.vue @@ -0,0 +1,216 @@ + + + + diff --git a/src/components/authentication/Signup3Wrapper.vue b/src/components/authentication/Signup3Wrapper.vue new file mode 100644 index 00000000..ef4e872b --- /dev/null +++ b/src/components/authentication/Signup3Wrapper.vue @@ -0,0 +1,148 @@ + + + + diff --git a/src/components/dialogs/EndSessionDialog.vue b/src/components/dialogs/EndSessionDialog.vue index d1759d72..625b4149 100644 --- a/src/components/dialogs/EndSessionDialog.vue +++ b/src/components/dialogs/EndSessionDialog.vue @@ -3,7 +3,7 @@ @@ -11,7 +11,7 @@ End Session @@ -35,6 +35,11 @@ export default { default: () => {}, }, }, + computed: { + tasks() { + return this.postSessionData ? this.postSessionData.tasks : []; + }, + }, data() { return { showTaskError: false, diff --git a/src/components/dialogs/NodeEditDialog.vue b/src/components/dialogs/NodeEditDialog.vue index c98d34f6..e068eaa2 100644 --- a/src/components/dialogs/NodeEditDialog.vue +++ b/src/components/dialogs/NodeEditDialog.vue @@ -8,7 +8,7 @@ @@ -48,7 +48,7 @@ export default { return { text: "", valid: false, - textRules: [(v) => !!v || "Title is required"], + textRules: [(v) => !!v || "Node title is required"], }; }, methods: { diff --git a/src/components/dialogs/NoteDialog.vue b/src/components/dialogs/NoteDialog.vue index bfca1f8d..8cb367a9 100644 --- a/src/components/dialogs/NoteDialog.vue +++ b/src/components/dialogs/NoteDialog.vue @@ -4,7 +4,7 @@ Take a Note - + {}, + }, + }, + watch: { + configItem: function (newValue) { + this.config = newValue; + + // set comment type by config + if (this.config.commentType && this.config.commentType !== "") { + this.comment.type = this.config.commentType; + } + + // set templates by config + this.config.templates.map((item) => { + let temp = Object.assign({}, item); + if (temp.type === "Note") { + this.comment.content = temp.precondition.content; + this.comment.text = temp.precondition.text; + } + }); + }, + }, data() { return { + config: this.configItem, comment: { - type: "Comment", + type: + this.conifgItem && + this.configItem.commentType && + this.configItem.commentType !== "" + ? this.configItem.commentType + : "Comment", content: "", text: "", tags: [], }, - commentTypes: TEXT_TYPES, + commentTypes: TEXT_TYPES.filter((item) => item !== "Summary"), tag: "", tags: [], }; @@ -116,7 +146,7 @@ export default { this.$root.$emit("close-notedialog"); }, handleSave() { - this.$emit("submit-note", this.comment); + this.$emit("submit-comment", this.comment); }, handleClear() { this.comment.type = "Comment"; diff --git a/src/components/dialogs/SummaryDialog.vue b/src/components/dialogs/SummaryDialog.vue new file mode 100644 index 00000000..b5753feb --- /dev/null +++ b/src/components/dialogs/SummaryDialog.vue @@ -0,0 +1,145 @@ + + + + + diff --git a/src/components/dialogs/__tests__/EndSessionDialog.spec.js b/src/components/dialogs/__tests__/EndSessionDialog.spec.js new file mode 100644 index 00000000..66c4fda1 --- /dev/null +++ b/src/components/dialogs/__tests__/EndSessionDialog.spec.js @@ -0,0 +1,73 @@ +import Vuetify from "vuetify"; + +import CheckTaskWrapper from "../../CheckTaskWrapper.vue"; +import EndSessionDialog from "../EndSessionDialog.vue"; + +import { mount, createLocalVue } from "@vue/test-utils"; + +let vuetify; +let wrapper; +let localVue; + +describe("EndSessionDialog.vue", () => { + beforeEach(() => { + const rootDiv = document.createElement("div"); + rootDiv.id = "root"; + document.body.appendChild(rootDiv); + + localVue = createLocalVue(); + vuetify = new Vuetify(); + + const App = localVue.component("App", { + components: { EndSessionDialog }, + data() { + return { + dialog: false, + showTaskError: false, + }; + }, + template: ` + + + + `, + }); + + wrapper = mount(App, { + localVue, + vuetify, + attachTo: "#root", + }); + }); + + test("render a dialog", async () => { + wrapper.setData({ + dialog: true, + }); + + await wrapper.vm.$nextTick(); + + expect(wrapper.findComponent(CheckTaskWrapper).exists()).toBe(true); + expect(wrapper.find(".btn-end").exists()).toBe(true); + }); + + test('trigger the click event of "End Session" button', async () => { + wrapper.setData({ + dialog: true, + }); + + await wrapper.vm.$nextTick(); + + const button = wrapper.find(".btn-end"); + const event = jest.fn(); + + button.vm.$on("click", event); + button.trigger("click"); + + expect(event).toHaveBeenCalled(); + }); +}); diff --git a/src/components/dialogs/__tests__/NodeEditDialog.spec.js b/src/components/dialogs/__tests__/NodeEditDialog.spec.js new file mode 100644 index 00000000..926165ce --- /dev/null +++ b/src/components/dialogs/__tests__/NodeEditDialog.spec.js @@ -0,0 +1,86 @@ +import Vuetify from "vuetify"; +import LogoWrapper from "../../LogoWrapper.vue"; +import NodeEditDialog from "../NodeEditDialog.vue"; + +import { mount, createLocalVue } from "@vue/test-utils"; + +let vuetify; +let wrapper; +let localVue; + +describe("NodeEditDialog.vue", () => { + beforeEach(() => { + const rootDiv = document.createElement("div"); + rootDiv.id = "root"; + document.body.appendChild(rootDiv); + + localVue = createLocalVue(); + vuetify = new Vuetify(); + + const App = localVue.component("App", { + components: { NodeEditDialog }, + data() { + return { + dialog: false, + }; + }, + template: ` + + + + `, + }); + + wrapper = mount(App, { + localVue, + vuetify, + attachTo: "#root", + }); + }); + + test("render a dialog", async () => { + wrapper.setData({ dialog: true }); + + await wrapper.vm.$nextTick(); + + expect(wrapper.findComponent(LogoWrapper).exists()).toBe(true); + expect(wrapper.find("form").exists()).toBe(true); + expect(wrapper.find("form input").exists()).toBe(true); + + expect(wrapper.findAll("button").length).toBe(2); + expect(wrapper.find("button:first-child").text()).toContain("Save"); + expect(wrapper.find("button:last-child").text()).toContain("Cancel"); + }); + + test('trigger the click event of "Save" button', async () => { + wrapper.setData({ dialog: true }); + + await wrapper.vm.$nextTick(); + + const button = wrapper.find("button:first-child"); + const event = jest.fn(); + + button.vm.$on("click", event); + button.trigger("click"); + + expect(event).toHaveBeenCalled(); + }); + + test('trigger the click event of "Cancel" button', async () => { + wrapper.setData({ dialog: true }); + + await wrapper.vm.$nextTick(); + + const button = wrapper.find("button:last-child"); + const event = jest.fn(); + + button.vm.$on("click", event); + button.trigger("click"); + + expect(event).toHaveBeenCalled(); + }); +}); diff --git a/src/components/dialogs/__tests__/NoteDialog.spec.js b/src/components/dialogs/__tests__/NoteDialog.spec.js index 4aabecf2..dd5426ed 100644 --- a/src/components/dialogs/__tests__/NoteDialog.spec.js +++ b/src/components/dialogs/__tests__/NoteDialog.spec.js @@ -20,6 +20,17 @@ describe("NoteDialog.vue", () => { const App = localVue.component("App", { components: { NoteDialog }, + propsData: { + configItem: { + commentType: "", + templates: [ + { + precondition: { comment: "", text: "" }, + type: "Note", + }, + ], + }, + }, data() { return { dialog: false, diff --git a/src/components/settings/ConfigCheckListTab.vue b/src/components/settings/ConfigCheckListTab.vue index a50f1001..536303dc 100644 --- a/src/components/settings/ConfigCheckListTab.vue +++ b/src/components/settings/ConfigCheckListTab.vue @@ -1,6 +1,9 @@ diff --git a/src/components/settings/GeneralTab.vue b/src/components/settings/GeneralTab.vue index 0c7a064b..f1896466 100644 --- a/src/components/settings/GeneralTab.vue +++ b/src/components/settings/GeneralTab.vue @@ -1,12 +1,12 @@ @@ -168,7 +134,7 @@ export default { content: "", text: "", }, - commentTypes: TEXT_TYPES, + commentTypes: TEXT_TYPES.filter((item) => item !== "Summary"), }; }, methods: { diff --git a/src/components/settings/ReportsTab.vue b/src/components/settings/ReportsTab.vue new file mode 100644 index 00000000..e754123c --- /dev/null +++ b/src/components/settings/ReportsTab.vue @@ -0,0 +1,85 @@ + + + + diff --git a/src/components/settings/TemplateTab.vue b/src/components/settings/TemplateTab.vue index bb143fd1..4b2bac2f 100644 --- a/src/components/settings/TemplateTab.vue +++ b/src/components/settings/TemplateTab.vue @@ -1,9 +1,14 @@