├── .browserslistrc ├── .env.production ├── .eslintrc.js ├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── 01_BUG_REPORT.md │ ├── 02_FEATURE_REQUEST.md │ ├── 03_CODEBASE_IMPROVEMENT.md │ ├── 04_SUPPORT_QUESTION.md │ └── config.yml ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── publish-action.yml │ └── unit-test-action.yml ├── .gitignore ├── .npmrc ├── .nvmrc ├── LICENSE ├── README.md ├── babel.config.js ├── build ├── CodeSignTool-v1.2.7 │ ├── CodeSignTool.bat │ ├── CodeSignTool.sh │ ├── conf │ │ ├── code_sign_tool.properties │ │ └── log4j2.xml │ ├── jar │ │ ├── bcpkix-jdk15on-1.65.jar │ │ ├── bcprov-jdk15on-1.65.01.jar │ │ ├── code_sign_tool-1.2.7.jar │ │ ├── commons-codec-1.15.jar │ │ ├── commons-io-2.8.0.jar │ │ ├── commons-lang3-3.9.jar │ │ ├── commons-logging-1.2.jar │ │ ├── commons-math3-3.6.1.jar │ │ ├── httpclient-4.5.13.jar │ │ ├── httpcore-4.4.13.jar │ │ ├── jansi-2.3.1.jar │ │ ├── jsign-core-3.1.jar │ │ ├── json-simple-1.1.1.jar │ │ ├── log4j-api-2.17.1.jar │ │ ├── log4j-core-2.17.1.jar │ │ ├── picocli-4.6.1.jar │ │ ├── poi-4.1.2.jar │ │ └── totp-1.0.jar │ └── release-notes.txt ├── entitlements.mac.plist └── signWindows.js ├── docs ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── SECURITY.md └── images │ ├── electron.svg │ ├── logo.png │ ├── pinata-1.png │ ├── pinata-2.png │ └── vuejs.svg ├── icons ├── icon.icns ├── icon.ico ├── icon.png └── linux │ ├── 128x128.png │ ├── 16x16.png │ ├── 24x24.png │ ├── 256x256.png │ ├── 32x32.png │ ├── 48x48.png │ ├── 512x512.png │ ├── 64x64.png │ └── 96x96.png ├── jest.config.js ├── jsconfig.json ├── package.json ├── public ├── drag-drop.png ├── icon │ ├── checkbox-off.svg │ ├── checkbox-on.svg │ ├── gear.svg │ └── indeterminate.svg ├── index.html ├── logo.png ├── pinata-logo-white.svg ├── pinata-logo.svg └── tiptap │ ├── bold.svg │ ├── italic.svg │ ├── link.svg │ ├── list-number.svg │ ├── list.svg │ ├── quotes.svg │ └── underline.svg ├── server ├── .gitignore ├── images │ ├── favicon.ico │ └── logo.png ├── jira_error.html ├── jira_success.html ├── modules │ └── JiraUtility.js ├── package.json ├── server.js └── yarn.lock ├── src ├── App.vue ├── assets │ ├── avatar.png │ ├── icon │ │ ├── add-emoticon.svg │ │ ├── align.svg │ │ ├── bell.svg │ │ ├── bold.svg │ │ ├── bug-gray.svg │ │ ├── bug.svg │ │ ├── bullet.svg │ │ ├── camera-gray.svg │ │ ├── camera-white.svg │ │ ├── camera.svg │ │ ├── color-panel.svg │ │ ├── compass.svg │ │ ├── connect-gray.svg │ │ ├── connect-white.svg │ │ ├── connect.svg │ │ ├── control-panel-icon │ │ │ ├── bug.svg │ │ │ ├── camera.svg │ │ │ ├── evidence.svg │ │ │ ├── file-search.svg │ │ │ ├── microphone.svg │ │ │ ├── mindmap.svg │ │ │ ├── notification.svg │ │ │ └── video.svg │ │ ├── cross.svg │ │ ├── diamond.svg │ │ ├── double-arrow.svg │ │ ├── download-white.svg │ │ ├── download.svg │ │ ├── downward-triangle.svg │ │ ├── drag-drop.png │ │ ├── edit.svg │ │ ├── ellipse.svg │ │ ├── error.svg │ │ ├── expand.svg │ │ ├── fill.svg │ │ ├── highlight.svg │ │ ├── jira.png │ │ ├── jira.svg │ │ ├── link.svg │ │ ├── list.svg │ │ ├── microphone-slash-solid-gray.svg │ │ ├── microphone-slash-solid-white.svg │ │ ├── microphone-slash-solid.svg │ │ ├── microphone-solid-gray.svg │ │ ├── microphone-solid-white.svg │ │ ├── microphone-solid.svg │ │ ├── nofill.svg │ │ ├── pause-gray.svg │ │ ├── pause-white.svg │ │ ├── pause.svg │ │ ├── pencil-gray.svg │ │ ├── pencil-white.svg │ │ ├── pencil-white1.svg │ │ ├── pencil.svg │ │ ├── pinata.png │ │ ├── play-gray.svg │ │ ├── play-white.svg │ │ ├── play.svg │ │ ├── plus-integration.svg │ │ ├── plus.png │ │ ├── plus.svg │ │ ├── practitest.png │ │ ├── practitest.svg │ │ ├── qtest.png │ │ ├── qtest.svg │ │ ├── rectangle.svg │ │ ├── refresh.svg │ │ ├── search.svg │ │ ├── setting.svg │ │ ├── shape.svg │ │ ├── stop-gray.svg │ │ ├── stop-white.svg │ │ ├── stop.svg │ │ ├── testfiesta.png │ │ ├── testrail.png │ │ ├── testrail.svg │ │ ├── text.svg │ │ ├── text_link.svg │ │ ├── thick.svg │ │ ├── thin.svg │ │ ├── timeline-icon │ │ │ ├── camera-blue.svg │ │ │ ├── camera-gray.svg │ │ │ ├── camera.svg │ │ │ ├── face-smile.svg │ │ │ ├── message-blue.svg │ │ │ ├── message-gray.svg │ │ │ ├── message.svg │ │ │ ├── microphone-blue.svg │ │ │ ├── microphone-gray.svg │ │ │ ├── microphone.svg │ │ │ ├── mindmap-blue.svg │ │ │ ├── mindmap-gray.svg │ │ │ ├── notes.svg │ │ │ ├── pause20px.svg │ │ │ ├── play.svg │ │ │ ├── play20px.svg │ │ │ ├── stop.svg │ │ │ ├── video-blue.svg │ │ │ └── video-gray.svg │ │ ├── transparent.svg │ │ ├── trash-red.svg │ │ ├── trash.svg │ │ ├── triangle.svg │ │ ├── underline.svg │ │ ├── union-gray.svg │ │ ├── union.svg │ │ ├── upload.svg │ │ ├── video-slash-solid-gray.svg │ │ ├── video-slash-solid-white.svg │ │ ├── video-slash-solid.svg │ │ ├── video-solid-gray.svg │ │ ├── video-solid-white.svg │ │ ├── video-solid.svg │ │ ├── video-white.svg │ │ ├── xray-logo.png │ │ ├── zephyr-scale.png │ │ ├── zephyr-squad.png │ │ ├── zoom-in.svg │ │ └── zoom-out.svg │ ├── logo.png │ ├── logo.svg │ └── mindmap-workspace.png ├── background.js ├── components │ ├── AudioWrapper.vue │ ├── BaseTipTap.vue │ ├── CheckTaskWrapper.vue │ ├── ConnectionPanel.vue │ ├── ControlPanel.vue │ ├── DeleteSessionButton.vue │ ├── EvidenceWrapper.vue │ ├── ExploratoryTestWrapper.vue │ ├── ExportPanel.vue │ ├── ExportSessionButton.vue │ ├── FileWrapper.vue │ ├── HeaderView.vue │ ├── ImageEditor.vue │ ├── LoggedInMenu.vue │ ├── LogoWrapper.vue │ ├── LowProfileControlWrapper.vue │ ├── MindmapEditor.vue │ ├── MindmapWrapper.vue │ ├── NewMindmapEditor.vue │ ├── NewNode.vue │ ├── NewNodeComponent.vue │ ├── NodeComponent.vue │ ├── NotesWrapper.vue │ ├── QuickTestWrapper.vue │ ├── RestartSessionButton.vue │ ├── ReviewWrapper.vue │ ├── SearchWrapper.vue │ ├── TestSettingWrapper.vue │ ├── TimeCounter.vue │ ├── TimelineWrapper.vue │ ├── VideoWrapper.vue │ ├── WaveForm.vue │ ├── WorkspaceWrapper.vue │ ├── __tests__ │ │ ├── AudioWrapper.spec.js │ │ ├── CheckTaskWrapper.spec.js │ │ ├── ControlPanel.spec.js │ │ ├── ExportPanel.spec.js │ │ ├── FileWrapper.spec.js │ │ ├── ImageEditor.spec.js │ │ ├── LogoWrapper.spec.js │ │ ├── MindmapEditor.spec.js │ │ ├── NotesWrapper.spec.js │ │ ├── ReviewWrapper.spec.js │ │ ├── SearchWrapper.spec.js │ │ ├── TestWrapper.spec.js │ │ ├── TimeCounter.spec.js │ │ ├── TimelineWrapper.spec.js │ │ ├── VideoWrapper.spec.js │ │ └── WorkspaceWrapper.spec.js │ ├── authentication │ │ ├── SigninJiraWrapper.vue │ │ ├── SigninTestRailWrapper.vue │ │ ├── SigninTestfiestaWrapper.vue │ │ ├── SigninWrapper.vue │ │ ├── SigninXrayWrapper.vue │ │ ├── SigninZephyrScaleWrapper.vue │ │ ├── SigninZephyrSquadWrapper.vue │ │ ├── SignupMainWrapper.vue │ │ ├── SignupPinataWrapper.vue │ │ └── __tests__ │ │ │ ├── SigninWrapper.spec.js │ │ │ ├── SignupMainWrapper.spec.js │ │ │ └── SignupPinataWrapper.spec.js │ ├── dialogs │ │ ├── AboutDialog.vue │ │ ├── AddEvidenceDialog.vue │ │ ├── AudioErrorDialog.vue │ │ ├── ChangeSourceTargetDialog.vue │ │ ├── ColorPickerDialog.vue │ │ ├── DeleteConfirmDialog.vue │ │ ├── DurationConfirmDialog.vue │ │ ├── EditEvidenceDialog.vue │ │ ├── EndSessionDialog.vue │ │ ├── KeyCaptureDialog.vue │ │ ├── NewSessionDialog.vue │ │ ├── NodeEditDialog.vue │ │ ├── NoteDialog.vue │ │ ├── ResetConfirmDialog.vue │ │ ├── ResetSessionDialog.vue │ │ ├── SaveConfirmDialog.vue │ │ ├── SettingsDialog.vue │ │ ├── ShareOAuthDialog.vue │ │ ├── ShareSessionDialog.vue │ │ ├── SourcePickerDialog.vue │ │ ├── SummaryDialog.vue │ │ ├── TipTapLinkDialog.vue │ │ └── __tests__ │ │ │ ├── AudioErrorDialog.spec.js │ │ │ ├── AudioRecordingDialog.spec.js │ │ │ ├── DeleteConfirmDialog.spec.js │ │ │ ├── DurationConfirmDialog.spec.js │ │ │ ├── EndSessionDialog.spec.js │ │ │ ├── NewSessionDialog.spec.js │ │ │ ├── NodeEditDialog.spec.js │ │ │ ├── NoteDialog.spec.js │ │ │ ├── ResetConfirmDialog.spec.js │ │ │ ├── SourcePIckerDialog.spec.js │ │ │ └── SummaryDialog.spec.js │ ├── jira │ │ ├── JiraAddIssue.vue │ │ ├── JiraAddIssueForm.vue │ │ └── JiraExportSession.vue │ ├── mindmap │ │ ├── ColorPanel.vue │ │ ├── ColorPicker.vue │ │ ├── LinkPad.vue │ │ ├── MarkerPad.vue │ │ ├── NodeDetailPad.vue │ │ ├── ShapePad.vue │ │ ├── TextPad.vue │ │ └── contextMenu.vue │ ├── settings │ │ ├── AddonsTab.vue │ │ ├── ConfigCheckListTab.vue │ │ ├── ConnectionsTab.vue │ │ ├── GeneralTab.vue │ │ ├── HotkeysTab.vue │ │ ├── ReportsTab.vue │ │ ├── SupportTab.vue │ │ ├── TagsTab.vue │ │ ├── TemplateTab.vue │ │ └── __tests__ │ │ │ ├── ConfigCheckListTab.spec.js │ │ │ ├── ConnectionsTab.spec.js │ │ │ ├── GeneralTab.spec.js │ │ │ ├── SupportTab.spec.js │ │ │ └── TemplateTab.spec.js │ ├── testrail │ │ └── TestRailExportSession.vue │ ├── xray │ │ └── XrayExportSession.vue │ └── zephyr │ │ ├── ZephyrScaleExportSession.vue │ │ └── ZephyrSquadExportSession.vue ├── helpers │ ├── HotkeyHelpers.js │ └── WebHelpers.js ├── i18n.js ├── integrations │ ├── IntegrationHelpers.js │ ├── JiraIntegrationHelpers.js │ ├── OpenAIIntegrationHelpers.js │ ├── TestRailIntegrationHelpers.js │ ├── TestfiestaIntegrationHelpers.js │ ├── XrayIntegrationHelpers.js │ ├── ZephyrScaleIntegrationHelpers.js │ └── ZephyrSquadIntegrationHelpers.js ├── layouts │ ├── Default.vue │ └── Minimize.vue ├── locales │ └── en.json ├── main.js ├── menu.js ├── mixins │ └── theme.js ├── modules │ ├── BrowserWindowUtility.js │ ├── CaptureUtility.js │ ├── FileSystemUtility.js │ ├── IpcHandlers.js │ ├── MenuUtility.js │ ├── PersistenceUtility.js │ ├── ServerUtility.js │ ├── SystemInfoUtility.js │ ├── WindowUtility.js │ ├── constants.js │ ├── migrations │ │ ├── v0.11.0.js │ │ ├── v0.6.0.js │ │ └── v0.8.0.js │ └── mindmap │ │ ├── parser │ │ ├── emojis.js │ │ └── regex.js │ │ ├── sass │ │ └── mindmap.sass │ │ └── utils │ │ ├── d3.js │ │ ├── dimensions.js │ │ ├── nodeToHTML.js │ │ └── subnodesToHTML.js ├── plugins │ └── vuetify.js ├── preload.js ├── router │ └── index.js ├── scss │ └── variables.scss ├── services │ ├── electronService.js │ ├── storage-options │ │ ├── localJsonDbService.js │ │ └── restApiService.js │ ├── storageInterface.js │ └── storageService.js ├── store │ ├── index.js │ ├── modules │ │ ├── auth.js │ │ └── config.js │ └── store-config.js └── views │ ├── AuthenticationView.vue │ ├── HomeView.vue │ ├── LowProfileView.vue │ ├── MainView.vue │ ├── PrintView.vue │ ├── ResultView.vue │ ├── SettingView.vue │ └── __tests__ │ ├── AuthenticationView.spec.js │ ├── HomeView.spec.js │ ├── LowProfileView.spec.js │ ├── MainView.spec.js │ ├── PrintView.spec.js │ ├── ResultView.spec.js │ └── SettingView.spec.js ├── tests ├── mocks.js ├── setup.js └── styleMock.js ├── vue.config.js └── yarn.lock /.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | not dead 4 | -------------------------------------------------------------------------------- /.env.production: -------------------------------------------------------------------------------- 1 | VUE_APP_I18N_LOCALE=en 2 | VUE_APP_I18N_FALLBACK_LOCALE=en 3 | 4 | VUE_APP_SERVER_PORT=64064 5 | 6 | VUE_APP_TESTFIESTA_API_URL="https://api.testfiesta.com/v1" 7 | VUE_APP_JIRA_OAUTH_KEY="3tPI6y3UgOxjUUVd2ELL3mhZr6cGAatt" 8 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | 4 | env: { 5 | node: true, 6 | }, 7 | 8 | extends: [ 9 | "plugin:vue/essential", 10 | "eslint:recommended", 11 | "plugin:prettier/recommended", 12 | ], 13 | 14 | parserOptions: { 15 | parser: "@babel/eslint-parser", 16 | }, 17 | 18 | rules: { 19 | "no-console": process.env.NODE_ENV === "production" ? "warn" : "off", 20 | "no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off", 21 | "prettier/prettier": ["error", { endOfLine: "auto" }], 22 | }, 23 | 24 | overrides: [ 25 | { 26 | files: [ 27 | "**/__tests__/*.{j,t}s?(x)", 28 | "**/tests/unit/**/*.spec.{j,t}s?(x)", 29 | "**/tests/*.{j,t}s?(x)", 30 | ], 31 | env: { 32 | jest: true, 33 | }, 34 | }, 35 | ], 36 | }; 37 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @dacoaster 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/01_BUG_REPORT.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug Report 3 | about: Create a report to help Pinata to improve 4 | title: "bug: " 5 | labels: "bug" 6 | assignees: "" 7 | --- 8 | 9 | # Bug Report 10 | 11 | **Platform:** 12 | 13 | 14 | 15 | **Pinata version:** 16 | 17 | 18 | 19 | **Current behavior:** 20 | 21 | 22 | 23 | **Expected behavior:** 24 | 25 | 26 | 27 | **Steps to reproduce:** 28 | 29 | 30 | 31 | **Related code:** 32 | 33 | 34 | 35 | ``` 36 | insert short code snippets here 37 | ``` 38 | 39 | **Other information:** 40 | 41 | 42 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/02_FEATURE_REQUEST.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature Request 3 | about: Suggest an idea for this project 4 | title: "feat: " 5 | labels: "enhancement" 6 | assignees: "" 7 | --- 8 | 9 | # Feature Request 10 | 11 | **Describe the Feature Request** 12 | 13 | 14 | 15 | **Describe Preferred Solution** 16 | 17 | 18 | 19 | **Describe Alternatives** 20 | 21 | 22 | 23 | **Related Code** 24 | 25 | 26 | 27 | **Additional Context** 28 | 29 | 30 | 31 | **If the feature request is approved, would you be willing to submit a PR?** 32 | _(Help can be provided if you need assistance submitting a PR)_ 33 | 34 | - [ ] Yes 35 | - [ ] No 36 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/03_CODEBASE_IMPROVEMENT.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Codebase improvement 3 | about: Provide your feedback for the existing codebase. Suggest a better solution for algorithms, development tools, etc. 4 | title: "dev: " 5 | labels: "enhancement" 6 | assignees: "" 7 | --- 8 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/04_SUPPORT_QUESTION.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Support Question 3 | about: Question on how to use this project 4 | title: "support: " 5 | labels: "question" 6 | assignees: "" 7 | --- 8 | 9 | # Support Question 10 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | --- 2 | blank_issues_enabled: false 3 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Pull Request type 4 | 5 | 6 | 7 | Please check the type of change your PR introduces: 8 | 9 | - [ ] Bugfix 10 | - [ ] Feature 11 | - [ ] Code style update (formatting, renaming) 12 | - [ ] Refactoring (no functional changes, no API changes) 13 | - [ ] Build-related changes 14 | - [ ] Documentation content changes 15 | - [ ] Other (please describe): 16 | 17 | ## What is the current behavior? 18 | 19 | 20 | 21 | Issue Number: N/A 22 | 23 | ## What is the new behavior? 24 | 25 | 26 | 27 | - 28 | - 29 | - 30 | 31 | ## Does this introduce a breaking change? 32 | 33 | - [ ] Yes 34 | - [ ] No 35 | 36 | 37 | 38 | ## Does this introduce UI changes? If so, have they been tested on both dark and light modes? 39 | 40 | - [ ] Yes 41 | - [ ] No 42 | 43 | ## Other information 44 | 45 | 46 | -------------------------------------------------------------------------------- /.github/workflows/unit-test-action.yml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs 3 | 4 | name: Unit Test CI 5 | 6 | on: 7 | push: 8 | branches: [ "main" ] 9 | pull_request: 10 | branches: [ "main" ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | strategy: 18 | matrix: 19 | node-version: [16.x] 20 | # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ 21 | 22 | steps: 23 | - uses: actions/checkout@v3 24 | - name: Use Node.js ${{ matrix.node-version }} 25 | uses: actions/setup-node@v3 26 | with: 27 | node-version: ${{ matrix.node-version }} 28 | cache: 'npm' 29 | - run: yarn install --frozen-lockfile 30 | - run: npm rebuild canvas --update-binary 31 | - run: yarn lint 32 | - run: yarn test:unit 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | db-rest-mock.json 5 | 6 | 7 | # local env files 8 | .env.local 9 | .env.*.local 10 | 11 | # Log files 12 | npm-debug.log* 13 | yarn-debug.log* 14 | yarn-error.log* 15 | pnpm-debug.log* 16 | 17 | # Editor directories and files 18 | .idea 19 | .vscode 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | 26 | #Electron-builder output 27 | /dist_electron 28 | 29 | /release 30 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | # .npmrc 2 | engine-strict=true -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 18.20.7 2 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ["@vue/cli-plugin-babel/preset"], 3 | }; 4 | -------------------------------------------------------------------------------- /build/CodeSignTool-v1.2.7/CodeSignTool.bat: -------------------------------------------------------------------------------- 1 | @echo OFF 2 | 3 | set code_sign_tool_path=%CODE_SIGN_TOOL_PATH% 4 | 5 | if defined code_sign_tool_path ( 6 | %code_sign_tool_path%\jdk-11.0.2\bin\java -cp "%code_sign_tool_path%\jar\picocli-4.6.1.jar;%code_sign_tool_path%\jar\bcprov-jdk15on-1.65.01.jar;%code_sign_tool_path%\jar\httpclient-4.5.13.jar;%code_sign_tool_path%\jar\json-simple-1.1.1.jar;%code_sign_tool_path%\jar\jsign-core-3.1.jar;%code_sign_tool_path%\jar\commons-io-2.8.0.jar;%code_sign_tool_path%\jar\bcpkix-jdk15on-1.65.jar;%code_sign_tool_path%\jar\code_sign_tool-1.2.7.jar;%code_sign_tool_path%\jar\httpcore-4.4.13.jar;%code_sign_tool_path%\jar\commons-logging-1.2.jar;%code_sign_tool_path%\jar\log4j-api-2.17.1.jar;%code_sign_tool_path%\jar\log4j-core-2.17.1.jar;%code_sign_tool_path%\jar\poi-4.1.2.jar;%code_sign_tool_path%\jar\commons-lang3-3.9.jar;%code_sign_tool_path%\jar\commons-math3-3.6.1.jar;%code_sign_tool_path%\jar\totp-1.0.jar;%code_sign_tool_path%\jar\commons-codec-1.15.jar" com.ssl.code.signing.tool.CodeSignTool %* 7 | ) else ( 8 | .\jdk-11.0.2\bin\java -cp ".\jar\picocli-4.6.1.jar;.\jar\bcprov-jdk15on-1.65.01.jar;.\jar\httpclient-4.5.13.jar;.\jar\json-simple-1.1.1.jar;.\jar\jsign-core-3.1.jar;.\jar\commons-io-2.8.0.jar;.\jar\bcpkix-jdk15on-1.65.jar;.\jar\code_sign_tool-1.2.7.jar;.\jar\httpcore-4.4.13.jar;.\jar\commons-logging-1.2.jar;.\jar\log4j-api-2.17.1.jar;.\jar\log4j-core-2.17.1.jar;.\jar\poi-4.1.2.jar;.\jar\commons-lang3-3.9.jar;.\jar\commons-math3-3.6.1.jar;.\jar\totp-1.0.jar;.\jar\commons-codec-1.15.jar" com.ssl.code.signing.tool.CodeSignTool %* 9 | ) -------------------------------------------------------------------------------- /build/CodeSignTool-v1.2.7/CodeSignTool.sh: -------------------------------------------------------------------------------- 1 | if [[ -z "${CODE_SIGN_TOOL_PATH}" ]]; then 2 | java -cp "./jar/picocli-4.6.1.jar:./jar/bcprov-jdk15on-1.65.01.jar:./jar/httpclient-4.5.13.jar:./jar/json-simple-1.1.1.jar:./jar/jsign-core-3.1.jar:./jar/commons-io-2.8.0.jar:./jar/bcpkix-jdk15on-1.65.jar:./jar/code_sign_tool-1.2.7.jar:./jar/httpcore-4.4.13.jar:./jar/commons-logging-1.2.jar:./jar/log4j-api-2.17.1.jar:./jar/log4j-core-2.17.1.jar:./jar/poi-4.1.2.jar:./jar/commons-lang3-3.9.jar:./jar/commons-math3-3.6.1.jar:./jar/totp-1.0.jar:./jar/commons-codec-1.15.jar" com.ssl.code.signing.tool.CodeSignTool $@ 3 | else 4 | java -cp "${CODE_SIGN_TOOL_PATH}/jar/picocli-4.6.1.jar:${CODE_SIGN_TOOL_PATH}/jar/bcprov-jdk15on-1.65.01.jar:${CODE_SIGN_TOOL_PATH}/jar/httpclient-4.5.13.jar:${CODE_SIGN_TOOL_PATH}/jar/json-simple-1.1.1.jar:${CODE_SIGN_TOOL_PATH}/jar/jsign-core-3.1.jar:${CODE_SIGN_TOOL_PATH}/jar/commons-io-2.8.0.jar:${CODE_SIGN_TOOL_PATH}/jar/bcpkix-jdk15on-1.65.jar:${CODE_SIGN_TOOL_PATH}/jar/code_sign_tool-1.2.7.jar:${CODE_SIGN_TOOL_PATH}/jar/httpcore-4.4.13.jar:${CODE_SIGN_TOOL_PATH}/jar/commons-logging-1.2.jar:${CODE_SIGN_TOOL_PATH}/jar/log4j-api-2.17.1.jar:${CODE_SIGN_TOOL_PATH}/jar/log4j-core-2.17.1.jar:${CODE_SIGN_TOOL_PATH}/jar/poi-4.1.2.jar:${CODE_SIGN_TOOL_PATH}/jar/commons-lang3-3.9.jar:${CODE_SIGN_TOOL_PATH}/jar/commons-math3-3.6.1.jar:${CODE_SIGN_TOOL_PATH}/jar/totp-1.0.jar:${CODE_SIGN_TOOL_PATH}/jar/commons-codec-1.15.jar" com.ssl.code.signing.tool.CodeSignTool $@ 5 | fi -------------------------------------------------------------------------------- /build/CodeSignTool-v1.2.7/conf/code_sign_tool.properties: -------------------------------------------------------------------------------- 1 | CLIENT_ID=kaXTRACNijSWsFdRKg_KAfD3fqrBlzMbWs6TwWHwAn8 2 | OAUTH2_ENDPOINT=https://login.ssl.com/oauth2/token 3 | CSC_API_ENDPOINT=https://cs.ssl.com 4 | TSA_URL=http://ts.ssl.com -------------------------------------------------------------------------------- /build/CodeSignTool-v1.2.7/conf/log4j2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | %d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n 6 | 7 | 8 | 9 | 11 | 12 | ${LOG_PATTERN} 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 27 | 28 | -------------------------------------------------------------------------------- /build/CodeSignTool-v1.2.7/jar/bcpkix-jdk15on-1.65.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testfiesta/pinata/d788c89e7428f59aca637441b66eb60158bf0c11/build/CodeSignTool-v1.2.7/jar/bcpkix-jdk15on-1.65.jar -------------------------------------------------------------------------------- /build/CodeSignTool-v1.2.7/jar/bcprov-jdk15on-1.65.01.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testfiesta/pinata/d788c89e7428f59aca637441b66eb60158bf0c11/build/CodeSignTool-v1.2.7/jar/bcprov-jdk15on-1.65.01.jar -------------------------------------------------------------------------------- /build/CodeSignTool-v1.2.7/jar/code_sign_tool-1.2.7.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testfiesta/pinata/d788c89e7428f59aca637441b66eb60158bf0c11/build/CodeSignTool-v1.2.7/jar/code_sign_tool-1.2.7.jar -------------------------------------------------------------------------------- /build/CodeSignTool-v1.2.7/jar/commons-codec-1.15.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testfiesta/pinata/d788c89e7428f59aca637441b66eb60158bf0c11/build/CodeSignTool-v1.2.7/jar/commons-codec-1.15.jar -------------------------------------------------------------------------------- /build/CodeSignTool-v1.2.7/jar/commons-io-2.8.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testfiesta/pinata/d788c89e7428f59aca637441b66eb60158bf0c11/build/CodeSignTool-v1.2.7/jar/commons-io-2.8.0.jar -------------------------------------------------------------------------------- /build/CodeSignTool-v1.2.7/jar/commons-lang3-3.9.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testfiesta/pinata/d788c89e7428f59aca637441b66eb60158bf0c11/build/CodeSignTool-v1.2.7/jar/commons-lang3-3.9.jar -------------------------------------------------------------------------------- /build/CodeSignTool-v1.2.7/jar/commons-logging-1.2.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testfiesta/pinata/d788c89e7428f59aca637441b66eb60158bf0c11/build/CodeSignTool-v1.2.7/jar/commons-logging-1.2.jar -------------------------------------------------------------------------------- /build/CodeSignTool-v1.2.7/jar/commons-math3-3.6.1.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testfiesta/pinata/d788c89e7428f59aca637441b66eb60158bf0c11/build/CodeSignTool-v1.2.7/jar/commons-math3-3.6.1.jar -------------------------------------------------------------------------------- /build/CodeSignTool-v1.2.7/jar/httpclient-4.5.13.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testfiesta/pinata/d788c89e7428f59aca637441b66eb60158bf0c11/build/CodeSignTool-v1.2.7/jar/httpclient-4.5.13.jar -------------------------------------------------------------------------------- /build/CodeSignTool-v1.2.7/jar/httpcore-4.4.13.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testfiesta/pinata/d788c89e7428f59aca637441b66eb60158bf0c11/build/CodeSignTool-v1.2.7/jar/httpcore-4.4.13.jar -------------------------------------------------------------------------------- /build/CodeSignTool-v1.2.7/jar/jansi-2.3.1.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testfiesta/pinata/d788c89e7428f59aca637441b66eb60158bf0c11/build/CodeSignTool-v1.2.7/jar/jansi-2.3.1.jar -------------------------------------------------------------------------------- /build/CodeSignTool-v1.2.7/jar/jsign-core-3.1.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testfiesta/pinata/d788c89e7428f59aca637441b66eb60158bf0c11/build/CodeSignTool-v1.2.7/jar/jsign-core-3.1.jar -------------------------------------------------------------------------------- /build/CodeSignTool-v1.2.7/jar/json-simple-1.1.1.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testfiesta/pinata/d788c89e7428f59aca637441b66eb60158bf0c11/build/CodeSignTool-v1.2.7/jar/json-simple-1.1.1.jar -------------------------------------------------------------------------------- /build/CodeSignTool-v1.2.7/jar/log4j-api-2.17.1.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testfiesta/pinata/d788c89e7428f59aca637441b66eb60158bf0c11/build/CodeSignTool-v1.2.7/jar/log4j-api-2.17.1.jar -------------------------------------------------------------------------------- /build/CodeSignTool-v1.2.7/jar/log4j-core-2.17.1.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testfiesta/pinata/d788c89e7428f59aca637441b66eb60158bf0c11/build/CodeSignTool-v1.2.7/jar/log4j-core-2.17.1.jar -------------------------------------------------------------------------------- /build/CodeSignTool-v1.2.7/jar/picocli-4.6.1.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testfiesta/pinata/d788c89e7428f59aca637441b66eb60158bf0c11/build/CodeSignTool-v1.2.7/jar/picocli-4.6.1.jar -------------------------------------------------------------------------------- /build/CodeSignTool-v1.2.7/jar/poi-4.1.2.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testfiesta/pinata/d788c89e7428f59aca637441b66eb60158bf0c11/build/CodeSignTool-v1.2.7/jar/poi-4.1.2.jar -------------------------------------------------------------------------------- /build/CodeSignTool-v1.2.7/jar/totp-1.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testfiesta/pinata/d788c89e7428f59aca637441b66eb60158bf0c11/build/CodeSignTool-v1.2.7/jar/totp-1.0.jar -------------------------------------------------------------------------------- /build/CodeSignTool-v1.2.7/release-notes.txt: -------------------------------------------------------------------------------- 1 | v1.2.2 2 | 3 | - Log4j version upgraded to 2.17.1. 4 | - New sign_hash command addded for signing the hash values 5 | - New get_certs command added for retrieving certificate chain from cloud signatures service 6 | 7 | v1.2.3 8 | 9 | - CodeSignTool command can be executed using relative path 10 | 11 | v1.2.4 12 | 13 | - Increased timeout values, so that CodeSignTool does not terminate connection from server, if request takes longer than 14 | expected e.g. batch signing of 100 files. 15 | 16 | v1.2.5 17 | 18 | - totp_secret parameter added in batch_sign_hash command 19 | 20 | v1.2.6 21 | 22 | - Released this version to make it consistent with non-windows version. Bug fixed in non-windows version 23 | 24 | v1.2.7 25 | 26 | - Support added for malware scan before signing code object 27 | - Support for signing using OVCS certificates 28 | - Support for OTP over SMS for second factor authentication 29 | - Use system settings during HTTP connection 30 | - get_scan_settings command added to check whether malware scan is enabled or not on server -------------------------------------------------------------------------------- /build/entitlements.mac.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.cs.allow-jit 6 | 7 | com.apple.security.cs.allow-unsigned-executable-memory 8 | 9 | com.apple.security.cs.allow-dyld-environment-variables 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /build/signWindows.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const fs = require('fs'); 3 | const childProcess = require('child_process'); 4 | 5 | const TEMP_DIR = path.join(__dirname, 'release', 'temp'); 6 | 7 | if (!fs.existsSync(TEMP_DIR)) { 8 | fs.mkdirSync(TEMP_DIR, { recursive: true }); 9 | } 10 | 11 | function sign(configuration) { 12 | // credentials from ssl.com 13 | const USER_NAME = process.env.WINDOWS_SIGN_USER_NAME; 14 | const USER_PASSWORD = process.env.WINDOWS_SIGN_USER_PASSWORD; 15 | const CREDENTIAL_ID = process.env.WINDOWS_SIGN_CREDENTIAL_ID; 16 | const USER_TOTP = process.env.WINDOWS_SIGN_USER_TOTP; 17 | if (USER_NAME && USER_PASSWORD && USER_TOTP && CREDENTIAL_ID) { 18 | console.log(`Signing ${configuration.path}`); 19 | const { name, dir } = path.parse(configuration.path); 20 | // CodeSignTool can't sign in place without verifying the overwrite with a 21 | // y/m interaction so we are creating a new file in a temp directory and 22 | // then replacing the original file with the signed file. 23 | const tempFile = path.join(TEMP_DIR, name); 24 | const setDir = `cd CodeSignTool`; 25 | const signFile = `"CodeSignTool" sign -input_file_path="${configuration.path}" -output_dir_path="${TEMP_DIR}" -credential_id="${CREDENTIAL_ID}" -username="${USER_NAME}" -password="${USER_PASSWORD}" -totp_secret="${USER_TOTP}"`; 26 | const moveFile = `mv "${tempFile}" "${dir}"`; 27 | childProcess.execSync(`ls && ls CodeSignTool/`, { stdio: 'inherit' }); 28 | childProcess.execSync(`${setDir} && ${signFile}`, { stdio: 'inherit' }); 29 | childProcess.execSync(`${moveFile}`, { stdio: 'inherit' }); 30 | } else { 31 | console.warn(`sign.js - Can't sign file ${configuration.path}, missing value for: 32 | ${USER_NAME ? '' : 'WINDOWS_SIGN_USER_NAME'} 33 | ${USER_PASSWORD ? '' : 'WINDOWS_SIGN_USER_PASSWORD'} 34 | ${CREDENTIAL_ID ? '' : 'WINDOWS_SIGN_CREDENTIAL_ID'} 35 | ${USER_TOTP ? '' : 'WINDOWS_SIGN_USER_TOTP'} 36 | `); 37 | process.exit(1); 38 | } 39 | } 40 | 41 | exports.default = sign; 42 | -------------------------------------------------------------------------------- /docs/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | When contributing to this repository, please first discuss the change you wish to make via issue, email, or any other method with the owners of this repository before making a change. 4 | Please note we have a [code of conduct](CODE_OF_CONDUCT.md), please follow it in all your interactions with the project. 5 | 6 | ## Development environment setup 7 | 8 | To set up a development environment, please follow these steps: 9 | 10 | 1. Clone the repo 11 | 12 | ```sh 13 | git clone https://github.com/dacoaster/pinata 14 | ``` 15 | 16 | 2. Install the deps 17 | 18 | ```sh 19 | yarn install 20 | ``` 21 | 22 | 3. Run locally 23 | ```sh 24 | yarn dev 25 | ``` 26 | 27 | ## Issues and feature requests 28 | 29 | You've found a bug in the source code or a mistake in the documentation? You can help us by [submitting an issue on GitHub](https://github.com/dacoaster/pinata/issues). Before you create an issue, make sure to search the issue archive -- your issue may have already been addressed! 30 | 31 | Or maybe you'd like a new feature? You can check out current feature requests, vote for your favorites, or add your own at [the feature request page](https://features.testfiesta.com). 32 | 33 | Please try to create bug reports that are: 34 | 35 | - _Reproducible._ Include steps to reproduce the problem. 36 | - _Specific._ Include as much detail as possible: which version, what environment, etc. 37 | - _Unique._ Do not duplicate existing opened issues. 38 | - _Scoped to a Single Bug._ One bug per report. 39 | 40 | **Even better: Submit a pull request with a fix or new feature!** 41 | 42 | ### How to submit a Pull Request 43 | 44 | 1. Search our repository for open or closed 45 | [Pull Requests](https://github.com/dacoaster/pinata/pulls) 46 | that relate to your submission. You don't want to duplicate effort. 47 | 2. Fork the project 48 | 3. Create your feature branch (`git checkout -b feat/amazing_feature`) 49 | 4. Commit your changes (`git commit -m 'feat: add amazing_feature'`) Pinata tries to use [conventional commits](https://www.conventionalcommits.org), so please follow the specification in your commit messages. 50 | 5. Push to the branch (`git push origin feat/amazing_feature`) 51 | 6. [Open a Pull Request](https://github.com/dacoaster/pinata/compare?expand=1) 52 | -------------------------------------------------------------------------------- /docs/SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Reporting a Vulnerability 4 | 5 | If there are any vulnerabilities in **Pinata**, don't hesitate to _report them_. 6 | 7 | 1. Use any of the [private contact addresses](https://github.com/dacoaster/pinata#support). 8 | 2. Describe the vulnerability. 9 | 10 | If you have a fix, that is most welcome -- please attach or summarize it in your message! 11 | 12 | 3. We will evaluate the vulnerability and, if necessary, release a fix or mitigating steps to address it. We will contact you to let you know the outcome, and will credit you in the report. 13 | 14 | Please **do not disclose the vulnerability publicly** until a fix is released! 15 | 16 | 4. Once we have either a) published a fix, or b) declined to address the vulnerability for whatever reason, you are free to publicly disclose it. 17 | -------------------------------------------------------------------------------- /docs/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testfiesta/pinata/d788c89e7428f59aca637441b66eb60158bf0c11/docs/images/logo.png -------------------------------------------------------------------------------- /docs/images/pinata-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testfiesta/pinata/d788c89e7428f59aca637441b66eb60158bf0c11/docs/images/pinata-1.png -------------------------------------------------------------------------------- /docs/images/pinata-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testfiesta/pinata/d788c89e7428f59aca637441b66eb60158bf0c11/docs/images/pinata-2.png -------------------------------------------------------------------------------- /icons/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testfiesta/pinata/d788c89e7428f59aca637441b66eb60158bf0c11/icons/icon.icns -------------------------------------------------------------------------------- /icons/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testfiesta/pinata/d788c89e7428f59aca637441b66eb60158bf0c11/icons/icon.ico -------------------------------------------------------------------------------- /icons/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testfiesta/pinata/d788c89e7428f59aca637441b66eb60158bf0c11/icons/icon.png -------------------------------------------------------------------------------- /icons/linux/128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testfiesta/pinata/d788c89e7428f59aca637441b66eb60158bf0c11/icons/linux/128x128.png -------------------------------------------------------------------------------- /icons/linux/16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testfiesta/pinata/d788c89e7428f59aca637441b66eb60158bf0c11/icons/linux/16x16.png -------------------------------------------------------------------------------- /icons/linux/24x24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testfiesta/pinata/d788c89e7428f59aca637441b66eb60158bf0c11/icons/linux/24x24.png -------------------------------------------------------------------------------- /icons/linux/256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testfiesta/pinata/d788c89e7428f59aca637441b66eb60158bf0c11/icons/linux/256x256.png -------------------------------------------------------------------------------- /icons/linux/32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testfiesta/pinata/d788c89e7428f59aca637441b66eb60158bf0c11/icons/linux/32x32.png -------------------------------------------------------------------------------- /icons/linux/48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testfiesta/pinata/d788c89e7428f59aca637441b66eb60158bf0c11/icons/linux/48x48.png -------------------------------------------------------------------------------- /icons/linux/512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testfiesta/pinata/d788c89e7428f59aca637441b66eb60158bf0c11/icons/linux/512x512.png -------------------------------------------------------------------------------- /icons/linux/64x64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testfiesta/pinata/d788c89e7428f59aca637441b66eb60158bf0c11/icons/linux/64x64.png -------------------------------------------------------------------------------- /icons/linux/96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testfiesta/pinata/d788c89e7428f59aca637441b66eb60158bf0c11/icons/linux/96x96.png -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: "@vue/cli-plugin-unit-jest", 3 | moduleFileExtensions: ["js", "ts", "json", "vue"], 4 | moduleNameMapper: { 5 | "^@/(.*)$": "/src/$1", 6 | "\\.(css|less)$": "/tests/styleMock.js", 7 | "vuetify/lib(.*)": "/node_modules/vuetify/es5$1", 8 | }, 9 | modulePaths: ["/src", "/node_modules"], 10 | transform: { 11 | ".+\\.(css|styl|less|sass|scss|png|jpg|ttf|woff|woff2)$": 12 | "jest-transform-stub", 13 | }, 14 | transformIgnorePatterns: ["/node_modules/(?!(vuetify)/)"], 15 | setupFilesAfterEnv: ["/tests/setup.js", "/tests/mocks.js"], 16 | }; 17 | -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "esnext", 5 | "baseUrl": "./", 6 | "moduleResolution": "node", 7 | "paths": { 8 | "@/*": [ 9 | "src/*" 10 | ] 11 | }, 12 | "lib": [ 13 | "esnext", 14 | "dom", 15 | "dom.iterable", 16 | "scripthost" 17 | ] 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /public/drag-drop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testfiesta/pinata/d788c89e7428f59aca637441b66eb60158bf0c11/public/drag-drop.png -------------------------------------------------------------------------------- /public/icon/checkbox-off.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/icon/checkbox-on.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /public/icon/gear.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /public/icon/indeterminate.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | <%= htmlWebpackPlugin.options.title %> 11 | 12 | 13 | 14 | 17 |
18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /public/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testfiesta/pinata/d788c89e7428f59aca637441b66eb60158bf0c11/public/logo.png -------------------------------------------------------------------------------- /public/tiptap/bold.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/tiptap/italic.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/tiptap/link.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /public/tiptap/list-number.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/tiptap/list.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/tiptap/quotes.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /public/tiptap/underline.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /server/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | package-lock.json 3 | /dist -------------------------------------------------------------------------------- /server/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testfiesta/pinata/d788c89e7428f59aca637441b66eb60158bf0c11/server/images/favicon.ico -------------------------------------------------------------------------------- /server/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testfiesta/pinata/d788c89e7428f59aca637441b66eb60158bf0c11/server/images/logo.png -------------------------------------------------------------------------------- /server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pinata-oauth-backend", 3 | "version": "0.1.0", 4 | "description": "Express backend for Pinata OAuth", 5 | "main": "server.js", 6 | "scripts": { 7 | "server": "node server.js", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "keywords": [ 11 | "node.js", 12 | "express", 13 | "oauth2" 14 | ], 15 | "author": "", 16 | "license": "ISC", 17 | "dependencies": { 18 | "axios": "^1.3.4", 19 | "body-parser": "^1.20.1", 20 | "cors": "^2.8.5", 21 | "express": "^4.18.2", 22 | "open": "^8.4.0", 23 | "path": "^0.12.7" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /server/server.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const express = require("express"); 3 | const bodyParser = require("body-parser"); 4 | const cors = require("cors"); 5 | const path = require("path"); 6 | 7 | const app = express(); 8 | const port = process.env.VUE_APP_SERVER_PORT || 64064; 9 | 10 | const corsOptions = { 11 | origin: `http://localhost:${port}`, 12 | }; 13 | 14 | app.use(cors(corsOptions)); 15 | 16 | // parse requests of content-type - application/json 17 | app.use(bodyParser.json()); 18 | 19 | // parse requests of content-type - application/x-www-form-urlencoded 20 | app.use(bodyParser.urlencoded({ extended: true })); 21 | 22 | // path 23 | app.use(express.static(path.join(__dirname, "images"))); 24 | 25 | app.disable("x-powered-by"); 26 | 27 | // modules 28 | require("./modules/JiraUtility")(app); 29 | 30 | try { 31 | app.listen(port, () => { 32 | console.log(`OAuth redirect server running at http://localhost:${port}`); 33 | }); 34 | } catch (err) { 35 | console.log(err); 36 | } 37 | -------------------------------------------------------------------------------- /src/assets/avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testfiesta/pinata/d788c89e7428f59aca637441b66eb60158bf0c11/src/assets/avatar.png -------------------------------------------------------------------------------- /src/assets/icon/align.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icon/bell.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icon/bold.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icon/bug-gray.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/icon/bug.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/icon/bullet.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icon/camera-gray.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icon/camera-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icon/camera.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icon/compass.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/icon/connect-gray.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icon/connect-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icon/connect.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icon/control-panel-icon/bug.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icon/control-panel-icon/camera.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/icon/control-panel-icon/evidence.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icon/control-panel-icon/file-search.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icon/control-panel-icon/microphone.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icon/control-panel-icon/mindmap.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icon/control-panel-icon/notification.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icon/control-panel-icon/video.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/icon/cross.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icon/diamond.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icon/double-arrow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icon/download-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icon/download.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icon/downward-triangle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icon/drag-drop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testfiesta/pinata/d788c89e7428f59aca637441b66eb60158bf0c11/src/assets/icon/drag-drop.png -------------------------------------------------------------------------------- /src/assets/icon/edit.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icon/ellipse.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icon/error.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icon/expand.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icon/fill.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icon/highlight.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icon/jira.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testfiesta/pinata/d788c89e7428f59aca637441b66eb60158bf0c11/src/assets/icon/jira.png -------------------------------------------------------------------------------- /src/assets/icon/link.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icon/list.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icon/microphone-slash-solid-gray.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Created with Fabric.js 4.6.0 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/assets/icon/microphone-slash-solid-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Created with Fabric.js 4.6.0 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/assets/icon/microphone-slash-solid.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Created with Fabric.js 4.6.0 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/assets/icon/microphone-solid-gray.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Created with Fabric.js 4.6.0 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/assets/icon/microphone-solid-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Created with Fabric.js 4.6.0 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/assets/icon/microphone-solid.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Created with Fabric.js 4.6.0 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/assets/icon/nofill.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icon/pause-gray.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icon/pause-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icon/pause.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icon/pencil-gray.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/icon/pencil-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icon/pencil-white1.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/icon/pencil.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/icon/pinata.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testfiesta/pinata/d788c89e7428f59aca637441b66eb60158bf0c11/src/assets/icon/pinata.png -------------------------------------------------------------------------------- /src/assets/icon/play-gray.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icon/play-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icon/play.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icon/plus-integration.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/assets/icon/plus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testfiesta/pinata/d788c89e7428f59aca637441b66eb60158bf0c11/src/assets/icon/plus.png -------------------------------------------------------------------------------- /src/assets/icon/plus.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 16 | 17 | 18 | 19 | 20 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/assets/icon/practitest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testfiesta/pinata/d788c89e7428f59aca637441b66eb60158bf0c11/src/assets/icon/practitest.png -------------------------------------------------------------------------------- /src/assets/icon/qtest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testfiesta/pinata/d788c89e7428f59aca637441b66eb60158bf0c11/src/assets/icon/qtest.png -------------------------------------------------------------------------------- /src/assets/icon/rectangle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icon/refresh.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icon/search.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icon/setting.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/icon/shape.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icon/stop-gray.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icon/stop-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icon/stop.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icon/testfiesta.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testfiesta/pinata/d788c89e7428f59aca637441b66eb60158bf0c11/src/assets/icon/testfiesta.png -------------------------------------------------------------------------------- /src/assets/icon/testrail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testfiesta/pinata/d788c89e7428f59aca637441b66eb60158bf0c11/src/assets/icon/testrail.png -------------------------------------------------------------------------------- /src/assets/icon/text.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icon/text_link.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icon/thick.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icon/thin.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icon/timeline-icon/face-smile.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/assets/icon/timeline-icon/message-blue.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icon/timeline-icon/message-gray.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icon/timeline-icon/message.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icon/timeline-icon/microphone-blue.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icon/timeline-icon/microphone-gray.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icon/timeline-icon/microphone.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icon/timeline-icon/mindmap-blue.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icon/timeline-icon/mindmap-gray.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icon/timeline-icon/notes.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icon/timeline-icon/pause20px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/icon/timeline-icon/play.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icon/timeline-icon/play20px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icon/timeline-icon/stop.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icon/timeline-icon/video-blue.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/icon/timeline-icon/video-gray.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/icon/transparent.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/assets/icon/trash-red.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icon/trash.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icon/triangle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icon/underline.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icon/upload.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icon/video-slash-solid-gray.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Created with Fabric.js 4.6.0 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/assets/icon/video-slash-solid-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Created with Fabric.js 4.6.0 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/assets/icon/video-slash-solid.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Created with Fabric.js 4.6.0 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/assets/icon/video-solid-gray.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Created with Fabric.js 4.6.0 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/assets/icon/video-solid-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Created with Fabric.js 4.6.0 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/assets/icon/video-solid.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Created with Fabric.js 4.6.0 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/assets/icon/video-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/icon/xray-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testfiesta/pinata/d788c89e7428f59aca637441b66eb60158bf0c11/src/assets/icon/xray-logo.png -------------------------------------------------------------------------------- /src/assets/icon/zephyr-scale.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testfiesta/pinata/d788c89e7428f59aca637441b66eb60158bf0c11/src/assets/icon/zephyr-scale.png -------------------------------------------------------------------------------- /src/assets/icon/zephyr-squad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testfiesta/pinata/d788c89e7428f59aca637441b66eb60158bf0c11/src/assets/icon/zephyr-squad.png -------------------------------------------------------------------------------- /src/assets/icon/zoom-in.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icon/zoom-out.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testfiesta/pinata/d788c89e7428f59aca637441b66eb60158bf0c11/src/assets/logo.png -------------------------------------------------------------------------------- /src/assets/mindmap-workspace.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/testfiesta/pinata/d788c89e7428f59aca637441b66eb60158bf0c11/src/assets/mindmap-workspace.png -------------------------------------------------------------------------------- /src/components/FileWrapper.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 44 | 71 | -------------------------------------------------------------------------------- /src/components/LogoWrapper.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 34 | -------------------------------------------------------------------------------- /src/components/SearchWrapper.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 39 | 44 | -------------------------------------------------------------------------------- /src/components/__tests__/CheckTaskWrapper.spec.js: -------------------------------------------------------------------------------- 1 | import Vuetify from "vuetify"; 2 | import CheckTaskWrapper from "../CheckTaskWrapper.vue"; 3 | 4 | import { mount, createLocalVue } from "@vue/test-utils"; 5 | 6 | const localVue = createLocalVue(); 7 | 8 | const vuetify = new Vuetify(); 9 | 10 | describe("CheckTaskWrapper.vue", () => { 11 | test("render a view", () => { 12 | const wrapper = mount(CheckTaskWrapper, { 13 | mocks: { 14 | $t: () => {}, 15 | $tc: () => {}, 16 | }, 17 | propsData: { 18 | showError: false, 19 | tasks: [ 20 | { 21 | id: 1, 22 | checked: false, 23 | content: "", 24 | }, 25 | ], 26 | type: "presession", 27 | }, 28 | localVue, 29 | vuetify, 30 | }); 31 | 32 | expect(wrapper.find(".task-wrapper .subtitle-2").exists()).toBe(true); 33 | expect(wrapper.find(".task-wrapper .list").exists()).toBe(true); 34 | expect(wrapper.findAll(".task-wrapper .list .one").length).toBe(1); 35 | expect(wrapper.find(".task-wrapper .error1").exists()).toBe(false); 36 | }); 37 | 38 | test("show a error panel", () => { 39 | const wrapper = mount(CheckTaskWrapper, { 40 | mocks: { 41 | $t: () => {}, 42 | $tc: () => {}, 43 | }, 44 | propsData: { 45 | showError: true, 46 | tasks: [ 47 | { 48 | id: 1, 49 | checked: false, 50 | content: "", 51 | }, 52 | ], 53 | type: "presession", 54 | }, 55 | localVue, 56 | vuetify, 57 | }); 58 | 59 | expect(wrapper.find(".task-wrapper .error1").exists()).toBe(true); 60 | expect(wrapper.find(".task-wrapper .error1 .content .title").exists()).toBe( 61 | true 62 | ); 63 | expect(wrapper.find(".task-wrapper .error1 .content .desc").exists()).toBe( 64 | true 65 | ); 66 | }); 67 | }); 68 | -------------------------------------------------------------------------------- /src/components/__tests__/ExportPanel.spec.js: -------------------------------------------------------------------------------- 1 | import { mount } from "@vue/test-utils"; 2 | import Vuetify from "vuetify"; 3 | 4 | import ExportPanel from "../ExportPanel.vue"; 5 | 6 | const vuetify = new Vuetify(); 7 | 8 | describe("ExportPanel.vue", () => { 9 | const items = []; 10 | 11 | test("Display buttons", () => { 12 | const wrapper = mount(ExportPanel, { 13 | mocks: { 14 | $t: () => {}, 15 | $tc: () => {}, 16 | }, 17 | propsData: { 18 | items: items, 19 | }, 20 | vuetify, 21 | }); 22 | 23 | expect(wrapper.find("button.v-btn").exists()).toBe(true); 24 | }); 25 | 26 | test('trigger the click event of "export session report" button', async () => { 27 | const wrapper = mount(ExportPanel, { 28 | mocks: { 29 | $t: () => {}, 30 | $tc: () => {}, 31 | }, 32 | items: items, 33 | vuetify, 34 | }); 35 | 36 | const button = wrapper.find("button.v-btn"); 37 | const event = jest.fn(); 38 | 39 | button.vm.$on("click", event); 40 | button.trigger("click"); 41 | 42 | expect(event).toHaveBeenCalled(); 43 | }); 44 | }); 45 | -------------------------------------------------------------------------------- /src/components/__tests__/FileWrapper.spec.js: -------------------------------------------------------------------------------- 1 | import FileWrapper from "../FileWrapper"; 2 | import Vuetify from "vuetify"; 3 | 4 | import { mount } from "@vue/test-utils"; 5 | 6 | describe("FileWrapper.vue", () => { 7 | const vuetify = new Vuetify(); 8 | 9 | test("load a view", () => { 10 | const wrapper = mount(FileWrapper, { 11 | mocks: { 12 | $t: () => {}, 13 | $tc: () => {}, 14 | }, 15 | propsData: { 16 | item: { 17 | fileName: "test.pdf", 18 | }, 19 | }, 20 | vuetify, 21 | }); 22 | 23 | expect(wrapper.find(".wrapper .file-icon").exists()).toBe(true); 24 | expect(wrapper.find(".wrapper p").exists()).toBe(true); 25 | expect(wrapper.find(".wrapper p").text()).toContain("test.pdf"); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /src/components/__tests__/ImageEditor.spec.js: -------------------------------------------------------------------------------- 1 | import Vuetify from "vuetify"; 2 | import ImageEditor from "../ImageEditor"; 3 | 4 | import { mount } from "@vue/test-utils"; 5 | 6 | const vuetify = new Vuetify(); 7 | 8 | describe("ImageEditor.vue", () => { 9 | test("render a view", () => { 10 | const wrapper = mount(ImageEditor, { 11 | mocks: { 12 | $t: () => {}, 13 | $tc: () => {}, 14 | }, 15 | propsData: { 16 | item: { 17 | filePath: "", 18 | }, 19 | }, 20 | vuetify, 21 | }); 22 | 23 | expect(wrapper.find(".image-editor").exists()).toBe(true); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/components/__tests__/LogoWrapper.spec.js: -------------------------------------------------------------------------------- 1 | import { mount } from "@vue/test-utils"; 2 | 3 | import LogoWrapper from "../LogoWrapper"; 4 | 5 | describe("LogoWrapper.vue", () => { 6 | test("displays logo", () => { 7 | const wrapper = mount(LogoWrapper); 8 | 9 | expect(wrapper.find(".v-image")).toBeTruthy(); 10 | 11 | const images = wrapper.findAll(".v-image"); 12 | expect(images.length).toBe(1); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /src/components/__tests__/MindmapEditor.spec.js: -------------------------------------------------------------------------------- 1 | import Vuetify from "vuetify"; 2 | import MindmapEditor from "../MindmapEditor.vue"; 3 | import NodeEditDialog from "../dialogs/NodeEditDialog.vue"; 4 | 5 | import { mount, createLocalVue } from "@vue/test-utils"; 6 | 7 | const vuetify = new Vuetify(); 8 | const localVue = createLocalVue(); 9 | 10 | describe("MindmapEditor.vue", () => { 11 | test("render a view", () => { 12 | const wrapper = mount(MindmapEditor, { 13 | mocks: { 14 | $t: () => {}, 15 | $tc: () => {}, 16 | }, 17 | localVue, 18 | vuetify, 19 | }); 20 | 21 | expect(wrapper.find(".wrapper").exists()).toBe(true); 22 | expect(wrapper.findComponent(NodeEditDialog).exists()).toBe(true); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/components/__tests__/NotesWrapper.spec.js: -------------------------------------------------------------------------------- 1 | import Vuetify from "vuetify"; 2 | import NotesWrapper from "../NotesWrapper.vue"; 3 | 4 | import { mount } from "@vue/test-utils"; 5 | 6 | const vuetify = new Vuetify(); 7 | 8 | describe("NotesWrapper.vue", () => { 9 | test("render a view", () => { 10 | const wrapper = mount(NotesWrapper, { 11 | mocks: { 12 | $t: () => {}, 13 | $tc: () => {}, 14 | }, 15 | data() { 16 | return { 17 | notes: { text: "", content: "" }, 18 | }; 19 | }, 20 | vuetify, 21 | }); 22 | expect(wrapper.find(".content").exists()).toBe(true); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/components/__tests__/ReviewWrapper.spec.js: -------------------------------------------------------------------------------- 1 | import Vuetify from "vuetify"; 2 | 3 | import ReviewWrappper from "../ReviewWrapper"; 4 | import ImageEditor from "../ImageEditor"; 5 | import VideoWrapper from "../VideoWrapper"; 6 | import AudioWrapper from "../AudioWrapper"; 7 | import FileWrapper from "../FileWrapper"; 8 | import MindmapEditor from "../MindmapEditor.vue"; 9 | 10 | import { mount } from "@vue/test-utils"; 11 | 12 | const vuetify = new Vuetify(); 13 | 14 | describe("ReivewWrapper.vue", () => { 15 | test("render a view", async () => { 16 | const wrapper = mount(ReviewWrappper, { 17 | mocks: { 18 | $t: () => {}, 19 | $tc: () => {}, 20 | }, 21 | propsData: { 22 | item: { 23 | fileType: "application/mindmap", 24 | content: { nodes: [] }, 25 | }, 26 | processing: false, 27 | triggerSave: false, 28 | autoSave: false, 29 | currentView: false, 30 | config: { 31 | defaultColor: "#000000", 32 | }, 33 | }, 34 | data() { 35 | return { 36 | sessionItem: { 37 | fileType: "application/mindmap", 38 | }, 39 | }; 40 | }, 41 | vuetify, 42 | }); 43 | 44 | expect(wrapper.findComponent(MindmapEditor).exists()).toBe(true); 45 | 46 | await wrapper.setData({ 47 | sessionItem: { 48 | fileType: "image/png", 49 | }, 50 | }); 51 | 52 | expect(wrapper.findComponent(ImageEditor).exists()).toBe(true); 53 | 54 | await wrapper.setData({ 55 | sessionItem: { 56 | fileType: "video/mp4", 57 | }, 58 | }); 59 | expect(wrapper.findComponent(VideoWrapper).exists()).toBe(true); 60 | 61 | await wrapper.setData({ 62 | sessionItem: { 63 | fileType: "audio/mp3", 64 | }, 65 | }); 66 | expect(wrapper.findComponent(AudioWrapper).exists()).toBe(true); 67 | 68 | await wrapper.setData({ 69 | sessionItem: { 70 | fileType: "other", 71 | }, 72 | }); 73 | expect(wrapper.findComponent(FileWrapper).exists()).toBe(true); 74 | }); 75 | }); 76 | -------------------------------------------------------------------------------- /src/components/__tests__/SearchWrapper.spec.js: -------------------------------------------------------------------------------- 1 | import { mount } from "@vue/test-utils"; 2 | import Vuetify from "vuetify"; 3 | 4 | import SearchWrapper from "../SearchWrapper.vue"; 5 | 6 | const vuetify = new Vuetify(); 7 | 8 | describe("SearchWrapper.vue", () => { 9 | test("load serach box", () => { 10 | const wrapper = mount(SearchWrapper, { 11 | mocks: { 12 | $t: () => {}, 13 | $tc: () => {}, 14 | }, 15 | vuetify, 16 | }); 17 | 18 | const searchInput = wrapper.find("input"); 19 | expect(searchInput.exists()).toBe(true); 20 | }); 21 | 22 | test("change the value of searchbox", async () => { 23 | const wrapper = mount(SearchWrapper, { 24 | mocks: { 25 | $t: () => {}, 26 | $tc: () => {}, 27 | }, 28 | vuetify, 29 | }); 30 | 31 | const searchInput = wrapper.find("input"); 32 | await searchInput.setValue("Search"); 33 | 34 | expect(wrapper.vm.search).toBe("Search"); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /src/components/__tests__/TimeCounter.spec.js: -------------------------------------------------------------------------------- 1 | import { mount, createLocalVue } from "@vue/test-utils"; 2 | import Vuex from "vuex"; 3 | import Vuetify from "vuetify"; 4 | import { cloneDeep } from "lodash"; 5 | 6 | import TimeCounter from "../TimeCounter"; 7 | import storeConfig from "../../store/store-config"; 8 | 9 | const vuetify = new Vuetify(); 10 | const localVue = createLocalVue(); 11 | 12 | localVue.use(Vuex); 13 | 14 | describe("TimeCounter.vue", () => { 15 | let store; 16 | 17 | beforeEach(() => { 18 | store = new Vuex.Store(cloneDeep(storeConfig)); 19 | }); 20 | 21 | test("displays elapsed time title", () => { 22 | const wrapper = mount(TimeCounter, { 23 | mocks: { 24 | $t: () => {}, 25 | $tc: () => {}, 26 | }, 27 | localVue, 28 | store, 29 | vuetify, 30 | }); 31 | 32 | expect( 33 | wrapper.find(".time-wrapper .time:nth-child(1) .time-title") 34 | ).toBeTruthy(); 35 | }); 36 | 37 | test("displays remaining time title", () => { 38 | const wrapper = mount(TimeCounter, { 39 | mocks: { 40 | $t: () => {}, 41 | $tc: () => {}, 42 | }, 43 | localVue, 44 | store, 45 | vuetify, 46 | }); 47 | 48 | expect( 49 | wrapper.find(".time-wrapper .time:nth-child(2) .time-title") 50 | ).toBeTruthy(); 51 | }); 52 | 53 | test("displays elapsed time value", () => { 54 | const wrapper = mount(TimeCounter, { 55 | mocks: { 56 | $t: () => {}, 57 | $tc: () => {}, 58 | }, 59 | localVue, 60 | store, 61 | vuetify, 62 | }); 63 | 64 | expect( 65 | wrapper.find(".time-wrapper .time:nth-child(1) .time-value") 66 | ).toBeTruthy(); 67 | expect( 68 | wrapper.find(".time-wrapper .time:nth-child(1) .time-value").text() 69 | ).toBe("00:00:00"); 70 | }); 71 | 72 | test("displays remaining time value", () => { 73 | const wrapper = mount(TimeCounter, { 74 | mocks: { 75 | $t: () => {}, 76 | $tc: () => {}, 77 | }, 78 | localVue, 79 | store, 80 | vuetify, 81 | }); 82 | 83 | expect( 84 | wrapper.find(".time-wrapper .time:nth-child(2) .time-value") 85 | ).toBeTruthy(); 86 | expect( 87 | wrapper.find(".time-wrapper .time:nth-child(2) .time-value").text() 88 | ).toBe("00:00:00"); 89 | }); 90 | }); 91 | -------------------------------------------------------------------------------- /src/components/__tests__/WorkspaceWrapper.spec.js: -------------------------------------------------------------------------------- 1 | import Vuetify from "vuetify"; 2 | import WorkspaceWrapper from "../WorkspaceWrapper.vue"; 3 | import NotesWrapper from "../NotesWrapper.vue"; 4 | import TimelineWrapper from "../TimelineWrapper.vue"; 5 | 6 | import { shallowMount } from "@vue/test-utils"; 7 | 8 | const vuetify = new Vuetify(); 9 | 10 | describe("WorkspaceWrapper.vue", () => { 11 | test("render a view", () => { 12 | const wrapper = shallowMount(WorkspaceWrapper, { 13 | mocks: { 14 | $t: () => {}, 15 | $tc: () => {}, 16 | }, 17 | propsData: { 18 | items: [], 19 | selectedItems: [], 20 | eventType: "", 21 | }, 22 | vuetify, 23 | }); 24 | 25 | expect(wrapper.find(".tab-bar .timeline-tab").exists()).toBe(true); 26 | expect(wrapper.find(".tab-bar .notes-tab").exists()).toBe(true); 27 | expect(wrapper.findComponent(NotesWrapper).exists()).toBe(true); 28 | expect(wrapper.findComponent(TimelineWrapper).exists()).toBe(true); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /src/components/authentication/__tests__/SigninWrapper.spec.js: -------------------------------------------------------------------------------- 1 | import Vuetify from "vuetify"; 2 | import SigninWrapper from "../SigninWrapper.vue"; 3 | 4 | import { mount } from "@vue/test-utils"; 5 | 6 | const vuetify = new Vuetify(); 7 | 8 | describe("SigninWrapper.vue", () => { 9 | test("render a view", () => { 10 | const wrapper = mount(SigninWrapper, { 11 | mocks: { 12 | $t: () => {}, 13 | $tc: () => {}, 14 | }, 15 | vuetify, 16 | }); 17 | 18 | expect(wrapper.find(".wrapper .header").exists()).toBe(true); 19 | expect(wrapper.find(".wrapper .subtitle-1").exists()).toBe(true); 20 | expect(wrapper.find(".wrapper .content").exists()).toBe(true); 21 | expect( 22 | wrapper.findAll(".wrapper .content .row:first-child button").length 23 | ).toBe(2); 24 | }); 25 | 26 | test('trigger the click event of "signin with pinata" button', () => { 27 | const wrapper = mount(SigninWrapper, { 28 | mocks: { 29 | $t: () => {}, 30 | $tc: () => {}, 31 | }, 32 | data() { 33 | return {}; 34 | }, 35 | vuetify, 36 | }); 37 | 38 | const event = jest.fn(); 39 | const button = wrapper.find("button.pinata"); 40 | 41 | button.vm.$on("click", event); 42 | button.trigger("click"); 43 | 44 | expect(event).toHaveBeenCalled(); 45 | }); 46 | 47 | test('trigger the click event of "signin with jira" button', () => { 48 | const wrapper = mount(SigninWrapper, { 49 | mocks: { 50 | $t: () => {}, 51 | $tc: () => {}, 52 | }, 53 | data() { 54 | return {}; 55 | }, 56 | vuetify, 57 | }); 58 | 59 | const event = jest.fn(); 60 | const button = wrapper.find("button.jira"); 61 | 62 | button.vm.$on("click", event); 63 | button.trigger("click"); 64 | 65 | expect(event).toHaveBeenCalled(); 66 | }); 67 | }); 68 | -------------------------------------------------------------------------------- /src/components/authentication/__tests__/SignupPinataWrapper.spec.js: -------------------------------------------------------------------------------- 1 | import Vuetify from "vuetify"; 2 | import SignupPinataWrapper from "../SignupPinataWrapper.vue"; 3 | 4 | import { mount } from "@vue/test-utils"; 5 | 6 | const vuetify = new Vuetify(); 7 | let wrapper; 8 | 9 | describe("SignupPinataWrapper.vue", () => { 10 | beforeEach(() => { 11 | wrapper = mount(SignupPinataWrapper, { 12 | mocks: { 13 | $t: () => {}, 14 | $tc: () => {}, 15 | }, 16 | data() { 17 | return { 18 | start: "individual", 19 | email: "", 20 | url: "", 21 | }; 22 | }, 23 | vuetify, 24 | }); 25 | }); 26 | 27 | test("render a view", () => { 28 | expect(wrapper.find(".wrapper .header").exists()).toBe(true); 29 | expect(wrapper.find(".wrapper .header .v-btn").exists()).toBe(true); 30 | expect(wrapper.find(".wrapper .header .subtitle-1").exists()).toBe(true); 31 | expect(wrapper.find(".wrapper .content").exists()).toBe(true); 32 | expect(wrapper.find(".wrapper .content .radio-box").exists()).toBe(true); 33 | expect(wrapper.find(".wrapper")); 34 | expect(wrapper.find(".wrapper .footer").exists()).toBe(true); 35 | }); 36 | 37 | test('trigger the click event of "back" button', async () => { 38 | const event = jest.fn(); 39 | const button = wrapper.find(".back-btn"); 40 | 41 | button.vm.$on("click", event); 42 | button.trigger("click"); 43 | 44 | await wrapper.vm.$nextTick(); 45 | 46 | // expect(event).toHaveBeenCalled(); 47 | }); 48 | }); 49 | -------------------------------------------------------------------------------- /src/components/dialogs/SettingsDialog.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /src/components/dialogs/__tests__/EndSessionDialog.spec.js: -------------------------------------------------------------------------------- 1 | import Vuetify from "vuetify"; 2 | import CheckTaskWrapper from "../../CheckTaskWrapper.vue"; 3 | import EndSessionDialog from "../EndSessionDialog.vue"; 4 | 5 | import { mount, createLocalVue } from "@vue/test-utils"; 6 | 7 | let vuetify; 8 | let wrapper; 9 | let localVue; 10 | 11 | describe("EndSessionDialog.vue", () => { 12 | beforeEach(() => { 13 | const rootDiv = document.createElement("div"); 14 | rootDiv.id = "root"; 15 | document.body.appendChild(rootDiv); 16 | 17 | localVue = createLocalVue(); 18 | 19 | vuetify = new Vuetify(); 20 | 21 | const App = localVue.component("App", { 22 | components: { EndSessionDialog }, 23 | data() { 24 | return { 25 | dialog: false, 26 | showTaskError: false, 27 | }; 28 | }, 29 | template: ` 30 | 31 | 36 | 37 | `, 38 | }); 39 | 40 | wrapper = mount(App, { 41 | mocks: { 42 | $t: () => {}, 43 | $tc: () => {}, 44 | }, 45 | localVue, 46 | vuetify, 47 | attachTo: "#root", 48 | }); 49 | }); 50 | 51 | test("render a dialog", async () => { 52 | wrapper.setData({ 53 | dialog: true, 54 | }); 55 | 56 | await wrapper.vm.$nextTick(); 57 | 58 | expect(wrapper.findComponent(CheckTaskWrapper).exists()).toBe(true); 59 | expect(wrapper.find(".btn-end").exists()).toBe(true); 60 | }); 61 | 62 | test('trigger the click event of "End Session" button', async () => { 63 | wrapper.setData({ 64 | dialog: true, 65 | }); 66 | 67 | await wrapper.vm.$nextTick(); 68 | 69 | const button = wrapper.find(".btn-end"); 70 | const event = jest.fn(); 71 | 72 | button.vm.$on("click", event); 73 | button.trigger("click"); 74 | 75 | expect(event).toHaveBeenCalled(); 76 | }); 77 | }); 78 | -------------------------------------------------------------------------------- /src/components/dialogs/__tests__/SourcePIckerDialog.spec.js: -------------------------------------------------------------------------------- 1 | import SourcePickerDialog from "../SourcePickerDialog"; 2 | import Vuetify from "vuetify"; 3 | 4 | import { mount, createLocalVue } from "@vue/test-utils"; 5 | 6 | let wrapper; 7 | let vuetify; 8 | let localVue; 9 | 10 | describe("SourcePickerDialog", () => { 11 | beforeEach(() => { 12 | const rootDiv = document.createElement("div"); 13 | rootDiv.id = "root"; 14 | document.body.appendChild(rootDiv); 15 | 16 | localVue = createLocalVue(); 17 | vuetify = new Vuetify(); 18 | 19 | const App = localVue.component("App", { 20 | components: { SourcePickerDialog }, 21 | data() { 22 | return { 23 | dialog: false, 24 | }; 25 | }, 26 | template: ` 27 | 28 | 32 | 33 | `, 34 | }); 35 | 36 | wrapper = mount(App, { 37 | mocks: { 38 | $t: () => {}, 39 | $tc: () => {}, 40 | }, 41 | localVue, 42 | vuetify, 43 | attachTo: "#root", 44 | }); 45 | }); 46 | 47 | test("render a dialog", async () => { 48 | await wrapper.setData({ dialog: true }); 49 | 50 | expect(wrapper.find(".header span").exists()).toBe(true); 51 | 52 | expect(wrapper.find(".content").exists()).toBe(true); 53 | expect(wrapper.findAll(".footer button").length).toBe(2); 54 | }); 55 | 56 | test('trigger the click event of "Cancel" button', async () => { 57 | await wrapper.setData({ dialog: true }); 58 | 59 | const button = wrapper.find(".footer button:first-child"); 60 | const event = jest.fn(); 61 | 62 | button.vm.$on("click", event); 63 | button.trigger("click"); 64 | 65 | expect(event).toHaveBeenCalled(); 66 | }); 67 | 68 | test('trigger the click event of "Start Recording" button', async () => { 69 | await wrapper.setData({ dialog: true }); 70 | 71 | const button = wrapper.find(".footer button:first-child"); 72 | const event = jest.fn(); 73 | 74 | button.vm.$on("click", event); 75 | button.trigger("click"); 76 | 77 | expect(event).toHaveBeenCalled(); 78 | }); 79 | }); 80 | -------------------------------------------------------------------------------- /src/components/mindmap/ColorPicker.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 80 | 98 | -------------------------------------------------------------------------------- /src/components/mindmap/MarkerPad.vue: -------------------------------------------------------------------------------- 1 | 53 | 54 | 70 | -------------------------------------------------------------------------------- /src/components/mindmap/contextMenu.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 35 | 36 | 54 | -------------------------------------------------------------------------------- /src/components/settings/SupportTab.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 29 | 70 | -------------------------------------------------------------------------------- /src/components/settings/__tests__/GeneralTab.spec.js: -------------------------------------------------------------------------------- 1 | import Vuetify from "vuetify"; 2 | import GeneralTab from "../GeneralTab"; 3 | 4 | import { mount, createLocalVue } from "@vue/test-utils"; 5 | 6 | const localVue = createLocalVue(); 7 | const vuetify = new Vuetify(); 8 | 9 | describe("GeneralTab.vue", () => { 10 | test("render a view", () => { 11 | const wrapper = mount(GeneralTab, { 12 | mocks: { 13 | $t: () => {}, 14 | $tc: () => {}, 15 | }, 16 | propsData: { 17 | config: {}, 18 | }, 19 | localVue, 20 | vuetify, 21 | }); 22 | 23 | expect(wrapper.find(".content-wrapper .theme-mode-section").exists()).toBe( 24 | true 25 | ); 26 | expect( 27 | wrapper 28 | .find(".content-wrapper .theme-mode-section .radio-control") 29 | .exists() 30 | ).toBe(true); 31 | 32 | expect(wrapper.find(".content-wrapper .screenshot-section").exists()).toBe( 33 | true 34 | ); 35 | expect( 36 | wrapper 37 | .find(".content-wrapper .screenshot-section .color-picker-wrapper") 38 | .exists() 39 | ).toBe(true); 40 | 41 | expect(wrapper.find(".content-wrapper .note-section").exists()).toBe(true); 42 | expect(wrapper.find(".content-wrapper .note-section input").exists()).toBe( 43 | true 44 | ); 45 | 46 | expect( 47 | wrapper.find(".content-wrapper .screen-recording-section").exists() 48 | ).toBe(true); 49 | expect( 50 | wrapper 51 | .find(".content-wrapper .screen-recording-section .switch-control") 52 | .exists() 53 | ).toBe(true); 54 | }); 55 | }); 56 | -------------------------------------------------------------------------------- /src/components/settings/__tests__/SupportTab.spec.js: -------------------------------------------------------------------------------- 1 | import Vuetify from "vuetify"; 2 | import SupportTab from "../SupportTab.vue"; 3 | 4 | import { mount, createLocalVue } from "@vue/test-utils"; 5 | 6 | const localVue = createLocalVue(); 7 | localVue.use(Vuetify); 8 | 9 | const vuetify = new Vuetify(); 10 | 11 | describe("SupportTab.vue", () => { 12 | test("render a view", () => { 13 | const wrapper = mount(SupportTab, { 14 | mocks: { 15 | $t: () => {}, 16 | $tc: () => {}, 17 | }, 18 | localVue, 19 | vuetify, 20 | }); 21 | 22 | expect(wrapper.find(".content-wrapper").exists()).toBe(true); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/helpers/HotkeyHelpers.js: -------------------------------------------------------------------------------- 1 | export default { 2 | findBinding(key, bindingConfig) { 3 | let keys = key.split("."); 4 | let bindingCursor = bindingConfig; 5 | for (const k of keys) { 6 | if (bindingCursor) { 7 | bindingCursor = bindingCursor?.[k]; 8 | } 9 | } 10 | if (bindingCursor && bindingCursor.constructor === String) { 11 | return this.findBinding(bindingCursor, bindingConfig); 12 | } 13 | return bindingCursor || []; 14 | }, 15 | printBindings(directBinding, bindingConfig) { 16 | if (directBinding.constructor === Array) { 17 | return directBinding.join(" + ").toUpperCase(); 18 | } else { 19 | return this.findBinding(directBinding, bindingConfig) 20 | .join(" + ") 21 | .toUpperCase(); 22 | } 23 | }, 24 | focusField(refs, label) { 25 | const el = refs[label]?.$el || refs[label]; 26 | let input = el.querySelector(".ProseMirror"); 27 | if (!input) { 28 | input = el.querySelector( 29 | "input:not([type=hidden]),textarea:not([type=hidden])" 30 | ); 31 | } 32 | if (input) { 33 | setTimeout(() => { 34 | input.focus(); 35 | }); 36 | } 37 | }, 38 | }; 39 | -------------------------------------------------------------------------------- /src/i18n.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import VueI18n from "vue-i18n"; 3 | 4 | Vue.use(VueI18n); 5 | 6 | function loadLocaleMessages() { 7 | const locales = require.context( 8 | "./locales", 9 | true, 10 | /[A-Za-z0-9-_,\s]+\.json$/i 11 | ); 12 | const messages = {}; 13 | locales.keys().forEach((key) => { 14 | const matched = key.match(/([A-Za-z0-9-_]+)\./i); 15 | if (matched && matched.length > 1) { 16 | const locale = matched[1]; 17 | messages[locale] = locales(key); 18 | } 19 | }); 20 | 21 | return messages; 22 | } 23 | 24 | export default new VueI18n({ 25 | locale: process.env.VUE_APP_I18N_LOCALE || "en", 26 | fallbackLocale: process.env.VUE_APP_I18N_FALLBACK_LOCALE || "en", 27 | messages: loadLocaleMessages(), 28 | silentTranslationWarn: true, //process.env.NODE_ENV === "production", 29 | }); 30 | -------------------------------------------------------------------------------- /src/integrations/TestRailIntegrationHelpers.js: -------------------------------------------------------------------------------- 1 | import { IPC_HANDLERS, IPC_FUNCTIONS } from "../modules/constants"; 2 | 3 | export default { 4 | saveCredentials(credentials, data) { 5 | // TODO - can this be generalized and the key passed 6 | let formattedData = this.formatData(data); 7 | 8 | if (!credentials) { 9 | credentials = {}; 10 | } 11 | 12 | if (credentials.testrail && credentials.testrail.length > 0) { 13 | let matched = false; 14 | for (const [testrailIndex, credential] of Object.entries( 15 | credentials.testrail 16 | )) { 17 | if (credential.user.id === formattedData.user.id) { 18 | credentials.testrail[testrailIndex] = formattedData; 19 | matched = true; 20 | } 21 | } 22 | if (!matched) { 23 | credentials.testrail.push(formattedData); 24 | } 25 | } else { 26 | credentials.testrail = [formattedData]; 27 | } 28 | 29 | window.ipc.invoke(IPC_HANDLERS.PERSISTENCE, { 30 | func: IPC_FUNCTIONS.UPDATE_CREDENTIALS, 31 | data: credentials, 32 | }); 33 | 34 | return credentials; 35 | }, 36 | formatData(data) { 37 | return { 38 | accessToken: data.accessToken, 39 | type: data.type, 40 | loggedInAt: data.loggedInAt, 41 | user: { 42 | id: data.profile.account_id, 43 | email: data.profile.email, 44 | name: data.profile.name, 45 | }, 46 | url: data.url, 47 | }; 48 | }, 49 | }; 50 | -------------------------------------------------------------------------------- /src/layouts/Minimize.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 15 | 20 | -------------------------------------------------------------------------------- /src/mixins/theme.js: -------------------------------------------------------------------------------- 1 | export default { 2 | computed: { 3 | currentTheme() { 4 | if (this.$vuetify.theme.dark) { 5 | return this.$vuetify.theme.themes.dark; 6 | } else { 7 | return this.$vuetify.theme.themes.light; 8 | } 9 | }, 10 | mainBg() { 11 | return this.$vuetify.theme.dark ? "#374151" : this.currentTheme.white; 12 | }, 13 | btnBg() { 14 | return this.$vuetify.theme.dark ? "#4B5563" : "#F2F4F7"; 15 | }, 16 | inputBg() { 17 | return this.$vuetify.theme.dark ? "#4B5563" : "#F9F9FB"; 18 | }, 19 | btnColor() { 20 | return this.$vuetify.theme.dark ? "#FFFFFF" : "#000000"; 21 | }, 22 | mainBgReverse() { 23 | return this.$vuetify.theme.dark ? "#F2F4F7" : "#161B26"; 24 | }, 25 | }, 26 | }; 27 | -------------------------------------------------------------------------------- /src/modules/BrowserWindowUtility.js: -------------------------------------------------------------------------------- 1 | const { VIEW_MODE } = require("./constants"); 2 | 3 | module.exports.setBrowserWindow = (browserWindow) => { 4 | this._browserWindow = browserWindow; 5 | }; 6 | 7 | module.exports.getBrowserWindow = () => { 8 | return this._browserWindow; 9 | }; 10 | 11 | module.exports.setLowProfiledWindow = (lowProfiledWindow) => { 12 | this._lowProfiledWindow = lowProfiledWindow; 13 | }; 14 | 15 | module.exports.getLowProfiledWindow = () => { 16 | return this._lowProfiledWindow; 17 | }; 18 | 19 | module.exports.setViewMode = (viewMode) => { 20 | this._viewMode = viewMode; 21 | }; 22 | 23 | module.exports.getViewMode = () => { 24 | return this._viewMode; 25 | }; 26 | 27 | module.exports.getParentWindow = () => { 28 | if (this._viewMode === VIEW_MODE.NORMAL) return this._browserWindow; 29 | else return this._lowProfiledWindow; 30 | }; 31 | -------------------------------------------------------------------------------- /src/modules/MenuUtility.js: -------------------------------------------------------------------------------- 1 | const { Menu } = require("electron"); 2 | 3 | const { SESSION_STATUSES } = require("./constants"); 4 | 5 | module.exports.changeMenuItemStatus = ({ sessionStatus }) => { 6 | const mainMenu = Menu.getApplicationMenu(); 7 | const fileMenu = mainMenu.items.find((item) => item.id === "menu_file"); 8 | const fileSubMenus = fileMenu.submenu.items; 9 | 10 | if (sessionStatus === SESSION_STATUSES.PENDING) { 11 | fileSubMenus.find( 12 | (item) => item.id === "menu_save_session" 13 | ).enabled = false; 14 | // fileSubMenus.find( 15 | // (item) => item.id === "menu_save_as_charter" 16 | // ).enabled = false; 17 | fileSubMenus.find( 18 | (item) => item.id === "menu_reset_session" 19 | ).enabled = false; 20 | } else { 21 | fileSubMenus.find((item) => item.id === "menu_save_session").enabled = true; 22 | // fileSubMenus.find( 23 | // (item) => item.id === "menu_save_as_charter" 24 | // ).enabled = true; 25 | fileSubMenus.find( 26 | (item) => item.id === "menu_reset_session" 27 | ).enabled = true; 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /src/modules/ServerUtility.js: -------------------------------------------------------------------------------- 1 | const { fork } = require("child_process"); 2 | const path = require("path"); 3 | const browserUtility = require("./BrowserWindowUtility"); 4 | 5 | let serverProcess = null; 6 | 7 | module.exports.startServer = async (vars) => { 8 | if (Object.keys(vars) < 1) { 9 | vars = {}; 10 | } 11 | 12 | const isDevelopment = process.env.NODE_ENV !== "production"; 13 | serverProcess = fork( 14 | isDevelopment 15 | ? path.resolve(__dirname, "../server/server.js") 16 | : path.resolve(process.resourcesPath, "./server/server.js"), 17 | { 18 | env: vars, 19 | } 20 | ); 21 | 22 | const browserWindow = browserUtility.getBrowserWindow(); 23 | 24 | serverProcess.on("message", (data) => { 25 | switch (data.type) { 26 | case "jira": 27 | browserWindow.webContents.send("JIRA_LOGIN", data.data); 28 | break; 29 | default: 30 | break; 31 | } 32 | }); 33 | await new Promise((resolve) => setTimeout(resolve, 1000)); 34 | // TODO: The above line is not ideal, but the express server doesn't seem to 35 | // send a 'spawn' event, so the below line doesn't work. 36 | //await once(serverProcess, 'spawn'); 37 | }; 38 | 39 | module.exports.stopServer = () => { 40 | if (serverProcess) { 41 | serverProcess.kill(); 42 | } 43 | }; 44 | -------------------------------------------------------------------------------- /src/modules/SystemInfoUtility.js: -------------------------------------------------------------------------------- 1 | const os = require("os"); 2 | const si = require("systeminformation"); 3 | 4 | async function getCurrentDateTime() { 5 | return new Date().toLocaleString("en-US", { timeZone: "UTC" }); 6 | } 7 | 8 | async function getComputerName() { 9 | return os.hostname(); 10 | } 11 | 12 | async function getOperatingSystem() { 13 | const osInfo = await si.osInfo(); 14 | return `${osInfo.distro} ${osInfo.release} ${osInfo.arch}`; 15 | } 16 | 17 | async function getSystemInfo() { 18 | const systemInfo = await si.system(); 19 | return { 20 | manufacturer: systemInfo.manufacturer, 21 | model: systemInfo.model, 22 | }; 23 | } 24 | 25 | async function getBIOSVersion() { 26 | const biosInfo = await si.bios(); 27 | return biosInfo.version; 28 | } 29 | 30 | async function getProcessor() { 31 | const cpuInfo = await si.cpu(); 32 | return `${cpuInfo.manufacturer} ${cpuInfo.brand} (${cpuInfo.physicalCores} CPUs), ~${cpuInfo.speed}GHz`; 33 | } 34 | 35 | async function getMemory() { 36 | const memInfo = await si.mem(); 37 | return `${(memInfo.total / 1024 / 1024).toFixed(0)}MB RAM`; 38 | } 39 | 40 | module.exports.getSystemInfo = async () => { 41 | try { 42 | const currentDateTime = await getCurrentDateTime(); 43 | const computerName = await getComputerName(); 44 | const operatingSystem = await getOperatingSystem(); 45 | const systemInfo = await getSystemInfo(); // Store the result of getSystemInfo() in a variable 46 | const systemManufacturer = systemInfo.manufacturer; 47 | const systemModel = systemInfo.model; 48 | const biosVersion = await getBIOSVersion(); 49 | const processor = await getProcessor(); 50 | const memory = await getMemory(); 51 | 52 | return { 53 | currentDateTime, 54 | computerName, 55 | operatingSystem, 56 | systemManufacturer, 57 | systemModel, 58 | biosVersion, 59 | processor, 60 | memory, 61 | }; 62 | } catch (error) { 63 | console.error("Error fetching system information:", error); 64 | return null; // or handle the error in a different way as per your requirement 65 | } 66 | }; 67 | -------------------------------------------------------------------------------- /src/modules/migrations/v0.6.0.js: -------------------------------------------------------------------------------- 1 | export const migrationStruct = { 2 | up: { 3 | meta: { 4 | credentialPath: "credentialsPath", 5 | }, 6 | }, 7 | down: { 8 | meta: { 9 | credentialsPath: "credentialPath", 10 | }, 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /src/modules/migrations/v0.8.0.js: -------------------------------------------------------------------------------- 1 | export const migrationStruct = { 2 | up: { 3 | meta: { 4 | meta: "..", 5 | dataPath: "sessionDataPath", 6 | }, 7 | }, 8 | down: { 9 | meta: { 10 | configPath: "meta.configPath", 11 | credentialsPath: "meta.credentialsPath", 12 | sessionDataPath: "meta.dataPath", 13 | version: "meta.version", 14 | }, 15 | }, 16 | }; 17 | -------------------------------------------------------------------------------- /src/modules/mindmap/parser/regex.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Extract text from the inner HTML of a node. 3 | */ 4 | const getText = (html) => { 5 | const res = []; 6 | 7 | // Match all text inside A tags. If there's no A tags, 8 | // match text inside P tags instead. 9 | const matchText = /]*>([^<]*)<\/a>|]*>([^>]*)<\/p>/g; 10 | let match = matchText.exec(html); 11 | 12 | while (match) { 13 | res.push(match[1] || match[2]); 14 | match = matchText.exec(html); 15 | } 16 | 17 | return res.join(" "); 18 | }; 19 | 20 | /* 21 | * Extract HREF content from the first link on a node. 22 | */ 23 | const getURL = (html) => { 24 | // Match HREF content inside A tags. 25 | const matchURL = /]*href="([^"]*)"[^>]*>[^<]*<\/a>/; 26 | const match = matchURL.exec(html); 27 | 28 | if (match) { 29 | return match[1]; 30 | } 31 | 32 | return ""; 33 | }; 34 | 35 | module.exports = { 36 | getText, 37 | getURL, 38 | }; 39 | -------------------------------------------------------------------------------- /src/modules/mindmap/sass/mindmap.sass: -------------------------------------------------------------------------------- 1 | $grey100: #f5f5f5 2 | $grey500: #9e9e9e 3 | $grey800: #424242 4 | $grey900: #212121 5 | $orange700: #f57c00 6 | $cyan100: #D6E4EE 7 | $cyan200: #A0D8F3 8 | $cyan300: #80B3FF 9 | 10 | $white: #fff 11 | $shadow: 0 2px 2px 0 rgba(0, 0, 0, .14), 0 3px 1px -2px rgba(0, 0, 0, .2), 0 1px 5px 0 rgba(0, 0, 0, .12) 12 | 13 | 14 | .mindmap-svg 15 | height: 100vh 16 | width: 100% 17 | 18 | &:focus 19 | outline: none 20 | 21 | .mindmap-node > a 22 | background: $grey100 23 | border-radius: 10px 24 | box-shadow: $shadow 25 | color: $grey900 26 | display: inline-block 27 | font-family: 'Raleway' 28 | font-size: 22px 29 | margin: 0 auto 30 | padding: 15px 31 | text-align: center 32 | text-decoration: none 33 | transition: background-color .2s, color .2s ease-out 34 | 35 | &[href] 36 | &:hover 37 | background-color: $orange700 38 | color: $white 39 | cursor: pointer 40 | 41 | .mindmap-node--editable 42 | cursor: all-scroll 43 | 44 | & > a 45 | pointer-events: none 46 | 47 | .mindmap-subnode-group 48 | align-items: center 49 | border-left: 4px solid $grey500 50 | display: flex 51 | margin-left: 15px 52 | padding: 5px 53 | 54 | a 55 | color: $grey900 56 | font-family: 'Raleway' 57 | font-size: 16px 58 | padding: 2px 5px 59 | 60 | .mindmap-connection 61 | fill: transparent 62 | stroke: $grey500 63 | stroke-dasharray: 10px 4px 64 | stroke-width: 3px 65 | 66 | .mindmap-connector-line 67 | fill: transparent 68 | stroke: $grey500 69 | stroke-dasharray: 10px 4px 70 | stroke-width: 3px 71 | 72 | .mindmap-label 73 | fill: $grey500 74 | font-size: 12px 75 | text-anchor: middle 76 | 77 | .mindmap-emoji 78 | height: 24px 79 | vertical-align: bottom 80 | width: 24px 81 | 82 | .mindmap-overlay 83 | fill: $cyan100 84 | stroke: $cyan300 85 | border: 1px solid $cyan200 86 | box-shadow: $shadow 87 | 88 | .reddit-emoji 89 | border-radius: 50% -------------------------------------------------------------------------------- /src/modules/mindmap/utils/dimensions.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Return the dimensions (width & height) that some HTML 3 | * with a given style would take in the page. 4 | */ 5 | export const getDimensions = (html, style, classname) => { 6 | const el = document.createElement("span"); 7 | const dimensions = {}; 8 | 9 | // Set display: inline-block so that the size of el 10 | // will depend on the size of its children. 11 | el.style.display = "inline-block"; 12 | 13 | // Hide the element (it will be added to the page for a short time). 14 | el.style.visibility = "hidden"; 15 | 16 | el.className = classname; 17 | el.innerHTML = html; 18 | 19 | // Apply CSS rules. 20 | Object.keys(style).forEach((rule) => { 21 | el.style[rule] = style[rule]; 22 | }); 23 | document.body.append(el); 24 | 25 | dimensions.width = el.offsetWidth; 26 | dimensions.height = el.offsetHeight; 27 | 28 | el.remove(); 29 | return dimensions; 30 | }; 31 | 32 | /* 33 | * Return the dimensions of an SVG viewport calculated from 34 | * some given nodes. 35 | */ 36 | export const getViewBox = (nodes) => { 37 | const Xs = []; 38 | const Ys = []; 39 | 40 | nodes.forEach((node) => { 41 | const x = node.x || node.fx; 42 | const y = node.y || node.fy; 43 | 44 | if (x) { 45 | Xs.push(x); 46 | } 47 | 48 | if (y) { 49 | Ys.push(y); 50 | } 51 | }); 52 | 53 | if (Xs.length === 0 || Ys.length === 0) { 54 | return "0 0 0 0"; 55 | } 56 | 57 | // Find the smallest coordinates... 58 | const min = [Math.min(...Xs) - 150, Math.min(...Ys) - 150]; 59 | 60 | // ...and the biggest ones. 61 | const max = [Math.max(...Xs) - min[0] + 150, Math.max(...Ys) - min[1] + 150]; 62 | 63 | return `${min.join(" ")} ${max.join(" ")}`; 64 | }; 65 | -------------------------------------------------------------------------------- /src/modules/mindmap/utils/nodeToHTML.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Return the HTML representation of a node. 3 | * The node is an object that has text, url, and category attributes 4 | * all of them optional. 5 | */ 6 | export default (node) => { 7 | return ` 8 |
9 |
10 |
11 |
12 |
13 |
14 | 21 |
`; 22 | }; 23 | -------------------------------------------------------------------------------- /src/modules/mindmap/utils/subnodesToHTML.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Return the HTML representation of a node. 3 | * The node is an object that has text, url, and category attributes 4 | * all of them optional. 5 | */ 6 | const subnodesToHTML = (subnodes = [], fcolor) => { 7 | let color = fcolor || ""; 8 | 9 | if (!fcolor && subnodes.length > 0 && subnodes[0].color) { 10 | color = `style="border-left-color: ${subnodes[0].color}"`; 11 | } 12 | 13 | return subnodes 14 | .map((subnode) => { 15 | let href = `href="${subnode.url}"`; 16 | 17 | if (!subnode.url) { 18 | href = ""; 19 | } 20 | 21 | return `
22 | ${subnode.text || ""} 23 |
${subnodesToHTML(subnode.nodes, color)}
24 |
`; 25 | }) 26 | .join(""); 27 | }; 28 | 29 | export default subnodesToHTML; 30 | -------------------------------------------------------------------------------- /src/plugins/vuetify.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import Vuetify from "vuetify/lib/framework"; 3 | import colors from "vuetify/lib/util/colors"; 4 | 5 | Vue.use(Vuetify); 6 | 7 | const getSavedThemeMod = () => 8 | localStorage.getItem("isDarkMode") === "true" ? true : false; 9 | 10 | export default new Vuetify({ 11 | theme: { 12 | dark: getSavedThemeMod(), 13 | themes: { 14 | dark: { 15 | default: "#6B7280", 16 | primary: "#0C2FF3", 17 | secondary: "#FFFFFF", 18 | third: "#F2F4F7", 19 | black: "#000000", 20 | white: "#FFFFFF", 21 | danger: "#F4284E", 22 | accent: colors.amber.accent2, 23 | warning: colors.orange, 24 | background: "#1F2937", 25 | // accenttext: colors.amber.lighten4, 26 | // darkerblue: "#1d262b", 27 | // componentmain: colors.blueGrey.darken4, 28 | }, 29 | light: { 30 | default: "#6B7280", 31 | primary: "#0C2FF3", 32 | secondary: "#111827", 33 | third: "#F2F4F7", 34 | black: "#000000", 35 | white: "#FFFFFF", 36 | danger: "#F4284E", 37 | accent: colors.indigo.accent2, 38 | warning: colors.red.accent3, 39 | background: "#FFFFFF", 40 | // accenttext: colors.indigo.darken2, 41 | // componentmain: colors.blueGrey.lighten5, 42 | }, 43 | }, 44 | }, 45 | }); 46 | -------------------------------------------------------------------------------- /src/preload.js: -------------------------------------------------------------------------------- 1 | const { contextBridge, ipcRenderer, webFrame } = require("electron"); 2 | window.ipcRenderer = ipcRenderer; 3 | 4 | contextBridge.exposeInMainWorld("ipc", { 5 | invoke: async (channel, data) => { 6 | return await ipcRenderer.invoke(channel, data); 7 | }, 8 | on: (channel, func) => { 9 | // Remove any old listeners 10 | ipcRenderer.removeAllListeners(channel); 11 | // Strip event as it includes `sender` and is a security risk 12 | ipcRenderer.on(channel, (event, ...args) => func(...args)); 13 | }, 14 | clearCache: () => { 15 | webFrame.clearCache(); 16 | }, 17 | eventNames: () => { 18 | return ipcRenderer.eventNames(); 19 | }, 20 | }); 21 | -------------------------------------------------------------------------------- /src/services/storageInterface.js: -------------------------------------------------------------------------------- 1 | export default class StorageInterface { 2 | // eslint-disable-next-line 3 | async getState(executionId) { 4 | throw new Error("Method 'getState()' must be implemented."); 5 | } 6 | 7 | // eslint-disable-next-line 8 | async updateState(state) { 9 | throw new Error("Method 'updateState()' must be implemented."); 10 | } 11 | 12 | async getConfig() { 13 | throw new Error("Method 'getConfig()' must be implemented."); 14 | } 15 | 16 | async getCredentials() { 17 | throw new Error("Method 'getCredentials()' must be implemented."); 18 | } 19 | 20 | // eslint-disable-next-line 21 | async updateCredentials(credentials) { 22 | throw new Error("Method 'updateCredentials()' must be implemented."); 23 | } 24 | 25 | async getItems() { 26 | throw new Error("Method 'fetchItems()' must be implemented."); 27 | } 28 | 29 | // eslint-disable-next-line 30 | async getItemById(id) { 31 | throw new Error("Method 'getItemById(id)' must be implemented."); 32 | } 33 | 34 | // eslint-disable-next-line 35 | async updateItems(state) { 36 | throw new Error("Method 'updateItems(state)' must be implemented."); 37 | } 38 | 39 | async getNotes() { 40 | throw new Error("Method 'getNotes()' must be implemented."); 41 | } 42 | 43 | // eslint-disable-next-line 44 | async updateNotes(notes) { 45 | throw new Error("Method 'updateNotes()' must be implemented."); 46 | } 47 | 48 | // eslint-disable-next-line 49 | async createNewSession(state) { 50 | throw new Error("Method 'createNewSession(state)' must be implemented."); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/store/modules/auth.js: -------------------------------------------------------------------------------- 1 | export const auth = { 2 | namespaced: true, 3 | state: () => ({ 4 | isAuthenticated: false, 5 | credentials: {}, 6 | }), 7 | mutations: { 8 | setIsAuthenticated(state, payload) { 9 | state.isAuthenticated = payload; 10 | }, 11 | setCredentials(state, payload) { 12 | state.credentials = payload; 13 | }, 14 | }, 15 | actions: {}, 16 | getters: { 17 | credentials: (state) => state.credentials, 18 | isAuthenticated: (state) => state.isAuthenticated, 19 | loggedInServices: (state) => { 20 | const services = {}; 21 | for (const credentialType of Object.keys(state.credentials)) { 22 | services[credentialType] = state.credentials[credentialType].length > 0; 23 | } 24 | return services; 25 | }, 26 | }, 27 | }; 28 | -------------------------------------------------------------------------------- /src/store/store-config.js: -------------------------------------------------------------------------------- 1 | // Used for tests - TODO clean up 2 | import { SESSION_STATUSES } from "../modules/constants"; 3 | 4 | export default { 5 | state: { 6 | title: "", 7 | charter: "", 8 | preconditions: "", 9 | duration: 0, 10 | status: SESSION_STATUSES.PENDING, 11 | timer: 0, 12 | started: "", 13 | ended: "", 14 | quickTest: false, 15 | }, 16 | getters: {}, 17 | mutations: { 18 | setTitle(state, payload) { 19 | state.title = payload; 20 | }, 21 | setCharter(state, payload) { 22 | state.charter = payload; 23 | }, 24 | setPrecondition(state, payload) { 25 | state.preconditions = payload; 26 | }, 27 | setDuration(state, payload) { 28 | state.duration = payload; 29 | }, 30 | setStarted(state, payload) { 31 | state.started = payload; 32 | }, 33 | setEnded(state, payload) { 34 | state.ended = payload; 35 | }, 36 | setQuickTest(state, payload) { 37 | state.quickTest = payload; 38 | }, 39 | updateSession(state, payload) { 40 | state.status = payload.status; 41 | state.timer = payload.timer; 42 | state.duration = payload.duration; 43 | }, 44 | resetState(state) { 45 | state.title = ""; 46 | state.charter = ""; 47 | state.preconditions = ""; 48 | state.status = SESSION_STATUSES.PENDING; 49 | state.timer = 0; 50 | state.duration = 0; 51 | state.started = ""; 52 | state.ended = ""; 53 | state.quickTest = false; 54 | }, 55 | }, 56 | actions: {}, 57 | modules: {}, 58 | }; 59 | -------------------------------------------------------------------------------- /src/views/AuthenticationView.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 68 | 74 | -------------------------------------------------------------------------------- /src/views/__tests__/AuthenticationView.spec.js: -------------------------------------------------------------------------------- 1 | import Vuetify from "vuetify"; 2 | import AuthenticationView from "../AuthenticationView.vue"; 3 | 4 | import { mount } from "@vue/test-utils"; 5 | 6 | const vuetify = new Vuetify(); 7 | 8 | describe("AutheticationView.vue", () => { 9 | test("render a view", () => { 10 | const wrapper = mount(AuthenticationView, { 11 | mocks: { 12 | $t: () => {}, 13 | $tc: () => {}, 14 | }, 15 | vuetify, 16 | }); 17 | 18 | expect(wrapper.find(".authentication-wrapper").exists()).toBe(true); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /src/views/__tests__/HomeView.spec.js: -------------------------------------------------------------------------------- 1 | import HomeView from "../HomeView"; 2 | import LogoWrapper from "../../components/LogoWrapper"; 3 | 4 | import { mount } from "@vue/test-utils"; 5 | 6 | describe("HomeView.vue", () => { 7 | test('displays "New session" button', () => { 8 | const wrapper = mount(HomeView, { 9 | mocks: { 10 | $t: () => {}, 11 | $tc: () => {}, 12 | }, 13 | stubs: ["router-link"], 14 | }); 15 | 16 | expect(wrapper.findComponent(LogoWrapper).exists()).toBe(true); 17 | 18 | expect( 19 | wrapper.find(".new-section .text-capitalize:nth-child(1)") 20 | ).toBeTruthy(); 21 | // expect(wrapper.text()).toContain("New session"); 22 | 23 | expect( 24 | wrapper.find(".open-section .text-capitalize:nth-child(1)") 25 | ).toBeTruthy(); 26 | // expect(wrapper.text()).toContain("Open Saved Session"); 27 | }); 28 | 29 | test('trigger the click envent of "New Session" button', () => { 30 | const wrapper = mount(HomeView, { 31 | mocks: { 32 | $t: () => {}, 33 | $tc: () => {}, 34 | }, 35 | stubs: ["router-link"], 36 | }); 37 | 38 | const button = wrapper.find(".new-section .text-capitalize:nth-child(1)"); 39 | const event = jest.fn(); 40 | 41 | button.vm.$on("click", event); 42 | button.trigger("click"); 43 | 44 | expect(event).toHaveBeenCalled(); 45 | }); 46 | 47 | test('trigger the click event of "Open Saved Session"', () => { 48 | const wrapper = mount(HomeView, { 49 | mocks: { 50 | $t: () => {}, 51 | $tc: () => {}, 52 | }, 53 | stubs: ["router-link"], 54 | }); 55 | 56 | const button = wrapper.find(".open-section .text-capitalize:nth-child(1)"); 57 | const event = jest.fn(); 58 | 59 | button.vm.$on("click", event); 60 | button.trigger("click"); 61 | 62 | expect(event).toHaveBeenCalled(); 63 | }); 64 | }); 65 | -------------------------------------------------------------------------------- /src/views/__tests__/LowProfileView.spec.js: -------------------------------------------------------------------------------- 1 | import Vuetify from "vuetify"; 2 | import LowProfileView from "../LowProfileView.vue"; 3 | import ControlPanel from "../../components/ControlPanel.vue"; 4 | 5 | import { shallowMount } from "@vue/test-utils"; 6 | 7 | const vuetify = new Vuetify(); 8 | 9 | describe("LowProfileView.vue", () => { 10 | test("render a view", () => { 11 | const wrapper = shallowMount(LowProfileView, { 12 | mocks: { 13 | $t: () => {}, 14 | $tc: () => {}, 15 | }, 16 | data() { 17 | return { 18 | overlay: true, 19 | }; 20 | }, 21 | vuetify, 22 | }); 23 | 24 | expect(wrapper.find(".header").exists()).toBe(true); 25 | expect(wrapper.find(".header .maximize-btn").exists()).toBe(true); 26 | expect(wrapper.find(".body").exists()).toBe(true); 27 | expect(wrapper.find(".body .overlay").exists()).toBe(true); 28 | expect(wrapper.findComponent(ControlPanel).exists()).toBe(true); 29 | }); 30 | 31 | test('trigger the click event of "Maximize" button', () => { 32 | const wrapper = shallowMount(LowProfileView, { 33 | mocks: { 34 | $t: () => {}, 35 | $tc: () => {}, 36 | }, 37 | data() { 38 | return { 39 | overlay: false, 40 | }; 41 | }, 42 | vuetify, 43 | }); 44 | 45 | const event = jest.fn(); 46 | const button = wrapper.find(".header .maximize-btn"); 47 | 48 | button.vm.$on("click", event); 49 | // button.trigger("click"); 50 | 51 | // expect(event).toHaveBeenCalled(); 52 | }); 53 | }); 54 | -------------------------------------------------------------------------------- /src/views/__tests__/PrintView.spec.js: -------------------------------------------------------------------------------- 1 | import Vuetify from "vuetify"; 2 | import Vuex from "vuex"; 3 | 4 | import PrintView from "../PrintView"; 5 | import storeConfig from "@/store/store-config"; 6 | 7 | import { mount, createLocalVue } from "@vue/test-utils"; 8 | import { cloneDeep } from "lodash"; 9 | 10 | const vuetify = new Vuetify(); 11 | const items = []; 12 | const selected = []; 13 | const localVue = createLocalVue(); 14 | 15 | localVue.use(Vuex); 16 | 17 | // const formatTime = (timer) => { 18 | // const seconds = ("0" + (timer % 60)).slice(-2); 19 | // const minutes = ("0" + (parseInt(timer / 60, 10) % 60)).slice(-2); 20 | // const hours = ("0" + (parseInt(timer / 3600, 10) % 24)).slice(-2); 21 | 22 | // return hours + ":" + minutes + ":" + seconds; 23 | // }; 24 | 25 | describe("PrintView.vue", () => { 26 | let store; 27 | 28 | beforeEach(() => { 29 | store = new Vuex.Store(cloneDeep(storeConfig)); 30 | }); 31 | 32 | test("render view", () => { 33 | const wrapper = mount(PrintView, { 34 | mocks: { 35 | $t: () => {}, 36 | $tc: () => {}, 37 | }, 38 | data() { 39 | return { 40 | items, 41 | titie: "", 42 | charter: "", 43 | precondition: "", 44 | window: "", 45 | screenWidth: "", 46 | screenHeight: "", 47 | selected, 48 | timer: "10287209", 49 | }; 50 | }, 51 | store: { 52 | ...store, 53 | state: { 54 | ...store.state, 55 | }, 56 | }, 57 | localVue, 58 | vuetify, 59 | }); 60 | 61 | expect(wrapper.find(".header").exists()).toBe(true); 62 | expect(wrapper.find(".header img").exists()).toBe(true); 63 | expect(wrapper.find(".content .timeline").exists()).toBe(true); 64 | expect(wrapper.find(".content .detail").exists()).toBe(true); 65 | 66 | expect(wrapper.find(".duration-text span").text()).toContain(""); 67 | expect(wrapper.find(".detail .title").exists()).toBe(true); 68 | expect(wrapper.find(".detail .charter").exists()).toBe(true); 69 | expect(wrapper.find(".detail .pre-condition").exists()).toBe(true); 70 | expect(wrapper.find(".detail .session-time").exists()).toBe(true); 71 | expect(wrapper.find(".detail .session-elapsed-time").exists()).toBe(true); 72 | expect(wrapper.find(".detail .environment").exists()).toBe(true); 73 | }); 74 | }); 75 | -------------------------------------------------------------------------------- /src/views/__tests__/SettingView.spec.js: -------------------------------------------------------------------------------- 1 | import Vuetify from "vuetify"; 2 | import VueRouter from "vue-router"; 3 | 4 | import SettingView from "../SettingView.vue"; 5 | 6 | import { mount, createLocalVue } from "@vue/test-utils"; 7 | 8 | const vuetify = new Vuetify(); 9 | 10 | const localVue = createLocalVue(); 11 | localVue.use(VueRouter); 12 | 13 | const router = new VueRouter(); 14 | 15 | describe("SettingView.vue", () => { 16 | test("loadin a view", () => { 17 | const wrapper = mount(SettingView, { 18 | mocks: { 19 | $t: () => {}, 20 | $tc: () => {}, 21 | }, 22 | data() { 23 | return { 24 | activeTab: "/settings", 25 | tabs: [{ id: 1, name: "General", route: `/settings` }], 26 | config: {}, 27 | }; 28 | }, 29 | localVue, 30 | router, 31 | vuetify, 32 | }); 33 | 34 | expect(wrapper.find(".settings-menu").exists()).toBe(true); 35 | expect(wrapper.findAll(".v-tab").length).toBe(1); 36 | expect(wrapper.findAll(".v-tab-item").length).toBe(0); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /tests/mocks.js: -------------------------------------------------------------------------------- 1 | Object.defineProperty(window, "matchMedia", { 2 | writable: true, 3 | value: jest.fn().mockImplementation((query) => ({ 4 | matches: false, 5 | media: query, 6 | onchange: null, 7 | addListener: jest.fn(), // Deprecated 8 | removeListener: jest.fn(), // Deprecated 9 | addEventListener: jest.fn(), 10 | removeEventListener: jest.fn(), 11 | dispatchEvent: jest.fn(), 12 | })), 13 | }); 14 | -------------------------------------------------------------------------------- /tests/setup.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import Vuetify from "vuetify"; 3 | import { config } from "@vue/test-utils"; 4 | 5 | Vue.config.productionTip = false; 6 | Vue.use(Vuetify); 7 | 8 | config.mocks.$store = { dispatch: jest.fn(), getters: {} }; 9 | 10 | // Required for Vuetify 11 | const app = document.createElement("div"); 12 | app.setAttribute("data-app", "true"); 13 | document.body.appendChild(app); 14 | 15 | // JSDOM does not implement HTMLMediaElement functions 16 | window.HTMLMediaElement.prototype.load = () => {}; 17 | window.HTMLMediaElement.prototype.play = () => {}; 18 | window.HTMLMediaElement.prototype.pause = () => {}; 19 | window.HTMLMediaElement.prototype.addTextTrack = () => {}; 20 | 21 | global.console = { 22 | ...console, 23 | log: jest.fn(), 24 | debug: jest.fn(), 25 | error: jest.fn(), 26 | }; 27 | -------------------------------------------------------------------------------- /tests/styleMock.js: -------------------------------------------------------------------------------- 1 | module.exports = {}; 2 | --------------------------------------------------------------------------------