From d01fc0c468f127a7f51cc47cd589b9e9ae25a75a Mon Sep 17 00:00:00 2001 From: Alex Grozav Date: Fri, 28 Jul 2023 10:51:07 +0300 Subject: [PATCH] feat(editor): Migrate Design System and Editor UI to Vue 3 (#6476) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: remove vue-fragment (no-changelog) * feat: partial design-system migration * feat: migrate info-accordion and info-tip components * feat: migrate several components to vue 3 * feat: migrated several components * feat: migrate several components * feat: migrate several components * feat: migrate several components * feat: re-exported all design system components * fix: fix design for popper components * fix: editor kind of working, lots of issues to fix * fix: fix several vue 3 migration issues * fix: replace @change with @update:modelValue in several places * fix: fix translation linking * fix: fix inline-edit input * fix: fix ndv and dialog design * fix: update parameter input event bindings * fix: rename deprecated lifecycle methods * fix: fix json view mapping * build: update lock file * fix(editor): revisit last conflict with master and fix issues * fix(editor): revisit last conflict with master and fix issues * fix: fix expression editor bug causing code mirror to no longer be reactive * fix: fix resource locator bug * fix: fix vue-agile integration * fix: remove global import for vue-agile * fix: replace element-plus buttons with n8n-buttons everywhere * fix(editor): Fix various element-plus styles (#6571) * fix(editor): Fix various element-plus styles Signed-off-by: Oleg Ivaniv * Remove debugging code Signed-off-by: Oleg Ivaniv * Address PR comments Signed-off-by: Oleg Ivaniv --------- Signed-off-by: Oleg Ivaniv * fix(editor): Fix loading in production mode [Vue 3] (#6578) Signed-off-by: Oleg Ivaniv * fix(editor): First round of e2e tests fixes with Vue 3 (#6579) * fix(editor): Fix broken smoke and workflow list e2e tests * ✔️ Fix failing canvas action tests. Updating some selectors used in credentials and workflow tests * feat: add vue 3 eslint rules and fix issues * fix: fix tags-dropdown * fix: fix white-space issues caused by i18n-t * fix: rename non-generic click events * fix: fix search in resources list layout * fix: fix datatable paginator * fix: fix popper select caret and dropdown size * fix: add width to action-dropdown * fix: fix workflow settings icon not being hidden * fix: refactor newly added code * fix: fix merge issue * fix: fix ndv credentials watcher * fix: fix workflow saving and grabber notch * fix: fix nodes list panel transition * fix: fix node title visibility * fix: fix data unpinning * fix: fix value access * fix: show input panel only if trigger panel enabled or not trigger node * fix: fix tags dropdown and executions status spcing * fix(editor): Prevent execution list to load back when leaving the route (#6697) fix(editor): prevent execution list to load back when leaving the route * fix: fix drawer visibility * fix: fix expression toggle padding * fix: fix expressions editor styling * chore: prepare for testing * fix: fix styling for el-button without patching * test: fix unit tests in design-system * test: fix most unit tests * fix: remove import cycle. * fix: fix personalization modal tests * fix further resource mapper test adjustments * fix: fix multiple tests and n8n-route attr duplication * fix: fix source control tets * fix: fixed remaining unit tests * fix: fix workflows and credentials e2e tests * fix: fix localizeNodeNames * fix: update ndv e2e tests * fix: fix popper left placement arrow * fix: fix 5-ndv e2e tests * fix: fix 6-code-node e2e tests * fix(editor): Drop click outside directive from NodeCreator (#6716) * fix(editor): Drop click outside directive from NodeCreator * fix(editor): make sure mouseup outside is unbound at least before the component is unmounted * fix: fix 10-settings-log-streaming e2e tests * fix: fix node redrawing * fix: fix tooltip buttons styling * fix: fix varous e2e suites * fix: fix 15-scheduler-node e2e suite * fix: fix route watcher * fix: fixed param name update and credential edit * feat: update event names * refactor: Remove deprecated `$data` (#6576) Co-authored-by: Alex Grozav * fix: fix 17-sharing e2e suite * fix: fix tags dropdown * fix: fix tags manager * fix(editor): move :deep selectors to a separate scoped style block * fix: fix sticky component and inline text edit * fix: update e2e tests * fix: remove button override references * fix(editor): Adjust spacing in templates for Vue 3 (#6744) * fix(editor): Adjust spacing in templates * fix: Undo unneeded change * fix: Undo unneeded change * fix(editor): Adjust NDV height for Vue 3 (#6742) fix(editor): Adjust NDV height * fix(editor): Restore collapsed sidebar items for Vue 3 (#6743) fix(editor): Restore collapsed sidebar items * fix: fix linting issues * fix: fix design-system deps * fix: post-merge fixes * fix: update tests * fix: increase timeout for executionslist tets * chore: fix linting issue * fix: fix 14-mapping e2e tests in ci * fix: re-enable tests * fix: fix workflow duplication e2e tests after tags update * fix(editor): Change component prop to be typed * fix: fix tags dropdown in duplicate wf modal * fix: fix focus behaviour in tags selector * fix: fix tag creation * fix: fix log streaming e2e race condition * fix(editor): Fix Vue 3 linting issues (#6748) * fix(editor): Fix Vue 3 linting issues Signed-off-by: Oleg Ivaniv * fix MainSidebar linter issues * revert pnpm lock * update pnpm lock file --------- Signed-off-by: Oleg Ivaniv Co-authored-by: Alex Grozav * fix(editor): Some css fixes for vue3 branch (#6749) * ✨ Fixing filter button height * ✨ Update input modal button position * ✨ Updating tags styling * ✨ Fix event logging settings spacing * 👕 Fixing lint errors * fix: fix linting issues * Revert to `// eslint-disable-next-line @typescript-eslint/no-misused-promises` disabling of mixins init Signed-off-by: Oleg Ivaniv * fix: fix css issue * fix(editor): Lint fix * fix(editor): Fix settings initialisation (#6750) Signed-off-by: Oleg Ivaniv * fix: fix initial settings loading * fix: replace realClick with click force * fix: fix randomly failing mapping e2e tests * fix(editor): Fix menu item event handling * fix: fix resource filters dropdown events (#6752) * fix: fix resource filters dropdown events * fix: remove teleported:false * fix: fix event selection event naming (#6753) * fix: removed console.log (#6754) * fix: rever await nextTick changes * fix: redo linting changes * fix(editor): Redraw node connections if adding more than one node to canvas (#6755) * fix(editor): Redraw node connections if adding more than one node to canvas Signed-off-by: Oleg Ivaniv * Update position before connection two nodes * Lint fix --------- Signed-off-by: Oleg Ivaniv Co-authored-by: Alex Grozav * fix(editor): Fix `ResourceMapper` unit tests (#6758) * ✔️ Fix matching columns test * ✔️ Fix multiple matching columns test * ✔️ Removing `skip` from the last test * fix: Allow pasting a big workflow (#6760) * fix: pasting a big workflow * chore: update comment * refactor: move try/catch to function * refactor: move try/catch to function * fix(editor): Fix modal layer width * fix: fix position changes * fix: undo it.only * fix: make undo/redo multiple steps more verbose * fix: Fix value survey styles (#6764) * fix: fix value survey styles * fix: lint * Revert "fix: lint" 72869c431f1448861df021be041b61c62f1e3118 * fix: lint * fix(editor): Fix collapsed sub menu * fix: Fix drawer animation (#6767) fix: drawer animation * fix(editor): Fix source control buttons (#6769) * fix(editor): Fix App loading & auth (#6768) * fix(editor): Fix App loading & auth Signed-off-by: Oleg Ivaniv * Await promises Signed-off-by: Oleg Ivaniv * Fix eslint error Signed-off-by: Oleg Ivaniv --------- Signed-off-by: Oleg Ivaniv --------- Signed-off-by: Oleg Ivaniv Co-authored-by: Csaba Tuncsik Co-authored-by: OlegIvaniv Co-authored-by: Milorad FIlipović Co-authored-by: Iván Ovejero Co-authored-by: Mutasem Aldmour <4711238+mutdmour@users.noreply.github.com> --- cypress/e2e/10-settings-log-streaming.cy.ts | 51 +- cypress/e2e/10-undo-redo.cy.ts | 29 +- cypress/e2e/12-canvas-actions.cy.ts | 1 - cypress/e2e/12-canvas.cy.ts | 2 +- .../14-data-transformation-expressions.cy.ts | 16 +- cypress/e2e/14-mapping.cy.ts | 15 +- cypress/e2e/15-scheduler-node.cy.ts | 7 +- cypress/e2e/16-webhook-node.cy.ts | 52 +- cypress/e2e/17-sharing.cy.ts | 16 +- cypress/e2e/17-workflow-tags.cy.ts | 15 +- cypress/e2e/18-user-management.cy.ts | 12 +- cypress/e2e/2-credentials.cy.ts | 69 +- cypress/e2e/21-community-nodes.cy.ts | 26 +- cypress/e2e/24-ndv-paired-item.cy.ts | 184 +- cypress/e2e/25-stickies.cy.ts | 109 +- cypress/e2e/26-resource-locator.cy.ts | 3 +- cypress/e2e/4-node-creator.cy.ts | 121 +- cypress/e2e/5-ndv.cy.ts | 12 +- cypress/e2e/7-workflow-actions.cy.ts | 92 +- cypress/pages/credentials.ts | 6 +- cypress/pages/modals/credentials-modal.ts | 4 +- cypress/pages/modals/message-box.ts | 8 +- cypress/pages/ndv.ts | 10 +- cypress/pages/settings-log-streaming.ts | 5 +- cypress/pages/settings-personal.ts | 7 +- cypress/pages/settings-users.ts | 16 +- cypress/pages/variables.ts | 2 +- cypress/pages/workflow.ts | 50 +- cypress/pages/workflows.ts | 2 +- cypress/support/commands.ts | 4 +- cypress/support/e2e.ts | 4 + cypress/utils/index.ts | 1 + cypress/utils/modal.ts | 3 + cypress/utils/popper.ts | 11 + package.json | 3 - packages/@n8n_io/eslint-config/frontend.js | 8 +- .../.storybook/font-awesome-icons.js | 8 - packages/design-system/.storybook/main.js | 83 +- packages/design-system/.storybook/preview.js | 27 +- .../design-system/.storybook/storybook.scss | 15 +- packages/design-system/package.json | 43 +- .../assets/images/storybook-logo-dark.png | Bin 0 -> 8215 bytes .../assets/images/storybook-logo-light.png | Bin 0 -> 3812 bytes packages/design-system/src/__tests__/setup.ts | 4 + .../N8nActionBox/ActionBox.stories.ts | 7 +- .../src/components/N8nActionBox/ActionBox.vue | 2 +- .../N8nActionBox/__tests__/ActionBox.spec.ts | 4 +- .../__snapshots__/ActionBox.spec.ts.snap | 12 +- .../ActionDropdown.stories.ts | 9 +- .../N8nActionDropdown/ActionDropdown.vue | 21 +- .../__tests__/ActionDropdown.spec.ts | 10 +- .../__snapshots__/ActionDropdown.spec.ts.snap | 14 +- .../N8nActionToggle/ActionToggle.stories.ts | 8 +- .../N8nActionToggle/ActionToggle.vue | 9 +- .../src/components/N8nAlert/Alert.stories.ts | 10 +- .../N8nAlert/__tests__/Alert.spec.ts | 25 +- .../components/N8nAvatar/Avatar.stories.ts | 8 +- .../src/components/N8nAvatar/Avatar.vue | 10 +- .../src/components/N8nBadge/Badge.stories.ts | 5 +- .../N8nBadge/__tests__/Badge.spec.ts | 12 +- .../__snapshots__/Badge.spec.ts.snap | 6 +- .../components/N8nBlockUi/BlockUi.stories.ts | 5 +- .../src/components/N8nBlockUi/BlockUi.vue | 2 +- .../src/components/N8nButton/Button.scss | 81 + .../components/N8nButton/Button.stories.ts | 55 +- .../src/components/N8nButton/Button.vue | 242 +- .../N8nButton/__tests__/Button.spec.ts | 70 +- .../__snapshots__/Button.spec.ts.snap | 22 +- .../N8nButton/overrides/ElButton.ts | 3 - .../N8nButton/overrides/ElButton.vue | 51 - .../components/N8nButton/overrides/index.ts | 1 - .../components/N8nCallout/Callout.stories.ts | 11 +- .../N8nCallout/__tests__/Callout.spec.ts | 28 +- .../__snapshots__/Callout.spec.ts.snap | 58 +- .../src/components/N8nCard/Card.stories.ts | 19 +- .../src/components/N8nCard/Card.vue | 4 +- .../__tests__/__snapshots__/Card.spec.ts.snap | 12 +- .../N8nCheckbox/Checkbox.stories.ts | 8 +- .../src/components/N8nCheckbox/Checkbox.vue | 12 +- .../__snapshots__/Checkbox.spec.ts.snap | 82 +- .../N8nColorPicker/ColorPicker.stories.ts | 7 +- .../components/N8nColorPicker/ColorPicker.vue | 22 +- .../__tests__/ColorPicker.spec.ts | 13 +- .../__snapshots__/ColorPicker.spec.ts.snap | 314 +- .../N8nDatatable/Datatable.stories.ts | 5 +- .../src/components/N8nDatatable/Datatable.vue | 8 +- .../N8nDatatable/__tests__/Datatable.spec.ts | 22 +- .../__snapshots__/Datatable.spec.ts.snap | 128 +- .../components/N8nFormBox/FormBox.stories.ts | 7 +- .../src/components/N8nFormBox/FormBox.vue | 6 +- .../N8nFormInput/FormInput.stories.ts | 7 +- .../src/components/N8nFormInput/FormInput.vue | 37 +- .../N8nFormInputs/FormInputs.stories.ts | 7 +- .../components/N8nFormInputs/FormInputs.vue | 24 +- .../components/N8nHeading/Heading.stories.ts | 9 +- .../src/components/N8nHeading/Heading.vue | 2 +- .../src/components/N8nIcon/Icon.stories.ts | 7 +- .../src/components/N8nIcon/Icon.vue | 2 +- .../N8nIconButton/IconButton.stories.ts | 10 +- .../components/N8nIconButton/IconButton.vue | 2 +- .../N8nInfoAccordion/InfoAccordion.stories.ts | 5 +- .../N8nInfoAccordion/InfoAccordion.vue | 10 +- .../components/N8nInfoTip/InfoTip.stories.ts | 5 +- .../N8nInfoTip/__tests__/InfoTip.spec.ts | 8 +- .../__snapshots__/InfoTip.spec.ts.snap | 4 +- .../src/components/N8nInput/Input.stories.ts | 16 +- .../src/components/N8nInput/Input.vue | 30 +- .../N8nInput/__tests__/Input.spec.ts | 6 +- .../__snapshots__/Input.spec.ts.snap | 14 +- .../N8nInputLabel/InputLabel.stories.ts | 10 +- .../components/N8nInputLabel/InputLabel.vue | 2 +- .../N8nInputNumber/InputNumber.stories.ts | 11 +- .../components/N8nInputNumber/InputNumber.vue | 18 +- .../src/components/N8nLink/Link.stories.ts | 7 +- .../src/components/N8nLink/Link.vue | 2 +- .../components/N8nLoading/Loading.stories.ts | 9 +- .../src/components/N8nLoading/Loading.vue | 7 +- .../N8nMarkdown/Markdown.stories.ts | 9 +- .../src/components/N8nMenu/Menu.stories.ts | 11 +- .../src/components/N8nMenu/Menu.vue | 43 +- .../N8nMenuItem/MenuItem.stories.ts | 7 +- .../src/components/N8nMenuItem/MenuItem.vue | 98 +- .../NodeCreatorNode.stories.ts | 14 +- .../N8nNodeCreatorNode/NodeCreatorNode.vue | 1 - .../N8nNodeIcon/NodeIcon.stories.ts | 5 +- .../src/components/N8nNodeIcon/NodeIcon.vue | 2 +- .../components/N8nNotice/Notice.stories.ts | 8 +- .../N8nNotice/__tests__/Notice.spec.ts | 19 +- .../__snapshots__/Notice.spec.ts.snap | 18 +- .../src/components/N8nOption/Option.vue | 17 +- .../N8nPagination/Pagination.stories.ts | 20 +- .../components/N8nPagination/Pagination.vue | 12 +- .../components/N8nPopover/Popover.stories.ts | 13 +- .../src/components/N8nPopover/Popover.vue | 23 +- .../src/components/N8nPulse/Pulse.stories.ts | 2 +- .../N8nRadioButtons/RadioButton.vue | 3 - .../N8nRadioButtons/RadioButtons.stories.ts | 7 +- .../N8nRadioButtons/RadioButtons.vue | 8 +- .../RecycleScroller.stories.ts | 7 +- .../N8nRecycleScroller/RecycleScroller.vue | 4 +- .../__tests__/RecycleScroller.spec.ts | 2 +- .../N8nResizeWrapper/ResizeWrapper.stories.js | 33 +- .../src/components/N8nRoute/Route.vue | 14 +- .../components/N8nSelect/Select.stories.ts | 18 +- .../src/components/N8nSelect/Select.vue | 31 +- .../N8nSelect/__tests__/Select.spec.ts | 24 +- .../__snapshots__/Select.spec.ts.snap | 50 +- .../components/N8nSpinner/Spinner.stories.ts | 9 +- .../components/N8nSticky/Sticky.stories.ts | 7 +- .../src/components/N8nSticky/Sticky.vue | 74 +- .../src/components/N8nTabs/Tabs.stories.ts | 7 +- .../src/components/N8nTabs/Tabs.vue | 11 +- .../src/components/N8nTag/Tag.stories.ts | 5 +- .../src/components/N8nTag/Tag.vue | 2 +- .../src/components/N8nTags/Tags.stories.ts | 5 +- .../src/components/N8nTags/Tags.vue | 2 +- .../src/components/N8nText/Text.stories.ts | 25 +- .../src/components/N8nText/Text.vue | 2 +- .../components/N8nTooltip/Tooltip.stories.ts | 31 +- .../src/components/N8nTooltip/Tooltip.vue | 23 +- .../src/components/N8nTree/Tree.stories.ts | 5 +- .../src/components/N8nTree/Tree.vue | 18 +- .../N8nUserInfo/UserInfo.stories.ts | 5 +- .../N8nUserSelect/UserSelect.stories.ts | 5 +- .../components/N8nUserSelect/UserSelect.vue | 11 +- .../N8nUsersList/UsersList.stories.ts | 5 +- .../ResizeObserver/ResizeObserver.vue | 2 +- .../design-system/src/components/index.ts | 9 +- packages/design-system/src/css/alert.scss | 2 +- packages/design-system/src/css/carousel.scss | 4 +- packages/design-system/src/css/checkbox.scss | 2 +- .../design-system/src/css/color-picker.scss | 9 +- .../design-system/src/css/common/popup.scss | 4 +- .../src/css/common/transition.scss | 20 +- packages/design-system/src/css/dialog.scss | 32 + packages/design-system/src/css/drawer.scss | 348 +- packages/design-system/src/css/dropdown.scss | 9 +- packages/design-system/src/css/icon.scss | 18 + packages/design-system/src/css/index.scss | 1 + packages/design-system/src/css/loading.scss | 2 +- packages/design-system/src/css/menu.scss | 49 +- .../design-system/src/css/message-box.scss | 22 +- packages/design-system/src/css/message.scss | 2 +- .../design-system/src/css/notification.scss | 10 +- .../design-system/src/css/pagination.scss | 248 +- packages/design-system/src/css/popper.scss | 99 +- packages/design-system/src/css/select.scss | 8 +- packages/design-system/src/css/switch.scss | 2 +- packages/design-system/src/css/tabs.scss | 8 +- packages/design-system/src/css/tag.scss | 8 +- packages/design-system/src/css/tooltip.scss | 36 +- .../src/locale/{format.js => format.ts} | 32 +- packages/design-system/src/locale/index.js | 42 - packages/design-system/src/locale/index.ts | 51 + .../src/locale/lang/{en.js => en.ts} | 14 +- packages/design-system/src/plugin.ts | 34 +- .../src/shims-vue2-boring-avatars.d.ts | 1 - .../src/styleguide/ColorCircles.vue | 7 +- .../design-system/src/styleguide/Sizes.vue | 7 +- .../src/styleguide/VariableTable.vue | 7 +- .../src/styleguide/utilities/lists.stories.ts | 2 +- .../styleguide/utilities/spacing.stories.ts | 5 +- packages/design-system/src/types/i18n.ts | 3 + packages/design-system/src/types/index.ts | 1 + packages/design-system/src/types/menu.ts | 4 +- .../design-system/theme/preview/index.html | 9 +- packages/design-system/vite.config.ts | 4 +- packages/editor-ui/package.json | 21 +- packages/editor-ui/src/App.vue | 51 +- packages/editor-ui/src/__tests__/render.ts | 86 + packages/editor-ui/src/__tests__/setup.ts | 21 - packages/editor-ui/src/__tests__/utils.ts | 8 - .../editor-ui/src/components/AboutModal.vue | 4 +- .../src/components/ActivationModal.vue | 4 +- .../editor-ui/src/components/AskAiModal.vue | 2 +- packages/editor-ui/src/components/Banner.vue | 2 +- .../src/components/BreakpointsObserver.vue | 30 +- .../src/components/ChangePasswordModal.vue | 4 +- .../CodeNodeEditor/CodeNodeEditor.vue | 12 +- .../components/CodeNodeEditor/completer.ts | 5 +- .../completions/base.completions.ts | 5 +- .../completions/execution.completions.ts | 5 +- .../completions/itemField.completions.ts | 5 +- .../completions/itemIndex.completions.ts | 5 +- .../completions/jsonField.completions.ts | 5 +- .../completions/luxon.completions.ts | 5 +- .../CodeNodeEditor/completions/package.json | 6 + .../completions/prevNode.completions.ts | 5 +- .../completions/require.completions.ts | 5 +- .../completions/variables.completions.ts | 5 +- .../completions/workflow.completions.ts | 5 +- .../src/components/CodeNodeEditor/linter.ts | 6 +- .../src/components/CodeNodeEditor/types.ts | 5 +- .../src/components/CollectionCard.vue | 7 +- .../src/components/CollectionParameter.vue | 29 +- .../src/components/CollectionWorkflowCard.vue | 2 +- .../src/components/CollectionsCarousel.vue | 23 +- .../CommunityPackageInstallModal.vue | 5 +- .../CommunityPackageManageConfirmModal.vue | 6 +- .../src/components/ContactPromptModal.vue | 2 +- .../editor-ui/src/components/CopyInput.vue | 5 +- .../src/components/CredentialCard.vue | 2 +- .../CredentialEdit/AuthTypeSelector.vue | 4 +- .../CredentialEdit/CredentialConfig.vue | 4 +- .../CredentialEdit/CredentialEdit.vue | 9 +- .../CredentialEdit/CredentialInfo.vue | 4 +- .../CredentialEdit/CredentialInputs.vue | 4 +- .../CredentialEdit/CredentialSharing.ee.vue | 6 +- .../src/components/CredentialsSelect.vue | 4 +- .../src/components/CredentialsSelectModal.vue | 6 +- .../src/components/DeleteUserModal.vue | 22 +- .../editor-ui/src/components/Draggable.vue | 6 - .../src/components/DraggableTarget.vue | 2 +- .../components/DuplicateWorkflowDialog.vue | 11 +- .../src/components/ExecutionFilter.vue | 345 +- .../src/components/ExecutionTime.vue | 2 +- .../src/components/ExecutionsList.vue | 145 +- .../src/components/ExecutionsModal.vue | 2 +- .../src/components/ExecutionsUsage.vue | 4 +- .../ExecutionsView/ExecutionCard.vue | 1 + .../ExecutionsView/ExecutionPreview.vue | 5 +- .../ExecutionsInfoAccordion.vue | 2 +- .../ExecutionsView/ExecutionsList.vue | 20 +- .../ExecutionsView/ExecutionsSidebar.vue | 12 +- .../ExpandableInput/ExpandableInputBase.vue | 25 +- .../ExpandableInput/ExpandableInputEdit.vue | 12 +- .../ExpandableInputPreview.vue | 18 +- .../src/components/ExpressionEdit.vue | 74 +- .../ExpressionEditorModalInput.vue | 9 +- .../ExpressionEditorModalOutput.vue | 2 +- .../components/ExpressionParameterInput.vue | 17 +- .../src/components/FeatureComingSoon.vue | 6 +- .../components/FixedCollectionParameter.vue | 38 +- .../src/components/HoverableNodeIcon.vue | 2 +- .../src/components/HtmlEditor/HtmlEditor.vue | 13 +- .../src/components/ImportCurlModal.vue | 6 +- .../InlineExpressionEditorInput.vue | 14 +- .../InlineExpressionEditorOutput.vue | 27 +- .../src/components/InlineNameEdit.vue | 18 +- .../src/components/InlineTextEdit.vue | 34 +- .../editor-ui/src/components/InputPanel.vue | 12 +- .../src/components/IntersectionObserved.vue | 9 +- .../src/components/IntersectionObserver.vue | 4 +- .../src/components/InviteUsersModal.vue | 4 +- .../src/components/MainHeader/MainHeader.vue | 4 +- .../src/components/MainHeader/TabBar.vue | 2 +- .../components/MainHeader/WorkflowDetails.vue | 180 +- .../editor-ui/src/components/MainSidebar.vue | 51 +- .../components/MainSidebarSourceControl.vue | 24 +- packages/editor-ui/src/components/Modal.vue | 25 +- .../editor-ui/src/components/ModalDrawer.vue | 21 +- .../src/components/MultipleParameter.vue | 53 +- .../src/components/NDVDraggablePanels.vue | 2 +- packages/editor-ui/src/components/Node.vue | 50 +- .../src/components/Node/NodeCreation.vue | 21 +- .../NodeCreator/ItemTypes/CategoryItem.vue | 2 +- .../Node/NodeCreator/ItemTypes/NodeItem.vue | 19 +- .../NodeCreator/ItemTypes/SubcategoryItem.vue | 7 +- .../Node/NodeCreator/Modes/ActionsMode.vue | 105 +- .../Node/NodeCreator/Modes/NodesMode.vue | 16 +- .../Node/NodeCreator/NodeCreator.vue | 31 +- .../Node/NodeCreator/Panel/NodesListPanel.vue | 26 +- .../Node/NodeCreator/Panel/SearchBar.vue | 18 +- .../Renderers/CategorizedItemsRenderer.vue | 2 +- .../NodeCreator/Renderers/ItemsRenderer.vue | 4 +- .../__tests__/CategoryItem.test.ts | 17 +- .../__tests__/ItemsRenderer.test.ts | 44 +- .../__tests__/NodesListPanel.test.ts | 115 +- .../__tests__/useKeyboardNavigation.test.ts | 18 +- .../composables/useKeyboardNavigation.ts | 4 +- .../NodeCreator/composables/useViewStacks.ts | 7 +- .../components/Node/NodeCreator/viewsData.ts | 45 +- .../src/components/NodeCredentials.vue | 8 +- .../src/components/NodeDetailsView.vue | 29 +- .../src/components/NodeExecuteButton.vue | 3 + .../editor-ui/src/components/NodeSettings.vue | 31 +- .../src/components/NodeSettingsTabs.vue | 12 +- .../editor-ui/src/components/NodeTitle.vue | 24 +- .../components/OnboardingCallSignupModal.vue | 4 +- .../editor-ui/src/components/OutputPanel.vue | 20 +- .../editor-ui/src/components/PageAlert.vue | 6 +- .../src/components/ParameterInput.vue | 228 +- .../src/components/ParameterInputExpanded.vue | 70 +- .../src/components/ParameterInputFull.vue | 117 +- .../src/components/ParameterInputList.vue | 51 +- .../src/components/ParameterInputWrapper.vue | 36 +- .../src/components/ParameterOptions.vue | 18 +- .../src/components/PersonalizationModal.vue | 13 +- .../ResourceLocator/ResourceLocator.vue | 86 +- .../ResourceLocatorDropdown.vue | 24 +- .../ResourceMapper/MappingFields.vue | 12 +- .../ResourceMapper/MappingModeSelect.vue | 80 +- .../ResourceMapper/MatchingColumnsSelect.vue | 24 +- .../ResourceMapper/ResourceMapper.vue | 8 +- packages/editor-ui/src/components/RunData.vue | 161 +- .../editor-ui/src/components/RunDataJson.vue | 128 +- .../src/components/RunDataJsonActions.vue | 25 +- .../src/components/RunDataSchema.vue | 28 +- .../editor-ui/src/components/RunDataTable.vue | 98 +- .../editor-ui/src/components/SSOLogin.vue | 14 +- .../editor-ui/src/components/SaveButton.vue | 1 - .../EventDestinationCard.ee.vue | 6 +- .../EventDestinationSettingsModal.ee.vue | 52 +- .../EventSelection.ee.vue | 18 +- .../components/SourceControlPullModal.ee.vue | 6 +- .../components/SourceControlPushModal.ee.vue | 59 +- .../src/components/SqlEditor/SqlEditor.vue | 19 +- packages/editor-ui/src/components/Sticky.vue | 16 +- .../src/components/TagsContainer.vue | 56 +- .../editor-ui/src/components/TagsDropdown.vue | 375 +- .../components/TagsManager/TagsManager.vue | 15 +- .../TagsManager/TagsView/TagsTable.vue | 17 +- .../TagsManager/TagsView/TagsTableHeader.vue | 6 +- .../TagsManager/TagsView/TagsView.vue | 52 +- .../src/components/TemplateDetails.vue | 7 +- .../src/components/TemplateFilters.vue | 8 +- .../editor-ui/src/components/TemplateList.vue | 2 +- .../editor-ui/src/components/TextEdit.vue | 24 +- .../editor-ui/src/components/TriggerPanel.vue | 11 +- .../editor-ui/src/components/UpdatesPanel.vue | 24 +- .../editor-ui/src/components/ValueSurvey.vue | 51 +- .../editor-ui/src/components/VariablesRow.vue | 13 +- .../editor-ui/src/components/VersionCard.vue | 22 +- .../src/components/WorkflowActivator.vue | 13 +- .../editor-ui/src/components/WorkflowCard.vue | 3 +- .../src/components/WorkflowPreview.vue | 2 +- .../src/components/WorkflowSettings.vue | 69 +- .../src/components/WorkflowShareModal.ee.vue | 15 +- .../components/__tests__/BannersStack.test.ts | 94 +- .../components/__tests__/CopyInput.test.ts | 8 +- .../__tests__/ExecutionFilter.test.ts | 110 +- .../__tests__/ExecutionsList.test.ts | 146 +- .../MainSidebarSourceControl.test.ts | 41 +- .../__tests__/PersonalizationModal.spec.ts | 62 +- .../__tests__/ResourceMapper.test.ts | 213 +- .../src/components/__tests__/RunData.test.ts | 227 +- .../components/__tests__/RunDataJson.test.ts | 76 +- .../__tests__/RunDataSchema.test.ts | 110 +- .../components/__tests__/SQLEditor.test.ts | 56 +- .../src/components/__tests__/SSOLogin.test.ts | 30 +- .../components/__tests__/VariablesRow.spec.ts | 47 +- .../PersonalizationModal.spec.ts.snap | 284 -- .../__snapshots__/RunDataJson.test.ts.snap | 1373 +++--- .../__snapshots__/RunDataSchema.test.ts.snap | 408 +- .../__snapshots__/VariablesRow.spec.ts.snap | 78 - .../__tests__/utils/ResourceMapper.utils.ts | 1 + .../forms/ResourceFiltersDropdown.vue | 35 +- .../forms/ResourceOwnershipSelect.ee.vue | 16 +- .../layouts/ResourcesListLayout.vue | 101 +- .../transitions/SlideTransition.vue | 2 +- .../__tests__/useHistoryHelper.test.ts | 2 +- .../src/composables/useCanvasMouseSelect.ts | 1 + .../src/composables/useGlobalLinkActions.ts | 5 +- packages/editor-ui/src/composables/useI18n.ts | 16 +- .../src/composables/useLoadingService.ts | 5 +- .../editor-ui/src/composables/useMessage.ts | 6 +- .../editor-ui/src/composables/useToast.ts | 22 +- packages/editor-ui/src/constants.ts | 3 + .../src/declarations/vue-json-pretty.d.ts | 5 - .../src/event-bus/code-node-editor.ts | 2 +- .../editor-ui/src/event-bus/data-pinning.ts | 2 +- .../editor-ui/src/event-bus/html-editor.ts | 2 +- .../editor-ui/src/event-bus/link-actions.ts | 2 +- packages/editor-ui/src/event-bus/node-view.ts | 2 +- packages/editor-ui/src/main.ts | 31 +- packages/editor-ui/src/mixins/copyPaste.ts | 2 +- .../editor-ui/src/mixins/expressionManager.ts | 11 +- packages/editor-ui/src/mixins/pinData.ts | 6 +- packages/editor-ui/src/mixins/userHelpers.ts | 6 +- .../editor-ui/src/mixins/workflowHelpers.ts | 6 +- packages/editor-ui/src/models/history.ts | 2 +- packages/editor-ui/src/n8n-theme.scss | 29 +- packages/editor-ui/src/plugins/components.ts | 31 +- packages/editor-ui/src/plugins/directives.ts | 13 +- packages/editor-ui/src/plugins/i18n/index.ts | 91 +- .../src/plugins/i18n/locales/en.json | 66 +- packages/editor-ui/src/plugins/icons/index.ts | 4 +- .../editor-ui/src/plugins/telemetry/index.ts | 23 +- .../src/plugins/telemetry/telemetry.types.ts | 10 +- packages/editor-ui/src/router.ts | 50 +- packages/editor-ui/src/shims-vue.d.ts | 25 +- packages/editor-ui/src/shims.d.ts | 4 +- .../src/stores/logStreaming.store.ts | 21 +- .../editor-ui/src/stores/n8nRoot.store.ts | 10 - .../editor-ui/src/stores/nodeTypes.store.ts | 9 + .../src/stores/sourceControl.store.ts | 4 +- packages/editor-ui/src/stores/ui.store.ts | 2 +- .../editor-ui/src/stores/workflows.store.ts | 9 +- .../src/utils/__tests__/userUtils.test.ts | 27 +- packages/editor-ui/src/views/AuthView.vue | 6 +- .../editor-ui/src/views/CanvasAddButton.vue | 3 +- .../src/views/ChangePasswordView.vue | 2 +- .../editor-ui/src/views/CredentialsView.vue | 7 +- packages/editor-ui/src/views/NodeView.vue | 210 +- .../editor-ui/src/views/SamlOnboarding.vue | 4 +- .../editor-ui/src/views/SettingsApiView.vue | 9 +- .../editor-ui/src/views/SettingsAuditLogs.vue | 4 +- .../src/views/SettingsCommunityNodesView.vue | 11 +- .../editor-ui/src/views/SettingsLdapView.vue | 25 +- .../src/views/SettingsLogStreamingView.vue | 39 +- .../src/views/SettingsPersonalView.vue | 31 +- .../src/views/SettingsSourceControl.vue | 20 +- packages/editor-ui/src/views/SettingsSso.vue | 4 +- .../src/views/SettingsUsageAndPlan.vue | 72 +- .../editor-ui/src/views/SettingsUsersView.vue | 2 +- .../src/views/TemplatesSearchView.vue | 5 +- .../editor-ui/src/views/VariablesView.vue | 65 +- .../editor-ui/src/views/WorkflowsView.vue | 9 +- .../src/views/__tests__/AuthView.test.ts | 28 +- .../views/__tests__/SamlOnboarding.test.ts | 25 +- .../views/__tests__/SettingsAuditLogs.test.ts | 51 +- .../__tests__/SettingsPersonalView.test.ts | 77 +- .../__tests__/SettingsSourceControl.test.ts | 60 +- .../src/views/__tests__/SettingsSso.test.ts | 52 +- .../src/views/__tests__/VariablesView.spec.ts | 17 +- packages/editor-ui/src/vue-agile.d.ts | 1 - packages/editor-ui/vite.config.ts | 29 +- patches/element-ui@2.15.12.patch | 262 -- pnpm-lock.yaml | 4079 ++++++----------- 459 files changed, 8820 insertions(+), 9918 deletions(-) create mode 100644 cypress/utils/index.ts create mode 100644 cypress/utils/modal.ts create mode 100644 cypress/utils/popper.ts delete mode 100644 packages/design-system/.storybook/font-awesome-icons.js create mode 100644 packages/design-system/public/assets/images/storybook-logo-dark.png create mode 100644 packages/design-system/public/assets/images/storybook-logo-light.png create mode 100644 packages/design-system/src/components/N8nButton/Button.scss delete mode 100644 packages/design-system/src/components/N8nButton/overrides/ElButton.ts delete mode 100644 packages/design-system/src/components/N8nButton/overrides/ElButton.vue delete mode 100644 packages/design-system/src/components/N8nButton/overrides/index.ts rename packages/design-system/src/locale/{format.js => format.ts} (53%) delete mode 100644 packages/design-system/src/locale/index.js create mode 100644 packages/design-system/src/locale/index.ts rename packages/design-system/src/locale/lang/{en.js => en.ts} (65%) delete mode 100644 packages/design-system/src/shims-vue2-boring-avatars.d.ts create mode 100644 packages/design-system/src/types/i18n.ts create mode 100644 packages/editor-ui/src/__tests__/render.ts create mode 100644 packages/editor-ui/src/components/CodeNodeEditor/completions/package.json delete mode 100644 packages/editor-ui/src/components/__tests__/__snapshots__/PersonalizationModal.spec.ts.snap delete mode 100644 packages/editor-ui/src/components/__tests__/__snapshots__/VariablesRow.spec.ts.snap delete mode 100644 packages/editor-ui/src/declarations/vue-json-pretty.d.ts delete mode 100644 packages/editor-ui/src/vue-agile.d.ts delete mode 100644 patches/element-ui@2.15.12.patch diff --git a/cypress/e2e/10-settings-log-streaming.cy.ts b/cypress/e2e/10-settings-log-streaming.cy.ts index 1261940df2f79..9acec76f420bc 100644 --- a/cypress/e2e/10-settings-log-streaming.cy.ts +++ b/cypress/e2e/10-settings-log-streaming.cy.ts @@ -1,4 +1,6 @@ import { SettingsLogStreamingPage } from '../pages'; +import { getVisibleModalOverlay } from '../utils/modal'; +import { getVisibleDropdown } from '../utils'; const settingsLogStreamingPage = new SettingsLogStreamingPage(); @@ -19,6 +21,7 @@ describe('Log Streaming Settings', () => { }); it('should show the add destination modal', () => { + cy.enableFeature('logStreaming'); cy.visit('/settings/log-streaming'); settingsLogStreamingPage.actions.clickAddFirstDestination(); cy.wait(100); @@ -27,7 +30,7 @@ describe('Log Streaming Settings', () => { settingsLogStreamingPage.getters.getSelectDestinationButton().should('be.visible'); settingsLogStreamingPage.getters.getSelectDestinationButton().should('have.attr', 'disabled'); settingsLogStreamingPage.getters - .getDestinationModalDialog() + .getDestinationModal() .invoke('css', 'width') .then((widthStr) => parseInt((widthStr as unknown as string).replace('px', ''))) .should('be.lessThan', 500); @@ -36,65 +39,67 @@ describe('Log Streaming Settings', () => { settingsLogStreamingPage.getters .getSelectDestinationButton() .should('not.have.attr', 'disabled'); - settingsLogStreamingPage.getters.getDestinationModal().click(1, 1); + getVisibleModalOverlay().click(1, 1); settingsLogStreamingPage.getters.getDestinationModal().should('not.exist'); }); it('should create a destination and delete it', () => { + cy.enableFeature('logStreaming'); cy.visit('/settings/log-streaming'); + cy.wait(1000); // Race condition with getDestinationDataFromBackend() settingsLogStreamingPage.actions.clickAddFirstDestination(); cy.wait(100); settingsLogStreamingPage.getters.getDestinationModal().should('be.visible'); settingsLogStreamingPage.getters.getSelectDestinationType().click(); settingsLogStreamingPage.getters.getSelectDestinationTypeItems().eq(0).click(); settingsLogStreamingPage.getters.getSelectDestinationButton().click(); - settingsLogStreamingPage.getters.getDestinationNameInput().click() + settingsLogStreamingPage.getters.getDestinationNameInput().click(); - settingsLogStreamingPage.getters.getDestinationNameInput().find('input').clear().type('Destination 0'); + settingsLogStreamingPage.getters + .getDestinationNameInput() + .find('input') + .clear() + .type('Destination 0'); settingsLogStreamingPage.getters.getDestinationSaveButton().click(); cy.wait(100); - settingsLogStreamingPage.getters.getDestinationModal().click(1, 1); + getVisibleModalOverlay().click(1, 1); cy.reload(); settingsLogStreamingPage.getters.getDestinationCards().eq(0).click(); settingsLogStreamingPage.getters.getDestinationDeleteButton().should('be.visible').click(); cy.get('.el-message-box').should('be.visible').find('.btn--cancel').click(); settingsLogStreamingPage.getters.getDestinationDeleteButton().click(); cy.get('.el-message-box').should('be.visible').find('.btn--confirm').click(); - cy.reload(); }); it('should create a destination and delete it via card actions', () => { + cy.enableFeature('logStreaming'); cy.visit('/settings/log-streaming'); + cy.wait(1000); // Race condition with getDestinationDataFromBackend() settingsLogStreamingPage.actions.clickAddFirstDestination(); cy.wait(100); settingsLogStreamingPage.getters.getDestinationModal().should('be.visible'); settingsLogStreamingPage.getters.getSelectDestinationType().click(); settingsLogStreamingPage.getters.getSelectDestinationTypeItems().eq(0).click(); settingsLogStreamingPage.getters.getSelectDestinationButton().click(); - settingsLogStreamingPage.getters.getDestinationNameInput().click() - settingsLogStreamingPage.getters.getDestinationNameInput().find('input').clear().type('Destination 1'); + settingsLogStreamingPage.getters.getDestinationNameInput().click(); + settingsLogStreamingPage.getters + .getDestinationNameInput() + .find('input') + .clear() + .type('Destination 1'); settingsLogStreamingPage.getters.getDestinationSaveButton().should('not.have.attr', 'disabled'); settingsLogStreamingPage.getters.getDestinationSaveButton().click(); cy.wait(100); - settingsLogStreamingPage.getters.getDestinationModal().click(1, 1); + getVisibleModalOverlay().click(1, 1); cy.reload(); - settingsLogStreamingPage.getters - .getDestinationCards() - .eq(0) - .find('.el-dropdown-selfdefine') - .click(); - cy.get('.el-dropdown-menu').find('.el-dropdown-menu__item').eq(0).click(); + settingsLogStreamingPage.getters.getDestinationCards().eq(0).find('.el-dropdown').click(); + getVisibleDropdown().find('.el-dropdown-menu__item').eq(0).click(); settingsLogStreamingPage.getters.getDestinationSaveButton().should('not.exist'); - settingsLogStreamingPage.getters.getDestinationModal().click(1, 1); + getVisibleModalOverlay().click(1, 1); - settingsLogStreamingPage.getters - .getDestinationCards() - .eq(0) - .find('.el-dropdown-selfdefine') - .click(); - cy.get('.el-dropdown-menu').find('.el-dropdown-menu__item').eq(1).click(); + settingsLogStreamingPage.getters.getDestinationCards().eq(0).find('.el-dropdown').click(); + getVisibleDropdown().find('.el-dropdown-menu__item').eq(1).click(); cy.get('.el-message-box').should('be.visible').find('.btn--confirm').click(); - cy.reload(); }); }); diff --git a/cypress/e2e/10-undo-redo.cy.ts b/cypress/e2e/10-undo-redo.cy.ts index 059777e3b9113..3a65beedb2bd0 100644 --- a/cypress/e2e/10-undo-redo.cy.ts +++ b/cypress/e2e/10-undo-redo.cy.ts @@ -119,18 +119,15 @@ describe('Undo/Redo', () => { WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME); cy.drag('[data-test-id="canvas-node"].jtk-drag-selected', [50, 150]); WorkflowPage.getters - .canvasNodes() - .last() + .canvasNodeByName('Code') .should('have.attr', 'style', 'left: 740px; top: 320px;'); WorkflowPage.actions.hitUndo(); WorkflowPage.getters - .canvasNodes() - .last() + .canvasNodeByName('Code') .should('have.attr', 'style', 'left: 640px; top: 220px;'); WorkflowPage.actions.hitRedo(); WorkflowPage.getters - .canvasNodes() - .last() + .canvasNodeByName('Code') .should('have.attr', 'style', 'left: 740px; top: 320px;'); }); @@ -138,7 +135,10 @@ describe('Undo/Redo', () => { WorkflowPage.actions.addNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME); WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME); WorkflowPage.getters.nodeConnections().realHover(); - cy.get('.connection-actions .delete').filter(':visible').should('be.visible').click(); + cy.get('.connection-actions .delete') + .filter(':visible') + .should('be.visible') + .click({ force: true }); WorkflowPage.getters.nodeConnections().should('have.length', 0); WorkflowPage.actions.hitUndo(); WorkflowPage.getters.nodeConnections().should('have.length', 1); @@ -256,6 +256,9 @@ describe('Undo/Redo', () => { }); it('should undo/redo multiple steps', () => { + const initialPosition = 'left: 420px; top: 220px;'; + const movedPosition = 'left: 540px; top: 360px;'; + WorkflowPage.actions.addNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME); WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME); WorkflowPage.actions.addNodeToCanvas(SET_NODE_NAME); @@ -266,8 +269,10 @@ describe('Undo/Redo', () => { WorkflowPage.getters.canvasNodes().last().click(); WorkflowPage.actions.hitDisableNodeShortcut(); // Move first one + WorkflowPage.getters.canvasNodes().first().should('have.attr', 'style', initialPosition); WorkflowPage.getters.canvasNodes().first().click(); cy.drag('[data-test-id="canvas-node"].jtk-drag-selected', [50, 150]); + WorkflowPage.getters.canvasNodes().first().should('have.attr', 'style', movedPosition); // Delete the set node WorkflowPage.getters.canvasNodeByName(SET_NODE_NAME).click().click(); cy.get('body').type('{backspace}'); @@ -278,10 +283,7 @@ describe('Undo/Redo', () => { WorkflowPage.getters.nodeConnections().should('have.length', 3); // Second undo: Should move first node to it's original position WorkflowPage.actions.hitUndo(); - WorkflowPage.getters - .canvasNodes() - .first() - .should('have.attr', 'style', 'left: 420px; top: 220px;'); + WorkflowPage.getters.canvasNodes().first().should('have.attr', 'style', initialPosition); // Third undo: Should enable last node WorkflowPage.actions.hitUndo(); WorkflowPage.getters.disabledNodes().should('have.length', 0); @@ -291,10 +293,7 @@ describe('Undo/Redo', () => { WorkflowPage.getters.disabledNodes().should('have.length', 1); // Second redo: Should move the first node WorkflowPage.actions.hitRedo(); - WorkflowPage.getters - .canvasNodes() - .first() - .should('have.attr', 'style', 'left: 540px; top: 360px;'); + WorkflowPage.getters.canvasNodes().first().should('have.attr', 'style', movedPosition); // Third redo: Should delete the Set node WorkflowPage.actions.hitRedo(); WorkflowPage.getters.canvasNodes().should('have.length', 3); diff --git a/cypress/e2e/12-canvas-actions.cy.ts b/cypress/e2e/12-canvas-actions.cy.ts index d336294f48c53..a4eaebc7be5a4 100644 --- a/cypress/e2e/12-canvas-actions.cy.ts +++ b/cypress/e2e/12-canvas-actions.cy.ts @@ -66,7 +66,6 @@ describe('Canvas Actions', () => { WorkflowPage.getters.nodeViewBackground().click({ force: true }); }); - it('should add a connected node using plus endpoint', () => { WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME); cy.get('.plus-endpoint').should('be.visible').click(); diff --git a/cypress/e2e/12-canvas.cy.ts b/cypress/e2e/12-canvas.cy.ts index 625b8b98f8115..e98b9dea9e2bb 100644 --- a/cypress/e2e/12-canvas.cy.ts +++ b/cypress/e2e/12-canvas.cy.ts @@ -107,7 +107,7 @@ describe('Canvas Node Manipulation and Navigation', () => { WorkflowPage.actions.zoomToFit(); cy.get('.plus-draggable-endpoint').filter(':visible').should('not.have.class', 'ep-success'); - cy.get('.jtk-connector.success').should('have.length', 4); + cy.get('.jtk-connector.success').should('have.length', 3); cy.get('.jtk-connector').should('have.length', 4); }); diff --git a/cypress/e2e/14-data-transformation-expressions.cy.ts b/cypress/e2e/14-data-transformation-expressions.cy.ts index 099e79ae7d7b0..fc303c603bc47 100644 --- a/cypress/e2e/14-data-transformation-expressions.cy.ts +++ b/cypress/e2e/14-data-transformation-expressions.cy.ts @@ -7,12 +7,10 @@ describe('Data transformation expressions', () => { beforeEach(() => { wf.actions.visit(); - cy.window().then( - (win) => { - // @ts-ignore - win.preventNodeViewBeforeUnload = true; - }, - ); + cy.window().then((win) => { + // @ts-ignore + win.preventNodeViewBeforeUnload = true; + }); }); it('$json + native string methods', () => { @@ -85,7 +83,7 @@ describe('Data transformation expressions', () => { ndv.getters.inlineExpressionEditorInput().clear().type(input); ndv.actions.execute(); - ndv.getters.outputDataContainer().find('[class*=value_]').should('exist') + ndv.getters.outputDataContainer().find('[class*=value_]').should('exist'); ndv.getters.outputDataContainer().find('[class*=value_]').should('contain', output); }); @@ -100,7 +98,7 @@ describe('Data transformation expressions', () => { ndv.getters.inlineExpressionEditorInput().clear().type(input); ndv.actions.execute(); - ndv.getters.outputDataContainer().find('[class*=value_]').should('exist') + ndv.getters.outputDataContainer().find('[class*=value_]').should('exist'); ndv.getters.outputDataContainer().find('[class*=value_]').should('contain', output); }); }); @@ -111,7 +109,7 @@ describe('Data transformation expressions', () => { const addSet = () => { wf.actions.addNodeToCanvas('Set', true, true); - ndv.getters.parameterInput('keepOnlySet').find('div[role=switch]').click(); // shorten output + ndv.getters.parameterInput('keepOnlySet').find('.el-switch').click(); // shorten output cy.get('input[placeholder="Add Value"]').click(); cy.get('span').contains('String').click(); ndv.getters.nthParam(3).contains('Expression').invoke('show').click(); // Values to Set > String > Value diff --git a/cypress/e2e/14-mapping.cy.ts b/cypress/e2e/14-mapping.cy.ts index 9ccda6b6a4f88..fa9bdd78a22a3 100644 --- a/cypress/e2e/14-mapping.cy.ts +++ b/cypress/e2e/14-mapping.cy.ts @@ -4,6 +4,7 @@ import { SCHEDULE_TRIGGER_NODE_NAME, } from './../constants'; import { WorkflowPage, NDV } from '../pages'; +import { getVisibleSelect } from '../utils'; const workflowPage = new WorkflowPage(); const ndv = new NDV(); @@ -28,11 +29,7 @@ describe('Data mapping', () => { ndv.getters.inputDataContainer().get('table', { timeout: 10000 }).should('exist'); ndv.getters.nodeParameters().find('input[placeholder*="Add Value"]').click(); - ndv.getters - .nodeParameters() - .find('.el-select-dropdown__list li:nth-child(3)') - .should('have.text', 'String') - .click(); + getVisibleSelect().find('li:nth-child(3)').should('have.text', 'String').click(); ndv.getters .parameterInput('name') .should('have.length', 1) @@ -128,7 +125,7 @@ describe('Data mapping', () => { .find('.json-data') .should( 'have.text', - '[{"input":[{"count":0,"with space":"!!","with.dot":"!!","with"quotes":"!!"}]},{"input":[{"count":1}]}]', + '[{"input": [{"count": 0,"with space": "!!","with.dot": "!!","with"quotes": "!!"}]},{"input": [{"count": 1}]}]', ) .find('span') .contains('"count"') @@ -178,6 +175,7 @@ describe('Data mapping', () => { it('maps expressions from previous nodes', () => { cy.createFixtureWorkflow('Test_workflow_3.json', `My test workflow`); + workflowPage.actions.zoomToFit(); workflowPage.actions.openNode('Set1'); ndv.actions.selectInputNode(SCHEDULE_TRIGGER_NODE_NAME); @@ -245,7 +243,8 @@ describe('Data mapping', () => { workflowPage.actions.addNodeToCanvas('Item Lists'); workflowPage.actions.openNode('Item Lists'); - ndv.getters.parameterInput('operation').click().find('li').contains('Sort').click(); + ndv.getters.parameterInput('operation').click(); + getVisibleSelect().find('li').contains('Sort').click(); ndv.getters.nodeParameters().find('button').contains('Add Field To Sort By').click(); @@ -274,6 +273,8 @@ describe('Data mapping', () => { ndv.actions.typeIntoParameterInput('value', 'fun'); ndv.actions.clearParameterInput('value'); // keep focus on param + ndv.actions.dismissMappingTooltip(); + cy.wait(300); ndv.getters.inputDataContainer().should('exist').find('span').contains('count').realMouseDown(); diff --git a/cypress/e2e/15-scheduler-node.cy.ts b/cypress/e2e/15-scheduler-node.cy.ts index d58a5416527c0..0021455619811 100644 --- a/cypress/e2e/15-scheduler-node.cy.ts +++ b/cypress/e2e/15-scheduler-node.cy.ts @@ -1,5 +1,6 @@ import { WorkflowPage, WorkflowsPage, NDV } from '../pages'; import { BACKEND_BASE_URL } from '../constants'; +import { getVisibleSelect } from '../utils'; const workflowsPage = new WorkflowsPage(); const workflowPage = new WorkflowPage(); @@ -24,11 +25,7 @@ describe('Schedule Trigger node', async () => { workflowPage.actions.openNode('Schedule Trigger'); cy.getByTestId('parameter-input-field').click(); - cy.getByTestId('parameter-input-field') - .find('.el-select-dropdown') - .find('.option-headline') - .contains('Seconds') - .click(); + getVisibleSelect().find('.option-headline').contains('Seconds').click(); cy.getByTestId('parameter-input-secondsInterval').clear().type('1'); ndv.getters.backToCanvas().click(); diff --git a/cypress/e2e/16-webhook-node.cy.ts b/cypress/e2e/16-webhook-node.cy.ts index 2ba59a8cfbe17..80a649a9523a9 100644 --- a/cypress/e2e/16-webhook-node.cy.ts +++ b/cypress/e2e/16-webhook-node.cy.ts @@ -2,6 +2,7 @@ import { WorkflowPage, NDV, CredentialsModal } from '../pages'; import { v4 as uuid } from 'uuid'; import { cowBase64 } from '../support/binaryTestFiles'; import { BACKEND_BASE_URL } from '../constants'; +import { getVisibleSelect } from '../utils'; const workflowPage = new WorkflowPage(); const ndv = new NDV(); @@ -34,11 +35,7 @@ const simpleWebhookCall = (options: SimpleWebhookCallOptions) => { workflowPage.actions.openNode('Webhook'); cy.getByTestId('parameter-input-httpMethod').click(); - cy.getByTestId('parameter-input-httpMethod') - .find('.el-select-dropdown') - .find('.option-headline') - .contains(method) - .click(); + getVisibleSelect().find('.option-headline').contains(method).click(); cy.getByTestId('parameter-input-path') .find('.parameter-input') .find('input') @@ -47,11 +44,7 @@ const simpleWebhookCall = (options: SimpleWebhookCallOptions) => { if (authentication) { cy.getByTestId('parameter-input-authentication').click(); - cy.getByTestId('parameter-input-authentication') - .find('.el-select-dropdown') - .find('.option-headline') - .contains(authentication) - .click(); + getVisibleSelect().find('.option-headline').contains(authentication).click(); } if (responseCode) { @@ -64,20 +57,12 @@ const simpleWebhookCall = (options: SimpleWebhookCallOptions) => { if (respondWith) { cy.getByTestId('parameter-input-responseMode').click(); - cy.getByTestId('parameter-input-responseMode') - .find('.el-select-dropdown') - .find('.option-headline') - .contains(respondWith) - .click(); + getVisibleSelect().find('.option-headline').contains(respondWith).click(); } if (responseData) { cy.getByTestId('parameter-input-responseData').click(); - cy.getByTestId('parameter-input-responseData') - .find('.el-select-dropdown') - .find('.option-headline') - .contains(responseData) - .click(); + getVisibleSelect().find('.option-headline').contains(responseData).click(); } if (executeNow) { @@ -136,13 +121,13 @@ describe('Webhook Trigger node', async () => { workflowPage.actions.addNodeToCanvas('Set'); workflowPage.actions.openNode('Set'); cy.get('.add-option').click(); - cy.get('.add-option').find('.el-select-dropdown__item').contains('Number').click(); + getVisibleSelect().find('.el-select-dropdown__item').contains('Number').click(); cy.get('.fixed-collection-parameter') .getByTestId('parameter-input-name') .clear() .type('MyValue'); cy.get('.fixed-collection-parameter').getByTestId('parameter-input-value').clear().type('1234'); - ndv.getters.backToCanvas().click(); + ndv.getters.backToCanvas().click({ force: true }); workflowPage.actions.addNodeToCanvas('Respond to Webhook'); @@ -185,13 +170,18 @@ describe('Webhook Trigger node', async () => { workflowPage.actions.addNodeToCanvas('Set'); workflowPage.actions.openNode('Set'); cy.get('.add-option').click(); - cy.get('.add-option').find('.el-select-dropdown__item').contains('Number').click(); + getVisibleSelect().find('.el-select-dropdown__item').contains('Number').click(); cy.get('.fixed-collection-parameter') .getByTestId('parameter-input-name') + .find('input') .clear() .type('MyValue'); - cy.get('.fixed-collection-parameter').getByTestId('parameter-input-value').clear().type('1234'); - ndv.getters.backToCanvas().click(); + cy.get('.fixed-collection-parameter') + .getByTestId('parameter-input-value') + .find('input') + .clear() + .type('1234'); + ndv.getters.backToCanvas().click({ force: true }); workflowPage.actions.executeWorkflow(); cy.wait(waitForWebhook); @@ -216,7 +206,7 @@ describe('Webhook Trigger node', async () => { workflowPage.actions.addNodeToCanvas('Set'); workflowPage.actions.openNode('Set'); cy.get('.add-option').click(); - cy.get('.add-option').find('.el-select-dropdown__item').contains('String').click(); + getVisibleSelect().find('.el-select-dropdown__item').contains('String').click(); cy.get('.fixed-collection-parameter').getByTestId('parameter-input-name').clear().type('data'); cy.get('.fixed-collection-parameter') .getByTestId('parameter-input-value') @@ -231,11 +221,7 @@ describe('Webhook Trigger node', async () => { workflowPage.actions.openNode('Move Binary Data'); cy.getByTestId('parameter-input-mode').click(); - cy.getByTestId('parameter-input-mode') - .find('.el-select-dropdown') - .find('.option-headline') - .contains('JSON to Binary') - .click(); + getVisibleSelect().find('.option-headline').contains('JSON to Binary').click(); ndv.getters.backToCanvas().click(); workflowPage.actions.executeWorkflow(); @@ -274,7 +260,7 @@ describe('Webhook Trigger node', async () => { }); // add credentials workflowPage.getters.nodeCredentialsSelect().click(); - workflowPage.getters.nodeCredentialsSelect().find('li').last().click(); + getVisibleSelect().find('li').last().click(); credentialsModal.getters.credentialsEditModal().should('be.visible'); credentialsModal.actions.fillCredentialsForm(); @@ -317,7 +303,7 @@ describe('Webhook Trigger node', async () => { }); // add credentials workflowPage.getters.nodeCredentialsSelect().click(); - workflowPage.getters.nodeCredentialsSelect().find('li').last().click(); + getVisibleSelect().find('li').last().click(); credentialsModal.getters.credentialsEditModal().should('be.visible'); credentialsModal.actions.fillCredentialsForm(); diff --git a/cypress/e2e/17-sharing.cy.ts b/cypress/e2e/17-sharing.cy.ts index cf0f4ccd3598b..acd61b05db51f 100644 --- a/cypress/e2e/17-sharing.cy.ts +++ b/cypress/e2e/17-sharing.cy.ts @@ -48,7 +48,7 @@ describe('Sharing', { disableAutoLogin: true }, () => { workflowPage.actions.setWorkflowName('Workflow W1'); workflowPage.actions.addInitialNodeToCanvas('Manual Trigger'); workflowPage.actions.addNodeToCanvas('Notion', true, true); - ndv.getters.credentialInput().should('contain', 'Credential C1'); + ndv.getters.credentialInput().find('input').should('have.value', 'Credential C1'); ndv.actions.close(); workflowPage.actions.openShareModal(); @@ -87,16 +87,12 @@ describe('Sharing', { disableAutoLogin: true }, () => { workflowsPage.getters.workflowCards().should('have.length', 1); workflowsPage.getters.workflowCard('Workflow W1').click(); workflowPage.actions.addNodeToCanvas('Airtable', true, true); - ndv.getters.credentialInput().should('contain', 'Credential C2'); + ndv.getters.credentialInput().find('input').should('have.value', 'Credential C2'); ndv.actions.close(); workflowPage.actions.saveWorkflowOnButtonClick(); workflowPage.actions.openNode('Notion'); - ndv.getters - .credentialInput() - .find('input') - .should('have.value', 'Credential C1') - .should('be.disabled'); + ndv.getters.credentialInput().should('have.value', 'Credential C1').should('be.disabled'); ndv.actions.close(); }); @@ -116,11 +112,7 @@ describe('Sharing', { disableAutoLogin: true }, () => { workflowsPage.getters.workflowCards().should('have.length', 2); workflowsPage.getters.workflowCard('Workflow W1').click(); workflowPage.actions.openNode('Notion'); - ndv.getters - .credentialInput() - .find('input') - .should('have.value', 'Credential C1') - .should('be.disabled'); + ndv.getters.credentialInput().should('have.value', 'Credential C1').should('be.disabled'); ndv.actions.close(); cy.waitForLoad(); diff --git a/cypress/e2e/17-workflow-tags.cy.ts b/cypress/e2e/17-workflow-tags.cy.ts index 56b548747d73a..966a506e7bf2b 100644 --- a/cypress/e2e/17-workflow-tags.cy.ts +++ b/cypress/e2e/17-workflow-tags.cy.ts @@ -30,7 +30,7 @@ describe('Workflow tags', () => { } cy.contains('Done').click(); - wf.getters.createTagButton().click(); + wf.getters.tagsDropdown().click(); wf.getters.tagsInDropdown().should('have.length', 5); wf.getters.tagPills().should('have.length', 0); // none attached }); @@ -45,7 +45,7 @@ describe('Workflow tags', () => { }); cy.contains('Done').click(); - wf.getters.createTagButton().click(); + wf.getters.tagsDropdown().click(); wf.getters.tagsInDropdown().should('have.length', 0); // none stored wf.getters.tagPills().should('have.length', 0); // none attached }); @@ -57,7 +57,8 @@ describe('Workflow tags', () => { cy.contains('Create a tag').click(); cy.getByTestId('tags-table').find('input').type(first).type('{enter}'); - cy.getByTestId('edit-tag-button').click({ force: true }); + cy.getByTestId('tags-table').should('contain.text', first); + cy.getByTestId('edit-tag-button').eq(-1).click({ force: true }); cy.wait(300); cy.getByTestId('tags-table') .find('.el-input--large') @@ -65,7 +66,7 @@ describe('Workflow tags', () => { .type(' Updated') .type('{enter}'); cy.contains('Done').click(); - wf.getters.createTagButton().click(); + wf.getters.tagsDropdown().click(); wf.getters.tagsInDropdown().should('have.length', 1); // one stored wf.getters.tagsInDropdown().contains('Updated').should('exist'); wf.getters.tagPills().should('have.length', 0); // none attached @@ -76,7 +77,7 @@ describe('Workflow tags', () => { wf.actions.addTags(TEST_TAGS); wf.getters.nthTagPill(1).click(); wf.getters.tagsDropdown().find('.el-tag__close').first().click(); - cy.get('body').type('{enter}'); + cy.get('body').click(0, 0); wf.getters.tagPills().should('have.length', TEST_TAGS.length - 1); }); @@ -84,8 +85,8 @@ describe('Workflow tags', () => { wf.getters.createTagButton().click(); wf.actions.addTags(TEST_TAGS); wf.getters.nthTagPill(1).click(); - wf.getters.tagsDropdown().find('li.selected').first().click(); - cy.get('body').type('{enter}'); + wf.getters.tagsInDropdown().filter('.selected').first().click(); + cy.get('body').click(0, 0); wf.getters.tagPills().should('have.length', TEST_TAGS.length - 1); }); }); diff --git a/cypress/e2e/18-user-management.cy.ts b/cypress/e2e/18-user-management.cy.ts index 6af5ba6b60460..cbc7a165f15f3 100644 --- a/cypress/e2e/18-user-management.cy.ts +++ b/cypress/e2e/18-user-management.cy.ts @@ -58,19 +58,19 @@ describe('User Management', { disableAutoLogin: true }, () => { it('should delete user and their data', () => { usersSettingsPage.actions.loginAndVisit(INSTANCE_OWNER.email, INSTANCE_OWNER.password, true); usersSettingsPage.actions.opedDeleteDialog(INSTANCE_MEMBERS[0].email); - usersSettingsPage.getters.deleteDataRadioButton().realClick(); + usersSettingsPage.getters.deleteDataRadioButton().click(); usersSettingsPage.getters.deleteDataInput().type('delete all data'); - usersSettingsPage.getters.deleteUserButton().realClick(); + usersSettingsPage.getters.deleteUserButton().click(); workflowPage.getters.successToast().should('contain', 'User deleted'); }); it('should delete user and transfer their data', () => { usersSettingsPage.actions.loginAndVisit(INSTANCE_OWNER.email, INSTANCE_OWNER.password, true); usersSettingsPage.actions.opedDeleteDialog(INSTANCE_MEMBERS[1].email); - usersSettingsPage.getters.transferDataRadioButton().realClick(); - usersSettingsPage.getters.userSelectDropDown().realClick(); - usersSettingsPage.getters.userSelectOptions().first().realClick(); - usersSettingsPage.getters.deleteUserButton().realClick(); + usersSettingsPage.getters.transferDataRadioButton().click(); + usersSettingsPage.getters.userSelectDropDown().click(); + usersSettingsPage.getters.userSelectOptions().first().click(); + usersSettingsPage.getters.deleteUserButton().click(); workflowPage.getters.successToast().should('contain', 'User deleted'); }); diff --git a/cypress/e2e/2-credentials.cy.ts b/cypress/e2e/2-credentials.cy.ts index 7d4f743a986cb..78dbe055aa46f 100644 --- a/cypress/e2e/2-credentials.cy.ts +++ b/cypress/e2e/2-credentials.cy.ts @@ -4,8 +4,6 @@ import { PIPEDRIVE_NODE_NAME, HTTP_REQUEST_NODE_NAME, NEW_QUERY_AUTH_ACCOUNT_NAME, -} from './../constants'; -import { GMAIL_NODE_NAME, NEW_GOOGLE_ACCOUNT_NAME, NEW_TRELLO_ACCOUNT_NAME, @@ -13,6 +11,7 @@ import { TRELLO_NODE_NAME, } from '../constants'; import { CredentialsPage, CredentialsModal, WorkflowPage, NDV } from '../pages'; +import { getVisibleSelect } from '../utils'; const credentialsPage = new CredentialsPage(); const credentialsModal = new CredentialsModal(); @@ -90,13 +89,16 @@ describe('Credentials', () => { workflowPage.getters.canvasNodes().last().click(); cy.get('body').type('{enter}'); workflowPage.getters.nodeCredentialsSelect().click(); - workflowPage.getters.nodeCredentialsSelect().find('li').last().click(); + getVisibleSelect().find('li').last().click(); credentialsModal.getters.credentialsEditModal().should('be.visible'); credentialsModal.getters.credentialAuthTypeRadioButtons().should('have.length', 2); credentialsModal.getters.credentialAuthTypeRadioButtons().first().click(); credentialsModal.actions.fillCredentialsForm(); cy.get('.el-message-box').find('button').contains('Close').click(); - workflowPage.getters.nodeCredentialsSelect().should('contain', NEW_GOOGLE_ACCOUNT_NAME); + workflowPage.getters + .nodeCredentialsSelect() + .find('input') + .should('have.value', NEW_GOOGLE_ACCOUNT_NAME); }); it('should show multiple credential types in the same dropdown', () => { @@ -107,7 +109,7 @@ describe('Credentials', () => { cy.get('body').type('{enter}'); workflowPage.getters.nodeCredentialsSelect().click(); // Add oAuth credentials - workflowPage.getters.nodeCredentialsSelect().find('li').last().click(); + getVisibleSelect().find('li').last().click(); credentialsModal.getters.credentialsEditModal().should('be.visible'); credentialsModal.getters.credentialAuthTypeRadioButtons().should('have.length', 2); credentialsModal.getters.credentialAuthTypeRadioButtons().first().click(); @@ -115,13 +117,14 @@ describe('Credentials', () => { cy.get('.el-message-box').find('button').contains('Close').click(); workflowPage.getters.nodeCredentialsSelect().click(); // Add Service account credentials - workflowPage.getters.nodeCredentialsSelect().find('li').last().click(); + getVisibleSelect().find('li').last().click(); credentialsModal.getters.credentialsEditModal().should('be.visible'); credentialsModal.getters.credentialAuthTypeRadioButtons().should('have.length', 2); credentialsModal.getters.credentialAuthTypeRadioButtons().last().click(); credentialsModal.actions.fillCredentialsForm(); // Both (+ the 'Create new' option) should be in the dropdown - workflowPage.getters.nodeCredentialsSelect().find('li').should('have.length.greaterThan', 3); + workflowPage.getters.nodeCredentialsSelect().click(); + getVisibleSelect().find('li').should('have.length.greaterThan', 2); }); it('should correctly render required and optional credentials', () => { @@ -132,18 +135,18 @@ describe('Credentials', () => { // Select incoming authentication nodeDetailsView.getters.parameterInput('incomingAuthentication').should('exist'); nodeDetailsView.getters.parameterInput('incomingAuthentication').click(); - nodeDetailsView.getters.parameterInput('incomingAuthentication').find('li').first().click(); + getVisibleSelect().find('li').first().click(); // There should be two credential fields workflowPage.getters.nodeCredentialsSelect().should('have.length', 2); workflowPage.getters.nodeCredentialsSelect().first().click(); - workflowPage.getters.nodeCredentialsSelect().first().find('li').last().click(); + getVisibleSelect().find('li').last().click(); // This one should show auth type selector credentialsModal.getters.credentialAuthTypeRadioButtons().should('have.length', 2); cy.get('body').type('{esc}'); workflowPage.getters.nodeCredentialsSelect().last().click(); - workflowPage.getters.nodeCredentialsSelect().last().find('li').last().click(); + getVisibleSelect().find('li').last().click(); // This one should not show auth type selector credentialsModal.getters.credentialsAuthTypeSelector().should('not.exist'); }); @@ -155,10 +158,13 @@ describe('Credentials', () => { workflowPage.getters.canvasNodes().last().click(); cy.get('body').type('{enter}'); workflowPage.getters.nodeCredentialsSelect().click(); - workflowPage.getters.nodeCredentialsSelect().find('li').last().click(); + getVisibleSelect().find('li').last().click(); credentialsModal.getters.credentialsAuthTypeSelector().should('not.exist'); credentialsModal.actions.fillCredentialsForm(); - workflowPage.getters.nodeCredentialsSelect().should('contain', NEW_TRELLO_ACCOUNT_NAME); + workflowPage.getters + .nodeCredentialsSelect() + .find('input') + .should('have.value', NEW_TRELLO_ACCOUNT_NAME); }); it('should delete credentials from NDV', () => { @@ -168,16 +174,22 @@ describe('Credentials', () => { workflowPage.getters.canvasNodes().last().click(); cy.get('body').type('{enter}'); workflowPage.getters.nodeCredentialsSelect().click(); - workflowPage.getters.nodeCredentialsSelect().find('li').last().click(); + getVisibleSelect().find('li').last().click(); credentialsModal.actions.fillCredentialsForm(); - workflowPage.getters.nodeCredentialsSelect().should('contain', NEW_NOTION_ACCOUNT_NAME); + workflowPage.getters + .nodeCredentialsSelect() + .find('input') + .should('have.value', NEW_NOTION_ACCOUNT_NAME); workflowPage.getters.nodeCredentialsEditButton().click(); credentialsModal.getters.credentialsEditModal().should('be.visible'); credentialsModal.getters.deleteButton().click(); cy.get('.el-message-box').find('button').contains('Yes').click(); workflowPage.getters.successToast().contains('Credential deleted'); - workflowPage.getters.nodeCredentialsSelect().should('not.contain', NEW_TRELLO_ACCOUNT_NAME); + workflowPage.getters + .nodeCredentialsSelect() + .find('input') + .should('not.have.value', NEW_TRELLO_ACCOUNT_NAME); }); it('should rename credentials from NDV', () => { @@ -187,17 +199,18 @@ describe('Credentials', () => { workflowPage.getters.canvasNodes().last().click(); cy.get('body').type('{enter}'); workflowPage.getters.nodeCredentialsSelect().click(); - workflowPage.getters.nodeCredentialsSelect().find('li').last().click(); + getVisibleSelect().find('li').last().click(); credentialsModal.actions.fillCredentialsForm(); - workflowPage.getters.nodeCredentialsSelect().should('contain', NEW_TRELLO_ACCOUNT_NAME); - workflowPage.getters.nodeCredentialsEditButton().click(); credentialsModal.getters.credentialsEditModal().should('be.visible'); credentialsModal.getters.name().click(); credentialsModal.actions.renameCredential(NEW_CREDENTIAL_NAME); credentialsModal.getters.saveButton().click(); credentialsModal.getters.closeButton().click(); - workflowPage.getters.nodeCredentialsSelect().should('contain', NEW_CREDENTIAL_NAME); + workflowPage.getters + .nodeCredentialsSelect() + .find('input') + .should('have.value', NEW_CREDENTIAL_NAME); }); it('should setup generic authentication for HTTP node', () => { @@ -207,20 +220,20 @@ describe('Credentials', () => { workflowPage.getters.canvasNodes().last().click(); cy.get('body').type('{enter}'); nodeDetailsView.getters.parameterInput('authentication').click(); - nodeDetailsView.getters.parameterInput('authentication').find('li').should('have.length', 3); - nodeDetailsView.getters.parameterInput('authentication').find('li').last().click(); + getVisibleSelect().find('li').should('have.length', 3); + getVisibleSelect().find('li').last().click(); nodeDetailsView.getters.parameterInput('genericAuthType').should('exist'); nodeDetailsView.getters.parameterInput('genericAuthType').click(); - nodeDetailsView.getters - .parameterInput('genericAuthType') - .find('li') - .should('have.length.greaterThan', 0); - nodeDetailsView.getters.parameterInput('genericAuthType').find('li').last().click(); + getVisibleSelect().find('li').should('have.length.greaterThan', 0); + getVisibleSelect().find('li').last().click(); workflowPage.getters.nodeCredentialsSelect().should('exist'); workflowPage.getters.nodeCredentialsSelect().click(); - workflowPage.getters.nodeCredentialsSelect().find('li').last().click(); + getVisibleSelect().find('li').last().click(); credentialsModal.actions.fillCredentialsForm(); - workflowPage.getters.nodeCredentialsSelect().should('contain', NEW_QUERY_AUTH_ACCOUNT_NAME); + workflowPage.getters + .nodeCredentialsSelect() + .find('input') + .should('have.value', NEW_QUERY_AUTH_ACCOUNT_NAME); }); }); diff --git a/cypress/e2e/21-community-nodes.cy.ts b/cypress/e2e/21-community-nodes.cy.ts index cfc76e46e04b8..39f572ba5c348 100644 --- a/cypress/e2e/21-community-nodes.cy.ts +++ b/cypress/e2e/21-community-nodes.cy.ts @@ -4,6 +4,7 @@ import { CredentialsModal, WorkflowPage } from '../pages'; import CustomNodeWithN8nCredentialFixture from '../fixtures/Custom_node_n8n_credential.json'; import CustomNodeWithCustomCredentialFixture from '../fixtures/Custom_node_custom_credential.json'; import CustomCredential from '../fixtures/Custom_credential.json'; +import { getVisibleSelect } from '../utils'; const credentialsModal = new CredentialsModal(); const nodeCreatorFeature = new NodeCreator(); @@ -20,9 +21,13 @@ describe('Community Nodes', () => { req.on('response', (res) => { const nodes = res.body || []; - nodes.push(CustomNodeFixture, CustomNodeWithN8nCredentialFixture, CustomNodeWithCustomCredentialFixture); + nodes.push( + CustomNodeFixture, + CustomNodeWithN8nCredentialFixture, + CustomNodeWithCustomCredentialFixture, + ); }); - }) + }); cy.intercept('/types/credentials.json', { middleware: true }, (req) => { req.headers['cache-control'] = 'no-cache, no-store'; @@ -31,8 +36,8 @@ describe('Community Nodes', () => { const credentials = res.body || []; credentials.push(CustomCredential); - }) - }) + }); + }); workflowPage.actions.visit(); }); @@ -45,7 +50,7 @@ describe('Community Nodes', () => { nodeCreatorFeature.getters .getCreatorItem(customNode) - .findChildByTestId('node-creator-item-tooltip') + .find('.el-tooltip__trigger') .should('exist'); nodeCreatorFeature.actions.selectNode(customNode); @@ -65,16 +70,9 @@ describe('Community Nodes', () => { secondParameter().find('label').contains('Resource').should('exist'); secondParameter().find('input.el-input__inner').should('have.value', 'option2'); secondParameter().find('.el-select').click(); - secondParameter().find('.el-select-dropdown__list').should('exist'); // Check if all options are rendered and select the fourth one - secondParameter().find('.el-select-dropdown__list').children().should('have.length', 4); - secondParameter() - .find('.el-select-dropdown__list') - .children() - .eq(3) - .contains('option4') - .should('exist') - .click(); + getVisibleSelect().find('li').should('have.length', 4); + getVisibleSelect().find('li').eq(3).contains('option4').should('exist').click(); secondParameter().find('input.el-input__inner').should('have.value', 'option4'); }); diff --git a/cypress/e2e/24-ndv-paired-item.cy.ts b/cypress/e2e/24-ndv-paired-item.cy.ts index 3bbd2f0b23bbe..a2bb52772344c 100644 --- a/cypress/e2e/24-ndv-paired-item.cy.ts +++ b/cypress/e2e/24-ndv-paired-item.cy.ts @@ -28,59 +28,36 @@ describe('NDV', () => { ndv.actions.switchOutputMode('Table'); // input to output - ndv.getters.inputTableRow(1) + ndv.getters + .inputTableRow(1) .should('exist') .invoke('attr', 'data-test-id') .should('equal', 'hovering-item'); - ndv.getters.inputTableRow(1) - .realHover(); - ndv.getters.outputTableRow(4) - .invoke('attr', 'data-test-id') - .should('equal', 'hovering-item'); + ndv.getters.inputTableRow(1).realHover(); + ndv.getters.outputTableRow(4).invoke('attr', 'data-test-id').should('equal', 'hovering-item'); - ndv.getters.inputTableRow(2) - .realHover(); - ndv.getters.outputTableRow(2) - .invoke('attr', 'data-test-id') - .should('equal', 'hovering-item'); - - ndv.getters.inputTableRow(3) - .realHover(); - ndv.getters.outputTableRow(6) - .invoke('attr', 'data-test-id') - .should('equal', 'hovering-item'); + ndv.getters.inputTableRow(2).realHover(); + ndv.getters.outputTableRow(2).invoke('attr', 'data-test-id').should('equal', 'hovering-item'); + + ndv.getters.inputTableRow(3).realHover(); + ndv.getters.outputTableRow(6).invoke('attr', 'data-test-id').should('equal', 'hovering-item'); // output to input - ndv.getters.outputTableRow(1) - .realHover(); - ndv.getters.inputTableRow(4) - .invoke('attr', 'data-test-id') - .should('equal', 'hovering-item'); + ndv.getters.outputTableRow(1).realHover(); + ndv.getters.inputTableRow(4).invoke('attr', 'data-test-id').should('equal', 'hovering-item'); - ndv.getters.outputTableRow(4) - .realHover(); - ndv.getters.inputTableRow(1) - .invoke('attr', 'data-test-id') - .should('equal', 'hovering-item'); + ndv.getters.outputTableRow(4).realHover(); + ndv.getters.inputTableRow(1).invoke('attr', 'data-test-id').should('equal', 'hovering-item'); - ndv.getters.outputTableRow(2) - .realHover(); - ndv.getters.inputTableRow(2) - .invoke('attr', 'data-test-id') - .should('equal', 'hovering-item'); - - ndv.getters.outputTableRow(6) - .realHover(); - ndv.getters.inputTableRow(3) - .invoke('attr', 'data-test-id') - .should('equal', 'hovering-item'); + ndv.getters.outputTableRow(2).realHover(); + ndv.getters.inputTableRow(2).invoke('attr', 'data-test-id').should('equal', 'hovering-item'); - ndv.getters.outputTableRow(1) - .realHover(); - ndv.getters.inputTableRow(4) - .invoke('attr', 'data-test-id') - .should('equal', 'hovering-item'); + ndv.getters.outputTableRow(6).realHover(); + ndv.getters.inputTableRow(3).invoke('attr', 'data-test-id').should('equal', 'hovering-item'); + + ndv.getters.outputTableRow(1).realHover(); + ndv.getters.inputTableRow(4).invoke('attr', 'data-test-id').should('equal', 'hovering-item'); }); it('maps paired input and output items based on selected input node', () => { @@ -92,9 +69,11 @@ describe('NDV', () => { workflowPage.actions.openNode('Set2'); ndv.getters.inputPanel().contains('6 items').should('exist'); - ndv.getters.outputRunSelector() + ndv.getters + .outputRunSelector() + .find('input') .should('exist') - .should('include.text', '2 of 2 (6 items)'); + .should('have.value', '2 of 2 (6 items)'); ndv.actions.switchInputMode('Table'); ndv.actions.switchOutputMode('Table'); @@ -106,7 +85,8 @@ describe('NDV', () => { ndv.actions.selectInputNode('Set1'); ndv.getters.backToCanvas().realHover(); // reset to default hover - ndv.getters.inputTableRow(1) + ndv.getters + .inputTableRow(1) .should('have.text', '1000') .invoke('attr', 'data-test-id') .should('equal', 'hovering-item'); @@ -119,7 +99,8 @@ describe('NDV', () => { ndv.actions.changeOutputRunSelector('1 of 2 (6 items)'); ndv.getters.backToCanvas().realHover(); // reset to default hover - ndv.getters.inputTableRow(1) + ndv.getters + .inputTableRow(1) .should('have.text', '1111') .invoke('attr', 'data-test-id') .should('equal', 'hovering-item'); @@ -137,11 +118,13 @@ describe('NDV', () => { workflowPage.actions.executeWorkflow(); workflowPage.actions.openNode('Set3'); - ndv.getters.inputRunSelector() + ndv.getters + .inputRunSelector() .should('exist') .find('input') .should('include.value', '2 of 2 (6 items)'); - ndv.getters.outputRunSelector() + ndv.getters + .outputRunSelector() .should('exist') .find('input') .should('include.value', '2 of 2 (6 items)'); @@ -150,23 +133,19 @@ describe('NDV', () => { ndv.actions.switchOutputMode('Table'); ndv.actions.changeOutputRunSelector('1 of 2 (6 items)'); - ndv.getters.inputRunSelector().find('input') - .should('include.value', '1 of 2 (6 items)'); - ndv.getters.outputRunSelector().find('input') - .should('include.value', '1 of 2 (6 items)'); + ndv.getters.inputRunSelector().find('input').should('include.value', '1 of 2 (6 items)'); + ndv.getters.outputRunSelector().find('input').should('include.value', '1 of 2 (6 items)'); - ndv.getters.inputTableRow(1) + ndv.getters + .inputTableRow(1) .should('have.text', '1111') .invoke('attr', 'data-test-id') .should('equal', 'hovering-item'); - ndv.getters.outputTableRow(1) - .should('have.text', '1111') - .realHover(); + ndv.getters.outputTableRow(1).should('have.text', '1111').realHover(); - ndv.getters.outputTableRow(3) - .should('have.text', '4444') - .realHover(); - ndv.getters.inputTableRow(3) + ndv.getters.outputTableRow(3).should('have.text', '4444').realHover(); + ndv.getters + .inputTableRow(3) .should('have.text', '4444') .invoke('attr', 'data-test-id') .should('equal', 'hovering-item'); @@ -174,18 +153,16 @@ describe('NDV', () => { ndv.actions.changeOutputRunSelector('2 of 2 (6 items)'); cy.wait(50); - ndv.getters.inputTableRow(1) - .should('have.text', '1000') - .realHover(); - ndv.getters.outputTableRow(1) + ndv.getters.inputTableRow(1).should('have.text', '1000').realHover(); + ndv.getters + .outputTableRow(1) .should('have.text', '1000') .invoke('attr', 'data-test-id') .should('equal', 'hovering-item'); - ndv.getters.outputTableRow(3) - .should('have.text', '2000') - .realHover(); - ndv.getters.inputTableRow(3) + ndv.getters.outputTableRow(3).should('have.text', '2000').realHover(); + ndv.getters + .inputTableRow(3) .should('have.text', '2000') .invoke('attr', 'data-test-id') .should('equal', 'hovering-item'); @@ -200,15 +177,18 @@ describe('NDV', () => { workflowPage.actions.openNode('Set2'); ndv.getters.inputPanel().contains('6 items').should('exist'); - ndv.getters.outputRunSelector() + ndv.getters + .outputRunSelector() + .find('input') .should('exist') - .should('include.text', '2 of 2 (6 items)'); + .should('have.value', '2 of 2 (6 items)'); ndv.actions.switchInputMode('Table'); ndv.actions.switchOutputMode('Table'); ndv.getters.backToCanvas().realHover(); // reset to default hover - ndv.getters.inputTableRow(1) + ndv.getters + .inputTableRow(1) .should('have.text', '1111') .invoke('attr', 'data-test-id') .should('equal', 'hovering-item'); @@ -218,28 +198,32 @@ describe('NDV', () => { ndv.actions.selectInputNode('Code1'); ndv.getters.inputTableRow(1).realHover(); - ndv.getters.inputTableRow(1) + ndv.getters + .inputTableRow(1) .should('have.text', '1000') .invoke('attr', 'data-test-id') .should('equal', 'hovering-item'); - ndv.getters.outputTableRow(1) - .should('have.text', '1000'); + ndv.getters.outputTableRow(1).should('have.text', '1000'); ndv.getters.parameterExpressionPreview('value').should('include.text', '1000'); ndv.actions.selectInputNode('Code'); ndv.getters.inputTableRow(1).realHover(); - ndv.getters.inputTableRow(1) - .should('have.text', '6666') - .invoke('attr', 'data-test-id') - .should('equal', 'hovering-item'); + ndv.getters + .inputTableRow(1) + .should('have.text', '6666') + .invoke('attr', 'data-test-id') + .should('equal', 'hovering-item'); ndv.getters.outputHoveringItem().should('not.exist'); ndv.getters.parameterExpressionPreview('value').should('include.text', '1000'); ndv.actions.selectInputNode('When clicking'); ndv.getters.inputTableRow(1).realHover(); - ndv.getters.inputTableRow(1).should('have.text', "This is an item, but it's empty.").realHover(); + ndv.getters + .inputTableRow(1) + .should('have.text', "This is an item, but it's empty.") + .realHover(); ndv.getters.outputHoveringItem().should('have.length', 6); ndv.getters.parameterExpressionPreview('value').should('include.text', '1000'); }); @@ -256,18 +240,16 @@ describe('NDV', () => { ndv.actions.switchOutputMode('Table'); ndv.actions.switchOutputBranch('False Branch (2 items)'); - ndv.getters.outputTableRow(1) - .should('have.text', '8888') - .realHover(); - ndv.getters.inputTableRow(5) + ndv.getters.outputTableRow(1).should('have.text', '8888').realHover(); + ndv.getters + .inputTableRow(5) .should('have.text', '8888') .invoke('attr', 'data-test-id') .should('equal', 'hovering-item'); - ndv.getters.outputTableRow(2) - .should('have.text', '9999') - .realHover(); - ndv.getters.inputTableRow(6) + ndv.getters.outputTableRow(2).should('have.text', '9999').realHover(); + ndv.getters + .inputTableRow(6) .should('have.text', '9999') .invoke('attr', 'data-test-id') .should('equal', 'hovering-item'); @@ -277,31 +259,21 @@ describe('NDV', () => { workflowPage.actions.openNode('Set5'); ndv.actions.switchInputBranch('True Branch'); - ndv.actions.changeOutputRunSelector('1 of 2 (2 items)') - ndv.getters.outputTableRow(1) - .should('have.text', '8888') - .realHover(); + ndv.actions.changeOutputRunSelector('1 of 2 (2 items)'); + ndv.getters.outputTableRow(1).should('have.text', '8888').realHover(); ndv.getters.inputHoveringItem().should('not.exist'); - ndv.getters.inputTableRow(1) - .should('have.text', '1111') - .realHover(); + ndv.getters.inputTableRow(1).should('have.text', '1111').realHover(); ndv.getters.outputHoveringItem().should('not.exist'); ndv.actions.switchInputBranch('False Branch'); - ndv.getters.inputTableRow(1) - .should('have.text', '8888') - .realHover(); + ndv.getters.inputTableRow(1).should('have.text', '8888').realHover(); - ndv.actions.changeOutputRunSelector('2 of 2 (4 items)') - ndv.getters.outputTableRow(1) - .should('have.text', '1111') - .realHover(); + ndv.actions.changeOutputRunSelector('2 of 2 (4 items)'); + ndv.getters.outputTableRow(1).should('have.text', '1111').realHover(); - ndv.actions.changeOutputRunSelector('1 of 2 (2 items)') - ndv.getters.inputTableRow(1) - .should('have.text', '8888') - .realHover(); + ndv.actions.changeOutputRunSelector('1 of 2 (2 items)'); + ndv.getters.inputTableRow(1).should('have.text', '8888').realHover(); ndv.getters.outputHoveringItem().should('have.text', '8888'); // todo there's a bug here need to fix ADO-534 // ndv.getters.outputHoveringItem().should('not.exist'); diff --git a/cypress/e2e/25-stickies.cy.ts b/cypress/e2e/25-stickies.cy.ts index afb2db3b5f42d..d0e219575fa62 100644 --- a/cypress/e2e/25-stickies.cy.ts +++ b/cypress/e2e/25-stickies.cy.ts @@ -2,7 +2,13 @@ import { WorkflowPage as WorkflowPageClass } from '../pages/workflow'; const workflowPage = new WorkflowPageClass(); -function checkStickiesStyle( top: number, left: number, height: number, width: number, zIndex?: number) { +function checkStickiesStyle( + top: number, + left: number, + height: number, + width: number, + zIndex?: number, +) { workflowPage.getters.stickies().should(($el) => { expect($el).to.have.css('top', `${top}px`); expect($el).to.have.css('left', `${left}px`); @@ -18,22 +24,23 @@ describe('Canvas Actions', () => { beforeEach(() => { workflowPage.actions.visit(); - cy.window().then( - (win) => { - // @ts-ignore - win.preventNodeViewBeforeUnload = true; - }, - ); + cy.window().then((win) => { + // @ts-ignore + win.preventNodeViewBeforeUnload = true; + }); }); - it('adds sticky to canvas with default text and position', () => { workflowPage.getters.addStickyButton().should('not.be.visible'); - addDefaultSticky() - workflowPage.getters.stickies().eq(0) + addDefaultSticky(); + workflowPage.getters + .stickies() + .eq(0) .should('have.text', 'I’m a note\nDouble click to edit me. Guide\n') - .find('a').contains('Guide').should('have.attr', 'href'); + .find('a') + .contains('Guide') + .should('have.attr', 'href'); }); it('drags sticky around to top left corner', () => { @@ -57,18 +64,19 @@ describe('Canvas Actions', () => { it('deletes sticky', () => { workflowPage.actions.addSticky(); - workflowPage.getters.stickies().should('have.length', 1) + workflowPage.getters.stickies().should('have.length', 1); workflowPage.actions.deleteSticky(); - workflowPage.getters.stickies().should('have.length', 0) + workflowPage.getters.stickies().should('have.length', 0); }); it('edits sticky and updates content as markdown', () => { workflowPage.actions.addSticky(); - workflowPage.getters.stickies() - .should('have.text', 'I’m a note\nDouble click to edit me. Guide\n') + workflowPage.getters + .stickies() + .should('have.text', 'I’m a note\nDouble click to edit me. Guide\n'); workflowPage.getters.stickies().dblclick(); workflowPage.actions.editSticky('# hello world \n ## text text'); @@ -159,32 +167,41 @@ describe('Canvas Actions', () => { cy.drag('[data-test-id="sticky"] [data-dir="topLeft"]', [-150, -150]); checkStickiesStyle(124, 256, 316, 384, -121); - workflowPage.getters.canvasNodes().eq(0) + workflowPage.getters + .canvasNodes() + .eq(0) .should(($el) => { expect($el).to.have.css('z-index', 'auto'); }); workflowPage.actions.addSticky(); - workflowPage.getters.stickies().eq(0) + workflowPage.getters + .stickies() + .eq(0) .should(($el) => { expect($el).to.have.css('z-index', '-121'); }); - workflowPage.getters.stickies().eq(1) + workflowPage.getters + .stickies() + .eq(1) .should(($el) => { expect($el).to.have.css('z-index', '-38'); }); cy.drag('[data-test-id="sticky"] [data-dir="topLeft"]', [-200, -200], { index: 1 }); - workflowPage.getters.stickies().eq(0) + workflowPage.getters + .stickies() + .eq(0) .should(($el) => { expect($el).to.have.css('z-index', '-121'); }); - workflowPage.getters.stickies().eq(1) + workflowPage.getters + .stickies() + .eq(1) .should(($el) => { expect($el).to.have.css('z-index', '-158'); }); - }); }); @@ -198,15 +215,20 @@ type BoundingBox = { width: number; top: number; left: number; -} +}; function dragRightEdge(curr: BoundingBox, move: number) { - workflowPage.getters.stickies().first().then(($el) => { - const { left, top, height, width } = curr; - cy.drag(`[data-test-id="sticky"] [data-dir="right"]`, [left + width + move, 0], { abs: true }); - stickyShouldBePositionedCorrectly({ top, left }); - stickyShouldHaveCorrectSize([height, width * 1.5 + move]); - }); + workflowPage.getters + .stickies() + .first() + .then(($el) => { + const { left, top, height, width } = curr; + cy.drag(`[data-test-id="sticky"] [data-dir="right"]`, [left + width + move, 0], { + abs: true, + }); + stickyShouldBePositionedCorrectly({ top, left }); + stickyShouldHaveCorrectSize([height, width * 1.5 + move]); + }); } function shouldHaveOneSticky() { @@ -214,17 +236,20 @@ function shouldHaveOneSticky() { } function shouldBeInDefaultLocation() { - workflowPage.getters.stickies().eq(0).should(($el) => { - expect($el).to.have.css('height', '160px'); - expect($el).to.have.css('width', '240px'); - }) + workflowPage.getters + .stickies() + .eq(0) + .should(($el) => { + expect($el).to.have.css('height', '160px'); + expect($el).to.have.css('width', '240px'); + }); } function shouldHaveDefaultSize() { workflowPage.getters.stickies().should(($el) => { expect($el).to.have.css('height', '160px'); expect($el).to.have.css('width', '240px'); - }) + }); } function addDefaultSticky() { @@ -237,21 +262,19 @@ function addDefaultSticky() { function stickyShouldBePositionedCorrectly(position: Position) { const yOffset = -100; const xOffset = -180; - workflowPage.getters.stickies() - .should(($el) => { - expect($el).to.have.css('top', `${yOffset + position.top}px`); - expect($el).to.have.css('left', `${xOffset + position.left}px`); - }); + workflowPage.getters.stickies().should(($el) => { + expect($el).to.have.css('top', `${yOffset + position.top}px`); + expect($el).to.have.css('left', `${xOffset + position.left}px`); + }); } function stickyShouldHaveCorrectSize(size: [number, number]) { const yOffset = 0; const xOffset = 0; - workflowPage.getters.stickies() - .should(($el) => { - expect($el).to.have.css('height', `${yOffset + size[0]}px`); - expect($el).to.have.css('width', `${xOffset + size[1]}px`); - }); + workflowPage.getters.stickies().should(($el) => { + expect($el).to.have.css('height', `${yOffset + size[0]}px`); + expect($el).to.have.css('width', `${xOffset + size[1]}px`); + }); } function moveSticky(target: Position) { diff --git a/cypress/e2e/26-resource-locator.cy.ts b/cypress/e2e/26-resource-locator.cy.ts index cedcfd628e6dc..5231d3fe29f6a 100644 --- a/cypress/e2e/26-resource-locator.cy.ts +++ b/cypress/e2e/26-resource-locator.cy.ts @@ -1,4 +1,5 @@ import { WorkflowPage, NDV, CredentialsModal } from '../pages'; +import { getVisibleSelect } from '../utils'; const workflowPage = new WorkflowPage(); const ndv = new NDV(); @@ -32,7 +33,7 @@ describe('Resource Locator', () => { workflowPage.actions.addNodeToCanvas('Google Sheets', true, true); workflowPage.getters.nodeCredentialsSelect().click(); // Add oAuth credentials - workflowPage.getters.nodeCredentialsSelect().find('li').last().click(); + getVisibleSelect().find('li').last().click(); credentialsModal.getters.credentialsEditModal().should('be.visible'); credentialsModal.getters.credentialAuthTypeRadioButtons().should('have.length', 2); credentialsModal.getters.credentialAuthTypeRadioButtons().first().click(); diff --git a/cypress/e2e/4-node-creator.cy.ts b/cypress/e2e/4-node-creator.cy.ts index fb0887a6832e7..fdd4e0e7d2ccf 100644 --- a/cypress/e2e/4-node-creator.cy.ts +++ b/cypress/e2e/4-node-creator.cy.ts @@ -1,6 +1,7 @@ import { NodeCreator } from '../pages/features/node-creator'; import { WorkflowPage as WorkflowPageClass } from '../pages/workflow'; import { NDV } from '../pages/ndv'; +import { getVisibleSelect } from '../utils'; const nodeCreatorFeature = new NodeCreator(); const WorkflowPage = new WorkflowPageClass(); @@ -85,7 +86,7 @@ describe('Node Creator', () => { nodeCreatorFeature.getters.getCreatorItem(editImageNode).click(); nodeCreatorFeature.getters.activeSubcategory().should('have.text', editImageNode); nodeCreatorFeature.getters.getCreatorItem('Crop Image').click(); - NDVModal.getters.parameterInput('operation').should('contain.text', 'Crop'); + NDVModal.getters.parameterInput('operation').find('input').should('have.value', 'Crop'); }); it('should search through actions and confirm added action', () => { @@ -95,9 +96,9 @@ describe('Node Creator', () => { nodeCreatorFeature.getters.activeSubcategory().should('have.text', 'FTP'); nodeCreatorFeature.getters.searchBar().find('input').clear().type('file'); // Navigate to rename action which should be the 4th item - nodeCreatorFeature.getters.searchBar().find('input').type('{uparrow}{uparrow}{rightarrow}'); - NDVModal.getters.parameterInput('operation').should('contain.text', 'Rename'); - }) + nodeCreatorFeature.getters.searchBar().find('input').type('{uparrow}{rightarrow}'); + NDVModal.getters.parameterInput('operation').find('input').should('have.value', 'Rename'); + }); it('should not show actions for single action nodes', () => { const singleActionNodes = [ @@ -110,19 +111,22 @@ describe('Node Creator', () => { 'Spontit', 'Vonage', 'Send Email', - 'Toggl Trigger' - ] - const doubleActionNode = 'OpenWeatherMap' + 'Toggl Trigger', + ]; + const doubleActionNode = 'OpenWeatherMap'; nodeCreatorFeature.actions.openNodeCreator(); singleActionNodes.forEach((node) => { nodeCreatorFeature.getters.searchBar().find('input').clear().type(node); - nodeCreatorFeature.getters.getCreatorItem(node).find('button[class*="panelIcon"]').should('not.exist'); - }) + nodeCreatorFeature.getters + .getCreatorItem(node) + .find('button[class*="panelIcon"]') + .should('not.exist'); + }); nodeCreatorFeature.getters.searchBar().find('input').clear().type(doubleActionNode); nodeCreatorFeature.getters.getCreatorItem(doubleActionNode).click(); nodeCreatorFeature.getters.creatorItem().should('have.length', 4); - }) + }); it('should have "Actions" section collapsed when opening actions view from Trigger root view', () => { nodeCreatorFeature.actions.openNodeCreator(); @@ -131,10 +135,19 @@ describe('Node Creator', () => { nodeCreatorFeature.getters.getCategoryItem('Actions').should('exist'); nodeCreatorFeature.getters.getCategoryItem('Triggers').should('exist'); - nodeCreatorFeature.getters.getCategoryItem('Triggers').parent().should('not.have.attr', 'data-category-collapsed'); - nodeCreatorFeature.getters.getCategoryItem('Actions').parent().should('have.attr', 'data-category-collapsed', 'true'); - nodeCreatorFeature.getters.getCategoryItem('Actions').click() - nodeCreatorFeature.getters.getCategoryItem('Actions').parent().should('not.have.attr', 'data-category-collapsed'); + nodeCreatorFeature.getters + .getCategoryItem('Triggers') + .parent() + .should('have.attr', 'data-category-collapsed', 'false'); + nodeCreatorFeature.getters + .getCategoryItem('Actions') + .parent() + .should('have.attr', 'data-category-collapsed', 'true'); + nodeCreatorFeature.getters.getCategoryItem('Actions').click(); + nodeCreatorFeature.getters + .getCategoryItem('Actions') + .parent() + .should('have.attr', 'data-category-collapsed', 'false'); }); it('should have "Triggers" section collapsed when opening actions view from Regular root view', () => { @@ -145,17 +158,33 @@ describe('Node Creator', () => { nodeCreatorFeature.getters.searchBar().find('input').clear().type('n8n'); nodeCreatorFeature.getters.getCreatorItem('n8n').click(); - nodeCreatorFeature.getters.getCategoryItem('Actions').parent().should('not.have.attr', 'data-category-collapsed'); - nodeCreatorFeature.getters.getCategoryItem('Actions').click() - nodeCreatorFeature.getters.getCategoryItem('Actions').parent().should('have.attr', 'data-category-collapsed'); - nodeCreatorFeature.getters.getCategoryItem('Triggers').parent().should('have.attr', 'data-category-collapsed'); - nodeCreatorFeature.getters.getCategoryItem('Triggers').click() - nodeCreatorFeature.getters.getCategoryItem('Triggers').parent().should('not.have.attr', 'data-category-collapsed'); + nodeCreatorFeature.getters + .getCategoryItem('Actions') + .parent() + .should('have.attr', 'data-category-collapsed', 'false'); + nodeCreatorFeature.getters.getCategoryItem('Actions').click(); + nodeCreatorFeature.getters + .getCategoryItem('Actions') + .parent() + .should('have.attr', 'data-category-collapsed', 'true'); + nodeCreatorFeature.getters + .getCategoryItem('Triggers') + .parent() + .should('have.attr', 'data-category-collapsed', 'true'); + nodeCreatorFeature.getters.getCategoryItem('Triggers').click(); + nodeCreatorFeature.getters + .getCategoryItem('Triggers') + .parent() + .should('have.attr', 'data-category-collapsed', 'false'); }); it('should show callout and two suggested nodes if node has no trigger actions', () => { nodeCreatorFeature.actions.openNodeCreator(); - nodeCreatorFeature.getters.searchBar().find('input').clear().type('Customer Datastore (n8n training)'); + nodeCreatorFeature.getters + .searchBar() + .find('input') + .clear() + .type('Customer Datastore (n8n training)'); nodeCreatorFeature.getters.getCreatorItem('Customer Datastore (n8n training)').click(); cy.getByTestId('actions-panel-no-triggers-callout').should('be.visible'); @@ -165,28 +194,32 @@ describe('Node Creator', () => { it('should show intro callout if user has not made a production execution', () => { nodeCreatorFeature.actions.openNodeCreator(); - nodeCreatorFeature.getters.searchBar().find('input').clear().type('Customer Datastore (n8n training)'); + nodeCreatorFeature.getters + .searchBar() + .find('input') + .clear() + .type('Customer Datastore (n8n training)'); nodeCreatorFeature.getters.getCreatorItem('Customer Datastore (n8n training)').click(); cy.getByTestId('actions-panel-activation-callout').should('be.visible'); nodeCreatorFeature.getters.activeSubcategory().find('button').click(); - nodeCreatorFeature.getters.searchBar().find('input').clear() + nodeCreatorFeature.getters.searchBar().find('input').clear(); nodeCreatorFeature.getters.getCreatorItem('On a schedule').click(); // Setup 1s interval execution cy.getByTestId('parameter-input-field').click(); - cy.getByTestId('parameter-input-field') - .find('.el-select-dropdown') - .find('.option-headline') - .contains('Seconds') - .click(); + getVisibleSelect().find('.option-headline').contains('Seconds').click(); cy.getByTestId('parameter-input-secondsInterval').clear().type('1'); NDVModal.actions.close(); nodeCreatorFeature.actions.openNodeCreator(); - nodeCreatorFeature.getters.searchBar().find('input').clear().type('Customer Datastore (n8n training)'); + nodeCreatorFeature.getters + .searchBar() + .find('input') + .clear() + .type('Customer Datastore (n8n training)'); nodeCreatorFeature.getters.getCreatorItem('Customer Datastore (n8n training)').click(); nodeCreatorFeature.getters.getCreatorItem('Get All People').click(); NDVModal.actions.close(); @@ -197,11 +230,15 @@ describe('Node Creator', () => { // Wait for schedule 1s execution to mark user as having made a production execution cy.wait(1500); - cy.reload() + cy.reload(); // Action callout should not be visible after user has made a production execution nodeCreatorFeature.actions.openNodeCreator(); - nodeCreatorFeature.getters.searchBar().find('input').clear().type('Customer Datastore (n8n training)'); + nodeCreatorFeature.getters + .searchBar() + .find('input') + .clear() + .type('Customer Datastore (n8n training)'); nodeCreatorFeature.getters.getCreatorItem('Customer Datastore (n8n training)').click(); cy.getByTestId('actions-panel-activation-callout').should('not.exist'); @@ -210,7 +247,11 @@ describe('Node Creator', () => { it('should show Trigger and Actions sections during search', () => { nodeCreatorFeature.actions.openNodeCreator(); - nodeCreatorFeature.getters.searchBar().find('input').clear().type('Customer Datastore (n8n training)'); + nodeCreatorFeature.getters + .searchBar() + .find('input') + .clear() + .type('Customer Datastore (n8n training)'); nodeCreatorFeature.getters.getCreatorItem('Customer Datastore (n8n training)').click(); nodeCreatorFeature.getters.searchBar().find('input').clear().type('Non existent action name'); @@ -228,7 +269,8 @@ describe('Node Creator', () => { { name: 'canvas add button', handler: () => nodeCreatorFeature.getters.canvasAddButton().click(), - }, { + }, + { name: 'plus button', handler: () => nodeCreatorFeature.getters.plusButton().click(), }, @@ -238,10 +280,10 @@ describe('Node Creator', () => { // name: 'tab key', // handler: () => cy.realPress('Tab'), // }, - ] + ]; sourcesWithAppend.forEach((source) => { it(`should append manual trigger when source is ${source.name}`, () => { - source.handler() + source.handler(); nodeCreatorFeature.getters.searchBar().find('input').clear().type('n8n'); nodeCreatorFeature.getters.getCreatorItem('n8n').click(); nodeCreatorFeature.getters.getCategoryItem('Actions').click(); @@ -251,6 +293,7 @@ describe('Node Creator', () => { }); }); + // @TODO FIX ADDING 2 NODES IN ONE GO it('should not append manual trigger when source is canvas related', () => { nodeCreatorFeature.getters.canvasAddButton().click(); nodeCreatorFeature.getters.searchBar().find('input').clear().type('n8n'); @@ -258,8 +301,8 @@ describe('Node Creator', () => { nodeCreatorFeature.getters.getCategoryItem('Actions').click(); nodeCreatorFeature.getters.getCreatorItem('Create a credential').click(); NDVModal.actions.close(); - WorkflowPage.actions.deleteNode('When clicking "Execute Workflow"') - WorkflowPage.getters.canvasNodePlusEndpointByName('n8n').click() + WorkflowPage.actions.deleteNode('When clicking "Execute Workflow"'); + WorkflowPage.getters.canvasNodePlusEndpointByName('n8n').click(); nodeCreatorFeature.getters.searchBar().find('input').clear().type('n8n'); nodeCreatorFeature.getters.getCreatorItem('n8n').click(); nodeCreatorFeature.getters.getCategoryItem('Actions').click(); @@ -267,8 +310,8 @@ describe('Node Creator', () => { NDVModal.actions.close(); WorkflowPage.getters.canvasNodes().should('have.length', 2); WorkflowPage.actions.zoomToFit(); - WorkflowPage.actions.addNodeBetweenNodes('n8n', 'n8n1', 'Item Lists', 'Summarize') + WorkflowPage.actions.addNodeBetweenNodes('n8n', 'n8n1', 'Item Lists', 'Summarize'); WorkflowPage.getters.canvasNodes().should('have.length', 3); - }) + }); }); }); diff --git a/cypress/e2e/5-ndv.cy.ts b/cypress/e2e/5-ndv.cy.ts index 6e3f38ca5bcb4..67010b4a13df3 100644 --- a/cypress/e2e/5-ndv.cy.ts +++ b/cypress/e2e/5-ndv.cy.ts @@ -117,7 +117,7 @@ describe('NDV', () => { setupSchemaWorkflow(); ndv.getters.outputDisplayMode().children().should('have.length', 3); ndv.getters.outputDisplayMode().find('[class*=active]').should('contain', 'Table'); - ndv.getters.outputDisplayMode().contains('Schema').click(); + ndv.actions.switchOutputMode('Schema'); ndv.getters.outputDisplayMode().find('[class*=active]').should('contain', 'Schema'); schemaKeys.forEach((key) => { @@ -130,7 +130,7 @@ describe('NDV', () => { }); it('should preserve schema view after execution', () => { setupSchemaWorkflow(); - ndv.getters.outputDisplayMode().contains('Schema').click(); + ndv.actions.switchOutputMode('Schema'); ndv.actions.execute(); ndv.getters.outputDisplayMode().find('[class*=active]').should('contain', 'Schema'); }); @@ -142,7 +142,7 @@ describe('NDV', () => { .outputPanel() .find('[data-test-id=run-data-schema-item]') .filter(':contains("objectValue")'); - ndv.getters.outputDisplayMode().contains('Schema').click(); + ndv.actions.switchOutputMode('Schema'); expandedObjectProps.forEach((key) => { ndv.getters @@ -173,9 +173,9 @@ describe('NDV', () => { ndv.actions.execute(); ndv.getters.outputPanel().contains('25 items').should('exist'); ndv.getters.outputPanel().find('[class*=_pagination]').should('exist'); - ndv.getters.outputDisplayMode().contains('Schema').click(); + ndv.actions.switchOutputMode('Schema'); ndv.getters.outputPanel().find('[class*=_pagination]').should('not.exist'); - ndv.getters.outputDisplayMode().contains('JSON').click(); + ndv.actions.switchOutputMode('JSON'); ndv.getters.outputPanel().find('[class*=_pagination]').should('exist'); }); it('should display large schema', () => { @@ -188,7 +188,7 @@ describe('NDV', () => { ndv.getters.outputPanel().contains('20 items').should('exist'); ndv.getters.outputPanel().find('[class*=_pagination]').should('exist'); - ndv.getters.outputDisplayMode().contains('Schema').click(); + ndv.actions.switchOutputMode('Schema'); ndv.getters.outputPanel().find('[class*=_pagination]').should('not.exist'); ndv.getters .outputPanel() diff --git a/cypress/e2e/7-workflow-actions.cy.ts b/cypress/e2e/7-workflow-actions.cy.ts index 7f9ee21c7c76e..fbba00e27b875 100644 --- a/cypress/e2e/7-workflow-actions.cy.ts +++ b/cypress/e2e/7-workflow-actions.cy.ts @@ -6,9 +6,11 @@ import { } from '../constants'; import { WorkflowPage as WorkflowPageClass } from '../pages/workflow'; import { WorkflowsPage as WorkflowsPageClass } from '../pages/workflows'; +import { getVisibleDropdown, getVisibleSelect } from '../utils'; const NEW_WORKFLOW_NAME = 'Something else'; -const IMPORT_WORKFLOW_URL = 'https://gist.githubusercontent.com/OlegIvaniv/010bd3f45c8a94f8eb7012e663a8b671/raw/3afea1aec15573cc168d9af7e79395bd76082906/test-workflow.json'; +const IMPORT_WORKFLOW_URL = + 'https://gist.githubusercontent.com/OlegIvaniv/010bd3f45c8a94f8eb7012e663a8b671/raw/3afea1aec15573cc168d9af7e79395bd76082906/test-workflow.json'; const DUPLICATE_WORKFLOW_NAME = 'Duplicated workflow'; const DUPLICATE_WORKFLOW_TAG = 'Duplicate'; @@ -67,11 +69,11 @@ describe('Workflow Actions', () => { it('should not save workflow if canvas is loading', () => { let interceptCalledCount = 0; - // There's no way in Cypress to check if intercept was not called + // There's no way in Cypress to check if intercept was not called // so we'll count the number of times it was called - cy.intercept('PATCH', '/rest/workflows/*', () => { - interceptCalledCount++; - }).as('saveWorkflow'); + cy.intercept('PATCH', '/rest/workflows/*', () => { + interceptCalledCount++; + }).as('saveWorkflow'); WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME); WorkflowPage.actions.saveWorkflowOnButtonClick(); @@ -84,11 +86,11 @@ describe('Workflow Actions', () => { (req) => { // Delay the response to give time for the save to be triggered req.on('response', async (res) => { - await new Promise((resolve) => setTimeout(resolve, 2000)) + await new Promise((resolve) => setTimeout(resolve, 2000)); res.send(); - }) - } - ) + }); + }, + ); cy.reload(); cy.get('.el-loading-mask').should('exist'); cy.get('body').type(META_KEY, { release: false }).type('s'); @@ -99,7 +101,7 @@ describe('Workflow Actions', () => { cy.get('body').type(META_KEY, { release: false }).type('s'); cy.wait('@saveWorkflow'); cy.wrap(null).then(() => expect(interceptCalledCount).to.eq(1)); - }) + }); it('should copy nodes', () => { WorkflowPage.actions.addNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME); WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME); @@ -127,7 +129,7 @@ describe('Workflow Actions', () => { cy.get('.el-message-box').should('be.visible'); cy.get('.el-message-box').find('input').type(IMPORT_WORKFLOW_URL); cy.get('body').type('{enter}'); - cy.waitForLoad(false) + cy.waitForLoad(false); WorkflowPage.actions.zoomToFit(); WorkflowPage.getters.canvasNodes().should('have.length', 2); WorkflowPage.getters.nodeConnections().should('have.length', 1); @@ -137,7 +139,7 @@ describe('Workflow Actions', () => { WorkflowPage.getters .workflowImportInput() .selectFile('cypress/fixtures/Test_workflow-actions_paste-data.json', { force: true }); - cy.waitForLoad(false) + cy.waitForLoad(false); WorkflowPage.actions.zoomToFit(); WorkflowPage.getters.canvasNodes().should('have.length', 2); WorkflowPage.getters.nodeConnections().should('have.length', 1); @@ -157,57 +159,33 @@ describe('Workflow Actions', () => { WorkflowPage.getters.workflowMenuItemSettings().click(); // Change all settings // totalWorkflows + 1 (current workflow) + 1 (no workflow option) - WorkflowPage.getters.workflowSettingsErrorWorkflowSelect().find('li').should('have.length', totalWorkflows + 2); - WorkflowPage.getters - .workflowSettingsErrorWorkflowSelect() - .find('li') - .last() - .click({ force: true }); - WorkflowPage.getters.workflowSettingsTimezoneSelect().find('li').should('exist'); - WorkflowPage.getters.workflowSettingsTimezoneSelect().find('li').eq(1).click({ force: true }); - WorkflowPage.getters - .workflowSettingsSaveFiledExecutionsSelect() - .find('li') - .should('have.length', 3); - WorkflowPage.getters - .workflowSettingsSaveFiledExecutionsSelect() - .find('li') - .last() - .click({ force: true }); - WorkflowPage.getters - .workflowSettingsSaveSuccessExecutionsSelect() - .find('li') - .should('have.length', 3); - WorkflowPage.getters - .workflowSettingsSaveSuccessExecutionsSelect() + WorkflowPage.getters.workflowSettingsErrorWorkflowSelect().click(); + getVisibleSelect() .find('li') - .last() - .click({ force: true }); - WorkflowPage.getters - .workflowSettingsSaveManualExecutionsSelect() - .find('li') - .should('have.length', 3); - WorkflowPage.getters - .workflowSettingsSaveManualExecutionsSelect() - .find('li') - .last() - .click({ force: true }); - WorkflowPage.getters - .workflowSettingsSaveExecutionProgressSelect() - .find('li') - .should('have.length', 3); - WorkflowPage.getters - .workflowSettingsSaveExecutionProgressSelect() - .find('li') - .last() - .click({ force: true }); + .should('have.length', totalWorkflows + 2); + getVisibleSelect().find('li').last().click({ force: true }); + WorkflowPage.getters.workflowSettingsTimezoneSelect().click(); + getVisibleSelect().find('li').should('exist'); + getVisibleSelect().find('li').eq(1).click({ force: true }); + WorkflowPage.getters.workflowSettingsSaveFiledExecutionsSelect().click(); + getVisibleSelect().find('li').should('have.length', 3); + getVisibleSelect().find('li').last().click({ force: true }); + WorkflowPage.getters.workflowSettingsSaveSuccessExecutionsSelect().click(); + getVisibleSelect().find('li').should('have.length', 3); + getVisibleSelect().find('li').last().click({ force: true }); + WorkflowPage.getters.workflowSettingsSaveManualExecutionsSelect().click(); + getVisibleSelect().find('li').should('have.length', 3); + getVisibleSelect().find('li').last().click({ force: true }); + WorkflowPage.getters.workflowSettingsSaveExecutionProgressSelect().click(); + getVisibleSelect().find('li').should('have.length', 3); + getVisibleSelect().find('li').last().click({ force: true }); WorkflowPage.getters.workflowSettingsTimeoutWorkflowSwitch().click(); WorkflowPage.getters.workflowSettingsTimeoutForm().find('input').first().type('1'); // Save settings WorkflowPage.getters.workflowSettingsSaveButton().click(); WorkflowPage.getters.workflowSettingsModal().should('not.exist'); WorkflowPage.getters.successToast().should('exist'); - }) + }); }); it('should not be able to delete unsaved workflow', () => { @@ -245,7 +223,7 @@ describe('Workflow Actions', () => { .find('.el-select__tags input') .type(DUPLICATE_WORKFLOW_TAG); WorkflowPage.getters.duplicateWorkflowModal().find('.el-select__tags input').type('{enter}'); - WorkflowPage.getters.duplicateWorkflowModal().find('.el-select__tags input').type('{enter}'); + WorkflowPage.getters.duplicateWorkflowModal().find('.el-select__tags input').type('{esc}'); WorkflowPage.getters .duplicateWorkflowModal() .find('button') diff --git a/cypress/pages/credentials.ts b/cypress/pages/credentials.ts index 7d3bf7ac9855e..733c030caa235 100644 --- a/cypress/pages/credentials.ts +++ b/cypress/pages/credentials.ts @@ -5,7 +5,7 @@ export class CredentialsPage extends BasePage { getters = { emptyListCreateCredentialButton: () => cy.getByTestId('empty-resources-list').find('button'), createCredentialButton: () => cy.getByTestId('resources-list-add'), - searchInput: () => cy.getByTestId('resources-list-search').find('input'), + searchInput: () => cy.getByTestId('resources-list-search'), emptyList: () => cy.getByTestId('resources-list-empty'), credentialCards: () => cy.getByTestId('resources-list-item'), credentialCard: (credentialName: string) => @@ -17,8 +17,8 @@ export class CredentialsPage extends BasePage { this.getters.credentialCard(credentialName).findChildByTestId('credential-card-actions'), credentialDeleteButton: () => cy.getByTestId('action-toggle-dropdown').filter(':visible').contains('Delete'), - sort: () => cy.getByTestId('resources-list-sort'), - sortOption: (label: string) => this.getters.sort().contains(label).first(), + sort: () => cy.getByTestId('resources-list-sort').first(), + sortOption: (label: string) => cy.getByTestId('resources-list-sort-item').contains(label).first(), filtersTrigger: () => cy.getByTestId('resources-list-filters-trigger'), filtersDropdown: () => cy.getByTestId('resources-list-filters-dropdown'), }; diff --git a/cypress/pages/modals/credentials-modal.ts b/cypress/pages/modals/credentials-modal.ts index 99d712e7e3f87..312e9edbf56c1 100644 --- a/cypress/pages/modals/credentials-modal.ts +++ b/cypress/pages/modals/credentials-modal.ts @@ -20,7 +20,7 @@ export class CredentialsModal extends BasePage { credentialsEditModal: () => cy.getByTestId('credential-edit-dialog'), credentialsAuthTypeSelector: () => cy.getByTestId('node-auth-type-selector'), credentialAuthTypeRadioButtons: () => - this.getters.credentialsAuthTypeSelector().find('label[role=radio]'), + this.getters.credentialsAuthTypeSelector().find('label.el-radio'), credentialInputs: () => cy.getByTestId('credential-connection-parameter'), menu: () => this.getters.editCredentialModal().get('.menu-container'), menuItem: (name: string) => this.getters.menu().get('.n8n-menu-item').contains(name), @@ -42,7 +42,7 @@ export class CredentialsModal extends BasePage { }, save: (test = false) => { cy.intercept('POST', '/rest/credentials').as('saveCredential'); - this.getters.saveButton().click(); + this.getters.saveButton().click({ force: true }); cy.wait('@saveCredential'); if (test) cy.wait('@testCredential'); diff --git a/cypress/pages/modals/message-box.ts b/cypress/pages/modals/message-box.ts index cfa38368b8d67..95f259ccb4838 100644 --- a/cypress/pages/modals/message-box.ts +++ b/cypress/pages/modals/message-box.ts @@ -5,15 +5,15 @@ export class MessageBox extends BasePage { modal: () => cy.get('.el-message-box', { withinSubject: null }), header: () => this.getters.modal().find('.el-message-box__title'), content: () => this.getters.modal().find('.el-message-box__content'), - confirm: () => this.getters.modal().find('.btn--confirm'), - cancel: () => this.getters.modal().find('.btn--cancel'), + confirm: () => this.getters.modal().find('.btn--confirm').first(), + cancel: () => this.getters.modal().find('.btn--cancel').first(), }; actions = { confirm: () => { - this.getters.confirm().click(); + this.getters.confirm().click({ force: true}); }, cancel: () => { - this.getters.cancel().click(); + this.getters.cancel().click({ force: true}); }, }; } diff --git a/cypress/pages/ndv.ts b/cypress/pages/ndv.ts index 3d580c1bf49ab..bde403a0fe626 100644 --- a/cypress/pages/ndv.ts +++ b/cypress/pages/ndv.ts @@ -1,4 +1,5 @@ import { BasePage } from './base'; +import { getVisibleSelect } from '../utils'; export class NDV extends BasePage { getters = { @@ -101,10 +102,11 @@ export class NDV extends BasePage { this.getters.parameterInput(parameterName).type(content); }, selectOptionInParameterDropdown: (parameterName: string, content: string) => { - this.getters.parameterInput(parameterName).find('.option-headline').contains(content).click(); + getVisibleSelect().find('.option-headline').contains(content).click(); }, dismissMappingTooltip: () => { cy.getByTestId('dismiss-mapping-tooltip').click(); + cy.getByTestId('dismiss-mapping-tooltip').should('not.be.visible'); }, rename: (newName: string) => { this.getters.nodeNameContainer().click(); @@ -139,11 +141,11 @@ export class NDV extends BasePage { }, changeInputRunSelector: (runName: string) => { this.getters.inputRunSelector().click(); - cy.get('.el-select-dropdown:visible .el-select-dropdown__item').contains(runName).click(); + getVisibleSelect().find('.el-select-dropdown__item').contains(runName).click(); }, changeOutputRunSelector: (runName: string) => { this.getters.outputRunSelector().click(); - cy.get('.el-select-dropdown:visible .el-select-dropdown__item').contains(runName).click(); + getVisibleSelect().find('.el-select-dropdown__item').contains(runName).click(); }, toggleOutputRunLinking: () => { this.getters.outputRunSelector().find('button').click(); @@ -159,7 +161,7 @@ export class NDV extends BasePage { }, setRLCValue: (paramName: string, value: string) => { this.getters.resourceLocatorModeSelector(paramName).click(); - this.getters.resourceLocatorModeSelector(paramName).find('li').last().click(); + getVisibleSelect().find('li').last().click(); this.getters.resourceLocatorInput(paramName).type(value); }, validateExpressionPreview: (paramName: string, value: string) => { diff --git a/cypress/pages/settings-log-streaming.ts b/cypress/pages/settings-log-streaming.ts index b95fcb38bfa63..2d056a4444c04 100644 --- a/cypress/pages/settings-log-streaming.ts +++ b/cypress/pages/settings-log-streaming.ts @@ -1,4 +1,5 @@ import { BasePage } from './base'; +import { getVisibleSelect } from '../utils'; export class SettingsLogStreamingPage extends BasePage { url = '/settings/log-streaming'; @@ -6,11 +7,9 @@ export class SettingsLogStreamingPage extends BasePage { getActionBoxUnlicensed: () => cy.getByTestId('action-box-unlicensed'), getActionBoxLicensed: () => cy.getByTestId('action-box-licensed'), getDestinationModal: () => cy.getByTestId('destination-modal'), - getDestinationModalDialog: () => this.getters.getDestinationModal().find('.el-dialog'), getSelectDestinationType: () => cy.getByTestId('select-destination-type'), getDestinationNameInput: () => cy.getByTestId('subtitle-showing-type'), - getSelectDestinationTypeItems: () => - this.getters.getSelectDestinationType().find('.el-select-dropdown__item'), + getSelectDestinationTypeItems: () => getVisibleSelect().find('.el-select-dropdown__item'), getSelectDestinationButton: () => cy.getByTestId('select-destination-button'), getContactUsButton: () => this.getters.getActionBoxUnlicensed().find('button'), getAddFirstDestinationButton: () => this.getters.getActionBoxLicensed().find('button'), diff --git a/cypress/pages/settings-personal.ts b/cypress/pages/settings-personal.ts index a454837011a19..0b129a29bc81b 100644 --- a/cypress/pages/settings-personal.ts +++ b/cypress/pages/settings-personal.ts @@ -11,7 +11,7 @@ export class PersonalSettingsPage extends BasePage { lastNameInput: () => cy.getByTestId('lastName').find('input').first(), emailInputContainer: () => cy.getByTestId('email'), emailInput: () => cy.getByTestId('email').find('input').first(), - changePasswordLink: () => cy.getByTestId('change-password-link').find('a').first(), + changePasswordLink: () => cy.getByTestId('change-password-link').first(), saveSettingsButton: () => cy.getByTestId('save-settings-button'), }; actions = { @@ -34,7 +34,10 @@ export class PersonalSettingsPage extends BasePage { }, tryToSetWeakPassword: (oldPassword: string, newPassword: string) => { this.actions.updatePassword(oldPassword, newPassword); - changePasswordModal.getters.newPasswordInputContainer().find('div[class^="_errorInput"]').should('exist'); + changePasswordModal.getters + .newPasswordInputContainer() + .find('div[class^="_errorInput"]') + .should('exist'); }, updateEmail: (newEmail: string) => { this.getters.emailInput().type('{selectall}').type(newEmail).type('{enter}'); diff --git a/cypress/pages/settings-users.ts b/cypress/pages/settings-users.ts index 41ae18711468f..d9a2e32df9f8b 100644 --- a/cypress/pages/settings-users.ts +++ b/cypress/pages/settings-users.ts @@ -4,8 +4,8 @@ import { WorkflowPage } from './workflow'; import { WorkflowsPage } from './workflows'; import { BasePage } from './base'; -const workflowPage = new WorkflowPage(); -const workflowsPage = new WorkflowsPage(); +const workflowPage = new WorkflowPage(); +const workflowsPage = new WorkflowsPage(); const mainSidebar = new MainSidebar(); const settingsSidebar = new SettingsSidebar(); @@ -18,11 +18,15 @@ export class SettingsUsersPage extends BasePage { inviteUsersModalEmailsInput: () => cy.getByTestId('emails').find('input').first(), userListItems: () => cy.get('[data-test-id^="user-list-item"]'), userItem: (email: string) => cy.getByTestId(`user-list-item-${email.toLowerCase()}`), - userActionsToggle: (email: string) => this.getters.userItem(email).find('[data-test-id="action-toggle"]'), - deleteUserAction: () => cy.getByTestId('action-toggle-dropdown').find('li:contains("Delete"):visible'), + userActionsToggle: (email: string) => + this.getters.userItem(email).find('[data-test-id="action-toggle"]'), + deleteUserAction: () => + cy.getByTestId('action-toggle-dropdown').find('li:contains("Delete"):visible'), confirmDeleteModal: () => cy.getByTestId('deleteUser-modal').last(), - transferDataRadioButton: () => this.getters.confirmDeleteModal().find('[role="radio"]').first(), - deleteDataRadioButton: () => this.getters.confirmDeleteModal().find('[role="radio"]').last(), + transferDataRadioButton: () => + this.getters.confirmDeleteModal().find('.el-radio .el-radio__input').first(), + deleteDataRadioButton: () => + this.getters.confirmDeleteModal().find('.el-radio .el-radio__input').last(), userSelectDropDown: () => this.getters.confirmDeleteModal().find('.n8n-select'), userSelectOptions: () => cy.get('.el-select-dropdown:visible .el-select-dropdown__item'), deleteUserButton: () => this.getters.confirmDeleteModal().find('button:contains("Delete")'), diff --git a/cypress/pages/variables.ts b/cypress/pages/variables.ts index 721d874351cfe..6091e5cf1b54a 100644 --- a/cypress/pages/variables.ts +++ b/cypress/pages/variables.ts @@ -10,7 +10,7 @@ export class VariablesPage extends BasePage { goToUpgrade: () => cy.getByTestId('go-to-upgrade'), actionBox: () => cy.getByTestId('action-box'), emptyResourcesListNewVariableButton: () => this.getters.emptyResourcesList().find('button'), - searchBar: () => cy.getByTestId('resources-list-search').find('input'), + searchBar: () => cy.getByTestId('resources-list-search'), createVariableButton: () => cy.getByTestId('resources-list-add'), variablesRows: () => cy.getByTestId('variables-row'), variablesEditableRows: () => diff --git a/cypress/pages/workflow.ts b/cypress/pages/workflow.ts index d324314dcc2d0..72aca5977ab90 100644 --- a/cypress/pages/workflow.ts +++ b/cypress/pages/workflow.ts @@ -1,5 +1,6 @@ import { META_KEY } from '../constants'; import { BasePage } from './base'; +import { getVisibleSelect } from '../utils'; export class WorkflowPage extends BasePage { url = '/workflow/new'; @@ -16,7 +17,7 @@ export class WorkflowPage extends BasePage { nthTagPill: (n: number) => cy.get(`[data-test-id="workflow-tags-container"] span.tags > span:nth-child(${n})`), tagsDropdown: () => cy.getByTestId('workflow-tags-dropdown'), - tagsInDropdown: () => cy.getByTestId('workflow-tags-dropdown').find('li').filter('.tag'), + tagsInDropdown: () => getVisibleSelect().find('li').filter('.tag'), createTagButton: () => cy.getByTestId('new-tag-link'), saveButton: () => cy.getByTestId('workflow-save-button'), nodeCreatorSearchBar: () => cy.getByTestId('node-creator-search-bar'), @@ -37,8 +38,8 @@ export class WorkflowPage extends BasePage { canvasNodePlusEndpointByName: (nodeName: string, index = 0) => { return cy.get(this.getters.getEndpointSelector('plus', nodeName, index)); }, - successToast: () => cy.get('.el-notification .el-icon-success').parent(), - errorToast: () => cy.get('.el-notification .el-icon-error'), + successToast: () => cy.get('.el-notification .el-notification--success').parent(), + errorToast: () => cy.get('.el-notification .el-notification--error'), activatorSwitch: () => cy.getByTestId('workflow-activate-switch'), workflowMenu: () => cy.getByTestId('workflow-menu'), firstStepButton: () => cy.getByTestId('canvas-add-button'), @@ -84,7 +85,8 @@ export class WorkflowPage extends BasePage { duplicateWorkflowModal: () => cy.getByTestId('duplicate-modal'), nodeViewBackground: () => cy.getByTestId('node-view-background'), nodeView: () => cy.getByTestId('node-view'), - inlineExpressionEditorInput: () => cy.getByTestId('inline-expression-editor-input').find('[role=textbox]'), + inlineExpressionEditorInput: () => + cy.getByTestId('inline-expression-editor-input').find('[role=textbox]'), inlineExpressionEditorOutput: () => cy.getByTestId('inline-expression-editor-output'), zoomInButton: () => cy.getByTestId('zoom-in-button'), zoomOutButton: () => cy.getByTestId('zoom-out-button'), @@ -92,8 +94,10 @@ export class WorkflowPage extends BasePage { executeWorkflowButton: () => cy.getByTestId('execute-workflow-button'), clearExecutionDataButton: () => cy.getByTestId('clear-execution-data-button'), stopExecutionButton: () => cy.getByTestId('stop-execution-button'), - stopExecutionWaitingForWebhookButton: () => cy.getByTestId('stop-execution-waiting-for-webhook-button'), + stopExecutionWaitingForWebhookButton: () => + cy.getByTestId('stop-execution-waiting-for-webhook-button'), nodeCredentialsSelect: () => cy.getByTestId('node-credentials-select'), + nodeCredentialsCreateOption: () => cy.getByTestId('node-credentials-select-item-new'), nodeCredentialsEditButton: () => cy.getByTestId('credential-edit-button'), nodeCreatorItems: () => cy.getByTestId('item-iterator-item'), ndvParameters: () => cy.getByTestId('parameter-item'), @@ -134,17 +138,17 @@ export class WorkflowPage extends BasePage { this.getters.nodeCreatorSearchBar().type(nodeDisplayName); this.getters.nodeCreatorSearchBar().type('{enter}'); - cy.wait(500) + cy.wait(500); cy.get('body').then((body) => { - if(body.find('[data-test-id=node-creator]').length > 0) { - if(action) { - cy.contains(action).click() + if (body.find('[data-test-id=node-creator]').length > 0) { + if (action) { + cy.contains(action).click(); } else { // Select the first action - cy.get('[data-keyboard-nav-type="action"]').eq(0).click() + cy.get('[data-keyboard-nav-type="action"]').eq(0).click(); } } - }) + }); if (!preventNdvClose) cy.get('body').type('{esc}'); }, @@ -157,7 +161,8 @@ export class WorkflowPage extends BasePage { }, openTagManagerModal: () => { this.getters.createTagButton().click(); - this.getters.tagsDropdown().find('li.manage-tags').first().click(); + this.getters.tagsDropdown().click(); + getVisibleSelect().find('li.manage-tags').first().click(); }, openInlineExpressionEditor: () => { cy.contains('Expression').invoke('show').click(); @@ -209,7 +214,7 @@ export class WorkflowPage extends BasePage { this.getters.workflowTagsInput().type(tag); this.getters.workflowTagsInput().type('{enter}'); }); - cy.get('body').type('{enter}'); + cy.get('body').click(0, 0); // For a brief moment the Element UI tag component shows the tags as(+X) string // so we need to wait for it to disappear this.getters.workflowTagsContainer().should('not.contain', `+${tags.length}`); @@ -241,7 +246,12 @@ export class WorkflowPage extends BasePage { executeWorkflow: () => { this.getters.executeWorkflowButton().click(); }, - addNodeBetweenNodes: (sourceNodeName: string, targetNodeName: string, newNodeName: string, action?: string) => { + addNodeBetweenNodes: ( + sourceNodeName: string, + targetNodeName: string, + newNodeName: string, + action?: string, + ) => { this.getters.getConnectionBetweenNodes(sourceNodeName, targetNodeName).first().realHover(); this.getters .getConnectionActionsBetweenNodes(sourceNodeName, targetNodeName) @@ -268,18 +278,10 @@ export class WorkflowPage extends BasePage { this.getters.addStickyButton().click(); }, deleteSticky: () => { - this.getters.stickies().eq(0) - .realHover() - .find('[data-test-id="delete-sticky"]') - .click(); + this.getters.stickies().eq(0).realHover().find('[data-test-id="delete-sticky"]').click(); }, editSticky: (content: string) => { - this.getters.stickies() - .dblclick() - .find('textarea') - .clear() - .type(content) - .type('{esc}'); + this.getters.stickies().dblclick().find('textarea').clear().type(content).type('{esc}'); }, }; } diff --git a/cypress/pages/workflows.ts b/cypress/pages/workflows.ts index d5700d07841fa..416528e85c87b 100644 --- a/cypress/pages/workflows.ts +++ b/cypress/pages/workflows.ts @@ -5,7 +5,7 @@ export class WorkflowsPage extends BasePage { getters = { newWorkflowButtonCard: () => cy.getByTestId('new-workflow-card'), newWorkflowTemplateCard: () => cy.getByTestId('new-workflow-template-card'), - searchBar: () => cy.getByTestId('resources-list-search').find('input'), + searchBar: () => cy.getByTestId('resources-list-search'), createWorkflowButton: () => cy.getByTestId('resources-list-add'), workflowCards: () => cy.getByTestId('resources-list-item'), workflowCard: (workflowName: string) => diff --git a/cypress/support/commands.ts b/cypress/support/commands.ts index 6c7adaea8f84b..1a1925b6a4697 100644 --- a/cypress/support/commands.ts +++ b/cypress/support/commands.ts @@ -53,12 +53,12 @@ Cypress.Commands.add('signin', ({ email, password }) => { }); Cypress.Commands.add('signout', () => { - cy.request('POST', '/rest/logout'); + cy.request('POST', `${BACKEND_BASE_URL}/rest/logout`); cy.getCookie(N8N_AUTH_COOKIE).should('not.exist'); }); Cypress.Commands.add('interceptREST', (method, url) => { - cy.intercept(method, `http://localhost:5678/rest${url}`); + cy.intercept(method, `${BACKEND_BASE_URL}/rest${url}`); }); const setFeature = (feature: string, enabled: boolean) => diff --git a/cypress/support/e2e.ts b/cypress/support/e2e.ts index dc3079282f232..a750918c6d051 100644 --- a/cypress/support/e2e.ts +++ b/cypress/support/e2e.ts @@ -6,6 +6,10 @@ before(() => { owner: INSTANCE_OWNER, members: INSTANCE_MEMBERS, }); + + Cypress.on('uncaught:exception', (err) => { + return !err.message.includes('ResizeObserver'); + }); }); beforeEach(() => { diff --git a/cypress/utils/index.ts b/cypress/utils/index.ts new file mode 100644 index 0000000000000..1929454b184ac --- /dev/null +++ b/cypress/utils/index.ts @@ -0,0 +1 @@ +export * from './popper'; diff --git a/cypress/utils/modal.ts b/cypress/utils/modal.ts new file mode 100644 index 0000000000000..4b260ca9e858a --- /dev/null +++ b/cypress/utils/modal.ts @@ -0,0 +1,3 @@ +export function getVisibleModalOverlay() { + return cy.get('.el-overlay .el-overlay-dialog').filter(':visible'); +} diff --git a/cypress/utils/popper.ts b/cypress/utils/popper.ts new file mode 100644 index 0000000000000..846b2ec88e39d --- /dev/null +++ b/cypress/utils/popper.ts @@ -0,0 +1,11 @@ +export function getVisiblePopper() { + return cy.get('.el-popper').filter(':visible'); +} + +export function getVisibleSelect() { + return getVisiblePopper().filter('.el-select__popper'); +} + +export function getVisibleDropdown() { + return getVisiblePopper().filter('.el-dropdown__popper'); +} diff --git a/package.json b/package.json index 4784ccb8469ec..015172f52aedd 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,6 @@ "@ngneat/falso": "^6.1.0", "@types/jest": "^29.5.0", "@types/supertest": "^2.0.12", - "@vitejs/plugin-vue2": "^2.2.0", "@vitest/coverage-c8": "^0.28.5", "c8": "^7.12.0", "cross-env": "^7.0.3", @@ -64,7 +63,6 @@ "typescript": "*", "vite": "^4.0.4", "vitest": "^0.28.5", - "vue-template-compiler": "^2.7.14", "vue-tsc": "^1.0.24" }, "pnpm": { @@ -92,7 +90,6 @@ "qqjs>globby": "^11.1.0" }, "patchedDependencies": { - "element-ui@2.15.12": "patches/element-ui@2.15.12.patch", "typedi@0.10.0": "patches/typedi@0.10.0.patch", "@sentry/cli@2.17.0": "patches/@sentry__cli@2.17.0.patch", "pkce-challenge@3.0.0": "patches/pkce-challenge@3.0.0.patch", diff --git a/packages/@n8n_io/eslint-config/frontend.js b/packages/@n8n_io/eslint-config/frontend.js index e36d4c33b7938..a93c510e2052a 100644 --- a/packages/@n8n_io/eslint-config/frontend.js +++ b/packages/@n8n_io/eslint-config/frontend.js @@ -4,7 +4,7 @@ module.exports = { plugins: ['vue'], - extends: ['plugin:vue/essential', '@vue/typescript', './base'], + extends: ['plugin:vue/vue3-essential', '@vue/typescript', './base'], env: { browser: true, @@ -37,6 +37,12 @@ module.exports = { 'vue/no-unused-components': 'error', 'vue/multi-word-component-names': 'off', + // TODO: fix these + '@typescript-eslint/no-unsafe-call': 'off', + '@typescript-eslint/no-unsafe-assignment': 'off', + '@typescript-eslint/restrict-template-expressions': 'off', + '@typescript-eslint/unbound-method': 'off', + // TODO: remove these 'vue/no-mutating-props': 'warn', 'vue/no-side-effects-in-computed-properties': 'warn', diff --git a/packages/design-system/.storybook/font-awesome-icons.js b/packages/design-system/.storybook/font-awesome-icons.js deleted file mode 100644 index 8713b3b6613f6..0000000000000 --- a/packages/design-system/.storybook/font-awesome-icons.js +++ /dev/null @@ -1,8 +0,0 @@ -/** - * These icons are only defined for storybook build - * Editor icons are defined seperately - */ -import { library } from '@fortawesome/fontawesome-svg-core'; -import { fas } from '@fortawesome/free-solid-svg-icons'; - -library.add(fas); diff --git a/packages/design-system/.storybook/main.js b/packages/design-system/.storybook/main.js index d3a302a1b40a4..8db209694203f 100644 --- a/packages/design-system/.storybook/main.js +++ b/packages/design-system/.storybook/main.js @@ -1,59 +1,42 @@ -const path = require('path'); +const { mergeConfig } = require('vite'); +const { resolve } = require('path'); -/** - * @type {import('@storybook/types').StorybookConfig} - */ module.exports = { - framework: { - name: '@storybook/vue-webpack5', - options: {}, - }, - stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.{ts,js}'], + stories: ['../src/**/*.stories.@(js|jsx|ts|tsx|mdx)'], addons: [ + '@storybook/addon-styling', '@storybook/addon-links', '@storybook/addon-essentials', - { - name: '@storybook/addon-postcss', - options: { - postcssLoaderOptions: { - implementation: require('postcss'), - }, - }, - }, - 'storybook-addon-themes', + // Disabled until this is actually used rather otherwise its a blank tab + // '@storybook/addon-interactions', + '@storybook/addon-a11y', + 'storybook-dark-mode', ], - webpackFinal: async (config) => { - config.module.rules.push({ - test: /\.scss$/, - oneOf: [ - { - resourceQuery: /module/, - use: [ - 'vue-style-loader', - { - loader: 'css-loader', - options: { - modules: { - localIdentName: '[path][name]__[local]--[hash:base64:5]', - }, - }, - }, - 'sass-loader', - ], - include: path.resolve(__dirname, '../'), - }, - { - use: ['vue-style-loader', 'css-loader', 'sass-loader'], - include: path.resolve(__dirname, '../'), - }, - ], + staticDirs: ['../public'], + framework: { + name: '@storybook/vue3-vite', + options: {}, + }, + disableTelemetry: true, + async viteFinal(config, { configType }) { + // return the customized config + return mergeConfig(config, { + // customize the Vite config here + resolve: { + alias: [ + { + find: /^@n8n-design-system\//, + replacement: `${resolve(__dirname, '..')}/src/`, + }, + { + find: /^n8n-design-system$/, + replacement: `${resolve(__dirname, '..')}/src/main.ts`, + }, + ], + }, }); - - config.resolve.alias = { - ...config.resolve.alias, - '@/': path.resolve(__dirname, '../src/'), - }; - - return config; + }, + docs: { + autodocs: true, }, }; diff --git a/packages/design-system/.storybook/preview.js b/packages/design-system/.storybook/preview.js index 1e01ec1dd4fdc..49708b358aec6 100644 --- a/packages/design-system/.storybook/preview.js +++ b/packages/design-system/.storybook/preview.js @@ -1,23 +1,24 @@ -import './font-awesome-icons'; +import { setup } from '@storybook/vue3'; + import './storybook.scss'; -import ElementUI from 'element-ui'; -import lang from 'element-ui/lib/locale/lang/en'; -import locale from 'element-ui/lib/locale'; +import { library } from '@fortawesome/fontawesome-svg-core'; +import { fas } from '@fortawesome/free-solid-svg-icons'; -import { N8nPlugin } from '../src/plugin'; +import ElementPlus from 'element-plus'; +import lang from 'element-plus/lib/locale/lang/en'; -import Vue from 'vue'; +import { N8nPlugin } from '../src/plugin'; -Vue.use(ElementUI); -Vue.use(N8nPlugin); +setup((app) => { + library.add(fas); -locale.use(lang); + app.use(ElementPlus, { + locale: lang, + }); -// https://github.com/storybookjs/storybook/issues/6153 -Vue.prototype.toJSON = function () { - return this; -}; + app.use(N8nPlugin); +}); export const parameters = { actions: { diff --git a/packages/design-system/.storybook/storybook.scss b/packages/design-system/.storybook/storybook.scss index 125f7d57a871b..ab48133c1eed9 100644 --- a/packages/design-system/.storybook/storybook.scss +++ b/packages/design-system/.storybook/storybook.scss @@ -1,12 +1,17 @@ @use './fonts.scss'; -@use '~/src/css/base.scss' with ( - $font-path: '~element-ui/lib/theme-chalk/fonts' -); +@use '../src/css/base.scss'; // @TODO CHECK IF NEEDED with ( +// $font-path: 'element-ui/lib/theme-chalk/fonts' +//); -@use '~/src/css/reset.scss'; -@use '~/src/css/index.scss'; +@use '../src/css/reset.scss'; +@use '../src/css/index.scss'; .multi-container > * { margin-bottom: 10px; } + +#storybook-root > div:not([class]) > *, +#storybook-root > * { + margin: var(--spacing-5xs); +} diff --git a/packages/design-system/package.json b/packages/design-system/package.json index d121f71adffd9..0e7b3b07a8a47 100644 --- a/packages/design-system/package.json +++ b/packages/design-system/package.json @@ -40,38 +40,43 @@ "devDependencies": { "@fortawesome/fontawesome-svg-core": "^1.2.36", "@fortawesome/free-solid-svg-icons": "^5.15.4", - "@fortawesome/vue-fontawesome": "^2.0.9", - "@storybook/addon-actions": "^7.0.7", - "@storybook/addon-docs": "^7.0.7", - "@storybook/addon-essentials": "^7.0.7", - "@storybook/addon-links": "^7.0.7", - "@storybook/addon-postcss": "^3.0.0-alpha.1", - "@storybook/vue": "^7.0.7", - "@storybook/vue-webpack5": "^7.0.7", + "@fortawesome/vue-fontawesome": "^3.0.3", + "@storybook/addon-a11y": "^7.0.21", + "@storybook/addon-actions": "^7.0.21", + "@storybook/addon-docs": "^7.0.21", + "@storybook/addon-essentials": "^7.0.21", + "@storybook/addon-links": "^7.0.21", + "@storybook/addon-postcss": "3.0.0-alpha.1", + "@storybook/addon-styling": "^1.3.0", + "@storybook/vue3": "^7.0.21", + "@storybook/vue3-vite": "^7.0.21", "@testing-library/jest-dom": "^5.16.5", "@testing-library/user-event": "^14.4.3", - "@testing-library/vue": "^5.8.3", + "@testing-library/vue": "^6.6.1", "@types/markdown-it": "^12.2.3", "@types/markdown-it-emoji": "^2.0.2", "@types/markdown-it-link-attributes": "^3.0.1", - "@types/sanitize-html": "^2.8.0", - "autoprefixer": "^10.4.13", - "core-js": "^3.27.2", + "@types/sanitize-html": "^2.9.0", + "@vitejs/plugin-vue": "^4.2.3", + "@vue/test-utils": "^2.4.1", + "autoprefixer": "^10.4.14", + "core-js": "^3.31.0", "jsdom": "21.1.0", - "sass": "^1.58.0", - "sass-loader": "^13.2.0", - "storybook": "^7.0.7", - "storybook-addon-themes": "^6.1.0" + "sass": "^1.63.4", + "sass-loader": "^13.3.2", + "storybook": "^7.0.21", + "storybook-addon-themes": "^6.1.0", + "storybook-dark-mode": "^3.0.0" }, "dependencies": { - "element-ui": "~2.15.12", + "element-plus": "^2.3.6", "markdown-it": "^13.0.1", "markdown-it-emoji": "^2.0.2", "markdown-it-link-attributes": "^4.0.1", "markdown-it-task-lists": "^2.1.1", "sanitize-html": "2.10.0", - "vue": "^2.7.14", - "vue2-boring-avatars": "^0.3.8", + "vue": "^3.3.4", + "vue-boring-avatars": "^1.3.0", "xss": "^1.0.14" } } diff --git a/packages/design-system/public/assets/images/storybook-logo-dark.png b/packages/design-system/public/assets/images/storybook-logo-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..fa5e878f0b6ed1a37158a9c6cf8a18d51a0cd304 GIT binary patch literal 8215 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 cQ;yn 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 ({ + setup: () => ({ args }), props: Object.keys(argTypes), components: { N8nActionBox, }, - template: '', + template: '', methods, }); diff --git a/packages/design-system/src/components/N8nActionBox/ActionBox.vue b/packages/design-system/src/components/N8nActionBox/ActionBox.vue index a21f6e00d5f75..3995e083491d3 100644 --- a/packages/design-system/src/components/N8nActionBox/ActionBox.vue +++ b/packages/design-system/src/components/N8nActionBox/ActionBox.vue @@ -20,7 +20,7 @@ :label="buttonText" :type="buttonType" size="large" - @click="$emit('click', $event)" + @click="$emit('click:button', $event)" /> { 'Long description that you should know something is the way it is because of how it is. ', buttonText: 'Do something', }, - stubs: ['n8n-heading', 'n8n-text', 'n8n-button', 'n8n-callout'], + global: { + stubs: ['n8n-heading', 'n8n-text', 'n8n-button', 'n8n-callout'], + }, }); expect(wrapper.html()).toMatchSnapshot(); }); diff --git a/packages/design-system/src/components/N8nActionBox/__tests__/__snapshots__/ActionBox.spec.ts.snap b/packages/design-system/src/components/N8nActionBox/__tests__/__snapshots__/ActionBox.spec.ts.snap index 40c7e9f605076..491d107483b22 100644 --- a/packages/design-system/src/components/N8nActionBox/__tests__/__snapshots__/ActionBox.spec.ts.snap +++ b/packages/design-system/src/components/N8nActionBox/__tests__/__snapshots__/ActionBox.spec.ts.snap @@ -1,15 +1,15 @@ // Vitest Snapshot v1 exports[`N8NActionBox > should render correctly 1`] = ` -"
-
😿
+"
+
😿
- Headline you need to know +
- Long description that you should know something is the way it is because of how it is. +
- - + +
" `; diff --git a/packages/design-system/src/components/N8nActionDropdown/ActionDropdown.stories.ts b/packages/design-system/src/components/N8nActionDropdown/ActionDropdown.stories.ts index feb43c23bf2ee..8e229231df646 100644 --- a/packages/design-system/src/components/N8nActionDropdown/ActionDropdown.stories.ts +++ b/packages/design-system/src/components/N8nActionDropdown/ActionDropdown.stories.ts @@ -1,5 +1,5 @@ import N8nActionDropdown from './ActionDropdown.vue'; -import type { StoryFn } from '@storybook/vue'; +import type { StoryFn } from '@storybook/vue3'; export default { title: 'Atoms/ActionDropdown', @@ -8,8 +8,8 @@ export default { placement: { control: { type: 'select', - options: ['top', 'top-end', 'top-start', 'bottom', 'bottom-end', 'bottom-start'], }, + options: ['top', 'top-end', 'top-start', 'bottom', 'bottom-end', 'bottom-start'], }, activatorIcon: { control: { @@ -19,18 +19,19 @@ export default { trigger: { control: { type: 'select', - options: ['click', 'hover'], }, + options: ['click', 'hover'], }, }, }; const template: StoryFn = (args, { argTypes }) => ({ + setup: () => ({ args }), props: Object.keys(argTypes), components: { N8nActionDropdown, }, - template: '', + template: '', }); export const defaultActionDropdown = template.bind({}); diff --git a/packages/design-system/src/components/N8nActionDropdown/ActionDropdown.vue b/packages/design-system/src/components/N8nActionDropdown/ActionDropdown.vue index 0c2177663310d..e37ddfa1e8c1d 100644 --- a/packages/design-system/src/components/N8nActionDropdown/ActionDropdown.vue +++ b/packages/design-system/src/components/N8nActionDropdown/ActionDropdown.vue @@ -6,7 +6,7 @@ @command="onSelect" ref="elementDropdown" > -
+