mirror of
https://github.com/n8n-io/n8n.git
synced 2024-11-15 09:04:07 -08:00
Merge branch 'master' of github.com:n8n-io/n8n into seatable_node_rework
This commit is contained in:
commit
aab002c74f
|
@ -7,3 +7,7 @@
|
|||
# refactor(editor): Apply Prettier (no-changelog) #4920
|
||||
|
||||
5ca2148c7ed06c90f999508928b7a51f9ac7a788
|
||||
|
||||
# refactor: Run lintfix (no-changelog) (#7537)
|
||||
|
||||
62c096710fab2f7e886518abdbded34b55e93f62
|
||||
|
|
17
.github/pull_request_template.md
vendored
17
.github/pull_request_template.md
vendored
|
@ -1 +1,16 @@
|
|||
Github issue / Community forum post (link here to close automatically):
|
||||
## Summary
|
||||
> Describe what the PR does and how to test. Photos and videos are recommended.
|
||||
|
||||
|
||||
|
||||
## Related tickets and issues
|
||||
> Include links to **Linear ticket** or Github issue or Community forum post. Important in order to close *automatically* and provide context to reviewers.
|
||||
|
||||
|
||||
|
||||
## Review / Merge checklist
|
||||
- [ ] PR title and summary are descriptive. **Remember, the title automatically goes into the changelog. Use `(no-changelog)` otherwise.** ([conventions](https://github.com/n8n-io/n8n/blob/master/.github/pull_request_title_conventions.md))
|
||||
- [ ] [Docs updated](https://github.com/n8n-io/n8n-docs) or follow-up ticket created.
|
||||
- [ ] Tests included.
|
||||
> A bug is not considered fixed, unless a test is added to prevent it from happening again.
|
||||
> A feature is not complete without tests.
|
112
.github/pull_request_title_conventions.md
vendored
Normal file
112
.github/pull_request_title_conventions.md
vendored
Normal file
|
@ -0,0 +1,112 @@
|
|||
# PR Title Convention
|
||||
|
||||
We have very precise rules over how Pull Requests (to the `master` branch) must be formatted. This format basically follows the [Angular Commit Message Convention](https://github.com/angular/angular/blob/master/CONTRIBUTING.md#commit). It leads to easier to read commit history and allows for automated generation of release notes:
|
||||
|
||||
A PR title consists of these elements:
|
||||
|
||||
```
|
||||
<type>(<scope>): <summary>
|
||||
│ │ │
|
||||
│ │ └─⫸ Summary: In imperative present tense.
|
||||
| | Capitalized
|
||||
| | No period at the end.
|
||||
│ │
|
||||
│ └─⫸ Scope: API|core|editor|* Node
|
||||
│
|
||||
└─⫸ Type: build|ci|docs|feat|fix|perf|refactor|test
|
||||
```
|
||||
|
||||
- PR title
|
||||
- type
|
||||
- scope (*optional*)
|
||||
- summary
|
||||
- PR description
|
||||
- body (optional)
|
||||
- blank line
|
||||
- footer (optional)
|
||||
|
||||
The structure looks like this:
|
||||
|
||||
### **Type**
|
||||
|
||||
Must be one of the following:
|
||||
|
||||
- `feat` - A new feature
|
||||
- `fix` - A bug fix
|
||||
- `perf` - A code change that improves performance
|
||||
- `test` - Adding missing tests or correcting existing tests
|
||||
- `docs` - Documentation only changes
|
||||
- `refactor` - A code change that neither fixes a bug nor adds a feature
|
||||
- `build` - Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)
|
||||
- `ci` - Changes to our CI configuration files and scripts (e.g. Github actions)
|
||||
|
||||
If the prefix is `feat`, `fix` or `perf`, it will appear in the changelog. However if there is any BREAKING CHANGE (see Footer section below), the commit will always appear in the changelog.
|
||||
|
||||
### **Scope (optional)**
|
||||
|
||||
The scope should specify the place of the commit change as long as the commit clearly addresses one of the following supported scopes. (Otherwise, omit the scope!)
|
||||
|
||||
- `API` - changes to the *public* API
|
||||
- `core` - changes to the core / private API / backend of n8n
|
||||
- `editor` - changes to the Editor UI
|
||||
- `* Node` - changes to a specific node or trigger node (”`*`” to be replaced with the node name, not its display name), e.g.
|
||||
- mattermost → Mattermost Node
|
||||
- microsoftToDo → Microsoft To Do Node
|
||||
- n8n → n8n Node
|
||||
|
||||
### **Summary**
|
||||
|
||||
The summary contains succinct description of the change:
|
||||
|
||||
- use the imperative, present tense: "change" not "changed" nor "changes"
|
||||
- capitalize the first letter
|
||||
- *no* dot (.) at the end
|
||||
- do *not* include Linear ticket IDs etc. (e.g. N8N-1234)
|
||||
- suffix with “(no-changelog)” for commits / PRs that should not get mentioned in the changelog.
|
||||
|
||||
### **Body (optional)**
|
||||
|
||||
Just as in the **summary**, use the imperative, present tense: "change" not "changed" nor "changes". The body should include the motivation for the change and contrast this with previous behavior.
|
||||
|
||||
### **Footer (optional)**
|
||||
|
||||
The footer can contain information about breaking changes and deprecations and is also the place to [reference GitHub issues](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword), Linear tickets, and other PRs that this commit closes or is related to. For example:
|
||||
|
||||
```
|
||||
BREAKING CHANGE: <breaking change summary>
|
||||
<BLANK LINE>
|
||||
<breaking change description + migration instructions>
|
||||
<BLANK LINE>
|
||||
<BLANK LINE>
|
||||
Fixes #<issue number>
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```
|
||||
DEPRECATED: <what is deprecated>
|
||||
<BLANK LINE>
|
||||
<deprecation description + recommended update path>
|
||||
<BLANK LINE>
|
||||
<BLANK LINE>
|
||||
Closes #<pr number>
|
||||
```
|
||||
|
||||
A Breaking Change section should start with the phrase "`BREAKING CHANGE:` " followed by a summary of the breaking change, a blank line, and a detailed description of the breaking change that also includes migration instructions.
|
||||
|
||||
> 💡 A breaking change can additionally also be marked by adding a “`!`” to the header, right before the “`:`”, e.g. `feat(editor)!: Remove support for dark mode`
|
||||
>
|
||||
> This makes locating breaking changes easier when just skimming through commit messages.
|
||||
|
||||
> 💡 The breaking changes must also be added to the [packages/cli/BREAKING-CHANGES.md](https://github.com/n8n-io/n8n/blob/master/packages/cli/BREAKING-CHANGES.md) file located in the n8n repository.
|
||||
|
||||
Similarly, a Deprecation section should start with "`DEPRECATED:` " followed by a short description of what is deprecated, a blank line, and a detailed description of the deprecation that also mentions the recommended update path.
|
||||
|
||||
### **Revert commits**
|
||||
|
||||
If the commit reverts a previous commit, it should begin with `revert:` , followed by the header of the reverted commit.
|
||||
|
||||
The content of the commit message body should contain:
|
||||
|
||||
- information about the SHA of the commit being reverted in the following format: `This reverts commit <SHA>`,
|
||||
- a clear description of the reason for reverting the commit message.
|
2
.github/scripts/bump-versions.mjs
vendored
2
.github/scripts/bump-versions.mjs
vendored
|
@ -43,7 +43,7 @@ for (const packageName in packageMap) {
|
|||
|
||||
packageJson.version = packageMap[packageName].nextVersion =
|
||||
isDirty ||
|
||||
Object.keys(packageJson.dependencies).some(
|
||||
Object.keys(packageJson.dependencies || {}).some(
|
||||
(dependencyName) => packageMap[dependencyName]?.isDirty,
|
||||
)
|
||||
? semver.inc(version, releaseType)
|
||||
|
|
9
.github/scripts/package.json
vendored
9
.github/scripts/package.json
vendored
|
@ -1,9 +1,12 @@
|
|||
{
|
||||
"dependencies": {
|
||||
"cacheable-lookup": "6.1.0",
|
||||
"conventional-changelog": "^4.0.0",
|
||||
"glob": "^10.3.0",
|
||||
"semver": "^7.5.4",
|
||||
"tempfile": "^5.0.0",
|
||||
"debug": "4.3.4",
|
||||
"glob": "10.3.10",
|
||||
"p-limit": "3.1.0",
|
||||
"semver": "7.5.4",
|
||||
"tempfile": "5.0.0",
|
||||
"typescript": "*"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,19 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
const packages = ['nodes-base', '@n8n/nodes-langchain'];
|
||||
const concurrency = 20;
|
||||
let exitCode = 0;
|
||||
|
||||
const debug = require('debug')('n8n');
|
||||
const path = require('path');
|
||||
const https = require('https');
|
||||
const glob = require('fast-glob');
|
||||
const glob = require('glob');
|
||||
const pLimit = require('p-limit');
|
||||
const Lookup = require('cacheable-lookup').default;
|
||||
|
||||
const nodesBaseDir = path.resolve(__dirname, '../packages/nodes-base');
|
||||
const agent = new https.Agent({ keepAlive: true, keepAliveMsecs: 5000 });
|
||||
new Lookup().install(agent);
|
||||
const limiter = pLimit(concurrency);
|
||||
|
||||
const validateUrl = async (kind, name, documentationUrl) =>
|
||||
new Promise((resolve, reject) => {
|
||||
|
@ -22,21 +30,26 @@ const validateUrl = async (kind, name, documentationUrl) =>
|
|||
port: 443,
|
||||
path: url.pathname,
|
||||
method: 'HEAD',
|
||||
agent,
|
||||
},
|
||||
(res) => {
|
||||
debug('✓', kind, name);
|
||||
resolve([name, res.statusCode]);
|
||||
},
|
||||
(res) => resolve([name, res.statusCode]),
|
||||
)
|
||||
.on('error', (e) => reject(e))
|
||||
.end();
|
||||
});
|
||||
|
||||
const checkLinks = async (kind) => {
|
||||
let types = require(path.join(nodesBaseDir, `dist/types/${kind}.json`));
|
||||
const checkLinks = async (baseDir, kind) => {
|
||||
let types = require(path.join(baseDir, `dist/types/${kind}.json`));
|
||||
if (kind === 'nodes')
|
||||
types = types.filter(({ codex }) => !!codex?.resources?.primaryDocumentation);
|
||||
const limit = pLimit(30);
|
||||
debug(kind, types.length);
|
||||
|
||||
const statuses = await Promise.all(
|
||||
types.map((type) =>
|
||||
limit(() => {
|
||||
limiter(() => {
|
||||
const documentationUrl =
|
||||
kind === 'credentials'
|
||||
? type.documentationUrl
|
||||
|
@ -55,10 +68,13 @@ const checkLinks = async (kind) => {
|
|||
|
||||
if (missingDocs.length) console.log('Documentation URL missing for %s', kind, missingDocs);
|
||||
if (invalidUrls.length) console.log('Documentation URL invalid for %s', kind, invalidUrls);
|
||||
if (missingDocs.length || invalidUrls.length) process.exit(1);
|
||||
if (missingDocs.length || invalidUrls.length) exitCode = 1;
|
||||
};
|
||||
|
||||
(async () => {
|
||||
await checkLinks('credentials');
|
||||
await checkLinks('nodes');
|
||||
for (const packageName of packages) {
|
||||
const baseDir = path.resolve(__dirname, '../../packages', packageName);
|
||||
await Promise.all([checkLinks(baseDir, 'credentials'), checkLinks(baseDir, 'nodes')]);
|
||||
if (exitCode !== 0) process.exit(exitCode);
|
||||
}
|
||||
})();
|
10
.github/workflows/check-documentation-urls.yml
vendored
10
.github/workflows/check-documentation-urls.yml
vendored
|
@ -27,16 +27,18 @@ jobs:
|
|||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Build nodes-base
|
||||
run: pnpm --filter @n8n/client-oauth2 --filter n8n-workflow --filter n8n-core --filter n8n-nodes-base build
|
||||
run: pnpm --filter @n8n/client-oauth2 --filter n8n-workflow --filter n8n-core --filter n8n-nodes-base --filter @n8n/n8n-nodes-langchain build
|
||||
|
||||
- name: Test URLS
|
||||
run: node scripts/validate-docs-links.js
|
||||
- run: npm install --prefix=.github/scripts --no-package-lock
|
||||
|
||||
- name: Test URLs
|
||||
run: node .github/scripts/validate-docs-links.js
|
||||
|
||||
- name: Notify Slack on failure
|
||||
uses: act10ns/slack@v2.0.0
|
||||
if: failure()
|
||||
with:
|
||||
status: ${{ job.status }}
|
||||
channel: '#updates-build-alerts'
|
||||
channel: '#mission-docs'
|
||||
webhook-url: ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||
message: Documentation URLs check failed (${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})
|
||||
|
|
2
.github/workflows/check-pr-title.yml
vendored
2
.github/workflows/check-pr-title.yml
vendored
|
@ -30,6 +30,6 @@ jobs:
|
|||
|
||||
- name: Validate PR title
|
||||
id: validate_pr_title
|
||||
uses: n8n-io/validate-n8n-pull-request-title@v1.1
|
||||
uses: n8n-io/validate-n8n-pull-request-title@v1.3
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
|
22
.github/workflows/checklist.yml
vendored
22
.github/workflows/checklist.yml
vendored
|
@ -1,22 +0,0 @@
|
|||
name: PR Checklist
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types:
|
||||
- opened
|
||||
- synchronize
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
checklist_job:
|
||||
runs-on: ubuntu-latest
|
||||
name: Checklist job
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3.5.3
|
||||
- name: Checklist
|
||||
uses: wyozi/contextual-qa-checklist-action@master
|
||||
with:
|
||||
gh-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
comment-footer: Make sure to check off this list before asking for review.
|
11
.github/workflows/ci-master.yml
vendored
11
.github/workflows/ci-master.yml
vendored
|
@ -13,7 +13,7 @@ jobs:
|
|||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [18.x, 20.5]
|
||||
node-version: [18.x, 20.x]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3.5.3
|
||||
|
@ -44,11 +44,12 @@ jobs:
|
|||
needs: install-and-build
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [18.x, 20.5]
|
||||
node-version: [18.x, 20.x]
|
||||
with:
|
||||
ref: ${{ inputs.branch }}
|
||||
nodeVersion: ${{ matrix.node-version }}
|
||||
cacheKey: ${{ github.sha }}-base:${{ matrix.node-version }}-test-lint
|
||||
cacheKey: ${{ github.sha }}-base:${{ matrix.node-version }}-test-lint
|
||||
collectCoverage: true
|
||||
|
||||
lint:
|
||||
name: Lint changes
|
||||
|
@ -56,7 +57,7 @@ jobs:
|
|||
needs: install-and-build
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [18.x, 20.5]
|
||||
node-version: [18.x, 20.x]
|
||||
steps:
|
||||
- uses: actions/checkout@v3.5.3
|
||||
with:
|
||||
|
@ -95,6 +96,6 @@ jobs:
|
|||
if: failure()
|
||||
with:
|
||||
status: ${{ job.status }}
|
||||
channel: '#updates-build-alerts'
|
||||
channel: '#alerts-build'
|
||||
webhook-url: ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||
message: Master branch (build or test or lint) failed (${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})
|
||||
|
|
14
.github/workflows/ci-postgres-mysql.yml
vendored
14
.github/workflows/ci-postgres-mysql.yml
vendored
|
@ -6,7 +6,11 @@ on:
|
|||
workflow_dispatch:
|
||||
pull_request:
|
||||
paths:
|
||||
- packages/cli/src/databases/migrations/**
|
||||
- packages/cli/src/databases/**
|
||||
|
||||
concurrency:
|
||||
group: db-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
@ -22,7 +26,7 @@ jobs:
|
|||
- run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Build Backend
|
||||
run: pnpm --filter @n8n/client-oauth2 --filter n8n-workflow --filter n8n-core --filter n8n build
|
||||
run: pnpm build:backend
|
||||
|
||||
- name: Cache build artifacts
|
||||
uses: actions/cache/save@v3.3.1
|
||||
|
@ -61,7 +65,7 @@ jobs:
|
|||
|
||||
- name: Test MySQL
|
||||
working-directory: packages/cli
|
||||
run: DB_TABLE_PREFIX=test_ pnpm test:mysql --runInBand
|
||||
run: pnpm test:mysql
|
||||
|
||||
postgres:
|
||||
name: Postgres
|
||||
|
@ -94,7 +98,7 @@ jobs:
|
|||
|
||||
- name: Test Postgres
|
||||
working-directory: packages/cli
|
||||
run: DB_POSTGRESDB_SCHEMA=alt_schema DB_TABLE_PREFIX=test_ pnpm test:postgres --runInBand
|
||||
run: pnpm test:postgres
|
||||
|
||||
notify-on-failure:
|
||||
name: Notify Slack on failure
|
||||
|
@ -106,6 +110,6 @@ jobs:
|
|||
if: failure() && github.ref == 'refs/heads/master'
|
||||
with:
|
||||
status: ${{ job.status }}
|
||||
channel: '#updates-build-alerts'
|
||||
channel: '#alerts-build'
|
||||
webhook-url: ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||
message: Postgres or MySQL tests failed (${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})
|
||||
|
|
7
.github/workflows/docker-images-nightly.yml
vendored
7
.github/workflows/docker-images-nightly.yml
vendored
|
@ -35,6 +35,11 @@ on:
|
|||
description: 'URL to call after Docker Image got built successfully.'
|
||||
required: false
|
||||
default: ''
|
||||
include-arm64:
|
||||
description: 'Include ARM64 support'
|
||||
type: boolean
|
||||
required: true
|
||||
default: false
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
@ -76,7 +81,7 @@ jobs:
|
|||
build-args: |
|
||||
N8N_RELEASE_TYPE=nightly
|
||||
file: ./docker/images/n8n-custom/Dockerfile
|
||||
platforms: linux/amd64
|
||||
platforms: ${{ github.event.inputs.include-arm64 == 'true' && 'linux/amd64,linux/arm64' || 'linux/amd64' }}
|
||||
provenance: false
|
||||
push: true
|
||||
tags: ${{ secrets.DOCKER_USERNAME }}/n8n:${{ github.event.inputs.tag || 'nightly' }}
|
||||
|
|
4
.github/workflows/e2e-reusable.yml
vendored
4
.github/workflows/e2e-reusable.yml
vendored
|
@ -40,7 +40,7 @@ on:
|
|||
containers:
|
||||
description: 'Number of containers to run tests in.'
|
||||
required: false
|
||||
default: '[1, 2, 3, 4, 5, 6, 7, 8]'
|
||||
default: '[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]'
|
||||
type: string
|
||||
pr_number:
|
||||
description: 'PR number to run tests for.'
|
||||
|
@ -99,6 +99,8 @@ jobs:
|
|||
runTests: false
|
||||
install: false
|
||||
build: pnpm build
|
||||
env:
|
||||
VUE_APP_MAX_PINNED_DATA_SIZE: 16384
|
||||
|
||||
- name: Cypress install
|
||||
run: pnpm cypress:install
|
||||
|
|
4
.github/workflows/e2e-tests-pr.yml
vendored
4
.github/workflows/e2e-tests-pr.yml
vendored
|
@ -6,6 +6,10 @@ on:
|
|||
branch:
|
||||
- 'master'
|
||||
|
||||
concurrency:
|
||||
group: e2e-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
run-e2e-tests:
|
||||
name: E2E [Electron/Node 18]
|
||||
|
|
2
.github/workflows/e2e-tests.yml
vendored
2
.github/workflows/e2e-tests.yml
vendored
|
@ -60,7 +60,7 @@ jobs:
|
|||
if: failure()
|
||||
with:
|
||||
status: ${{ job.status }}
|
||||
channel: '#updates-build-alerts'
|
||||
channel: '#alerts-build'
|
||||
webhook-url: ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||
message: E2E failure for branch `${{ inputs.branch || 'master' }}` deployed by ${{ inputs.user || 'schedule' }} (${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})
|
||||
|
||||
|
|
3
.github/workflows/release-publish.yml
vendored
3
.github/workflows/release-publish.yml
vendored
|
@ -36,6 +36,9 @@ jobs:
|
|||
- name: Build
|
||||
run: pnpm build
|
||||
|
||||
- name: Dry-run publishing
|
||||
run: pnpm publish -r --no-git-checks --dry-run
|
||||
|
||||
- name: Publish to NPM
|
||||
run: |
|
||||
echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}" > ~/.npmrc
|
||||
|
|
15
.github/workflows/release-push-to-channel.yml
vendored
15
.github/workflows/release-push-to-channel.yml
vendored
|
@ -39,4 +39,17 @@ jobs:
|
|||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
- run: docker buildx imagetools create -t n8nio/n8n:${{ github.event.inputs.release-channel }} n8nio/n8n:${{ github.event.inputs.version }}
|
||||
- run: docker buildx imagetools create -t ${{ secrets.DOCKER_USERNAME }}/n8n:${{ github.event.inputs.release-channel }} ${{ secrets.DOCKER_USERNAME }}/n8n:${{ github.event.inputs.version }}
|
||||
|
||||
release-to-github-container-registry:
|
||||
name: Release to GitHub Container Registry
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- uses: docker/login-action@v2.2.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- run: docker buildx imagetools create -t ghcr.io/${{ github.repository_owner }}/n8n:${{ github.event.inputs.release-channel }} ghcr.io/${{ github.repository_owner }}/n8n:${{ github.event.inputs.version }}
|
||||
|
|
4
.github/workflows/test-workflows.yml
vendored
4
.github/workflows/test-workflows.yml
vendored
|
@ -45,7 +45,7 @@ jobs:
|
|||
working-directory: n8n
|
||||
run: |
|
||||
pnpm install --frozen-lockfile
|
||||
pnpm --filter @n8n/client-oauth2 --filter n8n-workflow --filter n8n-core --filter n8n-nodes-base --filter n8n build
|
||||
pnpm build:backend
|
||||
shell: bash
|
||||
|
||||
- name: Import credentials
|
||||
|
@ -96,7 +96,7 @@ jobs:
|
|||
if: failure()
|
||||
with:
|
||||
status: ${{ job.status }}
|
||||
channel: '#updates-build-alerts'
|
||||
channel: '#alerts-build'
|
||||
webhook-url: ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||
message: |
|
||||
🛑 Workflow test failed 🛑:
|
||||
|
|
19
.github/workflows/units-tests-reusable.yml
vendored
19
.github/workflows/units-tests-reusable.yml
vendored
|
@ -18,11 +18,17 @@ on:
|
|||
required: false
|
||||
default: ''
|
||||
type: string
|
||||
collectCoverage:
|
||||
required: false
|
||||
default: 'false'
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
unit-test:
|
||||
name: Unit tests
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
COVERAGE_ENABLED: ${{ inputs.collectCoverage }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3.5.3
|
||||
with:
|
||||
|
@ -51,10 +57,17 @@ jobs:
|
|||
path: ./packages/**/dist
|
||||
key: ${{ inputs.cacheKey }}
|
||||
|
||||
- name: Test
|
||||
run: pnpm test
|
||||
- name: Test Backend
|
||||
run: pnpm test:backend
|
||||
|
||||
- name: Test Nodes
|
||||
run: pnpm test:nodes
|
||||
|
||||
- name: Test Frontend
|
||||
run: pnpm test:frontend
|
||||
|
||||
- name: Upload coverage to Codecov
|
||||
if: ${{ inputs.collectCoverage == 'true' }}
|
||||
uses: codecov/codecov-action@v3
|
||||
with:
|
||||
files: packages/@n8n/client-oauth2/coverage/cobertura-coverage.xml,packages/cli/coverage/cobertura-coverage.xml,packages/core/coverage/cobertura-coverage.xml,packages/design-system/coverage/cobertura-coverage.xml,packages/editor-ui/coverage/cobertura-coverage.xml,packages/nodes-base/coverage/cobertura-coverage.xml,packages/workflow/coverage/cobertura-coverage.xml
|
||||
files: packages/@n8n/chat/coverage/cobertura-coverage.xml,packages/@n8n/nodes-langchain/coverage/cobertura-coverage.xml,packages/@n8n/permissions/coverage/cobertura-coverage.xml,packages/@n8n/client-oauth2/coverage/cobertura-coverage.xml,packages/cli/coverage/cobertura-coverage.xml,packages/core/coverage/cobertura-coverage.xml,packages/design-system/coverage/cobertura-coverage.xml,packages/editor-ui/coverage/cobertura-coverage.xml,packages/nodes-base/coverage/cobertura-coverage.xml,packages/workflow/coverage/cobertura-coverage.xml
|
||||
|
|
441
CHANGELOG.md
441
CHANGELOG.md
|
@ -1,3 +1,444 @@
|
|||
# [1.24.0](https://github.com/n8n-io/n8n/compare/n8n@1.23.0...n8n@1.24.0) (2024-01-10)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **core:** Do not add Authentication header when `authentication` type is `body` ([#8201](https://github.com/n8n-io/n8n/issues/8201)) ([ac1c642](https://github.com/n8n-io/n8n/commit/ac1c642fddfac3b0ed1144c7eccd7c88fbd5a1a5))
|
||||
* **core:** Fix test webhook deregistration ([#8247](https://github.com/n8n-io/n8n/issues/8247)) ([5032bf0](https://github.com/n8n-io/n8n/commit/5032bf0e346dccf7cade17a1518b3031118af5e1))
|
||||
* **editor:** Items count display in running workflow ([#8148](https://github.com/n8n-io/n8n/issues/8148)) ([8a3c87f](https://github.com/n8n-io/n8n/commit/8a3c87f69c20de7c713dff021e390ea4ea32b103)), closes [/github.com/n8n-io/n8n/pull/7763/files#diff-f5dae80a64b9951bb6691f1b9d439423cf84fa0cc9601b3f2c00904f3135e8deR48](https://github.com//github.com/n8n-io/n8n/pull/7763/files/issues/diff-f5dae80a64b9951bb6691f1b9d439423cf84fa0cc9601b3f2c00904f3135e8deR48)
|
||||
* **editor:** Only load suggested templates for owners ([#8228](https://github.com/n8n-io/n8n/issues/8228)) ([8f22a26](https://github.com/n8n-io/n8n/commit/8f22a265d607047eff22ba957d627bbec7da7900))
|
||||
* **editor:** Tweaking button sizes in execution preview ([#8206](https://github.com/n8n-io/n8n/issues/8206)) ([9d40ae8](https://github.com/n8n-io/n8n/commit/9d40ae8b74594d4368591a62f9b39dde28efc64d))
|
||||
* **editor:** Unify canvas button positioning ([#8258](https://github.com/n8n-io/n8n/issues/8258)) ([b6c42cc](https://github.com/n8n-io/n8n/commit/b6c42cc08408d9d7cc49cc84245b4ad515fa3e6a))
|
||||
* **editor:** Vertically center workflow preview loading state ([#8231](https://github.com/n8n-io/n8n/issues/8231)) ([2d6e406](https://github.com/n8n-io/n8n/commit/2d6e406e215188dbbbeb593ac09ccad3914aaf81))
|
||||
* Fix issue with API key being required for the Qdrant Node ([#8237](https://github.com/n8n-io/n8n/issues/8237)) ([4401db3](https://github.com/n8n-io/n8n/commit/4401db3a2fad3464a5498e9a86fc6bba4f9c9f95))
|
||||
* Fix template credential setup for nodes that dont have credentials ([#8208](https://github.com/n8n-io/n8n/issues/8208)) ([cd3f5b5](https://github.com/n8n-io/n8n/commit/cd3f5b5b1f48e42cb6fa5ebcc15527c28502ceb9))
|
||||
* Fix user reinvites on FE and BE ([#8261](https://github.com/n8n-io/n8n/issues/8261)) ([0dabe5c](https://github.com/n8n-io/n8n/commit/0dabe5c74e5ad0969d4691b3db4a1e796ed8a08c))
|
||||
* **FTP Node:** FTP connection failed due to missing password credential in node ([#8131](https://github.com/n8n-io/n8n/issues/8131)) ([e056aa9](https://github.com/n8n-io/n8n/commit/e056aa9c4dd6c6a7717202029b25f4f65ddecb0d))
|
||||
* **Github Trigger Node:** Enforce SSL validation by default ([#8265](https://github.com/n8n-io/n8n/issues/8265)) ([1387541](https://github.com/n8n-io/n8n/commit/1387541e336e7311ba9c43907fa95d3196fae2eb))
|
||||
* Make params panel double width for all SQL nodes ([#8236](https://github.com/n8n-io/n8n/issues/8236)) ([048b588](https://github.com/n8n-io/n8n/commit/048b588852f5fed1c976889ba54ef564ca7f4894))
|
||||
* **Monday.com Node:** Migrate to api 2023-10 ([#8254](https://github.com/n8n-io/n8n/issues/8254)) ([ccde38a](https://github.com/n8n-io/n8n/commit/ccde38a8a8d65a21bf4d38ef7b09a5ffa3c7ad2d))
|
||||
* **MySQL Node:** Only escape table names when needed ([#8246](https://github.com/n8n-io/n8n/issues/8246)) ([3b01eb6](https://github.com/n8n-io/n8n/commit/3b01eb60c98d51d0d7572342b8d6d40763293719))
|
||||
* **Nextcloud Node:** Throw an actual error if server responded with Fatal error ([#8234](https://github.com/n8n-io/n8n/issues/8234)) ([b201ff8](https://github.com/n8n-io/n8n/commit/b201ff8f23b2bac6b00d5c16d91b4b2931f45ade))
|
||||
* **NocoDB Node:** Download attachments ([#8235](https://github.com/n8n-io/n8n/issues/8235)) ([43e8e5e](https://github.com/n8n-io/n8n/commit/43e8e5e540b9fcbca663fcf17a78a7aba2abb475))
|
||||
* **Postgres Node:** Stop marking autogenerated columns as required ([#8230](https://github.com/n8n-io/n8n/issues/8230)) ([bed04ec](https://github.com/n8n-io/n8n/commit/bed04ec122234b4329a5e415bf3627c115b3f32e)), closes [#7084](https://github.com/n8n-io/n8n/issues/7084)
|
||||
* Resolve expressions in credentials following paired item ([#8250](https://github.com/n8n-io/n8n/issues/8250)) ([ccb2b07](https://github.com/n8n-io/n8n/commit/ccb2b076f8240b0712949b72ec57ae72a36ef62d))
|
||||
* **Set Node:** Field not excluded if dot notation disabled and field was set by using drag-and-drop from ui ([#8233](https://github.com/n8n-io/n8n/issues/8233)) ([cda49a4](https://github.com/n8n-io/n8n/commit/cda49a4747ef4369ce7a971872c6fb8a74f4156d))
|
||||
* Store workflow settings when saving an execution ([#8288](https://github.com/n8n-io/n8n/issues/8288)) ([8a7c629](https://github.com/n8n-io/n8n/commit/8a7c629ea183f75f9916003edf11cb8aeef445eb))
|
||||
* **Webhook Node:** Fix handling of form-data files ([#8256](https://github.com/n8n-io/n8n/issues/8256)) ([fc29030](https://github.com/n8n-io/n8n/commit/fc2903096e6e64e5b2a14593418d5651e07ca9ee))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Add Chat Trigger node ([#7409](https://github.com/n8n-io/n8n/issues/7409)) ([af49e95](https://github.com/n8n-io/n8n/commit/af49e95cc7ccf70f233f9bd1e34fbb57f7f08ccf))
|
||||
* **core:** Cache test webhook registrations ([#8176](https://github.com/n8n-io/n8n/issues/8176)) ([22a5f52](https://github.com/n8n-io/n8n/commit/22a5f5258da0a973e1ad44c0d3d4f0fda1d23444)), closes [#8155](https://github.com/n8n-io/n8n/issues/8155)
|
||||
* **core:** Validate shutdown handlers on startup ([#8260](https://github.com/n8n-io/n8n/issues/8260)) ([3b996a7](https://github.com/n8n-io/n8n/commit/3b996a7da0137a75c3047656a4bc8cc336ebfc1e))
|
||||
* **editor:** Add fullscreen view to code editor ([#8084](https://github.com/n8n-io/n8n/issues/8084)) ([071e6d6](https://github.com/n8n-io/n8n/commit/071e6d6b6e32b7196f34043710c23331ad28fac0))
|
||||
* **editor:** Update copy: `Execute` --> `Test` ([#8137](https://github.com/n8n-io/n8n/issues/8137)) ([df5d07b](https://github.com/n8n-io/n8n/commit/df5d07bcb8beba760bc17118b36ccd531bc3c755))
|
||||
* **Google Sheets Node:** Add "By Name" option to selector for Sheets ([#8241](https://github.com/n8n-io/n8n/issues/8241)) ([dce28f9](https://github.com/n8n-io/n8n/commit/dce28f9cb98db33bf22bcfee181f8e9ca64dd2bc))
|
||||
* **HTTP Request Node:** Interval Between Requests option for pagination ([#8224](https://github.com/n8n-io/n8n/issues/8224)) ([270328c](https://github.com/n8n-io/n8n/commit/270328ccf6e5502adc092f6f85d146ffb98e1208))
|
||||
* Implement MistralCloud Chat & Embeddings nodes ([#8239](https://github.com/n8n-io/n8n/issues/8239)) ([d37b908](https://github.com/n8n-io/n8n/commit/d37b9084b2c657d8b5b8bae6dbb51b428db26e1e))
|
||||
* **MongoDB Node:** Add support for TLS ([#8266](https://github.com/n8n-io/n8n/issues/8266)) ([e796e7f](https://github.com/n8n-io/n8n/commit/e796e7f06d73a74a403000c53942d56cab91781b))
|
||||
* **Switch Node:** Overhaul ([#7855](https://github.com/n8n-io/n8n/issues/7855)) ([f4092a9](https://github.com/n8n-io/n8n/commit/f4092a9e49f66845612420ba59a013796ed80d45))
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **core:** Improve caching service ([#8213](https://github.com/n8n-io/n8n/issues/8213)) ([f53c482](https://github.com/n8n-io/n8n/commit/f53c482939db938c47523ac11a9538e35e1926a9)), closes [#7747](https://github.com/n8n-io/n8n/issues/7747)
|
||||
* **core:** Optimize workflow activation errors ([#8242](https://github.com/n8n-io/n8n/issues/8242)) ([f293956](https://github.com/n8n-io/n8n/commit/f2939568cf399e67214e89bc7f859323aaeda8dd))
|
||||
|
||||
|
||||
|
||||
# [1.23.0](https://github.com/n8n-io/n8n/compare/n8n@1.22.0...n8n@1.23.0) (2024-01-03)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Asana Node:** Omit body from GET, HEAD, and DELETE requests ([#8057](https://github.com/n8n-io/n8n/issues/8057)) ([15ffd4f](https://github.com/n8n-io/n8n/commit/15ffd4fb9f967302e2444a873a804d2ccb64e748))
|
||||
* **core:** Better input validation for the changeRole endpoint ([#8189](https://github.com/n8n-io/n8n/issues/8189)) ([cfe9525](https://github.com/n8n-io/n8n/commit/cfe9525dd4e2dbf2496bd86ad854bb744b5dc8fe))
|
||||
* **core:** Fix issue that pinnedData is not used with Test-Webhooks ([#8123](https://github.com/n8n-io/n8n/issues/8123)) ([fa8bd8b](https://github.com/n8n-io/n8n/commit/fa8bd8b9eb202989229028cb6975cd2b50e5eef9))
|
||||
* **core:** Handle empty executions table in pruning in migrations ([#8121](https://github.com/n8n-io/n8n/issues/8121)) ([ffaa30d](https://github.com/n8n-io/n8n/commit/ffaa30ddc4ee312f44726c17a7ec91b5551092ad))
|
||||
* **core:** Remove circular dependency in WorkflowService and ActiveWorkflowRunner ([#8128](https://github.com/n8n-io/n8n/issues/8128)) ([21788d9](https://github.com/n8n-io/n8n/commit/21788d9153fb730965dabbce64c50c3b929ee728)), closes [#8122](https://github.com/n8n-io/n8n/issues/8122)
|
||||
* **core:** Use pinned data only for manual mode ([#8164](https://github.com/n8n-io/n8n/issues/8164)) ([ea7e76f](https://github.com/n8n-io/n8n/commit/ea7e76fa3b3dc1f37b0415e14ea5ff90b8017b9a))
|
||||
* **Discord Node:** Remove unnecessary requirement on parameters ([#8060](https://github.com/n8n-io/n8n/issues/8060)) ([ef3a577](https://github.com/n8n-io/n8n/commit/ef3a57719eb42777502cafdd38009e6cb5b484ce))
|
||||
* **editor:** Avoid sanitizing output to search node data ([#8126](https://github.com/n8n-io/n8n/issues/8126)) ([c83d9f4](https://github.com/n8n-io/n8n/commit/c83d9f45bab986eb930e9da69eec970d3a72263d))
|
||||
* **editor:** Enable explicit undo keyboard shortcut across all code editors ([#8178](https://github.com/n8n-io/n8n/issues/8178)) ([cf7f668](https://github.com/n8n-io/n8n/commit/cf7f6688bac5dd31dc3a45df4ecce579939141e2)), closes [#5297](https://github.com/n8n-io/n8n/issues/5297)
|
||||
* **editor:** Fix operation change failing in certain conditions ([#8114](https://github.com/n8n-io/n8n/issues/8114)) ([711fa2b](https://github.com/n8n-io/n8n/commit/711fa2b9251154b50d8e5e7cd9a857ccb2c0bec6)), closes [/github.com/n8n-io/n8n/blob/7806a65229878a473f5526bad0b94614e8bfa8aa/packages/workflow/src/NodeHelpers.ts#L786](https://github.com//github.com/n8n-io/n8n/blob/7806a65229878a473f5526bad0b94614e8bfa8aa/packages/workflow/src/NodeHelpers.ts/issues/L786)
|
||||
* **editor:** Fix templates view layout ([#8196](https://github.com/n8n-io/n8n/issues/8196)) ([d01e42a](https://github.com/n8n-io/n8n/commit/d01e42a2aabedfd4c0f79799bbfc9b1a235d4233))
|
||||
* **editor:** Fix UI urls when hosted behind a path prefix ([#8198](https://github.com/n8n-io/n8n/issues/8198)) ([5c078f1](https://github.com/n8n-io/n8n/commit/5c078f1b3d78c7038bfdbb083fd029ef61bf2dfc)), closes [#8061](https://github.com/n8n-io/n8n/issues/8061)
|
||||
* **editor:** Prevent browser zoom when scrolling inside sticky edit mode ([#8116](https://github.com/n8n-io/n8n/issues/8116)) ([e928210](https://github.com/n8n-io/n8n/commit/e928210ccdc00ad8a38e3f96ba5145c35e7b007b))
|
||||
* **editor:** Prevent canvas undo/redo when NDV is open ([#8118](https://github.com/n8n-io/n8n/issues/8118)) ([39e45d8](https://github.com/n8n-io/n8n/commit/39e45d8b929d474f1e7587329b003fe15b61636d))
|
||||
* **editor:** Prevent storing pairedItem data inside of pinData ([#8173](https://github.com/n8n-io/n8n/issues/8173)) ([405e267](https://github.com/n8n-io/n8n/commit/405e26757e2591b42a4bfeedd1fea997fbbb08c9))
|
||||
* **GitHub Node:** Fix issue that File->Get did not run once per item ([#8190](https://github.com/n8n-io/n8n/issues/8190)) ([11cda41](https://github.com/n8n-io/n8n/commit/11cda41214100a1a3e65309434ab8be3ccef1898))
|
||||
* **Invoice Ninja Node:** Fix issue with custom invoice numbers not working with v5 ([#8200](https://github.com/n8n-io/n8n/issues/8200)) ([3b6ae2d](https://github.com/n8n-io/n8n/commit/3b6ae2d0a510a57b27fc1a44cb3e710e2a783800))
|
||||
* **Microsoft Excel 365 Node:** Ensure arg is string during worksheet table search ([#8154](https://github.com/n8n-io/n8n/issues/8154)) ([8e873ca](https://github.com/n8n-io/n8n/commit/8e873ca2f3c73ddd7bbef2218d8da82032f66cec))
|
||||
* **Notion Node:** Ensure arg is string during page ID extraction ([#8153](https://github.com/n8n-io/n8n/issues/8153)) ([e94b8a6](https://github.com/n8n-io/n8n/commit/e94b8a6c30aaa2e59117d5a0cc03e1590d7ea8ca))
|
||||
* **Redis Trigger Node:** Activating a workflow with a Redis trigger fails ([#8129](https://github.com/n8n-io/n8n/issues/8129)) ([a169b74](https://github.com/n8n-io/n8n/commit/a169b7406279de43dbd3fd7d13166d987c60d01a))
|
||||
* **Schedule Trigger Node:** Use the correct `moment` import ([#8185](https://github.com/n8n-io/n8n/issues/8185)) ([17a4e2e](https://github.com/n8n-io/n8n/commit/17a4e2ea80c664e248c136b7e66eef710ccba7f2)), closes [#8184](https://github.com/n8n-io/n8n/issues/8184)
|
||||
* Show public API upgrade CTA when feature is not enabled ([#8109](https://github.com/n8n-io/n8n/issues/8109)) ([e9c7fd7](https://github.com/n8n-io/n8n/commit/e9c7fd73975ced504d5a16a0dbbc313f15ccd8ab))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **core:** Add closeFunction support to Sub-Nodes ([#7708](https://github.com/n8n-io/n8n/issues/7708)) ([bec0fae](https://github.com/n8n-io/n8n/commit/bec0faed9e51fe6ea20ab3b07b4dfa849b28516b))
|
||||
* **core:** Add user.profile.beforeUpdate hook ([#8144](https://github.com/n8n-io/n8n/issues/8144)) ([e126ed7](https://github.com/n8n-io/n8n/commit/e126ed74f3d9ed3dae72252cb8c9e8a6f7620808))
|
||||
* **core:** Improvements/overhaul for nodes working with binary data ([#7651](https://github.com/n8n-io/n8n/issues/7651)) ([5e16dd4](https://github.com/n8n-io/n8n/commit/5e16dd4ab4457acf21d3d7a3566d07944ff7f857))
|
||||
* **core:** Remove discontinued crypto-js ([#8104](https://github.com/n8n-io/n8n/issues/8104)) ([01e9a79](https://github.com/n8n-io/n8n/commit/01e9a79238bbd2c14ae77a12e54fc1c6f41e1246))
|
||||
* **core:** Unify application components shutdown ([#8097](https://github.com/n8n-io/n8n/issues/8097)) ([3a881be](https://github.com/n8n-io/n8n/commit/3a881be6c25b3e16d8c53227dc851cb420f5f1bf))
|
||||
* **editor:** Add node execution status indicator to output panel ([#8124](https://github.com/n8n-io/n8n/issues/8124)) ([ab74bad](https://github.com/n8n-io/n8n/commit/ab74bade05cb30e7fa65a491789a3df3ab7bf8b8))
|
||||
* **editor:** Add template Id to workflow metadata ([#8088](https://github.com/n8n-io/n8n/issues/8088)) ([517b050](https://github.com/n8n-io/n8n/commit/517b050d0ae1a64987ac00d5795c5e59ed176f3f))
|
||||
* **Home Assistant Node:** Use the new Home Assistant logo ([#8150](https://github.com/n8n-io/n8n/issues/8150)) ([518a99e](https://github.com/n8n-io/n8n/commit/518a99e5287dc648edafd34a4dd27c9205eb8629))
|
||||
* **Qdrant Vector Store Node:** Qdrant vector store support ([#8080](https://github.com/n8n-io/n8n/issues/8080)) ([66460f6](https://github.com/n8n-io/n8n/commit/66460f66b0b02ae6f342e52500b29fe8b724e1dc))
|
||||
* **Wordpress Node:** Add option to ignore error when using self signed certificates ([#8199](https://github.com/n8n-io/n8n/issues/8199)) ([65c8e12](https://github.com/n8n-io/n8n/commit/65c8e12b96ac8c1c53d3879d91982ca834f3cda1))
|
||||
|
||||
|
||||
|
||||
# [1.22.0](https://github.com/n8n-io/n8n/compare/n8n@1.21.0...n8n@1.22.0) (2023-12-21)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **ActiveCampaign Node:** Fix pagination issue when loading tags ([#8017](https://github.com/n8n-io/n8n/issues/8017)) ([1943857](https://github.com/n8n-io/n8n/commit/19438572312cf9354c333aeb52ccbf1ab81fc51f))
|
||||
* **core:** Close db connection gracefully when exiting ([#8045](https://github.com/n8n-io/n8n/issues/8045)) ([e69707e](https://github.com/n8n-io/n8n/commit/e69707efd4bd947fdf6b9c66f373da63d34f41e9))
|
||||
* **core:** Consider timeout in shutdown an error ([#8050](https://github.com/n8n-io/n8n/issues/8050)) ([4cae976](https://github.com/n8n-io/n8n/commit/4cae976a3b428bd528fe71ef0b240c0fd6e23bbf))
|
||||
* **core:** Do not display error when stopping jobless execution in queue mode ([#8007](https://github.com/n8n-io/n8n/issues/8007)) ([8e6b951](https://github.com/n8n-io/n8n/commit/8e6b951a76e08b9ee9740fdd853f77553ad60cd6))
|
||||
* **core:** Fix shutdown if terminating before hooks are initialized ([#8047](https://github.com/n8n-io/n8n/issues/8047)) ([6ae2f5e](https://github.com/n8n-io/n8n/commit/6ae2f5efea65e23029475ccdc5a65ec7c8152423))
|
||||
* **core:** Handle multiple termination signals correctly ([#8046](https://github.com/n8n-io/n8n/issues/8046)) ([67bd8ad](https://github.com/n8n-io/n8n/commit/67bd8ad698bd0afe6ff7183d75da8bca4085598e))
|
||||
* **core:** Initialize queue once in queue mode ([#8025](https://github.com/n8n-io/n8n/issues/8025)) ([53c0b49](https://github.com/n8n-io/n8n/commit/53c0b49d15047461e3b65baed65c9d76dff99539))
|
||||
* **core:** Prevent axios from force setting a form-urlencoded content-type ([#8117](https://github.com/n8n-io/n8n/issues/8117)) ([bba9576](https://github.com/n8n-io/n8n/commit/bba95761e2f2b54af1fcab8a7b1d626ca10d537e)), closes [/github.com/axios/axios/blob/v1.x/lib/core/dispatchRequest.js#L45-L47](https://github.com//github.com/axios/axios/blob/v1.x/lib/core/dispatchRequest.js/issues/L45-L47)
|
||||
* **core:** Remove circular references before serializing executions in public API ([#8043](https://github.com/n8n-io/n8n/issues/8043)) ([989888d](https://github.com/n8n-io/n8n/commit/989888d9bcec6f4eb3c811ce10d480737d96b102)), closes [#8030](https://github.com/n8n-io/n8n/issues/8030)
|
||||
* **core:** Restore workflow ID during execution creation ([#8031](https://github.com/n8n-io/n8n/issues/8031)) ([c5e6ba8](https://github.com/n8n-io/n8n/commit/c5e6ba8cdd4a8f117ccc2e89e55497117156d8af)), closes [/github.com/n8n-io/n8n/pull/8002/files#diff-c8cbb62ca9ab2ae45e5f565cd8c63fff6475809a6241ea0b90acc575615224](https://github.com//github.com/n8n-io/n8n/pull/8002/files/issues/diff-c8cbb62ca9ab2ae45e5f565cd8c63fff6475809a6241ea0b90acc575615224)
|
||||
* **core:** Use relative imports for dynamic imports in SecurityAuditService ([#8086](https://github.com/n8n-io/n8n/issues/8086)) ([785bf99](https://github.com/n8n-io/n8n/commit/785bf9974e38ea84c016e210a3108f4af567510d)), closes [#8085](https://github.com/n8n-io/n8n/issues/8085)
|
||||
* **editor:** Add back credential `use` permission ([#8023](https://github.com/n8n-io/n8n/issues/8023)) ([329e5bf](https://github.com/n8n-io/n8n/commit/329e5bf9eed8556aba2bbd50bad9dbd6d3b373ad))
|
||||
* **editor:** Cleanup Executions page component ([#8053](https://github.com/n8n-io/n8n/issues/8053)) ([2689c37](https://github.com/n8n-io/n8n/commit/2689c37e87c5b3ae5029121f4d3dc878841e8844))
|
||||
* **editor:** Disable auto scroll and list size check when clicking on executions ([#7983](https://github.com/n8n-io/n8n/issues/7983)) ([fcb8b91](https://github.com/n8n-io/n8n/commit/fcb8b91f37e1fb0ef42f411c84390180e1ed7bbe))
|
||||
* **editor:** Ensure execution data overrides pinned data when copying in executions view ([#8009](https://github.com/n8n-io/n8n/issues/8009)) ([1d1cb0d](https://github.com/n8n-io/n8n/commit/1d1cb0d3c530856e0c26d8f146f60b2555625ab6))
|
||||
* **editor:** Fix copy/paste issue when switch node is in workflow ([#8103](https://github.com/n8n-io/n8n/issues/8103)) ([4b86926](https://github.com/n8n-io/n8n/commit/4b86926752fb1304a46385cb46bdf34fda0d53b6))
|
||||
* **editor:** Make keyboard shortcuts more strict; don't accept extra Ctrl/Alt/Shift keys ([#8024](https://github.com/n8n-io/n8n/issues/8024)) ([8df49e1](https://github.com/n8n-io/n8n/commit/8df49e134d886267f9f7475573d013371220dcac))
|
||||
* **editor:** Show credential share info only to appropriate users ([#8020](https://github.com/n8n-io/n8n/issues/8020)) ([b29b4d4](https://github.com/n8n-io/n8n/commit/b29b4d442bb0617aa516748ec48379eae0996cf0))
|
||||
* **editor:** Turn off executions list auto-refresh after leaving the page ([#8005](https://github.com/n8n-io/n8n/issues/8005)) ([e3c363d](https://github.com/n8n-io/n8n/commit/e3c363d72cf4ee49086d012f92a7b34be958482f))
|
||||
* **editor:** Update image sizes in template description not to be full width always ([#8037](https://github.com/n8n-io/n8n/issues/8037)) ([63a6e7e](https://github.com/n8n-io/n8n/commit/63a6e7e0340e1b00719f212ac620600a90d70ef1))
|
||||
* **HTTP Request Node:** Do not create circular references in HTTP request node output ([#8030](https://github.com/n8n-io/n8n/issues/8030)) ([5b7ea16](https://github.com/n8n-io/n8n/commit/5b7ea16d9a20880c72779b02620e99ebe9f3617a))
|
||||
* Stop binary data restoration from preventing execution from finishing ([#8082](https://github.com/n8n-io/n8n/issues/8082)) ([5ffff1b](https://github.com/n8n-io/n8n/commit/5ffff1bb22691c09c5ca8b3ada2a19d5ce155a0b))
|
||||
* Upgrade axios to address CVE-2023-45857 ([#7713](https://github.com/n8n-io/n8n/issues/7713)) ([64eb9bb](https://github.com/n8n-io/n8n/commit/64eb9bbc3624ee8f2fa90812711ad568926fdca8))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Add config option to prefer GET request over LIST when using Hashicorp Vault ([#8049](https://github.com/n8n-io/n8n/issues/8049)) ([439a22d](https://github.com/n8n-io/n8n/commit/439a22d68f7bf32f281b1078b71607307640a09b))
|
||||
* Add option to `returnIntermediateSteps` for AI agents ([#8113](https://github.com/n8n-io/n8n/issues/8113)) ([7806a65](https://github.com/n8n-io/n8n/commit/7806a65229878a473f5526bad0b94614e8bfa8aa))
|
||||
* **core:** Add N8N_GRACEFUL_SHUTDOWN_TIMEOUT env var ([#8068](https://github.com/n8n-io/n8n/issues/8068)) ([614f488](https://github.com/n8n-io/n8n/commit/614f48838626e2af8e3f2e76ee4a144af2d40f72))
|
||||
* **editor:** Add lead enrichment suggestions to workflow list ([#8042](https://github.com/n8n-io/n8n/issues/8042)) ([36a923c](https://github.com/n8n-io/n8n/commit/36a923cf7bd4d42b8f8decbf01255c41d6dc1671)), closes [-update-workflows-list-page-to-show-fake-door-templates#comment-b6644c99](https://github.com/-update-workflows-list-page-to-show-fake-door-templates/issues/comment-b6644c99)
|
||||
* **editor:** Finalize workers view ([#8052](https://github.com/n8n-io/n8n/issues/8052)) ([edfa784](https://github.com/n8n-io/n8n/commit/edfa78414d6bce901becc05e9d860f2521139688))
|
||||
* **editor:** Gracefully ignore invalid payloads in postMessage handler ([#8096](https://github.com/n8n-io/n8n/issues/8096)) ([9d22c7a](https://github.com/n8n-io/n8n/commit/9d22c7a2782a1908f81bcf80260cd91cb296e239))
|
||||
* **editor:** Upgrade frontend tooling to address a few vulnerabilities ([#8100](https://github.com/n8n-io/n8n/issues/8100)) ([19b7f1f](https://github.com/n8n-io/n8n/commit/19b7f1ffb17dcd6ac77839f97c2544f60f4ad55e))
|
||||
* **Filter Node:** Overhaul UI by adding the new filter component ([#8016](https://github.com/n8n-io/n8n/issues/8016)) ([3d53052](https://github.com/n8n-io/n8n/commit/3d530522f828dfc985ae98e4bb551aa3a2bd44c6))
|
||||
* **Respond to Webhook Node:** Overhaul with improvements like returning all items ([#8093](https://github.com/n8n-io/n8n/issues/8093)) ([32d397e](https://github.com/n8n-io/n8n/commit/32d397eff315fdc77677c0b134a7a25bcd8ca5d0))
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **editor:** Improve canvas rendering performance ([#8022](https://github.com/n8n-io/n8n/issues/8022)) ([b780436](https://github.com/n8n-io/n8n/commit/b780436a6b445dc5951217b5a1f2c61b34961757))
|
||||
|
||||
|
||||
|
||||
# [1.21.0](https://github.com/n8n-io/n8n/compare/n8n@1.20.0...n8n@1.21.0) (2023-12-13)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **core:** Ensure inviter and invitee are set correctly in invite link ([#7943](https://github.com/n8n-io/n8n/issues/7943)) ([386bd61](https://github.com/n8n-io/n8n/commit/386bd619676e54e960ca0af3ff47fa3b9c16c813)), closes [4#a39f9e5ba64a48b58a71d81c837e8227](https://github.com/4/issues/a39f9e5ba64a48b58a71d81c837e8227) [4#f6a177d32bde4b57ae2da0b8e454](https://github.com/4/issues/f6a177d32bde4b57ae2da0b8e454) [4#fef2d36ab02247e1a0f65a74f6fb534](https://github.com/4/issues/fef2d36ab02247e1a0f65a74f6fb534)
|
||||
* **core:** Fix user comparison in same-user subworkflow caller policy ([#7913](https://github.com/n8n-io/n8n/issues/7913)) ([92bab72](https://github.com/n8n-io/n8n/commit/92bab72cffb1083b495d211d0a31920e83e66769))
|
||||
* **core:** Perform multi-main leader check against key ID ([#7964](https://github.com/n8n-io/n8n/issues/7964)) ([1a87f70](https://github.com/n8n-io/n8n/commit/1a87f70e8404218308072ee2f35c6ba2af34c23f)), closes [4#a39f9e5ba64a48b58a71d81c837e8227](https://github.com/4/issues/a39f9e5ba64a48b58a71d81c837e8227) [4#f6a177d32bde4b57ae2da0b8e454](https://github.com/4/issues/f6a177d32bde4b57ae2da0b8e454) [4#fef2d36ab02247e1a0f65a74f6fb534](https://github.com/4/issues/fef2d36ab02247e1a0f65a74f6fb534)
|
||||
* **core:** Prevent workflow history saving error from happening ([#7812](https://github.com/n8n-io/n8n/issues/7812)) ([e5581ce](https://github.com/n8n-io/n8n/commit/e5581ce8023e21d3dcf140099f3a53e5ffb4584f))
|
||||
* **editor:** Add missing string for worker in log streaming ([#7971](https://github.com/n8n-io/n8n/issues/7971)) ([148bc1d](https://github.com/n8n-io/n8n/commit/148bc1d303af3aafd73e73e11c3dd9cefd40a1dd)), closes [4#a39f9e5ba64a48b58a71d81c837e8227](https://github.com/4/issues/a39f9e5ba64a48b58a71d81c837e8227) [4#f6a177d32bde4b57ae2da0b8e454](https://github.com/4/issues/f6a177d32bde4b57ae2da0b8e454) [4#fef2d36ab02247e1a0f65a74f6fb534](https://github.com/4/issues/fef2d36ab02247e1a0f65a74f6fb534)
|
||||
* **editor:** Allow SSH protocol in git repository URL for environments ([#7944](https://github.com/n8n-io/n8n/issues/7944)) ([bc1c72f](https://github.com/n8n-io/n8n/commit/bc1c72f992a47a9c263aec175ca820088cf340ec)), closes [4#a39f9e5ba64a48b58a71d81c837e8227](https://github.com/4/issues/a39f9e5ba64a48b58a71d81c837e8227) [4#f6a177d32bde4b57ae2da0b8e454](https://github.com/4/issues/f6a177d32bde4b57ae2da0b8e454) [4#fef2d36ab02247e1a0f65a74f6fb534](https://github.com/4/issues/fef2d36ab02247e1a0f65a74f6fb534)
|
||||
* **editor:** Fix bug with node names with certain characters ([#8013](https://github.com/n8n-io/n8n/issues/8013)) ([26f0d57](https://github.com/n8n-io/n8n/commit/26f0d57f5fb71a06c92968a4997cceae62f32312))
|
||||
* **editor:** Fix Webhook URL expansion icon ([#8011](https://github.com/n8n-io/n8n/issues/8011)) ([b00b905](https://github.com/n8n-io/n8n/commit/b00b9057a42f23cd9c4bb6675a3e6134610bf81b))
|
||||
* **editor:** Prevent opening NDV search if `/` is typed in a contenteditable element ([#7968](https://github.com/n8n-io/n8n/issues/7968)) ([e8a493f](https://github.com/n8n-io/n8n/commit/e8a493f71863e6a5d2685b48a61a0d11daf5edc5))
|
||||
* **editor:** Return early in ws message handler if no 'command' keyword is found ([#7946](https://github.com/n8n-io/n8n/issues/7946)) ([5b2defc](https://github.com/n8n-io/n8n/commit/5b2defc867a0627a861bf0fb98abfd99f8efe934))
|
||||
* Ensure external hooks post workflow execute run in queue mode ([#7947](https://github.com/n8n-io/n8n/issues/7947)) ([3ba7deb](https://github.com/n8n-io/n8n/commit/3ba7deb337963d40ae70f40ffb2f4eb23cac89b7)), closes [4#a39f9e5ba64a48b58a71d81c837e8227](https://github.com/4/issues/a39f9e5ba64a48b58a71d81c837e8227) [4#f6a177d32bde4b57ae2da0b8e454](https://github.com/4/issues/f6a177d32bde4b57ae2da0b8e454) [4#fef2d36ab02247e1a0f65a74f6fb534](https://github.com/4/issues/fef2d36ab02247e1a0f65a74f6fb534)
|
||||
* **FileMaker Node:** Prevent erroring on zero fields loaded ([#7955](https://github.com/n8n-io/n8n/issues/7955)) ([10ad386](https://github.com/n8n-io/n8n/commit/10ad3866048ad06d0e8455ed2c52c618ae9e5032)), closes [4#a39f9e5ba64a48b58a71d81c837e8227](https://github.com/4/issues/a39f9e5ba64a48b58a71d81c837e8227) [4#f6a177d32bde4b57ae2da0b8e454](https://github.com/4/issues/f6a177d32bde4b57ae2da0b8e454) [4#fef2d36ab02247e1a0f65a74f6fb534](https://github.com/4/issues/fef2d36ab02247e1a0f65a74f6fb534)
|
||||
* Fix issue preventing secrets from loading if the path contains - or / ([#7988](https://github.com/n8n-io/n8n/issues/7988)) ([0ac9594](https://github.com/n8n-io/n8n/commit/0ac959463f25187c5be4116a2209411afd903d87))
|
||||
* **Google Sheets Node:** Prevent erroring on zero sheet search results ([#7957](https://github.com/n8n-io/n8n/issues/7957)) ([9b877a9](https://github.com/n8n-io/n8n/commit/9b877a942787c855c3a3a011c19c5d1d30b8da67)), closes [4#a39f9e5ba64a48b58a71d81c837e8227](https://github.com/4/issues/a39f9e5ba64a48b58a71d81c837e8227) [4#f6a177d32bde4b57ae2da0b8e454](https://github.com/4/issues/f6a177d32bde4b57ae2da0b8e454) [4#fef2d36ab02247e1a0f65a74f6fb534](https://github.com/4/issues/fef2d36ab02247e1a0f65a74f6fb534)
|
||||
* **Google Sheets Node:** Prevent erroring when fetching mapping columns ([#7972](https://github.com/n8n-io/n8n/issues/7972)) ([29a1066](https://github.com/n8n-io/n8n/commit/29a10668d17cdeb8b0e93c912f59c5976b6fc6c6)), closes [4#a39f9e5ba64a48b58a71d81c837e8227](https://github.com/4/issues/a39f9e5ba64a48b58a71d81c837e8227) [4#f6a177d32bde4b57ae2da0b8e454](https://github.com/4/issues/f6a177d32bde4b57ae2da0b8e454) [4#fef2d36ab02247e1a0f65a74f6fb534](https://github.com/4/issues/fef2d36ab02247e1a0f65a74f6fb534)
|
||||
* **Postgres Node:** Do not include id column in upsert fields selection if it's not unique ([#7975](https://github.com/n8n-io/n8n/issues/7975)) ([435392c](https://github.com/n8n-io/n8n/commit/435392cbfe150c5e85d092686b3b7e20273421cc))
|
||||
* **Postgres Trigger Node:** Increase manual trigger timeout from 30 to 60 seconds ([#8015](https://github.com/n8n-io/n8n/issues/8015)) ([09a5729](https://github.com/n8n-io/n8n/commit/09a5729305a8072f5e98a320c85ad1c83a6946ed))
|
||||
* Restrict updating/deleting of shared but not owned credentials ([#7950](https://github.com/n8n-io/n8n/issues/7950)) ([42e828d](https://github.com/n8n-io/n8n/commit/42e828d5c655e54b6a4ec83c398c684996b9cc3e)), closes [4#a39f9e5ba64a48b58a71d81c837e8227](https://github.com/4/issues/a39f9e5ba64a48b58a71d81c837e8227) [4#f6a177d32bde4b57ae2da0b8e454](https://github.com/4/issues/f6a177d32bde4b57ae2da0b8e454) [4#fef2d36ab02247e1a0f65a74f6fb534](https://github.com/4/issues/fef2d36ab02247e1a0f65a74f6fb534)
|
||||
* **Webhook Node:** Binary data handling ([#7804](https://github.com/n8n-io/n8n/issues/7804)) ([565b409](https://github.com/n8n-io/n8n/commit/565b409a82ca6173efd19f26a5f5b27a359a3b87))
|
||||
* **Webhook Node:** Do not create binary data when there is no data in the request ([#8000](https://github.com/n8n-io/n8n/issues/8000)) ([70f0755](https://github.com/n8n-io/n8n/commit/70f0755278e0a2bdb61c29623f27623b65473ab4)), closes [/github.com/n8n-io/n8n/pull/7804/files#r1422641833](https://github.com//github.com/n8n-io/n8n/pull/7804/files/issues/r1422641833)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Add config option for external secret update interval ([#7995](https://github.com/n8n-io/n8n/issues/7995)) ([b6c1c04](https://github.com/n8n-io/n8n/commit/b6c1c04b541d0944c5baac1ab021539c8f020f10))
|
||||
* AI nodes usability fixes + Summarization Chain V2 ([#7949](https://github.com/n8n-io/n8n/issues/7949)) ([dcf1286](https://github.com/n8n-io/n8n/commit/dcf12867b3c49596cd214812caee3292d2e794de)), closes [4#a39f9e5ba64a48b58a71d81c837e8227](https://github.com/4/issues/a39f9e5ba64a48b58a71d81c837e8227) [4#f6a177d32bde4b57ae2da0b8e454](https://github.com/4/issues/f6a177d32bde4b57ae2da0b8e454) [4#fef2d36ab02247e1a0f65a74f6fb534](https://github.com/4/issues/fef2d36ab02247e1a0f65a74f6fb534)
|
||||
* Data transformation nodes and actions in Nodes Panel ([#7760](https://github.com/n8n-io/n8n/issues/7760)) ([675ec21](https://github.com/n8n-io/n8n/commit/675ec21d335af2b2c9598bc2bec18194506ef71a))
|
||||
* **editor:** Add AppCues tracking for onboarding event ([#7945](https://github.com/n8n-io/n8n/issues/7945)) ([04cabaf](https://github.com/n8n-io/n8n/commit/04cabafef7acbc30cba647732e2ca8ae8a02d29a)), closes [4#a39f9e5ba64a48b58a71d81c837e8227](https://github.com/4/issues/a39f9e5ba64a48b58a71d81c837e8227) [4#f6a177d32bde4b57ae2da0b8e454](https://github.com/4/issues/f6a177d32bde4b57ae2da0b8e454) [4#fef2d36ab02247e1a0f65a74f6fb534](https://github.com/4/issues/fef2d36ab02247e1a0f65a74f6fb534)
|
||||
* **editor:** Add option to disable NDV in workflow previews ([#7990](https://github.com/n8n-io/n8n/issues/7990)) ([393afef](https://github.com/n8n-io/n8n/commit/393afef1747f168d5fa42be2424fd02125f1bbac)), closes [4#a39f9e5ba64a48b58a71d81c837e8227](https://github.com/4/issues/a39f9e5ba64a48b58a71d81c837e8227) [4#f6a177d32bde4b57ae2da0b8e454](https://github.com/4/issues/f6a177d32bde4b57ae2da0b8e454) [4#fef2d36ab02247e1a0f65a74f6fb534](https://github.com/4/issues/fef2d36ab02247e1a0f65a74f6fb534)
|
||||
* **editor:** Filter component + implement in If node ([#7490](https://github.com/n8n-io/n8n/issues/7490)) ([8a53434](https://github.com/n8n-io/n8n/commit/8a5343401dd355436120a9a424ae455e80b50da6))
|
||||
* **editor:** Show template credential setup based on feature flag ([#7989](https://github.com/n8n-io/n8n/issues/7989)) ([08ee307](https://github.com/n8n-io/n8n/commit/08ee3072093fb26b14b48e2b35d8c8d018317f13))
|
||||
* **Google Ads Node:** Update to support v15 ([#7962](https://github.com/n8n-io/n8n/issues/7962)) ([7f01269](https://github.com/n8n-io/n8n/commit/7f0126915aae514a0ab515a4baf5582da2aeb1e3))
|
||||
* Introduce advanced permissions ([#7844](https://github.com/n8n-io/n8n/issues/7844)) ([dbd62a4](https://github.com/n8n-io/n8n/commit/dbd62a4992ab8aca59e3cb50d3d970454e462238))
|
||||
* **Local File Trigger Node:** Add polling option typically good to watch network files/folders ([#7942](https://github.com/n8n-io/n8n/issues/7942)) ([2fbdfec](https://github.com/n8n-io/n8n/commit/2fbdfec0c0a3f5da64764e7821e84db30b664e49))
|
||||
* **n8n Form Trigger Node:** Improvements ([#7571](https://github.com/n8n-io/n8n/issues/7571)) ([953a58f](https://github.com/n8n-io/n8n/commit/953a58f18bfdd36fa8b526ca6213631aacab49cb))
|
||||
|
||||
|
||||
|
||||
# [1.20.0](https://github.com/n8n-io/n8n/compare/n8n@1.19.0...n8n@1.20.0) (2023-12-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **AWS DynamoDB Node:** Improve error message parsing ([#7793](https://github.com/n8n-io/n8n/issues/7793)) ([5ba5ed8](https://github.com/n8n-io/n8n/commit/5ba5ed8e3c8ba2f909859bde129d92576fbda46f))
|
||||
* **core:** Allow grace period for binary data deletion after manual execution ([#7889](https://github.com/n8n-io/n8n/issues/7889)) ([61d8aeb](https://github.com/n8n-io/n8n/commit/61d8aebeaf6487269b252b353fdf16dcb67f41ff))
|
||||
* **core:** Consolidate ownership and sharing data on workflows and credentials ([#7920](https://github.com/n8n-io/n8n/issues/7920)) ([38b88b9](https://github.com/n8n-io/n8n/commit/38b88b946bab67dc1a964bb3c980a627d4a32595))
|
||||
* **core:** Fix hard deletes stopping if database query throws ([#7848](https://github.com/n8n-io/n8n/issues/7848)) ([46dd4d3](https://github.com/n8n-io/n8n/commit/46dd4d3105db3a15c81903ae81c9bbb21a45397b))
|
||||
* **core:** Make sure mfa secret and recovery codes are not returned on login ([#7936](https://github.com/n8n-io/n8n/issues/7936)) ([f5502cc](https://github.com/n8n-io/n8n/commit/f5502cc628f6b348f7fe3325b96ec9dc3360beaf)), closes [/github.com/n8n-io/n8n/pull/6994/files#diff-95a87cb029a3d26e6722df2e68132453fc254fc1f4540cbdaa95cfdbda1893deL91](https://github.com//github.com/n8n-io/n8n/pull/6994/files/issues/diff-95a87cb029a3d26e6722df2e68132453fc254fc1f4540cbdaa95cfdbda1893deL91) [4#a39f9e5ba64a48b58a71d81c837e8227](https://github.com/4/issues/a39f9e5ba64a48b58a71d81c837e8227) [4#f6a177d32bde4b57ae2da0b8e454](https://github.com/4/issues/f6a177d32bde4b57ae2da0b8e454) [4#fef2d36ab02247e1a0f65a74f6fb534](https://github.com/4/issues/fef2d36ab02247e1a0f65a74f6fb534)
|
||||
* **editor:** Fix deletion of last execution at execution preview ([#7883](https://github.com/n8n-io/n8n/issues/7883)) ([ce2d388](https://github.com/n8n-io/n8n/commit/ce2d388f059c0bb32d27f4b29e901d1a70083610))
|
||||
* **editor:** Replace isInstanceOwner checks with scopes where applicable ([#7858](https://github.com/n8n-io/n8n/issues/7858)) ([132d691](https://github.com/n8n-io/n8n/commit/132d691cbf983f60293c7423de0077fb7c97e0af))
|
||||
* **Google Sheets Node:** Fix issue with paired items not being set correctly ([#7862](https://github.com/n8n-io/n8n/issues/7862)) ([5207a2f](https://github.com/n8n-io/n8n/commit/5207a2fe5210e40d3b2aedd95182a18e497c72ab))
|
||||
* **Notion Node:** Fix broken Notion node parameters ([#7864](https://github.com/n8n-io/n8n/issues/7864)) ([51d1f5b](https://github.com/n8n-io/n8n/commit/51d1f5b82070542d45c3d57387343959a3f0abb2)), closes [#7791](https://github.com/n8n-io/n8n/issues/7791)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **BambooHR Node:** Add support for Only Current on company reports ([#7878](https://github.com/n8n-io/n8n/issues/7878)) ([4175801](https://github.com/n8n-io/n8n/commit/4175801c90ad4f744d1a7c331d4fb20891ed2e9e))
|
||||
* **core:** Allow admin creation ([#7837](https://github.com/n8n-io/n8n/issues/7837)) ([476806e](https://github.com/n8n-io/n8n/commit/476806ebb0f31f656992fb67aba37116f10e1475))
|
||||
* **editor:** Add sections to create node panel ([#7831](https://github.com/n8n-io/n8n/issues/7831)) ([39fa8d2](https://github.com/n8n-io/n8n/commit/39fa8d21bbee5d870b2620ec65401a5ca134c4f1))
|
||||
* **editor:** Open template credential setup from collection ([#7882](https://github.com/n8n-io/n8n/issues/7882)) ([627ddb9](https://github.com/n8n-io/n8n/commit/627ddb91fb6c00796671a1f72f59a251cd89004d))
|
||||
* **editor:** Select credentials in template setup if theres only one ([#7879](https://github.com/n8n-io/n8n/issues/7879)) ([fe3417a](https://github.com/n8n-io/n8n/commit/fe3417a615534a01cb0c7b5e8f47bc18abd5cd4d))
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **editor:** Improve node rendering performance when opening large workflows ([#7904](https://github.com/n8n-io/n8n/issues/7904)) ([a8049a0](https://github.com/n8n-io/n8n/commit/a8049a0def21506ebf4fb1d3b69ae28ec35fdc21)), closes [#7901](https://github.com/n8n-io/n8n/issues/7901) [4#a39f9e5ba64a48b58a71d81c837e8227](https://github.com/4/issues/a39f9e5ba64a48b58a71d81c837e8227) [4#f6a177d32bde4b57ae2da0b8e454](https://github.com/4/issues/f6a177d32bde4b57ae2da0b8e454) [4#fef2d36ab02247e1a0f65a74f6fb534](https://github.com/4/issues/fef2d36ab02247e1a0f65a74f6fb534)
|
||||
* **editor:** Improve performance when opening large workflows with node issues ([#7901](https://github.com/n8n-io/n8n/issues/7901)) ([4bd7ae2](https://github.com/n8n-io/n8n/commit/4bd7ae29f7c82b8817420e617a123024147c6c70))
|
||||
|
||||
|
||||
|
||||
# [1.19.0](https://github.com/n8n-io/n8n/compare/n8n@1.18.0...n8n@1.19.0) (2023-11-29)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **core:** Ensure member and admin cannot be promoted to owner ([#7830](https://github.com/n8n-io/n8n/issues/7830)) ([9b87a59](https://github.com/n8n-io/n8n/commit/9b87a596ca4aec462faedcca1ba4655b168bc3bc)), closes [/linear.app/n8n/issue/PAY-985/add-user-role-modification-endpoint#comment-62355f6](https://github.com//linear.app/n8n/issue/PAY-985/add-user-role-modification-endpoint/issues/comment-62355f6)
|
||||
* **core:** Prevent error messages due to statistics about data loading ([#7824](https://github.com/n8n-io/n8n/issues/7824)) ([847f6ac](https://github.com/n8n-io/n8n/commit/847f6ac771316eea270d2e83adac5d8a6483475a))
|
||||
* **core:** Tighten checks for multi-main setup usage ([#7788](https://github.com/n8n-io/n8n/issues/7788)) ([fdb2c18](https://github.com/n8n-io/n8n/commit/fdb2c18ecc49d1e8759e809d2e2c2e4aa17009da))
|
||||
* **core:** Use AbortController to notify nodes to abort execution ([#6141](https://github.com/n8n-io/n8n/issues/6141)) ([d2c18c5](https://github.com/n8n-io/n8n/commit/d2c18c57274cac02e70cf1cc9e533a6ca98f0ec6))
|
||||
* **editor:** Add telemetry to workflow history ([#7811](https://github.com/n8n-io/n8n/issues/7811)) ([d497041](https://github.com/n8n-io/n8n/commit/d4970410e1ba328b05ddc23abcbf33c719de5624))
|
||||
* **editor:** Allow owners and admins to share workflows and credentials they don't own ([#7833](https://github.com/n8n-io/n8n/issues/7833)) ([3ab3ec9](https://github.com/n8n-io/n8n/commit/3ab3ec9da88f7b7ae07a98d7ef7c4f9892079048))
|
||||
* **editor:** Disable context menu actions in read-only mode ([#7789](https://github.com/n8n-io/n8n/issues/7789)) ([902beff](https://github.com/n8n-io/n8n/commit/902beffce51d547094ea249d1fbbb70a879165d6))
|
||||
* **editor:** Fix cloud plan data loading on instance ([#7841](https://github.com/n8n-io/n8n/issues/7841)) ([8b99384](https://github.com/n8n-io/n8n/commit/8b99384367161a47b3de13b7e83bcf6d07e3bf19))
|
||||
* **editor:** Fix credential icon for old node type version ([#7843](https://github.com/n8n-io/n8n/issues/7843)) ([4074107](https://github.com/n8n-io/n8n/commit/40741075117dd8018ca1c6d03c050c3959142ebd))
|
||||
* **editor:** Fix icon for unknown node type ([#7842](https://github.com/n8n-io/n8n/issues/7842)) ([28ac5a7](https://github.com/n8n-io/n8n/commit/28ac5a750eb28e9ab41611a76fa5fb9c30ce64dc))
|
||||
* **editor:** Fix mouse position in workflow previews ([#7853](https://github.com/n8n-io/n8n/issues/7853)) ([c063398](https://github.com/n8n-io/n8n/commit/c0633987bfd6de24db0efc4bcb73adba9e9b6a74))
|
||||
* **editor:** Show nice error when environment is not set up ([#7778](https://github.com/n8n-io/n8n/issues/7778)) ([5835e05](https://github.com/n8n-io/n8n/commit/5835e055d39921cdf2aa9799e427931bec8e5e2c))
|
||||
* **editor:** Suppress dev server websocket messages in workflow view ([#7808](https://github.com/n8n-io/n8n/issues/7808)) ([685ffd7](https://github.com/n8n-io/n8n/commit/685ffd74137199f5e10145a33f3e0f03dabc2e7f))
|
||||
* **Google Sheets Node:** Read operation execute for each item ([#7800](https://github.com/n8n-io/n8n/issues/7800)) ([d548872](https://github.com/n8n-io/n8n/commit/d5488725a83f6705b95c9de9d8736adf1b870134))
|
||||
* **HTTP Request Node:** Enable expressions for binary input data fields ([#7782](https://github.com/n8n-io/n8n/issues/7782)) ([6208af0](https://github.com/n8n-io/n8n/commit/6208af07eb393b0fb8483b8ae4949a63423cc409))
|
||||
* **Microsoft SQL Node:** Prevent double escaping table name ([#7801](https://github.com/n8n-io/n8n/issues/7801)) ([73ec753](https://github.com/n8n-io/n8n/commit/73ec7533ce4724940c2b23f677a9dfcf75de6a16))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Add AI tool building capabilities ([#7336](https://github.com/n8n-io/n8n/issues/7336)) ([87def60](https://github.com/n8n-io/n8n/commit/87def60979bd6525b59df4bd811571a2afe83bec))
|
||||
* Add initial scope checks via decorators ([#7737](https://github.com/n8n-io/n8n/issues/7737)) ([a37f1cb](https://github.com/n8n-io/n8n/commit/a37f1cb0bad87c486c387383f8179aa42f0b9e1a))
|
||||
* Add user role select to users list settings page ([#7796](https://github.com/n8n-io/n8n/issues/7796)) ([137e238](https://github.com/n8n-io/n8n/commit/137e23853fdbd3e62037a6cb7f742811af41a03d))
|
||||
* Ado 1296 spike credential setup in templates ([#7786](https://github.com/n8n-io/n8n/issues/7786)) ([aae45b0](https://github.com/n8n-io/n8n/commit/aae45b043b9e1427f9ffc44ef88d719782fccee5))
|
||||
* **core:** Add Support for custom CORS origins for webhooks ([#7455](https://github.com/n8n-io/n8n/issues/7455)) ([99a9ea4](https://github.com/n8n-io/n8n/commit/99a9ea497a3d21739f911da5c88c076f60471bed))
|
||||
* **core:** Allow user role modification ([#7797](https://github.com/n8n-io/n8n/issues/7797)) ([7a86d36](https://github.com/n8n-io/n8n/commit/7a86d3606852fcbc537533af24eef34279b229c6))
|
||||
* **core:** Set up endpoint for all existing roles with license flag ([#7834](https://github.com/n8n-io/n8n/issues/7834)) ([2356fb0](https://github.com/n8n-io/n8n/commit/2356fb0f0c247271ffa00d1cf25460e06212f1c4))
|
||||
* **editor:** Add node name and version to NDV node settings ([#7731](https://github.com/n8n-io/n8n/issues/7731)) ([da85198](https://github.com/n8n-io/n8n/commit/da851986f6f7cd4375b06c28a149dcb375fe8b83))
|
||||
* **editor:** Add routing middleware, permission checks, RBAC store, RBAC component ([#7702](https://github.com/n8n-io/n8n/issues/7702)) ([67a8891](https://github.com/n8n-io/n8n/commit/67a88914f2f2d11c413e7f627d659333d8419af8))
|
||||
* **editor:** Replace middleware for Role checks with Scope checks ([#7847](https://github.com/n8n-io/n8n/issues/7847)) ([72852a6](https://github.com/n8n-io/n8n/commit/72852a60eb15cbf45ebcdd390770c4cd9929a457))
|
||||
* **editor:** Show avatars for users currently working on the same workflow ([#7763](https://github.com/n8n-io/n8n/issues/7763)) ([77bc8ec](https://github.com/n8n-io/n8n/commit/77bc8ecd4b1552f7253bc1348087db518ce7ce07))
|
||||
* **Notion Node:** Option to simplify output in getChildBlocks operation ([#7791](https://github.com/n8n-io/n8n/issues/7791)) ([d667bca](https://github.com/n8n-io/n8n/commit/d667bca658a2b79fa5d0afba9ef25f26a10cdfc2))
|
||||
* **Slack Node:** Add support for getting the profile of a user ([#7829](https://github.com/n8n-io/n8n/issues/7829)) ([90bb6ba](https://github.com/n8n-io/n8n/commit/90bb6ba4174a71f0d42e8dc9f009b879ca9d4616))
|
||||
|
||||
|
||||
|
||||
# [1.18.0](https://github.com/n8n-io/n8n/compare/n8n@1.17.0...n8n@1.18.0) (2023-11-22)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **core:** Account for non-ASCII chars in filename on binary data download ([#7742](https://github.com/n8n-io/n8n/issues/7742)) ([b4ebb1a](https://github.com/n8n-io/n8n/commit/b4ebb1a28dc87c297721299a635e836dcaa273b7))
|
||||
* **core:** Correct permissions for getstatus ([#7724](https://github.com/n8n-io/n8n/issues/7724)) ([f96c1d2](https://github.com/n8n-io/n8n/commit/f96c1d204400028c55a2120d0569180379c0649f))
|
||||
* **core:** Ensure failed executions are saved in queue mode ([#7744](https://github.com/n8n-io/n8n/issues/7744)) ([b7c5c74](https://github.com/n8n-io/n8n/commit/b7c5c7406f6f978bbd84737de34114e9492ae5f6))
|
||||
* **core:** Guard against node not found on cancelling test webhook ([#7750](https://github.com/n8n-io/n8n/issues/7750)) ([6be453b](https://github.com/n8n-io/n8n/commit/6be453b716eff14df420ef565ea1b5ffb3ce73f0))
|
||||
* **editor:** Handle permission edge cases (empty scopes) ([#7723](https://github.com/n8n-io/n8n/issues/7723)) ([e2ffd39](https://github.com/n8n-io/n8n/commit/e2ffd397fc0ab8d88128ba78d02c5df003af4a9d))
|
||||
* **editor:** Make sure LineController is registered with chart.js ([#7730](https://github.com/n8n-io/n8n/issues/7730)) ([ebee1a5](https://github.com/n8n-io/n8n/commit/ebee1a590873aa56c23fd610616196ee27fe657a))
|
||||
* **editor:** Move workerview entry into settings menu ([#7761](https://github.com/n8n-io/n8n/issues/7761)) ([366cd67](https://github.com/n8n-io/n8n/commit/366cd672a74649a19fc927e0327ae7c19ed5a1fc))
|
||||
* **editor:** Only show push to git menu item to owners ([#7766](https://github.com/n8n-io/n8n/issues/7766)) ([0d3d33d](https://github.com/n8n-io/n8n/commit/0d3d33dd1f2354248ac341a0c9f2553812f404e0))
|
||||
* **editor:** Show v1 banner dismiss button if owner ([#7722](https://github.com/n8n-io/n8n/issues/7722)) ([44d3b3e](https://github.com/n8n-io/n8n/commit/44d3b3ed7ee77715006591a4f49049388fcd4035))
|
||||
* **editor:** Use project diagram icon for worker view ([#7764](https://github.com/n8n-io/n8n/issues/7764)) ([ff0b651](https://github.com/n8n-io/n8n/commit/ff0b6511f74831c499ab032910dfa9cf38356e8c))
|
||||
* **editor:** Validate user info before submiting ([#7608](https://github.com/n8n-io/n8n/issues/7608)) ([2064f7f](https://github.com/n8n-io/n8n/commit/2064f7f251913a0cc22b4e27bb38df921f711109))
|
||||
* **GitHub Node:** Fix issue preventing file edits on branches ([#7734](https://github.com/n8n-io/n8n/issues/7734)) ([ce002a6](https://github.com/n8n-io/n8n/commit/ce002a6cc672d1e13cc3d3470add78781d1ef20e))
|
||||
* **Google Sheets Node:** Check for `null` before destructuring ([#7729](https://github.com/n8n-io/n8n/issues/7729)) ([5d4a52d](https://github.com/n8n-io/n8n/commit/5d4a52d3b7e35924e1a8c9a2c808418bdf224d2c))
|
||||
* **Item Lists Node:** Don't check same type in remove duplicates operation ([#7678](https://github.com/n8n-io/n8n/issues/7678)) ([4f30764](https://github.com/n8n-io/n8n/commit/4f307646f3a5691331c7c610c62f562921a005f8))
|
||||
* **JotForm Trigger Node:** Fix iteration on form loader ([#7751](https://github.com/n8n-io/n8n/issues/7751)) ([82f3202](https://github.com/n8n-io/n8n/commit/82f3202a2de2863f01abe3cf84d6f37eba4fb6fa))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Add Creator hub link to Templates page ([#7721](https://github.com/n8n-io/n8n/issues/7721)) ([4dbae0e](https://github.com/n8n-io/n8n/commit/4dbae0e2e95d1b5f46cfc50a5a9fc6bb761defde))
|
||||
* **core:** Coordinate manual workflow activation and deactivation in multi-main scenario ([#7643](https://github.com/n8n-io/n8n/issues/7643)) ([4c40825](https://github.com/n8n-io/n8n/commit/4c4082503c916d654758da738321f9e78a098ce5)), closes [#7566](https://github.com/n8n-io/n8n/issues/7566)
|
||||
* **editor:** Add node context menu ([#7620](https://github.com/n8n-io/n8n/issues/7620)) ([8d12c1a](https://github.com/n8n-io/n8n/commit/8d12c1ad8d9283764647836bdd50224259d506e9))
|
||||
* **editor:** Node IO filter ([#7503](https://github.com/n8n-io/n8n/issues/7503)) ([1881765](https://github.com/n8n-io/n8n/commit/18817651ec5d9ed5e774379ae5cf8f57c5461e43))
|
||||
|
||||
|
||||
|
||||
# [1.17.0](https://github.com/n8n-io/n8n/compare/n8n@1.16.0...n8n@1.17.0) (2023-11-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Convert to/from binary data Node:** Better mime type defaults ([#7693](https://github.com/n8n-io/n8n/issues/7693)) ([9b3be0c](https://github.com/n8n-io/n8n/commit/9b3be0cfd8b0b58903d89ea3bf0b73be579a4f89))
|
||||
* **core:** Consider subworkflows successfully run when in waiting state ([#7699](https://github.com/n8n-io/n8n/issues/7699)) ([0e00dab](https://github.com/n8n-io/n8n/commit/0e00dab9f5d5a6622cdc22fa8bfbecc039f6b67a))
|
||||
* **core:** Fix named parameter resolution in migrations ([#7688](https://github.com/n8n-io/n8n/issues/7688)) ([4441ed5](https://github.com/n8n-io/n8n/commit/4441ed51169e8be930c548b17f54147ff6bd8e7d)), closes [#7628](https://github.com/n8n-io/n8n/issues/7628)
|
||||
* **core:** Initialize JWT Secret before it's used anywhere ([#7707](https://github.com/n8n-io/n8n/issues/7707)) ([3460eb5](https://github.com/n8n-io/n8n/commit/3460eb5eeba95e51ccdac05084daf883c9750022))
|
||||
* **core:** Reduce memory usage in credentials risk auditing ([#7663](https://github.com/n8n-io/n8n/issues/7663)) ([9fd6319](https://github.com/n8n-io/n8n/commit/9fd6319583d0446e41de4fb80d4bc5a6c5e1ca07))
|
||||
* **Date & Time Node:** Add fromFormat option to solve ambiguous date strings ([#7675](https://github.com/n8n-io/n8n/issues/7675)) ([d2d11e0](https://github.com/n8n-io/n8n/commit/d2d11e0208e8a20145910bbdd02e7b273fb0aa13))
|
||||
* **editor:** Fix resource mapper component being truncated ([#7664](https://github.com/n8n-io/n8n/issues/7664)) ([00dff50](https://github.com/n8n-io/n8n/commit/00dff50140d12e37bfeecdf1300ff313c179ec89))
|
||||
* **editor:** More securely clear executions tab auto refresh timer ([#7685](https://github.com/n8n-io/n8n/issues/7685)) ([37dd658](https://github.com/n8n-io/n8n/commit/37dd658dc5bc1128c91d86105bf7f49dfcf96985))
|
||||
* **editor:** Redirect to workflow editor after saving in debug mode ([#7645](https://github.com/n8n-io/n8n/issues/7645)) ([020042e](https://github.com/n8n-io/n8n/commit/020042ef1a329e805035061fbf6743bde892e3b1))
|
||||
* **Google Sheets Node:** Append exceeding grid limits ([#7684](https://github.com/n8n-io/n8n/issues/7684)) ([88efb99](https://github.com/n8n-io/n8n/commit/88efb9958711bac446b6a698dfba50afd2b46132))
|
||||
* **HTTP Request Node:** Support generic credentials when using pagination ([#7686](https://github.com/n8n-io/n8n/issues/7686)) ([48b240b](https://github.com/n8n-io/n8n/commit/48b240b0269858adb8fde8abb8a7211b2a3e78e0)), closes [#7653](https://github.com/n8n-io/n8n/issues/7653)
|
||||
* **HubSpot Node:** Fetching available parameters fails when using expressions ([#7672](https://github.com/n8n-io/n8n/issues/7672)) ([a9ab738](https://github.com/n8n-io/n8n/commit/a9ab73896e6a42b2fd5df296c9ee95ac82936b7e))
|
||||
* **HubSpot Node:** Update deal owner on Hubspot Deal ([#7673](https://github.com/n8n-io/n8n/issues/7673)) ([3c0734b](https://github.com/n8n-io/n8n/commit/3c0734bd2d92e9d2b9e99658c2e14710f57f36ef))
|
||||
* **Spreadsheet File Node:** Read file as utf-8 in v1 ([#7701](https://github.com/n8n-io/n8n/issues/7701)) ([786b4ad](https://github.com/n8n-io/n8n/commit/786b4adcce910fa52104550d90a688c4046628f9))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **core:** Expression function $ifEmpty ([#7660](https://github.com/n8n-io/n8n/issues/7660)) ([1c7225e](https://github.com/n8n-io/n8n/commit/1c7225ebdb1d92ce45313bbab27b0839d963fc4c))
|
||||
* **Date & Time Node:** Option to include other fields in output item ([#7661](https://github.com/n8n-io/n8n/issues/7661)) ([aea3c50](https://github.com/n8n-io/n8n/commit/aea3c501313debaf1cf2b194023a534f829290ea))
|
||||
* **Discord Node:** Overhaul ([#5351](https://github.com/n8n-io/n8n/issues/5351)) ([6a53c2a](https://github.com/n8n-io/n8n/commit/6a53c2a375ca71ffad1491da5ae7e6ec461a1a56))
|
||||
* **Discourse Node:** Add new options to Get Users ([#7674](https://github.com/n8n-io/n8n/issues/7674)) ([2e8c841](https://github.com/n8n-io/n8n/commit/2e8c841277c2ba45ab2ab3e823135f2b15a7e570))
|
||||
* **editor:** Add color selector to sticky node ([#7453](https://github.com/n8n-io/n8n/issues/7453)) ([8359364](https://github.com/n8n-io/n8n/commit/8359364536809e667be86f4b4df0838c94a801d7))
|
||||
* **editor:** Add HTTP request nodes for credentials without a node ([#7157](https://github.com/n8n-io/n8n/issues/7157)) ([14035e1](https://github.com/n8n-io/n8n/commit/14035e1244fee5bc49b9afe57d63d9e887f25dd0))
|
||||
* **editor:** Add workflow filters to querystring ([#7456](https://github.com/n8n-io/n8n/issues/7456)) ([afd637b](https://github.com/n8n-io/n8n/commit/afd637b5eab2bba33fd9ec8b24104bef5e2a4cc0))
|
||||
* **editor:** Adds a EE view to show worker details and job status ([#7600](https://github.com/n8n-io/n8n/issues/7600)) ([cbc6909](https://github.com/n8n-io/n8n/commit/cbc690907fa36e2fde0218dd6f7737d00498c674))
|
||||
* **GitLab Node:** Add support for pagination on getIssues ([#7529](https://github.com/n8n-io/n8n/issues/7529)) ([0a0798e](https://github.com/n8n-io/n8n/commit/0a0798e48500b0c159aa37deae7ce5d144f4f4c7))
|
||||
* **OpenAI Node:** Add dall-e-3 support ([#7655](https://github.com/n8n-io/n8n/issues/7655)) ([a9c7188](https://github.com/n8n-io/n8n/commit/a9c7188c4d9d3a020cb26647c9030f6ffd47a35a))
|
||||
* **RabbitMQ Trigger Node:** Add exchange and routing key options ([#7547](https://github.com/n8n-io/n8n/issues/7547)) ([5aee2b7](https://github.com/n8n-io/n8n/commit/5aee2b768f7743c6508c518bab35206577035380))
|
||||
* **Telegram Node:** Add support for markdownv2 ([#7679](https://github.com/n8n-io/n8n/issues/7679)) ([819b3a7](https://github.com/n8n-io/n8n/commit/819b3a746a1cfbb785c97d0c681734211a599852))
|
||||
* **Venafi TLS Protect Cloud Node:** Add region parameter to Venafi protect cloud ([#7689](https://github.com/n8n-io/n8n/issues/7689)) ([a08fca5](https://github.com/n8n-io/n8n/commit/a08fca51d928b7bfb7c0081287a38274048892bb))
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **core:** Lazyload security audit reporters ([#7696](https://github.com/n8n-io/n8n/issues/7696)) ([b2ca050](https://github.com/n8n-io/n8n/commit/b2ca0500311d85742ef8abf8f9f0d1436e6d9ba1))
|
||||
|
||||
|
||||
|
||||
# [1.16.0](https://github.com/n8n-io/n8n/compare/n8n@1.15.1...n8n@1.16.0) (2023-11-08)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **core:** Comply with custom default for workflow saving settings ([#7634](https://github.com/n8n-io/n8n/issues/7634)) ([48c068f](https://github.com/n8n-io/n8n/commit/48c068f97b6c7df08fec9fd9d80a0e7eaacc95f5))
|
||||
* **core:** Decrease reset password token expire time ([#7598](https://github.com/n8n-io/n8n/issues/7598)) ([2aa7f63](https://github.com/n8n-io/n8n/commit/2aa7f6375a01625980278aee714bdc06002b0948))
|
||||
* **core:** Ensure `init` before checking leader or follower in multi-main scenario ([#7621](https://github.com/n8n-io/n8n/issues/7621)) ([a994ba5](https://github.com/n8n-io/n8n/commit/a994ba5e8d7092edeae05e7aa5fdfbb9fd854034))
|
||||
* **core:** Ensure pruning starts only after migrations have completed ([#7626](https://github.com/n8n-io/n8n/issues/7626)) ([f748de9](https://github.com/n8n-io/n8n/commit/f748de9567ed1ecebea0ee35e9c71f8ea0e2d450))
|
||||
* **core:** Fix accessor error when running partial execution ([#7618](https://github.com/n8n-io/n8n/issues/7618)) ([26361df](https://github.com/n8n-io/n8n/commit/26361dfcd31c9952c8ef109314ca88f5f03e40f4)), closes [#6229](https://github.com/n8n-io/n8n/issues/6229)
|
||||
* **core:** Make password-reset urls valid only for single-use ([#7622](https://github.com/n8n-io/n8n/issues/7622)) ([6031424](https://github.com/n8n-io/n8n/commit/60314248f4b021f451eb744184fe150ddc03bc6e))
|
||||
* **Crypto Node:** Fix issue with value not appearing for Sign action ([#7619](https://github.com/n8n-io/n8n/issues/7619)) ([5df583f](https://github.com/n8n-io/n8n/commit/5df583f783731e46500600e6a23ff3b7fdfb4e52))
|
||||
* **editor:** Allow overriding theme from query params ([#7591](https://github.com/n8n-io/n8n/issues/7591)) ([2854a0c](https://github.com/n8n-io/n8n/commit/2854a0cf467258c6dacc15c2b200cf6480b6ecef))
|
||||
* **editor:** Fix issue that frontend breaks with unkown nodes ([#7596](https://github.com/n8n-io/n8n/issues/7596)) ([db56a9e](https://github.com/n8n-io/n8n/commit/db56a9ee37e8b041ea8958fc8400b9e5b6b81316))
|
||||
* **editor:** Fix local storage flags defaulting to undefined string ([#7603](https://github.com/n8n-io/n8n/issues/7603)) ([151e60f](https://github.com/n8n-io/n8n/commit/151e60f829663e79982aae6ac1cd8489f3083224))
|
||||
* **editor:** Fix workflow history prune time limit (getting hours instead of days) ([#7644](https://github.com/n8n-io/n8n/issues/7644)) ([3d5a485](https://github.com/n8n-io/n8n/commit/3d5a485bcf7fef4c6b7d96df3a77c041178951a6))
|
||||
* **editor:** Hide not supported node options ([#7597](https://github.com/n8n-io/n8n/issues/7597)) ([b532a7b](https://github.com/n8n-io/n8n/commit/b532a7bdb7d33d5ffb20665dfde58cb664d39b4a))
|
||||
* **editor:** Remove unknown credentials on pasting workflow ([#7582](https://github.com/n8n-io/n8n/issues/7582)) ([d633753](https://github.com/n8n-io/n8n/commit/d63375368713b31e15735721c7a7603fe08a6645))
|
||||
* **editor:** Reset canvas zoom before workspace reset in node view ([#7625](https://github.com/n8n-io/n8n/issues/7625)) ([78b84af](https://github.com/n8n-io/n8n/commit/78b84af8d1cfed005c7d9c715d832e8c91fd9e3f))
|
||||
* **editor:** Zoom in/out on canvas the same amount on scroll/gesture ([#7602](https://github.com/n8n-io/n8n/issues/7602)) ([c92402a](https://github.com/n8n-io/n8n/commit/c92402a3cabfdc227f3c929bc7731d42f4516776))
|
||||
* **Facebook Lead Ads Trigger Node:** Fix issue with missing scope for business management ([#7616](https://github.com/n8n-io/n8n/issues/7616)) ([32b85ba](https://github.com/n8n-io/n8n/commit/32b85ba2fec6e74d8648be7e718b52140c1bc4fc))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **core:** Add the node version to telemetry in node_graph_string ([#7449](https://github.com/n8n-io/n8n/issues/7449)) ([59dc36a](https://github.com/n8n-io/n8n/commit/59dc36abd9141a863cb41c17a9115410b27bdb16))
|
||||
* **core:** Coordinate workflow activation in multiple main scenario in internal API ([#7566](https://github.com/n8n-io/n8n/issues/7566)) ([c857e42](https://github.com/n8n-io/n8n/commit/c857e42677ef0d415caf66f00d7af029546dfd79))
|
||||
* **core:** Initial support for two-way communication over websockets ([#7570](https://github.com/n8n-io/n8n/issues/7570)) ([ac87701](https://github.com/n8n-io/n8n/commit/ac877014eda83eb2ee61c87f29e2583f3fbfd125))
|
||||
* **core:** Log executed migrations with info level ([#7586](https://github.com/n8n-io/n8n/issues/7586)) ([7dac9ab](https://github.com/n8n-io/n8n/commit/7dac9ab82c2f91cfbb66a57f175c1865e8c8107a))
|
||||
* **core:** Rate limit forgot password endpoint ([#7604](https://github.com/n8n-io/n8n/issues/7604)) ([5790e25](https://github.com/n8n-io/n8n/commit/5790e251b8072679d7c061e2d2fa1f4229e03cf8))
|
||||
* **LinkedIn Node:** Add support for Article thumbnails ([#7489](https://github.com/n8n-io/n8n/issues/7489)) ([e6d3d1a](https://github.com/n8n-io/n8n/commit/e6d3d1a4c2dd6a860e935df4b0ce3f13e23030c7))
|
||||
* **NocoDB Node:** Add new data apis and workspace support ([#7329](https://github.com/n8n-io/n8n/issues/7329)) ([da2d2a8](https://github.com/n8n-io/n8n/commit/da2d2a83bbfb05db3a10aef99bfde3ccaf160d60))
|
||||
|
||||
|
||||
|
||||
## [1.15.1](https://github.com/n8n-io/n8n/compare/n8n@1.14.0...n8n@1.15.1) (2023-11-02)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **core:** Ensure execution deletion in worker lifecycle hook ([#7481](https://github.com/n8n-io/n8n/issues/7481)) ([742c8a8](https://github.com/n8n-io/n8n/commit/742c8a8534098522fe103fad09fa95f70c460b3d))
|
||||
* **core:** Fix data encryption on credentials import ([#7560](https://github.com/n8n-io/n8n/issues/7560)) ([b350568](https://github.com/n8n-io/n8n/commit/b350568505d48ec880fe98d2b62ef090d5399c5f))
|
||||
* **core:** Fix issue that prevents owner logging in when using ldap ([#7408](https://github.com/n8n-io/n8n/issues/7408)) ([479f902](https://github.com/n8n-io/n8n/commit/479f90231d0a03c69b17189384812b5a1d81ef3d))
|
||||
* **core:** Handle missing resultData in runData ([#7523](https://github.com/n8n-io/n8n/issues/7523)) ([1055bd3](https://github.com/n8n-io/n8n/commit/1055bd3762b90b013a300bd87e3fa902e902cb9e))
|
||||
* **core:** Permission check for subworkflow properly checking for workflow settings ([#7576](https://github.com/n8n-io/n8n/issues/7576)) ([437c95e](https://github.com/n8n-io/n8n/commit/437c95e84e144cc77f2866a74d6b670c415895cd))
|
||||
* **core:** Prevent executions from becoming forever running ([#7569](https://github.com/n8n-io/n8n/issues/7569)) ([9bdb85c](https://github.com/n8n-io/n8n/commit/9bdb85c4ced96fde75435e334dc757d6c7679926))
|
||||
* **core:** Upgrade crypto-js to address CVE-2023-46233 ([#7519](https://github.com/n8n-io/n8n/issues/7519)) ([65e5593](https://github.com/n8n-io/n8n/commit/65e559323371e8235b92e2134d9908d69043fac4))
|
||||
* **editor:** Do not truncate form inputs ([#7528](https://github.com/n8n-io/n8n/issues/7528)) ([ae616f1](https://github.com/n8n-io/n8n/commit/ae616f146bc2ce8d37f8cf5116c6c4c8682a91a6))
|
||||
* **editor:** Fix NDV close after using input select ([#7544](https://github.com/n8n-io/n8n/issues/7544)) ([3b5e181](https://github.com/n8n-io/n8n/commit/3b5e181e66f8d7e3860e3078dae7cbb20e92551a))
|
||||
* **editor:** Fix NDV unexpected re-render ([#7532](https://github.com/n8n-io/n8n/issues/7532)) ([2853fcf](https://github.com/n8n-io/n8n/commit/2853fcff735fd0b98c19c1192349ef2c659d2493))
|
||||
* **editor:** Fix route component caching, incorrect use of array reduce method and enable WF history feature ([#7434](https://github.com/n8n-io/n8n/issues/7434)) ([12a89e6](https://github.com/n8n-io/n8n/commit/12a89e6d1441f81380d5e477274a5e2d3eb29f2d))
|
||||
* **editor:** Fixes the issue that Switch Node can not be created ([#7516](https://github.com/n8n-io/n8n/issues/7516)) ([df89685](https://github.com/n8n-io/n8n/commit/df89685e1548219f4c06614287abafbc96697817))
|
||||
* **editor:** Handle `localStorage` being blocked/unavailable ([#7348](https://github.com/n8n-io/n8n/issues/7348)) ([c05bc67](https://github.com/n8n-io/n8n/commit/c05bc6728d3227af4931ddcda5ed8bc6a3539dd0))
|
||||
* Fix dark mode small issues ([#7573](https://github.com/n8n-io/n8n/issues/7573)) ([1d81afc](https://github.com/n8n-io/n8n/commit/1d81afcbdf17166f3ebf468673e3ba348ae7fecb))
|
||||
* **Jira Software Node:** Handle missing issue types in issue types loader ([#7534](https://github.com/n8n-io/n8n/issues/7534)) ([9762705](https://github.com/n8n-io/n8n/commit/9762705833c809fd2781de179279a15c1be988eb))
|
||||
* **Switch Node:** Allow sortable Switch rules ([#7555](https://github.com/n8n-io/n8n/issues/7555)) ([7a56e58](https://github.com/n8n-io/n8n/commit/7a56e58a608132ef795d8c5cdaccb8caa49c0e8f))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **core:** Add optional Error-Output ([#7460](https://github.com/n8n-io/n8n/issues/7460)) ([655efea](https://github.com/n8n-io/n8n/commit/655efeaf669e9722895b66fef47f000507459210))
|
||||
* **core:** Make queue mode settings configurable ([#7526](https://github.com/n8n-io/n8n/issues/7526)) ([3d95b24](https://github.com/n8n-io/n8n/commit/3d95b243e935e4eba97a418d05fa687169ab7d07))
|
||||
* **core:** Set up leader selection for multiple main instances ([#7527](https://github.com/n8n-io/n8n/issues/7527)) ([442c73e](https://github.com/n8n-io/n8n/commit/442c73e63bb54f50657a511d88912a80cab64c7f))
|
||||
* **editor:** Implement the `UserStack` design system component ([#7559](https://github.com/n8n-io/n8n/issues/7559)) ([ce14f62](https://github.com/n8n-io/n8n/commit/ce14f6266b30caadb477b08d4257b82c769a74c3))
|
||||
* **HTTP Request Node:** Add pagination support ([#5993](https://github.com/n8n-io/n8n/issues/5993)) ([cc2bd2e](https://github.com/n8n-io/n8n/commit/cc2bd2e19c8b75320b236de215d389220fbe24ae))
|
||||
* **HTTP Request Node:** Update icon and default color ([#7572](https://github.com/n8n-io/n8n/issues/7572)) ([ff279ab](https://github.com/n8n-io/n8n/commit/ff279ab4112435c341b84081d68b976ff03bf261))
|
||||
* **n8n Form Trigger Node:** Add text area and password input types ([#7474](https://github.com/n8n-io/n8n/issues/7474)) ([b72040a](https://github.com/n8n-io/n8n/commit/b72040aa5423aa6cb16dea2e7c6ea6439376b653))
|
||||
* **editor:** Dark mode is here! You can change it under personal settings.([#6980](https://github.com/n8n-io/n8n/pull/6980)) ([0746783](https://github.com/n8n-io/n8n/commit/0746783e027ebe6715588a68db399a34e0211a96))
|
||||
|
||||
|
||||
|
||||
# [1.15.0](https://github.com/n8n-io/n8n/compare/n8n@1.14.0...n8n@1.15.0) (2023-11-02)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **core:** Ensure execution deletion in worker lifecycle hook ([#7481](https://github.com/n8n-io/n8n/issues/7481)) ([742c8a8](https://github.com/n8n-io/n8n/commit/742c8a8534098522fe103fad09fa95f70c460b3d))
|
||||
* **core:** Fix data encryption on credentials import ([#7560](https://github.com/n8n-io/n8n/issues/7560)) ([b350568](https://github.com/n8n-io/n8n/commit/b350568505d48ec880fe98d2b62ef090d5399c5f))
|
||||
* **core:** Fix issue that prevents owner logging in when using ldap ([#7408](https://github.com/n8n-io/n8n/issues/7408)) ([479f902](https://github.com/n8n-io/n8n/commit/479f90231d0a03c69b17189384812b5a1d81ef3d))
|
||||
* **core:** Handle missing resultData in runData ([#7523](https://github.com/n8n-io/n8n/issues/7523)) ([1055bd3](https://github.com/n8n-io/n8n/commit/1055bd3762b90b013a300bd87e3fa902e902cb9e))
|
||||
* **core:** Permission check for subworkflow properly checking for workflow settings ([#7576](https://github.com/n8n-io/n8n/issues/7576)) ([437c95e](https://github.com/n8n-io/n8n/commit/437c95e84e144cc77f2866a74d6b670c415895cd))
|
||||
* **core:** Prevent executions from becoming forever running ([#7569](https://github.com/n8n-io/n8n/issues/7569)) ([9bdb85c](https://github.com/n8n-io/n8n/commit/9bdb85c4ced96fde75435e334dc757d6c7679926))
|
||||
* **core:** Upgrade crypto-js to address CVE-2023-46233 ([#7519](https://github.com/n8n-io/n8n/issues/7519)) ([65e5593](https://github.com/n8n-io/n8n/commit/65e559323371e8235b92e2134d9908d69043fac4))
|
||||
* **editor:** Do not truncate form inputs ([#7528](https://github.com/n8n-io/n8n/issues/7528)) ([ae616f1](https://github.com/n8n-io/n8n/commit/ae616f146bc2ce8d37f8cf5116c6c4c8682a91a6))
|
||||
* **editor:** Fix NDV close after using input select ([#7544](https://github.com/n8n-io/n8n/issues/7544)) ([3b5e181](https://github.com/n8n-io/n8n/commit/3b5e181e66f8d7e3860e3078dae7cbb20e92551a))
|
||||
* **editor:** Fix NDV unexpected re-render ([#7532](https://github.com/n8n-io/n8n/issues/7532)) ([2853fcf](https://github.com/n8n-io/n8n/commit/2853fcff735fd0b98c19c1192349ef2c659d2493))
|
||||
* **editor:** Fix route component caching, incorrect use of array reduce method and enable WF history feature ([#7434](https://github.com/n8n-io/n8n/issues/7434)) ([12a89e6](https://github.com/n8n-io/n8n/commit/12a89e6d1441f81380d5e477274a5e2d3eb29f2d))
|
||||
* **editor:** Fixes the issue that Switch Node can not be created ([#7516](https://github.com/n8n-io/n8n/issues/7516)) ([df89685](https://github.com/n8n-io/n8n/commit/df89685e1548219f4c06614287abafbc96697817))
|
||||
* **editor:** Handle `localStorage` being blocked/unavailable ([#7348](https://github.com/n8n-io/n8n/issues/7348)) ([c05bc67](https://github.com/n8n-io/n8n/commit/c05bc6728d3227af4931ddcda5ed8bc6a3539dd0))
|
||||
* Fix dark mode small issues ([#7573](https://github.com/n8n-io/n8n/issues/7573)) ([1d81afc](https://github.com/n8n-io/n8n/commit/1d81afcbdf17166f3ebf468673e3ba348ae7fecb))
|
||||
* **Jira Software Node:** Handle missing issue types in issue types loader ([#7534](https://github.com/n8n-io/n8n/issues/7534)) ([9762705](https://github.com/n8n-io/n8n/commit/9762705833c809fd2781de179279a15c1be988eb))
|
||||
* **Switch Node:** Allow sortable Switch rules ([#7555](https://github.com/n8n-io/n8n/issues/7555)) ([7a56e58](https://github.com/n8n-io/n8n/commit/7a56e58a608132ef795d8c5cdaccb8caa49c0e8f))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **core:** Add optional Error-Output ([#7460](https://github.com/n8n-io/n8n/issues/7460)) ([655efea](https://github.com/n8n-io/n8n/commit/655efeaf669e9722895b66fef47f000507459210))
|
||||
* **core:** Make queue mode settings configurable ([#7526](https://github.com/n8n-io/n8n/issues/7526)) ([3d95b24](https://github.com/n8n-io/n8n/commit/3d95b243e935e4eba97a418d05fa687169ab7d07))
|
||||
* **core:** Set up leader selection for multiple main instances ([#7527](https://github.com/n8n-io/n8n/issues/7527)) ([442c73e](https://github.com/n8n-io/n8n/commit/442c73e63bb54f50657a511d88912a80cab64c7f))
|
||||
* **editor:** Implement the `UserStack` design system component ([#7559](https://github.com/n8n-io/n8n/issues/7559)) ([ce14f62](https://github.com/n8n-io/n8n/commit/ce14f6266b30caadb477b08d4257b82c769a74c3))
|
||||
* **HTTP Request Node:** Add pagination support ([#5993](https://github.com/n8n-io/n8n/issues/5993)) ([cc2bd2e](https://github.com/n8n-io/n8n/commit/cc2bd2e19c8b75320b236de215d389220fbe24ae))
|
||||
* **HTTP Request Node:** Update icon and default color ([#7572](https://github.com/n8n-io/n8n/issues/7572)) ([ff279ab](https://github.com/n8n-io/n8n/commit/ff279ab4112435c341b84081d68b976ff03bf261))
|
||||
* **n8n Form Trigger Node:** Add text area and password input types ([#7474](https://github.com/n8n-io/n8n/issues/7474)) ([b72040a](https://github.com/n8n-io/n8n/commit/b72040aa5423aa6cb16dea2e7c6ea6439376b653))
|
||||
* * **editor:** Dark mode is here! You can change it under personal settings.([#6980](https://github.com/n8n-io/n8n/pull/6980)) ([0746783](https://github.com/n8n-io/n8n/commit/0746783e027ebe6715588a68db399a34e0211a96))
|
||||
|
||||
|
||||
|
||||
# [1.14.0](https://github.com/n8n-io/n8n/compare/n8n@1.13.0...n8n@1.14.0) (2023-10-25)
|
||||
|
||||
|
||||
|
|
|
@ -1,50 +0,0 @@
|
|||
paths:
|
||||
'packages/**':
|
||||
- If fixing bug, added test to cover scenario.
|
||||
- If addressing forum or Github issue, added link to description.
|
||||
'packages/**/*.ts':
|
||||
- Added unit tests to cover new or updated functionality.
|
||||
'**/*.vue':
|
||||
- Used composition API for all new components.
|
||||
- Added component or unit tests to cover functionality.
|
||||
|
||||
# cli
|
||||
'packages/cli/src/databases/migrations/**':
|
||||
- Requested review from at least two engineers on migration.
|
||||
- Avoided irreversible data migrations.
|
||||
- Avoided deleting or updating data keys.
|
||||
- Wrote 'down' migration if possible.
|
||||
'n8n/packages/cli/src/api/**':
|
||||
- Added integration tests for new endpoints.
|
||||
|
||||
# editor ui
|
||||
'packages/editor-ui/**/*.vue':
|
||||
- Added E2E if adding new features.
|
||||
- Used design system tokens (colors, spacings...) where possible.
|
||||
'packages/editor-ui/src/mixins/restApi.ts':
|
||||
- Avoided adding new methods. Only deleted from here.
|
||||
'packages/editor-ui/src/mixins/**':
|
||||
- Avoided adding new mixins (use composables instead). Only removed code from here.
|
||||
'packages/editor-ui/src/views/NodeView.vue':
|
||||
- Avoided adding code here. Only refactored to make it smaller.
|
||||
'packages/editor-ui/src/hooks/**':
|
||||
- Avoided adding new hooks. Only refactored to move hooks to relevant store instead.
|
||||
|
||||
# nodes-base
|
||||
'packages/nodes-base/nodes/**':
|
||||
- Added workflow tests for nodes if possible.
|
||||
'packages/nodes-base/package.json':
|
||||
- Avoided adding dependencies for nodes if not absolutely necessary.
|
||||
|
||||
# design-system
|
||||
'packages/design-system/**/*.vue':
|
||||
- Used design system tokens (colors, spacings...) where possible.
|
||||
- Updated Storybook with new component or updated functionality.
|
||||
|
||||
# e2e
|
||||
'cypress/e2e/**':
|
||||
- Avoided chaining commands more than two or three times (to avoid flakiness because only last one will be retried).
|
||||
- Spoofed endpoints that are not critical for the test (to avoid flakiness).
|
||||
- Picked most efficient path to start the test (for example skipped account setup and starting at /workflow/new for a canvas test).
|
||||
- Avoided adding waits on time (use request intercepts instead).
|
||||
- Ensured each spec does not depend on any another spec to pass.
|
|
@ -5,24 +5,27 @@ Great that you are here and you want to contribute to n8n
|
|||
## Contents
|
||||
|
||||
- [Contributing to n8n](#contributing-to-n8n)
|
||||
- [Contents](#contents)
|
||||
- [Code of conduct](#code-of-conduct)
|
||||
- [Directory structure](#directory-structure)
|
||||
- [Development setup](#development-setup)
|
||||
- [Requirements](#requirements)
|
||||
- [Node.js](#nodejs)
|
||||
- [pnpm](#pnpm)
|
||||
- [pnpm workspaces](#pnpm-workspaces)
|
||||
- [corepack](#corepack)
|
||||
- [Build tools](#build-tools)
|
||||
- [Actual n8n setup](#actual-n8n-setup)
|
||||
- [Start](#start)
|
||||
- [Development cycle](#development-cycle)
|
||||
- [Test suite](#test-suite)
|
||||
- [Releasing](#releasing)
|
||||
- [Create custom nodes](#create-custom-nodes)
|
||||
- [Extend documentation](#extend-documentation)
|
||||
- [Contributor License Agreement](#contributor-license-agreement)
|
||||
- [Contents](#contents)
|
||||
- [Code of conduct](#code-of-conduct)
|
||||
- [Directory structure](#directory-structure)
|
||||
- [Development setup](#development-setup)
|
||||
- [Requirements](#requirements)
|
||||
- [Node.js](#nodejs)
|
||||
- [pnpm](#pnpm)
|
||||
- [pnpm workspaces](#pnpm-workspaces)
|
||||
- [corepack](#corepack)
|
||||
- [Build tools](#build-tools)
|
||||
- [Actual n8n setup](#actual-n8n-setup)
|
||||
- [Start](#start)
|
||||
- [Development cycle](#development-cycle)
|
||||
- [Test suite](#test-suite)
|
||||
- [Unit tests](#unit-tests)
|
||||
- [E2E tests](#e2e-tests)
|
||||
- [Releasing](#releasing)
|
||||
- [Create custom nodes](#create-custom-nodes)
|
||||
- [Extend documentation](#extend-documentation)
|
||||
- [Contribute workflow templates](#contribute-workflow-templates)
|
||||
- [Contributor License Agreement](#contributor-license-agreement)
|
||||
|
||||
## Code of conduct
|
||||
|
||||
|
@ -186,7 +189,9 @@ automatically build your code, restart the backend and refresh the frontend
|
|||
|
||||
### Test suite
|
||||
|
||||
The tests can be started via:
|
||||
#### Unit tests
|
||||
|
||||
Unit tests can be started via:
|
||||
|
||||
```
|
||||
pnpm test
|
||||
|
@ -196,6 +201,16 @@ If that gets executed in one of the package folders it will only run the tests
|
|||
of this package. If it gets executed in the n8n-root folder it will run all
|
||||
tests of all packages.
|
||||
|
||||
#### E2E tests
|
||||
|
||||
E2E tests can be started via one of the following commands:
|
||||
|
||||
- `pnpm test:e2e:ui`: Start n8n and run e2e tests interactively using built UI code. Does not react to code changes (i.e. runs `pnpm start` and `cypress open`)
|
||||
- `pnpm test:e2e:dev`: Start n8n in development mode and run e2e tests interactively. Reacts to code changes (i.e. runs `pnpm dev` and `cypress open`)
|
||||
- `pnpm test:e2e:all`: Start n8n and run e2e tests headless (i.e. runs `pnpm start` and `cypress run --headless`)
|
||||
|
||||
⚠️ Remember to stop your dev server before. Otherwise port binding will fail.
|
||||
|
||||
## Releasing
|
||||
|
||||
To start a release, trigger [this workflow](https://github.com/n8n-io/n8n/actions/workflows/release-create-pr.yml) with the SemVer release type, and select a branch to cut this release from. This workflow will then:
|
||||
|
@ -220,6 +235,14 @@ Learn about [building nodes](https://docs.n8n.io/integrations/creating-nodes/) t
|
|||
|
||||
The repository for the n8n documentation on [docs.n8n.io](https://docs.n8n.io) can be found [here](https://github.com/n8n-io/n8n-docs).
|
||||
|
||||
## Contribute workflow templates
|
||||
|
||||
You can submit your workflows to n8n's template library.
|
||||
|
||||
n8n is working on a creator program, and developing a marketplace of templates. This is an ongoing project, and details are likely to change.
|
||||
|
||||
Refer to [n8n Creator hub](https://www.notion.so/n8n/n8n-Creator-hub-7bd2cbe0fce0449198ecb23ff4a2f76f) for information on how to submit templates and become a creator.
|
||||
|
||||
## Contributor License Agreement
|
||||
|
||||
That we do not have any potential problems later it is sadly necessary to sign a [Contributor License Agreement](CONTRIBUTOR_LICENSE_AGREEMENT.md). That can be done literally with the push of a button.
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
# n8n - Workflow automation tool
|
||||
|
||||
n8n is an extendable workflow automation tool. With a [fair-code](http://faircode.io) distribution model, n8n
|
||||
n8n is an extendable workflow automation tool. With a [fair-code](https://faircode.io) distribution model, n8n
|
||||
will always have visible source code, be available to self-host, and allow you to add your own custom
|
||||
functions, logic and apps. n8n's node-based approach makes it highly versatile, enabling you to connect
|
||||
anything to everything.
|
||||
|
@ -94,7 +94,7 @@ development environment ready in minutes.
|
|||
|
||||
## License
|
||||
|
||||
n8n is [fair-code](http://faircode.io) distributed under the
|
||||
n8n is [fair-code](https://faircode.io) distributed under the
|
||||
[**Sustainable Use License**](https://github.com/n8n-io/n8n/blob/master/packages/cli/LICENSE.md) and the
|
||||
[**n8n Enterprise License**](https://github.com/n8n-io/n8n/blob/master/packages/cli/LICENSE_EE.md).
|
||||
|
||||
|
|
|
@ -19,4 +19,9 @@ module.exports = defineConfig({
|
|||
experimentalInteractiveRunEvents: true,
|
||||
experimentalSessionAndOrigin: true,
|
||||
},
|
||||
env: {
|
||||
MAX_PINNED_DATA_SIZE: process.env.VUE_APP_MAX_PINNED_DATA_SIZE
|
||||
? parseInt(process.env.VUE_APP_MAX_PINNED_DATA_SIZE, 10)
|
||||
: 16 * 1024,
|
||||
},
|
||||
});
|
||||
|
|
12
cypress/composables/featureFlags.ts
Normal file
12
cypress/composables/featureFlags.ts
Normal file
|
@ -0,0 +1,12 @@
|
|||
export const overrideFeatureFlag = (name: string, value: boolean | string) => {
|
||||
cy.window().then((win) => {
|
||||
// If feature flags hasn't been initialized yet, we store the override
|
||||
// in local storage and it gets loaded when the feature flags are
|
||||
// initialized.
|
||||
win.localStorage.setItem('N8N_EXPERIMENT_OVERRIDES', JSON.stringify({ [name]: value }));
|
||||
|
||||
if (win.featureFlags) {
|
||||
win.featureFlags.override(name, value);
|
||||
}
|
||||
});
|
||||
};
|
48
cypress/composables/modals/chat-modal.ts
Normal file
48
cypress/composables/modals/chat-modal.ts
Normal file
|
@ -0,0 +1,48 @@
|
|||
/**
|
||||
* Getters
|
||||
*/
|
||||
|
||||
export function getManualChatModal() {
|
||||
return cy.getByTestId('lmChat-modal');
|
||||
}
|
||||
|
||||
export function getManualChatInput() {
|
||||
return cy.getByTestId('workflow-chat-input');
|
||||
}
|
||||
|
||||
export function getManualChatSendButton() {
|
||||
return getManualChatModal().getByTestId('workflow-chat-send-button');
|
||||
}
|
||||
|
||||
export function getManualChatMessages() {
|
||||
return getManualChatModal().get('.messages .message');
|
||||
}
|
||||
|
||||
export function getManualChatModalCloseButton() {
|
||||
return getManualChatModal().get('.el-dialog__close');
|
||||
}
|
||||
|
||||
export function getManualChatModalLogs() {
|
||||
return getManualChatModal().getByTestId('lm-chat-logs');
|
||||
}
|
||||
|
||||
export function getManualChatModalLogsTree() {
|
||||
return getManualChatModalLogs().getByTestId('lm-chat-logs-tree');
|
||||
}
|
||||
|
||||
export function getManualChatModalLogsEntries() {
|
||||
return getManualChatModalLogs().getByTestId('lm-chat-logs-entry');
|
||||
}
|
||||
|
||||
/**
|
||||
* Actions
|
||||
*/
|
||||
|
||||
export function sendManualChatMessage(message: string) {
|
||||
getManualChatInput().type(message);
|
||||
getManualChatSendButton().click();
|
||||
}
|
||||
|
||||
export function closeManualChatModal() {
|
||||
getManualChatModalCloseButton().click();
|
||||
}
|
54
cypress/composables/modals/credential-modal.ts
Normal file
54
cypress/composables/modals/credential-modal.ts
Normal file
|
@ -0,0 +1,54 @@
|
|||
/**
|
||||
* Getters
|
||||
*/
|
||||
|
||||
export function getCredentialConnectionParameterInputs() {
|
||||
return cy.getByTestId('credential-connection-parameter');
|
||||
}
|
||||
|
||||
export function getCredentialConnectionParameterInputByName(name: string) {
|
||||
return cy.getByTestId(`parameter-input-${name}`);
|
||||
}
|
||||
|
||||
export function getEditCredentialModal() {
|
||||
return cy.getByTestId('editCredential-modal', { timeout: 5000 });
|
||||
}
|
||||
|
||||
export function getCredentialSaveButton() {
|
||||
return cy.getByTestId('credential-save-button', { timeout: 5000 });
|
||||
}
|
||||
|
||||
export function getCredentialDeleteButton() {
|
||||
return cy.getByTestId('credential-delete-button');
|
||||
}
|
||||
|
||||
export function getCredentialModalCloseButton() {
|
||||
return getEditCredentialModal().find('.el-dialog__close').first();
|
||||
}
|
||||
|
||||
/**
|
||||
* Actions
|
||||
*/
|
||||
|
||||
export function setCredentialConnectionParameterInputByName(name: string, value: string) {
|
||||
getCredentialConnectionParameterInputByName(name).type(value);
|
||||
}
|
||||
|
||||
export function saveCredential() {
|
||||
getCredentialSaveButton().click({ force: true });
|
||||
}
|
||||
|
||||
export function closeCredentialModal() {
|
||||
getCredentialModalCloseButton().click();
|
||||
}
|
||||
|
||||
export function setCredentialValues(values: Record<string, any>, save = true) {
|
||||
Object.entries(values).forEach(([key, value]) => {
|
||||
setCredentialConnectionParameterInputByName(key, value);
|
||||
});
|
||||
|
||||
if (save) {
|
||||
saveCredential();
|
||||
closeCredentialModal();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
/**
|
||||
* Getters
|
||||
*/
|
||||
|
||||
export const getWorkflowCredentialsModal = () => cy.getByTestId('setup-workflow-credentials-modal');
|
||||
|
||||
export const getContinueButton = () => cy.getByTestId('continue-button');
|
||||
|
||||
/**
|
||||
* Actions
|
||||
*/
|
||||
|
||||
export const closeModalFromContinueButton = () => getContinueButton().click();
|
73
cypress/composables/ndv.ts
Normal file
73
cypress/composables/ndv.ts
Normal file
|
@ -0,0 +1,73 @@
|
|||
/**
|
||||
* Getters
|
||||
*/
|
||||
|
||||
export function getCredentialSelect(eq = 0) {
|
||||
return cy.getByTestId('node-credentials-select').eq(eq);
|
||||
}
|
||||
|
||||
export function getCreateNewCredentialOption() {
|
||||
return cy.getByTestId('node-credentials-select-item-new');
|
||||
}
|
||||
|
||||
export function getBackToCanvasButton() {
|
||||
return cy.getByTestId('back-to-canvas');
|
||||
}
|
||||
|
||||
export function getExecuteNodeButton() {
|
||||
return cy.getByTestId('node-execute-button');
|
||||
}
|
||||
|
||||
export function getParameterInputByName(name: string) {
|
||||
return cy.getByTestId(`parameter-input-${name}`);
|
||||
}
|
||||
|
||||
export function getInputPanel() {
|
||||
return cy.getByTestId('input-panel');
|
||||
}
|
||||
|
||||
export function getMainPanel() {
|
||||
return cy.getByTestId('node-parameters');
|
||||
}
|
||||
|
||||
export function getOutputPanel() {
|
||||
return cy.getByTestId('output-panel');
|
||||
}
|
||||
|
||||
export function getOutputPanelDataContainer() {
|
||||
return getOutputPanel().getByTestId('ndv-data-container');
|
||||
}
|
||||
|
||||
export function getOutputPanelTable() {
|
||||
return getOutputPanelDataContainer().get('table');
|
||||
}
|
||||
|
||||
/**
|
||||
* Actions
|
||||
*/
|
||||
|
||||
export function openCredentialSelect(eq = 0) {
|
||||
getCredentialSelect(eq).click();
|
||||
}
|
||||
|
||||
export function setCredentialByName(name: string) {
|
||||
openCredentialSelect();
|
||||
getCredentialSelect().contains(name).click();
|
||||
}
|
||||
|
||||
export function clickCreateNewCredential() {
|
||||
openCredentialSelect();
|
||||
getCreateNewCredentialOption().click();
|
||||
}
|
||||
|
||||
export function clickGetBackToCanvas() {
|
||||
getBackToCanvasButton().click();
|
||||
}
|
||||
|
||||
export function clickExecuteNode() {
|
||||
getExecuteNodeButton().click();
|
||||
}
|
||||
|
||||
export function setParameterInputByName(name: string, value: string) {
|
||||
getParameterInputByName(name).clear().type(value);
|
||||
}
|
14
cypress/composables/setup-template-form-step.ts
Normal file
14
cypress/composables/setup-template-form-step.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
/**
|
||||
* Getters
|
||||
*/
|
||||
|
||||
export const getFormStep = () => cy.getByTestId('setup-credentials-form-step');
|
||||
|
||||
export const getStepHeading = ($el: JQuery<HTMLElement>) =>
|
||||
cy.wrap($el).findChildByTestId('credential-step-heading');
|
||||
|
||||
export const getStepDescription = ($el: JQuery<HTMLElement>) =>
|
||||
cy.wrap($el).findChildByTestId('credential-step-description');
|
||||
|
||||
export const getCreateAppCredentialsButton = (appName: string) =>
|
||||
cy.get(`button:contains("Create new ${appName} credential")`);
|
5
cypress/composables/setup-workflow-credentials-button.ts
Normal file
5
cypress/composables/setup-workflow-credentials-button.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
/**
|
||||
* Getters
|
||||
*/
|
||||
|
||||
export const getSetupWorkflowCredentialsButton = () => cy.get(`button:contains("Set up template")`);
|
32
cypress/composables/versions.ts
Normal file
32
cypress/composables/versions.ts
Normal file
|
@ -0,0 +1,32 @@
|
|||
/**
|
||||
* Getters
|
||||
*/
|
||||
|
||||
export function getVersionUpdatesPanelOpenButton() {
|
||||
return cy.getByTestId('version-updates-panel-button');
|
||||
}
|
||||
|
||||
export function getVersionUpdatesPanel() {
|
||||
return cy.getByTestId('version-updates-panel');
|
||||
}
|
||||
|
||||
export function getVersionUpdatesPanelCloseButton() {
|
||||
return getVersionUpdatesPanel().get('.el-drawer__close-btn').first();
|
||||
}
|
||||
|
||||
export function getVersionCard() {
|
||||
return cy.getByTestId('version-card');
|
||||
}
|
||||
|
||||
/**
|
||||
* Actions
|
||||
*/
|
||||
|
||||
export function openVersionUpdatesPanel() {
|
||||
getVersionUpdatesPanelOpenButton().click();
|
||||
getVersionUpdatesPanel().should('be.visible');
|
||||
}
|
||||
|
||||
export function closeVersionUpdatesPanel() {
|
||||
getVersionUpdatesPanelCloseButton().click();
|
||||
}
|
142
cypress/composables/workflow.ts
Normal file
142
cypress/composables/workflow.ts
Normal file
|
@ -0,0 +1,142 @@
|
|||
import { ROUTES } from '../constants';
|
||||
import { getManualChatModal } from './modals/chat-modal';
|
||||
|
||||
/**
|
||||
* Types
|
||||
*/
|
||||
|
||||
export type EndpointType =
|
||||
| 'ai_chain'
|
||||
| 'ai_document'
|
||||
| 'ai_embedding'
|
||||
| 'ai_languageModel'
|
||||
| 'ai_memory'
|
||||
| 'ai_outputParser'
|
||||
| 'ai_tool'
|
||||
| 'ai_retriever'
|
||||
| 'ai_textSplitter'
|
||||
| 'ai_vectorRetriever'
|
||||
| 'ai_vectorStore';
|
||||
|
||||
/**
|
||||
* Getters
|
||||
*/
|
||||
|
||||
export function getAddInputEndpointByType(nodeName: string, endpointType: EndpointType) {
|
||||
return cy.get(
|
||||
`.add-input-endpoint[data-jtk-scope-${endpointType}][data-endpoint-name="${nodeName}"]`,
|
||||
);
|
||||
}
|
||||
|
||||
export function getNodeCreatorItems() {
|
||||
return cy.getByTestId('item-iterator-item');
|
||||
}
|
||||
|
||||
export function getExecuteWorkflowButton() {
|
||||
return cy.getByTestId('execute-workflow-button');
|
||||
}
|
||||
|
||||
export function getManualChatButton() {
|
||||
return cy.getByTestId('workflow-chat-button');
|
||||
}
|
||||
|
||||
export function getNodes() {
|
||||
return cy.getByTestId('canvas-node');
|
||||
}
|
||||
|
||||
export function getNodeByName(name: string) {
|
||||
return cy.getByTestId('canvas-node').filter(`[data-name="${name}"]`).eq(0);
|
||||
}
|
||||
|
||||
export function getConnectionBySourceAndTarget(source: string, target: string) {
|
||||
return cy
|
||||
.get('.jtk-connector')
|
||||
.filter(`[data-source-node="${source}"][data-target-node="${target}"]`)
|
||||
.eq(0);
|
||||
}
|
||||
|
||||
export function getNodeCreatorSearchBar() {
|
||||
return cy.getByTestId('node-creator-search-bar');
|
||||
}
|
||||
|
||||
export function getNodeCreatorPlusButton() {
|
||||
return cy.getByTestId('node-creator-plus-button');
|
||||
}
|
||||
|
||||
/**
|
||||
* Actions
|
||||
*/
|
||||
|
||||
export function addNodeToCanvas(
|
||||
nodeDisplayName: string,
|
||||
plusButtonClick = true,
|
||||
preventNdvClose?: boolean,
|
||||
action?: string,
|
||||
) {
|
||||
if (plusButtonClick) {
|
||||
getNodeCreatorPlusButton().click();
|
||||
}
|
||||
|
||||
getNodeCreatorSearchBar().type(nodeDisplayName);
|
||||
getNodeCreatorSearchBar().type('{enter}');
|
||||
cy.wait(500);
|
||||
cy.get('body').then((body) => {
|
||||
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();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (!preventNdvClose) cy.get('body').type('{esc}');
|
||||
}
|
||||
|
||||
export function navigateToNewWorkflowPage(preventNodeViewUnload = true) {
|
||||
cy.visit(ROUTES.NEW_WORKFLOW_PAGE);
|
||||
cy.waitForLoad();
|
||||
cy.window().then((win) => {
|
||||
win.preventNodeViewBeforeUnload = preventNodeViewUnload;
|
||||
});
|
||||
}
|
||||
|
||||
export function addSupplementalNodeToParent(
|
||||
nodeName: string,
|
||||
endpointType: EndpointType,
|
||||
parentNodeName: string,
|
||||
) {
|
||||
getAddInputEndpointByType(parentNodeName, endpointType).click({ force: true });
|
||||
getNodeCreatorItems().contains(nodeName).click();
|
||||
getConnectionBySourceAndTarget(parentNodeName, nodeName).should('exist');
|
||||
}
|
||||
|
||||
export function addLanguageModelNodeToParent(nodeName: string, parentNodeName: string) {
|
||||
addSupplementalNodeToParent(nodeName, 'ai_languageModel', parentNodeName);
|
||||
}
|
||||
|
||||
export function addMemoryNodeToParent(nodeName: string, parentNodeName: string) {
|
||||
addSupplementalNodeToParent(nodeName, 'ai_memory', parentNodeName);
|
||||
}
|
||||
|
||||
export function addToolNodeToParent(nodeName: string, parentNodeName: string) {
|
||||
addSupplementalNodeToParent(nodeName, 'ai_tool', parentNodeName);
|
||||
}
|
||||
|
||||
export function addOutputParserNodeToParent(nodeName: string, parentNodeName: string) {
|
||||
addSupplementalNodeToParent(nodeName, 'ai_outputParser', parentNodeName);
|
||||
}
|
||||
|
||||
export function clickExecuteWorkflowButton() {
|
||||
getExecuteWorkflowButton().click();
|
||||
}
|
||||
|
||||
export function clickManualChatButton() {
|
||||
getManualChatButton().click();
|
||||
getManualChatModal().should('be.visible');
|
||||
}
|
||||
|
||||
export function openNode(nodeName: string) {
|
||||
getNodeByName(nodeName).dblclick();
|
||||
}
|
|
@ -12,6 +12,13 @@ export const INSTANCE_OWNER = {
|
|||
lastName: randLastName(),
|
||||
};
|
||||
|
||||
export const INSTANCE_ADMIN = {
|
||||
email: 'admin@n8n.io',
|
||||
password: DEFAULT_USER_PASSWORD,
|
||||
firstName: randFirstName(),
|
||||
lastName: randLastName(),
|
||||
};
|
||||
|
||||
export const INSTANCE_MEMBERS = [
|
||||
{
|
||||
email: 'rebecca@n8n.io',
|
||||
|
@ -28,12 +35,13 @@ export const INSTANCE_MEMBERS = [
|
|||
];
|
||||
|
||||
export const MANUAL_TRIGGER_NODE_NAME = 'Manual Trigger';
|
||||
export const MANUAL_TRIGGER_NODE_DISPLAY_NAME = 'When clicking "Execute Workflow"';
|
||||
export const MANUAL_TRIGGER_NODE_DISPLAY_NAME = 'When clicking "Test Workflow"';
|
||||
export const MANUAL_CHAT_TRIGGER_NODE_NAME = 'Chat Trigger';
|
||||
export const SCHEDULE_TRIGGER_NODE_NAME = 'Schedule Trigger';
|
||||
export const CODE_NODE_NAME = 'Code';
|
||||
export const SET_NODE_NAME = 'Set';
|
||||
export const EDIT_FIELDS_SET_NODE_NAME = 'Edit Fields';
|
||||
export const IF_NODE_NAME = 'IF';
|
||||
export const IF_NODE_NAME = 'If';
|
||||
export const MERGE_NODE_NAME = 'Merge';
|
||||
export const SWITCH_NODE_NAME = 'Switch';
|
||||
export const GMAIL_NODE_NAME = 'Gmail';
|
||||
|
@ -41,6 +49,14 @@ export const TRELLO_NODE_NAME = 'Trello';
|
|||
export const NOTION_NODE_NAME = 'Notion';
|
||||
export const PIPEDRIVE_NODE_NAME = 'Pipedrive';
|
||||
export const HTTP_REQUEST_NODE_NAME = 'HTTP Request';
|
||||
export const AGENT_NODE_NAME = 'AI Agent';
|
||||
export const BASIC_LLM_CHAIN_NODE_NAME = 'Basic LLM Chain';
|
||||
export const AI_MEMORY_WINDOW_BUFFER_MEMORY_NODE_NAME = 'Window Buffer Memory';
|
||||
export const AI_TOOL_CALCULATOR_NODE_NAME = 'Calculator';
|
||||
export const AI_TOOL_CODE_NODE_NAME = 'Custom Code Tool';
|
||||
export const AI_TOOL_WIKIPEDIA_NODE_NAME = 'Wikipedia';
|
||||
export const AI_LANGUAGE_MODEL_OPENAI_CHAT_MODEL_NODE_NAME = 'OpenAI Chat Model';
|
||||
export const AI_OUTPUT_PARSER_AUTO_FIXING_NODE_NAME = 'Auto-fixing Output Parser';
|
||||
|
||||
export const META_KEY = Cypress.platform === 'darwin' ? '{meta}' : '{ctrl}';
|
||||
|
||||
|
@ -48,3 +64,7 @@ export const NEW_GOOGLE_ACCOUNT_NAME = 'Gmail account';
|
|||
export const NEW_TRELLO_ACCOUNT_NAME = 'Trello account';
|
||||
export const NEW_NOTION_ACCOUNT_NAME = 'Notion account';
|
||||
export const NEW_QUERY_AUTH_ACCOUNT_NAME = 'Query Auth account';
|
||||
|
||||
export const ROUTES = {
|
||||
NEW_WORKFLOW_PAGE: '/workflow/new',
|
||||
};
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
import { CODE_NODE_NAME, SET_NODE_NAME, EDIT_FIELDS_SET_NODE_NAME } from './../constants';
|
||||
import { SCHEDULE_TRIGGER_NODE_NAME } from '../constants';
|
||||
import { WorkflowPage as WorkflowPageClass } from '../pages/workflow';
|
||||
import { MessageBox as MessageBoxClass } from '../pages/modals/message-box';
|
||||
import { NDV } from '../pages/ndv';
|
||||
|
||||
// Suite-specific constants
|
||||
const CODE_NODE_NEW_NAME = 'Something else';
|
||||
|
||||
const WorkflowPage = new WorkflowPageClass();
|
||||
const messageBox = new MessageBoxClass();
|
||||
const ndv = new NDV();
|
||||
|
||||
describe('Undo/Redo', () => {
|
||||
|
@ -44,7 +46,7 @@ describe('Undo/Redo', () => {
|
|||
WorkflowPage.getters
|
||||
.canvasNodeByName('Code')
|
||||
.should('have.css', 'left', '860px')
|
||||
.should('have.css', 'top', '220px')
|
||||
.should('have.css', 'top', '220px');
|
||||
|
||||
WorkflowPage.actions.hitUndo();
|
||||
WorkflowPage.getters.canvasNodes().should('have.have.length', 2);
|
||||
|
@ -62,16 +64,13 @@ describe('Undo/Redo', () => {
|
|||
WorkflowPage.getters
|
||||
.canvasNodeByName('Code')
|
||||
.should('have.css', 'left', '860px')
|
||||
.should('have.css', 'top', '220px')
|
||||
.should('have.css', 'top', '220px');
|
||||
});
|
||||
|
||||
it('should undo/redo deleting node using delete button', () => {
|
||||
it('should undo/redo deleting node using context menu', () => {
|
||||
WorkflowPage.actions.addNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME);
|
||||
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
|
||||
WorkflowPage.getters
|
||||
.canvasNodeByName(CODE_NODE_NAME)
|
||||
.find('[data-test-id=delete-node-button]')
|
||||
.click({ force: true });
|
||||
WorkflowPage.actions.deleteNodeFromContextMenu(CODE_NODE_NAME);
|
||||
WorkflowPage.getters.canvasNodes().should('have.have.length', 1);
|
||||
WorkflowPage.getters.nodeConnections().should('have.length', 0);
|
||||
WorkflowPage.actions.hitUndo();
|
||||
|
@ -137,21 +136,21 @@ describe('Undo/Redo', () => {
|
|||
WorkflowPage.getters
|
||||
.canvasNodeByName('Code')
|
||||
.should('have.css', 'left', '740px')
|
||||
.should('have.css', 'top', '320px')
|
||||
.should('have.css', 'top', '320px');
|
||||
|
||||
WorkflowPage.actions.hitUndo();
|
||||
WorkflowPage.getters
|
||||
.canvasNodeByName('Code')
|
||||
.should('have.css', 'left', '640px')
|
||||
.should('have.css', 'top', '220px')
|
||||
.should('have.css', 'top', '220px');
|
||||
WorkflowPage.actions.hitRedo();
|
||||
WorkflowPage.getters
|
||||
.canvasNodeByName('Code')
|
||||
.should('have.css', 'left', '740px')
|
||||
.should('have.css', 'top', '320px')
|
||||
.should('have.css', 'top', '320px');
|
||||
});
|
||||
|
||||
it('should undo/redo deleting a connection by pressing delete button', () => {
|
||||
it('should undo/redo deleting a connection using context menu', () => {
|
||||
WorkflowPage.actions.addNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME);
|
||||
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
|
||||
WorkflowPage.getters.nodeConnections().realHover();
|
||||
|
@ -177,14 +176,10 @@ describe('Undo/Redo', () => {
|
|||
WorkflowPage.getters.nodeConnections().should('have.length', 0);
|
||||
});
|
||||
|
||||
it('should undo/redo disabling a node using disable button', () => {
|
||||
it('should undo/redo disabling a node using context menu', () => {
|
||||
WorkflowPage.actions.addNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME);
|
||||
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
|
||||
WorkflowPage.getters
|
||||
.canvasNodes()
|
||||
.last()
|
||||
.find('[data-test-id="disable-node-button"]')
|
||||
.click({ force: true });
|
||||
WorkflowPage.actions.disableNode(CODE_NODE_NAME);
|
||||
WorkflowPage.getters.disabledNodes().should('have.length', 1);
|
||||
WorkflowPage.actions.hitUndo();
|
||||
WorkflowPage.getters.disabledNodes().should('have.length', 0);
|
||||
|
@ -252,11 +247,7 @@ describe('Undo/Redo', () => {
|
|||
it('should undo/redo duplicating a node', () => {
|
||||
WorkflowPage.actions.addNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME);
|
||||
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
|
||||
WorkflowPage.getters
|
||||
.canvasNodes()
|
||||
.last()
|
||||
.find('[data-test-id="duplicate-node-button"]')
|
||||
.click({ force: true });
|
||||
WorkflowPage.actions.duplicateNode(CODE_NODE_NAME);
|
||||
WorkflowPage.actions.hitUndo();
|
||||
WorkflowPage.getters.canvasNodes().should('have.length', 2);
|
||||
WorkflowPage.actions.hitRedo();
|
||||
|
@ -276,9 +267,6 @@ 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);
|
||||
|
@ -289,48 +277,115 @@ describe('Undo/Redo', () => {
|
|||
// Disable last node
|
||||
WorkflowPage.getters.canvasNodes().last().click();
|
||||
WorkflowPage.actions.hitDisableNodeShortcut();
|
||||
|
||||
// Move first one
|
||||
WorkflowPage.getters.canvasNodes()
|
||||
.first()
|
||||
.should('have.css', 'left', initialPosition.left)
|
||||
.should('have.css', 'top', initialPosition.top)
|
||||
WorkflowPage.actions
|
||||
.getNodePosition(WorkflowPage.getters.canvasNodes().first())
|
||||
.then((initialPosition) => {
|
||||
WorkflowPage.getters.canvasNodes().first().click();
|
||||
cy.drag('[data-test-id="canvas-node"].jtk-drag-selected', [50, 150], {
|
||||
clickToFinish: true,
|
||||
});
|
||||
WorkflowPage.getters
|
||||
.canvasNodes()
|
||||
.first()
|
||||
.should('have.css', 'left', `${initialPosition.left + 120}px`)
|
||||
.should('have.css', 'top', `${initialPosition.top + 140}px`);
|
||||
|
||||
WorkflowPage.getters.canvasNodes().first().click();
|
||||
cy.drag('[data-test-id="canvas-node"].jtk-drag-selected', [50, 150], { clickToFinish: true });
|
||||
WorkflowPage.getters.canvasNodes()
|
||||
.first()
|
||||
.should('have.css', 'left', movedPosition.left)
|
||||
.should('have.css', 'top', movedPosition.top)
|
||||
// Delete the set node
|
||||
WorkflowPage.getters.canvasNodeByName(EDIT_FIELDS_SET_NODE_NAME).click().click();
|
||||
cy.get('body').type('{backspace}');
|
||||
// Delete the set node
|
||||
WorkflowPage.getters.canvasNodeByName(EDIT_FIELDS_SET_NODE_NAME).click().click();
|
||||
cy.get('body').type('{backspace}');
|
||||
|
||||
// First undo: Should return deleted node
|
||||
WorkflowPage.actions.hitUndo();
|
||||
// First undo: Should return deleted node
|
||||
WorkflowPage.actions.hitUndo();
|
||||
WorkflowPage.getters.canvasNodes().should('have.length', 4);
|
||||
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.css', 'left', `${initialPosition.left}px`)
|
||||
.should('have.css', 'top', `${initialPosition.top}px`);
|
||||
// Third undo: Should enable last node
|
||||
WorkflowPage.actions.hitUndo();
|
||||
WorkflowPage.getters.disabledNodes().should('have.length', 0);
|
||||
|
||||
// First redo: Should disable last node
|
||||
WorkflowPage.actions.hitRedo();
|
||||
WorkflowPage.getters.disabledNodes().should('have.length', 1);
|
||||
// Second redo: Should move the first node
|
||||
WorkflowPage.actions.hitRedo();
|
||||
WorkflowPage.getters
|
||||
.canvasNodes()
|
||||
.first()
|
||||
.should('have.css', 'left', `${initialPosition.left + 120}px`)
|
||||
.should('have.css', 'top', `${initialPosition.top + 140}px`);
|
||||
// Third redo: Should delete the Set node
|
||||
WorkflowPage.actions.hitRedo();
|
||||
WorkflowPage.getters.canvasNodes().should('have.length', 3);
|
||||
WorkflowPage.getters.nodeConnections().should('have.length', 2);
|
||||
});
|
||||
});
|
||||
|
||||
it('should be able to copy and paste pinned data nodes in workflows with dynamic Switch node', () => {
|
||||
cy.fixture('Test_workflow_form_switch.json').then((data) => {
|
||||
cy.get('body').paste(JSON.stringify(data));
|
||||
});
|
||||
WorkflowPage.actions.zoomToFit();
|
||||
|
||||
WorkflowPage.getters.canvasNodes().should('have.length', 2);
|
||||
WorkflowPage.getters.nodeConnections().should('have.length', 1);
|
||||
cy.get(WorkflowPage.getters.getEndpointSelector('input', 'Switch')).should('have.length', 1);
|
||||
cy.get(WorkflowPage.getters.getEndpointSelector('input', 'Switch'))
|
||||
.should('have.css', 'left', `637px`)
|
||||
.should('have.css', 'top', `501px`);
|
||||
|
||||
cy.fixture('Test_workflow_form_switch.json').then((data) => {
|
||||
cy.get('body').paste(JSON.stringify(data));
|
||||
});
|
||||
WorkflowPage.getters.canvasNodes().should('have.length', 4);
|
||||
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.css', 'left', initialPosition.left)
|
||||
.should('have.css', 'top', initialPosition.top)
|
||||
// Third undo: Should enable last node
|
||||
WorkflowPage.actions.hitUndo();
|
||||
WorkflowPage.getters.disabledNodes().should('have.length', 0);
|
||||
|
||||
// First redo: Should disable last node
|
||||
WorkflowPage.actions.hitRedo();
|
||||
WorkflowPage.getters.disabledNodes().should('have.length', 1);
|
||||
// Second redo: Should move the first node
|
||||
WorkflowPage.actions.hitRedo();
|
||||
WorkflowPage.getters.canvasNodes()
|
||||
.first()
|
||||
.should('have.css', 'left', movedPosition.left)
|
||||
.should('have.css', 'top', movedPosition.top)
|
||||
// Third redo: Should delete the Set node
|
||||
WorkflowPage.actions.hitRedo();
|
||||
WorkflowPage.getters.canvasNodes().should('have.length', 3);
|
||||
WorkflowPage.getters.nodeConnections().should('have.length', 2);
|
||||
|
||||
WorkflowPage.actions.hitUndo();
|
||||
|
||||
WorkflowPage.getters.canvasNodes().should('have.length', 2);
|
||||
WorkflowPage.getters.nodeConnections().should('have.length', 1);
|
||||
cy.get(WorkflowPage.getters.getEndpointSelector('input', 'Switch')).should('have.length', 1);
|
||||
cy.get(WorkflowPage.getters.getEndpointSelector('input', 'Switch'))
|
||||
.should('have.css', 'left', `637px`)
|
||||
.should('have.css', 'top', `501px`);
|
||||
});
|
||||
|
||||
it('should not undo/redo when NDV or a modal is open', () => {
|
||||
WorkflowPage.actions.addInitialNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME, { keepNdvOpen: true });
|
||||
// Try while NDV is open
|
||||
WorkflowPage.actions.hitUndo();
|
||||
WorkflowPage.getters.canvasNodes().should('have.have.length', 1);
|
||||
ndv.getters.backToCanvas().click();
|
||||
// Try while modal is open
|
||||
cy.getByTestId('menu-item').contains('About n8n').click({ force: true });
|
||||
cy.getByTestId('about-modal').should('be.visible');
|
||||
WorkflowPage.actions.hitUndo();
|
||||
WorkflowPage.getters.canvasNodes().should('have.have.length', 1);
|
||||
cy.getByTestId('close-about-modal-button').click();
|
||||
// Should work now
|
||||
WorkflowPage.actions.hitUndo();
|
||||
WorkflowPage.getters.canvasNodes().should('have.have.length', 0);
|
||||
});
|
||||
|
||||
it('should not undo/redo when NDV or a prompt is open', () => {
|
||||
WorkflowPage.actions.addInitialNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME, { keepNdvOpen: false });
|
||||
WorkflowPage.getters.workflowMenu().click();
|
||||
WorkflowPage.getters.workflowMenuItemImportFromURLItem().should('be.visible');
|
||||
WorkflowPage.getters.workflowMenuItemImportFromURLItem().click();
|
||||
// Try while prompt is open
|
||||
messageBox.getters.header().click();
|
||||
WorkflowPage.actions.hitUndo();
|
||||
WorkflowPage.getters.canvasNodes().should('have.have.length', 1);
|
||||
// Close prompt and try again
|
||||
messageBox.actions.cancel();
|
||||
WorkflowPage.actions.hitUndo();
|
||||
WorkflowPage.getters.canvasNodes().should('have.have.length', 0);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -67,6 +67,6 @@ describe('Inline expression editor', () => {
|
|||
WorkflowPage.getters.inlineExpressionEditorInput().type('{{');
|
||||
// Resolving $parameter is slow, especially on CI runner
|
||||
WorkflowPage.getters.inlineExpressionEditorInput().type('$parameter["operation"]');
|
||||
WorkflowPage.getters.inlineExpressionEditorOutput().contains(/^get$/);
|
||||
WorkflowPage.getters.inlineExpressionEditorOutput().should('have.text', 'getAll');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -134,7 +134,7 @@ describe('Canvas Actions', () => {
|
|||
.canvasNodes()
|
||||
.last()
|
||||
.should('have.css', 'left', '860px')
|
||||
.should('have.css', 'top', '220px')
|
||||
.should('have.css', 'top', '220px');
|
||||
});
|
||||
|
||||
it('should delete connections by pressing the delete button', () => {
|
||||
|
@ -163,21 +163,29 @@ describe('Canvas Actions', () => {
|
|||
.find('[data-test-id="execute-node-button"]')
|
||||
.click({ force: true });
|
||||
WorkflowPage.getters.successToast().should('contain', 'Node executed successfully');
|
||||
WorkflowPage.actions.executeNode(CODE_NODE_NAME);
|
||||
WorkflowPage.getters.successToast().should('contain', 'Node executed successfully');
|
||||
});
|
||||
|
||||
it('should copy selected nodes', () => {
|
||||
WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME);
|
||||
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
|
||||
WorkflowPage.actions.selectAll();
|
||||
|
||||
WorkflowPage.actions.hitCopy();
|
||||
WorkflowPage.getters.successToast().should('contain', 'Copied!');
|
||||
|
||||
WorkflowPage.actions.copyNode(CODE_NODE_NAME);
|
||||
WorkflowPage.getters.successToast().should('contain', 'Copied!');
|
||||
});
|
||||
|
||||
it('should select all nodes', () => {
|
||||
it('should select/deselect all nodes', () => {
|
||||
WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME);
|
||||
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
|
||||
WorkflowPage.actions.selectAll();
|
||||
WorkflowPage.getters.selectedNodes().should('have.length', 2);
|
||||
WorkflowPage.actions.deselectAll();
|
||||
WorkflowPage.getters.selectedNodes().should('have.length', 0);
|
||||
});
|
||||
|
||||
it('should select nodes using arrow keys', () => {
|
||||
|
@ -205,22 +213,21 @@ describe('Canvas Actions', () => {
|
|||
WorkflowPage.getters
|
||||
.canvasNodes()
|
||||
.last()
|
||||
.findChildByTestId('disable-node-button').as('disableNodeButton');
|
||||
cy.drag('@disableNodeButton', [200, 200]);
|
||||
.findChildByTestId('execute-node-button')
|
||||
.as('executeNodeButton');
|
||||
cy.drag('@executeNodeButton', [200, 200]);
|
||||
WorkflowPage.actions.testLassoSelection([100, 100], [200, 200]);
|
||||
});
|
||||
|
||||
it('should not break lasso selection with multiple clicks on node action buttons', () => {
|
||||
WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME);
|
||||
WorkflowPage.actions.testLassoSelection([100, 100], [200, 200]);
|
||||
WorkflowPage.getters
|
||||
.canvasNodes()
|
||||
.last().as('lastNode');
|
||||
cy.get('@lastNode').findChildByTestId('disable-node-button').as('disableNodeButton');
|
||||
WorkflowPage.getters.canvasNodes().last().as('lastNode');
|
||||
cy.get('@lastNode').findChildByTestId('execute-node-button').as('executeNodeButton');
|
||||
for (let i = 0; i < 20; i++) {
|
||||
cy.get('@lastNode').realHover();
|
||||
cy.get('@disableNodeButton').should('be.visible');
|
||||
cy.get('@disableNodeButton').realTouch();
|
||||
cy.get('@executeNodeButton').should('be.visible');
|
||||
cy.get('@executeNodeButton').realTouch();
|
||||
cy.getByTestId('execute-workflow-button').realHover();
|
||||
WorkflowPage.actions.testLassoSelection([100, 100], [200, 200]);
|
||||
}
|
||||
|
|
|
@ -16,12 +16,13 @@ const NDVDialog = new NDV();
|
|||
const DEFAULT_ZOOM_FACTOR = 1;
|
||||
const ZOOM_IN_X1_FACTOR = 1.25; // Zoom in factor after one click
|
||||
const ZOOM_IN_X2_FACTOR = 1.5625; // Zoom in factor after two clicks
|
||||
const ZOOM_OUT_X1_FACTOR = 0.75;
|
||||
const ZOOM_OUT_X2_FACTOR = 0.5625;
|
||||
const ZOOM_OUT_X1_FACTOR = 0.8;
|
||||
const ZOOM_OUT_X2_FACTOR = 0.64;
|
||||
|
||||
const PINCH_ZOOM_IN_FACTOR = 1.32;
|
||||
const PINCH_ZOOM_OUT_FACTOR = 0.4752;
|
||||
const PINCH_ZOOM_IN_FACTOR = 1.05702;
|
||||
const PINCH_ZOOM_OUT_FACTOR = 0.946058;
|
||||
const RENAME_NODE_NAME = 'Something else';
|
||||
const RENAME_NODE_NAME2 = 'Something different';
|
||||
|
||||
describe('Canvas Node Manipulation and Navigation', () => {
|
||||
beforeEach(() => {
|
||||
|
@ -30,22 +31,30 @@ describe('Canvas Node Manipulation and Navigation', () => {
|
|||
|
||||
it('should add switch node and test connections', () => {
|
||||
const desiredOutputs = 4;
|
||||
WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME, true);
|
||||
WorkflowPage.actions.addNodeToCanvas(SWITCH_NODE_NAME, true, true);
|
||||
|
||||
for (let i = 0; i < desiredOutputs; i++) {
|
||||
cy.contains('Add Routing Rule').click()
|
||||
cy.contains('Add Routing Rule').click();
|
||||
}
|
||||
|
||||
NDVDialog.actions.close()
|
||||
NDVDialog.actions.close();
|
||||
for (let i = 0; i < desiredOutputs; i++) {
|
||||
WorkflowPage.getters.canvasNodePlusEndpointByName(SWITCH_NODE_NAME, i).click({ force: true });
|
||||
WorkflowPage.getters.nodeCreatorSearchBar().should('be.visible');
|
||||
WorkflowPage.actions.addNodeToCanvas(EDIT_FIELDS_SET_NODE_NAME, false);
|
||||
WorkflowPage.actions.zoomToFit();
|
||||
}
|
||||
WorkflowPage.getters.nodeViewBackground().click({ force: true });
|
||||
WorkflowPage.getters.canvasNodePlusEndpointByName(`${EDIT_FIELDS_SET_NODE_NAME}3`).click();
|
||||
WorkflowPage.actions.addNodeToCanvas(SWITCH_NODE_NAME, false);
|
||||
WorkflowPage.actions.saveWorkflowOnButtonClick();
|
||||
cy.reload();
|
||||
cy.waitForLoad();
|
||||
// Make sure outputless switch was connected correctly
|
||||
cy.get(
|
||||
`[data-target-node="${SWITCH_NODE_NAME}1"][data-source-node="${EDIT_FIELDS_SET_NODE_NAME}3"]`,
|
||||
).should('be.visible');
|
||||
// Make sure all connections are there after reload
|
||||
for (let i = 0; i < desiredOutputs; i++) {
|
||||
const setName = `${EDIT_FIELDS_SET_NODE_NAME}${i > 0 ? i : ''}`;
|
||||
|
@ -121,13 +130,10 @@ describe('Canvas Node Manipulation and Navigation', () => {
|
|||
cy.get('.jtk-connector').should('have.length', 4);
|
||||
});
|
||||
|
||||
it('should delete node using node action button', () => {
|
||||
it('should delete node using context menu', () => {
|
||||
WorkflowPage.actions.addNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME);
|
||||
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
|
||||
WorkflowPage.getters
|
||||
.canvasNodeByName(CODE_NODE_NAME)
|
||||
.find('[data-test-id=delete-node-button]')
|
||||
.click({ force: true });
|
||||
WorkflowPage.actions.deleteNodeFromContextMenu(CODE_NODE_NAME);
|
||||
WorkflowPage.getters.canvasNodes().should('have.length', 1);
|
||||
WorkflowPage.getters.nodeConnections().should('have.length', 0);
|
||||
});
|
||||
|
@ -154,13 +160,38 @@ describe('Canvas Node Manipulation and Navigation', () => {
|
|||
WorkflowPage.getters.nodeConnections().should('have.length', 1);
|
||||
});
|
||||
|
||||
it('should delete multiple nodes', () => {
|
||||
it('should delete multiple nodes (context menu or shortcut)', () => {
|
||||
WorkflowPage.actions.addNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME);
|
||||
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
|
||||
cy.wait(500);
|
||||
WorkflowPage.actions.selectAll();
|
||||
cy.get('body').type('{backspace}');
|
||||
WorkflowPage.getters.canvasNodes().should('have.length', 0);
|
||||
|
||||
WorkflowPage.actions.addNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME);
|
||||
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
|
||||
cy.wait(500);
|
||||
WorkflowPage.actions.selectAllFromContextMenu();
|
||||
WorkflowPage.actions.openContextMenu();
|
||||
WorkflowPage.actions.contextMenuAction('delete');
|
||||
WorkflowPage.getters.canvasNodes().should('have.length', 0);
|
||||
});
|
||||
|
||||
it('should delete multiple nodes (context menu or shortcut)', () => {
|
||||
WorkflowPage.actions.addNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME);
|
||||
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
|
||||
cy.wait(500);
|
||||
WorkflowPage.actions.selectAll();
|
||||
cy.get('body').type('{backspace}');
|
||||
WorkflowPage.getters.canvasNodes().should('have.length', 0);
|
||||
|
||||
WorkflowPage.actions.addNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME);
|
||||
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
|
||||
cy.wait(500);
|
||||
WorkflowPage.actions.selectAllFromContextMenu();
|
||||
WorkflowPage.actions.openContextMenu();
|
||||
WorkflowPage.actions.contextMenuAction('delete');
|
||||
WorkflowPage.getters.canvasNodes().should('have.length', 0);
|
||||
});
|
||||
|
||||
it('should move node', () => {
|
||||
|
@ -168,12 +199,13 @@ describe('Canvas Node Manipulation and Navigation', () => {
|
|||
WorkflowPage.getters.canvasNodeByName(MANUAL_TRIGGER_NODE_DISPLAY_NAME).click();
|
||||
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
|
||||
WorkflowPage.actions.zoomToFit();
|
||||
|
||||
cy.drag('[data-test-id="canvas-node"].jtk-drag-selected', [50, 150], { clickToFinish: true });
|
||||
WorkflowPage.getters
|
||||
.canvasNodes()
|
||||
.last()
|
||||
.should('have.css', 'left', '740px')
|
||||
.should('have.css', 'top', '320px')
|
||||
.should('have.css', 'top', '320px');
|
||||
});
|
||||
|
||||
it('should zoom in', () => {
|
||||
|
@ -214,8 +246,8 @@ describe('Canvas Node Manipulation and Navigation', () => {
|
|||
);
|
||||
});
|
||||
|
||||
it('should zoom using pinch to zoom', () => {
|
||||
WorkflowPage.actions.pinchToZoom(2, 'zoomIn');
|
||||
it('should zoom using scroll or pinch gesture', () => {
|
||||
WorkflowPage.actions.pinchToZoom(1, 'zoomIn');
|
||||
WorkflowPage.getters
|
||||
.nodeView()
|
||||
.should(
|
||||
|
@ -224,7 +256,11 @@ describe('Canvas Node Manipulation and Navigation', () => {
|
|||
`matrix(${PINCH_ZOOM_IN_FACTOR}, 0, 0, ${PINCH_ZOOM_IN_FACTOR}, 0, 0)`,
|
||||
);
|
||||
|
||||
WorkflowPage.actions.pinchToZoom(4, 'zoomOut');
|
||||
WorkflowPage.actions.pinchToZoom(1, 'zoomOut');
|
||||
// Zoom in 1x + Zoom out 1x should reset to default (=1)
|
||||
WorkflowPage.getters.nodeView().should('have.css', 'transform', `matrix(1, 0, 0, 1, 0, 0)`);
|
||||
|
||||
WorkflowPage.actions.pinchToZoom(1, 'zoomOut');
|
||||
WorkflowPage.getters
|
||||
.nodeView()
|
||||
.should(
|
||||
|
@ -259,39 +295,42 @@ describe('Canvas Node Manipulation and Navigation', () => {
|
|||
WorkflowPage.getters.canvasNodes().last().should('be.visible');
|
||||
});
|
||||
|
||||
it('should disable node by pressing the disable button', () => {
|
||||
WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME);
|
||||
WorkflowPage.getters.canvasNodeByName(MANUAL_TRIGGER_NODE_DISPLAY_NAME).click();
|
||||
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
|
||||
WorkflowPage.getters
|
||||
.canvasNodes()
|
||||
.last()
|
||||
.find('[data-test-id="disable-node-button"]')
|
||||
.click({ force: true });
|
||||
WorkflowPage.getters.disabledNodes().should('have.length', 1);
|
||||
});
|
||||
|
||||
it('should disable node using keyboard shortcut', () => {
|
||||
it('should disable node (context menu or shortcut)', () => {
|
||||
WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME);
|
||||
WorkflowPage.getters.canvasNodeByName(MANUAL_TRIGGER_NODE_DISPLAY_NAME).click();
|
||||
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
|
||||
WorkflowPage.getters.canvasNodes().last().click();
|
||||
WorkflowPage.actions.hitDisableNodeShortcut();
|
||||
WorkflowPage.getters.disabledNodes().should('have.length', 1);
|
||||
|
||||
WorkflowPage.actions.disableNode(CODE_NODE_NAME);
|
||||
WorkflowPage.getters.disabledNodes().should('have.length', 0);
|
||||
});
|
||||
|
||||
it('should disable multiple nodes', () => {
|
||||
it('should disable multiple nodes (context menu or shortcut)', () => {
|
||||
WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME);
|
||||
WorkflowPage.getters.canvasNodeByName(MANUAL_TRIGGER_NODE_DISPLAY_NAME).click();
|
||||
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
|
||||
cy.get('body').type('{esc}');
|
||||
cy.get('body').type('{esc}');
|
||||
WorkflowPage.actions.selectAll();
|
||||
|
||||
// Keyboard shortcut
|
||||
WorkflowPage.actions.hitDisableNodeShortcut();
|
||||
WorkflowPage.getters.disabledNodes().should('have.length', 2);
|
||||
WorkflowPage.actions.hitDisableNodeShortcut();
|
||||
WorkflowPage.getters.disabledNodes().should('have.length', 0);
|
||||
|
||||
// Context menu
|
||||
WorkflowPage.actions.openContextMenu();
|
||||
WorkflowPage.actions.contextMenuAction('toggle_activation');
|
||||
WorkflowPage.getters.disabledNodes().should('have.length', 2);
|
||||
WorkflowPage.actions.openContextMenu();
|
||||
WorkflowPage.actions.contextMenuAction('toggle_activation');
|
||||
WorkflowPage.getters.disabledNodes().should('have.length', 0);
|
||||
});
|
||||
|
||||
it('should rename node using keyboard shortcut', () => {
|
||||
it('should rename node (context menu or shortcut)', () => {
|
||||
WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME);
|
||||
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
|
||||
WorkflowPage.getters.canvasNodes().last().click();
|
||||
|
@ -300,19 +339,25 @@ describe('Canvas Node Manipulation and Navigation', () => {
|
|||
cy.get('body').type(RENAME_NODE_NAME);
|
||||
cy.get('body').type('{enter}');
|
||||
WorkflowPage.getters.canvasNodeByName(RENAME_NODE_NAME).should('exist');
|
||||
|
||||
WorkflowPage.actions.renameNode(RENAME_NODE_NAME);
|
||||
cy.get('.rename-prompt').should('be.visible');
|
||||
cy.get('body').type(RENAME_NODE_NAME2);
|
||||
cy.get('body').type('{enter}');
|
||||
WorkflowPage.getters.canvasNodeByName(RENAME_NODE_NAME2).should('exist');
|
||||
});
|
||||
|
||||
it('should duplicate node', () => {
|
||||
it('should duplicate nodes (context menu or shortcut)', () => {
|
||||
WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME);
|
||||
WorkflowPage.getters.canvasNodeByName(MANUAL_TRIGGER_NODE_DISPLAY_NAME).click();
|
||||
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
|
||||
WorkflowPage.getters
|
||||
.canvasNodes()
|
||||
.last()
|
||||
.find('[data-test-id="duplicate-node-button"]')
|
||||
.click({ force: true });
|
||||
WorkflowPage.actions.duplicateNode(CODE_NODE_NAME);
|
||||
WorkflowPage.getters.canvasNodes().should('have.length', 3);
|
||||
WorkflowPage.getters.nodeConnections().should('have.length', 1);
|
||||
|
||||
WorkflowPage.actions.selectAll();
|
||||
WorkflowPage.actions.hitDuplicateNodeShortcut();
|
||||
WorkflowPage.getters.canvasNodes().should('have.length', 5);
|
||||
});
|
||||
|
||||
// ADO-1240: Connections would get deleted after activating and deactivating NodeView
|
||||
|
@ -344,5 +389,47 @@ describe('Canvas Node Manipulation and Navigation', () => {
|
|||
cy.waitForLoad();
|
||||
WorkflowPage.getters.canvasNodes().should('have.length', 2);
|
||||
cy.get('.rect-input-endpoint.jtk-endpoint-connected').should('have.length', 1);
|
||||
})
|
||||
});
|
||||
|
||||
it('should remove unknown credentials on pasting workflow', () => {
|
||||
cy.fixture('workflow-with-unknown-credentials.json').then((data) => {
|
||||
cy.get('body').paste(JSON.stringify(data));
|
||||
|
||||
WorkflowPage.getters.canvasNodes().should('have.have.length', 2);
|
||||
|
||||
WorkflowPage.actions.openNodeFromContextMenu('n8n');
|
||||
cy.get('[class*=hasIssues]').should('have.length', 1);
|
||||
NDVDialog.actions.close();
|
||||
});
|
||||
});
|
||||
|
||||
it('should render connections correctly if unkown nodes are present', () => {
|
||||
const unknownNodeName = 'Unknown node';
|
||||
cy.createFixtureWorkflow('workflow-with-unknown-nodes.json', 'Unknown nodes');
|
||||
|
||||
WorkflowPage.getters.canvasNodeByName(`${unknownNodeName} 1`).should('exist');
|
||||
WorkflowPage.getters.canvasNodeByName(`${unknownNodeName} 2`).should('exist');
|
||||
WorkflowPage.actions.zoomToFit();
|
||||
|
||||
cy.draganddrop(
|
||||
WorkflowPage.getters.getEndpointSelector('plus', `${unknownNodeName} 1`),
|
||||
WorkflowPage.getters.getEndpointSelector('input', EDIT_FIELDS_SET_NODE_NAME),
|
||||
);
|
||||
|
||||
cy.draganddrop(
|
||||
WorkflowPage.getters.getEndpointSelector('plus', `${unknownNodeName} 2`),
|
||||
WorkflowPage.getters.getEndpointSelector('input', `${EDIT_FIELDS_SET_NODE_NAME}1`),
|
||||
);
|
||||
|
||||
WorkflowPage.actions.executeWorkflow();
|
||||
cy.contains('Unrecognized node type').should('be.visible');
|
||||
|
||||
WorkflowPage.actions.deselectAll();
|
||||
WorkflowPage.actions.deleteNodeFromContextMenu(`${unknownNodeName} 1`);
|
||||
WorkflowPage.actions.deleteNodeFromContextMenu(`${unknownNodeName} 2`);
|
||||
|
||||
WorkflowPage.actions.executeWorkflow();
|
||||
|
||||
cy.contains('Unrecognized node type').should('not.exist');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -3,6 +3,7 @@ import {
|
|||
MANUAL_TRIGGER_NODE_NAME,
|
||||
PIPEDRIVE_NODE_NAME,
|
||||
EDIT_FIELDS_SET_NODE_NAME,
|
||||
BACKEND_BASE_URL,
|
||||
} from '../constants';
|
||||
import { WorkflowPage, NDV } from '../pages';
|
||||
|
||||
|
@ -62,13 +63,77 @@ describe('Data pinning', () => {
|
|||
|
||||
workflowPage.actions.saveWorkflowOnButtonClick();
|
||||
|
||||
cy.reload();
|
||||
workflowPage.actions.openNode('Schedule Trigger');
|
||||
|
||||
ndv.getters.outputTableHeaders().first().should('include.text', 'test');
|
||||
ndv.getters.outputTbodyCell(1, 0).should('include.text', 1);
|
||||
});
|
||||
|
||||
it('Should be duplicating pin data when duplicating node', () => {
|
||||
workflowPage.actions.addInitialNodeToCanvas('Schedule Trigger');
|
||||
workflowPage.actions.addNodeToCanvas(EDIT_FIELDS_SET_NODE_NAME, true, true);
|
||||
ndv.getters.container().should('be.visible');
|
||||
ndv.getters.pinDataButton().should('not.exist');
|
||||
ndv.getters.editPinnedDataButton().should('be.visible');
|
||||
|
||||
ndv.actions.setPinnedData([{ test: 1 }]);
|
||||
ndv.actions.close();
|
||||
|
||||
workflowPage.actions.duplicateNode(EDIT_FIELDS_SET_NODE_NAME);
|
||||
|
||||
workflowPage.actions.saveWorkflowOnButtonClick();
|
||||
|
||||
workflowPage.actions.openNode('Edit Fields1');
|
||||
|
||||
ndv.getters.outputTableHeaders().first().should('include.text', 'test');
|
||||
ndv.getters.outputTbodyCell(1, 0).should('include.text', 1);
|
||||
});
|
||||
|
||||
it('Should be able to pin data from canvas (context menu or shortcut)', () => {
|
||||
workflowPage.actions.addInitialNodeToCanvas('Schedule Trigger');
|
||||
workflowPage.actions.addNodeToCanvas(EDIT_FIELDS_SET_NODE_NAME);
|
||||
workflowPage.actions.openContextMenu(EDIT_FIELDS_SET_NODE_NAME, 'overflow-button');
|
||||
workflowPage.getters
|
||||
.contextMenuAction('toggle_pin')
|
||||
.parent()
|
||||
.should('have.class', 'is-disabled');
|
||||
|
||||
// Unpin using context menu
|
||||
workflowPage.actions.openNode(EDIT_FIELDS_SET_NODE_NAME);
|
||||
ndv.actions.setPinnedData([{ test: 1 }]);
|
||||
ndv.actions.close();
|
||||
workflowPage.actions.pinNode(EDIT_FIELDS_SET_NODE_NAME);
|
||||
workflowPage.actions.openNode(EDIT_FIELDS_SET_NODE_NAME);
|
||||
ndv.getters.nodeOutputHint().should('exist');
|
||||
ndv.actions.close();
|
||||
|
||||
// Unpin using shortcut
|
||||
workflowPage.actions.openNode(EDIT_FIELDS_SET_NODE_NAME);
|
||||
ndv.actions.setPinnedData([{ test: 1 }]);
|
||||
ndv.actions.close();
|
||||
workflowPage.getters.canvasNodeByName(EDIT_FIELDS_SET_NODE_NAME).click();
|
||||
workflowPage.actions.hitPinNodeShortcut();
|
||||
workflowPage.actions.openNode(EDIT_FIELDS_SET_NODE_NAME);
|
||||
ndv.getters.nodeOutputHint().should('exist');
|
||||
});
|
||||
|
||||
it('Should show an error when maximum pin data size is exceeded', () => {
|
||||
workflowPage.actions.addInitialNodeToCanvas('Schedule Trigger');
|
||||
workflowPage.actions.addNodeToCanvas(EDIT_FIELDS_SET_NODE_NAME, true, true);
|
||||
ndv.getters.container().should('be.visible');
|
||||
ndv.getters.pinDataButton().should('not.exist');
|
||||
ndv.getters.editPinnedDataButton().should('be.visible');
|
||||
|
||||
ndv.actions.setPinnedData([
|
||||
{
|
||||
test: '1'.repeat(Cypress.env('MAX_PINNED_DATA_SIZE')),
|
||||
},
|
||||
]);
|
||||
workflowPage.getters
|
||||
.errorToast()
|
||||
.should('contain', 'Workflow has reached the maximum allowed pinned data size');
|
||||
});
|
||||
|
||||
it('Should be able to reference paired items in a node located before pinned data', () => {
|
||||
workflowPage.actions.addInitialNodeToCanvas(MANUAL_TRIGGER_NODE_NAME);
|
||||
workflowPage.actions.addNodeToCanvas(HTTP_REQUEST_NODE_NAME, true, true);
|
||||
|
@ -86,10 +151,25 @@ describe('Data pinning', () => {
|
|||
|
||||
cy.get('div').contains(output).should('be.visible');
|
||||
});
|
||||
|
||||
it('should use pin data in manual executions that are started by a webhook', () => {
|
||||
cy.createFixtureWorkflow('Test_workflow_webhook_with_pin_data.json', 'Test');
|
||||
|
||||
workflowPage.actions.executeWorkflow();
|
||||
|
||||
cy.request('GET', `${BACKEND_BASE_URL}/webhook-test/b0d79ddb-df2d-49b1-8555-9fa2b482608f`).then((response) => {
|
||||
expect(response.status).to.eq(200);
|
||||
});
|
||||
|
||||
workflowPage.actions.openNode('End');
|
||||
|
||||
ndv.getters.outputTableRow(1).should('exist')
|
||||
ndv.getters.outputTableRow(1).should('have.text', 'pin-overwritten');
|
||||
});
|
||||
});
|
||||
|
||||
function setExpressionOnStringValueInSet(expression: string) {
|
||||
cy.get('button').contains('Execute node').click();
|
||||
cy.get('button').contains('Test step').click();
|
||||
cy.get('.fixed-collection-parameter > :nth-child(2) > .button > span').click();
|
||||
|
||||
ndv.getters.nthParam(4).contains('Expression').invoke('show').click();
|
||||
|
|
|
@ -80,14 +80,14 @@ describe('Data mapping', () => {
|
|||
.parameterExpressionPreview('value')
|
||||
.should('include.text', '0')
|
||||
.invoke('css', 'color')
|
||||
.should('equal', 'rgb(125, 125, 135)');
|
||||
.should('equal', 'rgb(113, 116, 122)');
|
||||
|
||||
ndv.getters.inputTbodyCell(2, 0).realHover();
|
||||
ndv.getters
|
||||
.parameterExpressionPreview('value')
|
||||
.should('include.text', '1')
|
||||
.invoke('css', 'color')
|
||||
.should('equal', 'rgb(125, 125, 135)');
|
||||
.should('equal', 'rgb(113, 116, 122)');
|
||||
|
||||
ndv.actions.execute();
|
||||
|
||||
|
@ -96,14 +96,14 @@ describe('Data mapping', () => {
|
|||
.parameterExpressionPreview('value')
|
||||
.should('include.text', '0')
|
||||
.invoke('css', 'color')
|
||||
.should('equal', 'rgb(125, 125, 135)'); // todo update color
|
||||
.should('equal', 'rgb(113, 116, 122)'); // todo update color
|
||||
|
||||
ndv.getters.outputTbodyCell(2, 0).realHover();
|
||||
ndv.getters
|
||||
.parameterExpressionPreview('value')
|
||||
.should('include.text', '1')
|
||||
.invoke('css', 'color')
|
||||
.should('equal', 'rgb(125, 125, 135)');
|
||||
.should('equal', 'rgb(113, 116, 122)');
|
||||
});
|
||||
|
||||
it('maps expressions from json view', () => {
|
||||
|
@ -235,11 +235,8 @@ describe('Data mapping', () => {
|
|||
|
||||
ndv.actions.close();
|
||||
|
||||
workflowPage.actions.addNodeToCanvas('Item Lists');
|
||||
workflowPage.actions.openNode('Item Lists');
|
||||
|
||||
ndv.getters.parameterInput('operation').click();
|
||||
getVisibleSelect().find('li').contains('Sort').click();
|
||||
workflowPage.actions.addNodeToCanvas('Sort');
|
||||
workflowPage.actions.openNode('Sort');
|
||||
|
||||
ndv.getters.nodeParameters().find('button').contains('Add Field To Sort By').click();
|
||||
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
import { WorkflowPage, NDV } from '../pages';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import { getPopper, getVisiblePopper, getVisibleSelect } from '../utils';
|
||||
import { META_KEY } from '../constants';
|
||||
import { getVisibleSelect } from '../utils';
|
||||
|
||||
const workflowPage = new WorkflowPage();
|
||||
const ndv = new NDV();
|
||||
|
@ -13,10 +11,7 @@ describe('n8n Form Trigger', () => {
|
|||
|
||||
it("add node by clicking on 'On form submission'", () => {
|
||||
workflowPage.getters.canvasPlusButton().click();
|
||||
cy.get('#node-view-root > div:nth-child(2) > div > div > aside ')
|
||||
.find('span')
|
||||
.contains('On form submission')
|
||||
.click();
|
||||
workflowPage.getters.nodeCreatorNodeItems().contains('On form submission').click();
|
||||
ndv.getters.parameterInput('formTitle').type('Test Form');
|
||||
ndv.getters.parameterInput('formDescription').type('Test Form Description');
|
||||
ndv.getters.parameterInput('fieldLabel').type('Test Field 1');
|
||||
|
@ -76,12 +71,25 @@ describe('n8n Form Trigger', () => {
|
|||
)
|
||||
.find('input')
|
||||
.type('Option 2');
|
||||
//add optionall submitted message
|
||||
cy.get('.param-options > .button').click();
|
||||
cy.get('.indent > .parameter-item')
|
||||
.find('input')
|
||||
|
||||
//add optional submitted message
|
||||
cy.get('.param-options').click();
|
||||
cy.contains('span', 'Text to Show')
|
||||
.should('exist')
|
||||
.parent()
|
||||
.parent()
|
||||
.next()
|
||||
.children()
|
||||
.children()
|
||||
.children()
|
||||
.children()
|
||||
.children()
|
||||
.children()
|
||||
.children()
|
||||
.first()
|
||||
.clear()
|
||||
.type('Your test form was successfully submitted');
|
||||
|
||||
ndv.getters.backToCanvas().click();
|
||||
workflowPage.getters.nodeIssuesByName('n8n Form Trigger').should('not.exist');
|
||||
});
|
||||
|
|
|
@ -187,12 +187,14 @@ describe('Webhook Trigger node', async () => {
|
|||
|
||||
ndv.getters.backToCanvas().click();
|
||||
|
||||
workflowPage.actions.addNodeToCanvas('Convert to/from binary data');
|
||||
workflowPage.actions.addNodeToCanvas('Convert to File');
|
||||
workflowPage.actions.zoomToFit();
|
||||
|
||||
workflowPage.actions.openNode('Convert to/from binary data');
|
||||
workflowPage.actions.openNode('Convert to File');
|
||||
cy.getByTestId('parameter-input-operation').click();
|
||||
getVisibleSelect().find('.option-headline').contains('Convert to JSON').click();
|
||||
cy.getByTestId('parameter-input-mode').click();
|
||||
getVisibleSelect().find('.option-headline').contains('JSON to Binary').click();
|
||||
getVisibleSelect().find('.option-headline').contains('Each Item to Separate File').click();
|
||||
ndv.getters.backToCanvas().click();
|
||||
|
||||
workflowPage.actions.executeWorkflow();
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { INSTANCE_MEMBERS, INSTANCE_OWNER } from '../constants';
|
||||
import { INSTANCE_MEMBERS, INSTANCE_OWNER, INSTANCE_ADMIN } from '../constants';
|
||||
import {
|
||||
CredentialsModal,
|
||||
CredentialsPage,
|
||||
|
@ -7,6 +7,7 @@ import {
|
|||
WorkflowSharingModal,
|
||||
WorkflowsPage,
|
||||
} from '../pages';
|
||||
import { getVisibleSelect } from '../utils';
|
||||
|
||||
/**
|
||||
* User U1 - Instance owner
|
||||
|
@ -59,6 +60,7 @@ describe('Sharing', { disableAutoLogin: true }, () => {
|
|||
cy.visit(workflowsPage.url);
|
||||
workflowsPage.getters.createWorkflowButton().click();
|
||||
cy.createFixtureWorkflow('Test_workflow_1.json', 'Workflow W2');
|
||||
workflowPage.actions.saveWorkflowOnButtonClick();
|
||||
cy.url().then((url) => {
|
||||
workflowW2Url = url;
|
||||
});
|
||||
|
@ -96,6 +98,26 @@ describe('Sharing', { disableAutoLogin: true }, () => {
|
|||
ndv.actions.close();
|
||||
});
|
||||
|
||||
it('should open W1, add node using C2 as U2', () => {
|
||||
cy.signin(INSTANCE_MEMBERS[0]);
|
||||
|
||||
cy.visit(workflowsPage.url);
|
||||
workflowsPage.getters.workflowCards().should('have.length', 2);
|
||||
workflowsPage.getters.workflowCard('Workflow W1').click();
|
||||
workflowPage.actions.addNodeToCanvas('Airtable', true, true);
|
||||
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.enabled');
|
||||
ndv.actions.close();
|
||||
});
|
||||
|
||||
it('should not have access to W2, as U3', () => {
|
||||
cy.signin(INSTANCE_MEMBERS[1]);
|
||||
|
||||
|
@ -128,4 +150,41 @@ describe('Sharing', { disableAutoLogin: true }, () => {
|
|||
credentialsPage.getters.credentialCard('Credential C2').click();
|
||||
credentialsModal.getters.testSuccessTag().should('be.visible');
|
||||
});
|
||||
|
||||
it('should work for admin role on credentials created by others (also can share it with themselves)', () => {
|
||||
cy.signin(INSTANCE_MEMBERS[0]);
|
||||
|
||||
cy.visit(credentialsPage.url);
|
||||
credentialsPage.getters.createCredentialButton().click();
|
||||
credentialsModal.getters.newCredentialTypeOption('Notion API').click();
|
||||
credentialsModal.getters.newCredentialTypeButton().click({ force: true });
|
||||
credentialsModal.getters.connectionParameter('Internal Integration Secret').type('1234567890');
|
||||
credentialsModal.actions.setName('Credential C3');
|
||||
credentialsModal.actions.save();
|
||||
credentialsModal.actions.close();
|
||||
|
||||
cy.signout();
|
||||
cy.signin(INSTANCE_ADMIN);
|
||||
cy.visit(credentialsPage.url);
|
||||
credentialsPage.getters.credentialCard('Credential C3').click();
|
||||
credentialsModal.getters.testSuccessTag().should('be.visible');
|
||||
cy.get('input').should('not.have.length');
|
||||
credentialsModal.actions.changeTab('Sharing');
|
||||
cy.contains(
|
||||
'You can view this credential because you have permission to read and share',
|
||||
).should('be.visible');
|
||||
|
||||
credentialsModal.getters.usersSelect().click();
|
||||
cy.getByTestId('user-email')
|
||||
.filter(':visible')
|
||||
.should('have.length', 3)
|
||||
.contains(INSTANCE_ADMIN.email)
|
||||
.should('have.length', 1);
|
||||
getVisibleSelect().contains(INSTANCE_OWNER.email.toLowerCase()).click();
|
||||
|
||||
credentialsModal.actions.addUser(INSTANCE_MEMBERS[1].email);
|
||||
credentialsModal.actions.addUser(INSTANCE_ADMIN.email);
|
||||
credentialsModal.actions.saveSharing();
|
||||
credentialsModal.actions.close();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { INSTANCE_MEMBERS, INSTANCE_OWNER } from '../constants';
|
||||
import { SettingsUsersPage, WorkflowPage } from '../pages';
|
||||
import { INSTANCE_MEMBERS, INSTANCE_OWNER, INSTANCE_ADMIN } from '../constants';
|
||||
import { MainSidebar, SettingsSidebar, SettingsUsersPage, WorkflowPage } from '../pages';
|
||||
import { PersonalSettingsPage } from '../pages/settings-personal';
|
||||
import { getVisibleSelect } from '../utils';
|
||||
|
||||
/**
|
||||
* User A - Instance owner
|
||||
|
@ -25,9 +26,13 @@ const updatedPersonalData = {
|
|||
const usersSettingsPage = new SettingsUsersPage();
|
||||
const workflowPage = new WorkflowPage();
|
||||
const personalSettingsPage = new PersonalSettingsPage();
|
||||
const settingsSidebar = new SettingsSidebar();
|
||||
const mainSidebar = new MainSidebar();
|
||||
|
||||
describe('User Management', { disableAutoLogin: true }, () => {
|
||||
before(() => cy.enableFeature('sharing'));
|
||||
before(() => {
|
||||
cy.enableFeature('sharing');
|
||||
});
|
||||
|
||||
it('should prevent non-owners to access UM settings', () => {
|
||||
usersSettingsPage.actions.loginAndVisit(
|
||||
|
@ -44,7 +49,7 @@ describe('User Management', { disableAutoLogin: true }, () => {
|
|||
it('should properly render UM settings page for instance owners', () => {
|
||||
usersSettingsPage.actions.loginAndVisit(INSTANCE_OWNER.email, INSTANCE_OWNER.password, true);
|
||||
// All items in user list should be there
|
||||
usersSettingsPage.getters.userListItems().should('have.length', 3);
|
||||
usersSettingsPage.getters.userListItems().should('have.length', 4);
|
||||
// List item for current user should have the `Owner` badge
|
||||
usersSettingsPage.getters
|
||||
.userItem(INSTANCE_OWNER.email)
|
||||
|
@ -53,6 +58,93 @@ describe('User Management', { disableAutoLogin: true }, () => {
|
|||
// Other users list items should contain action pop-up list
|
||||
usersSettingsPage.getters.userActionsToggle(INSTANCE_MEMBERS[0].email).should('exist');
|
||||
usersSettingsPage.getters.userActionsToggle(INSTANCE_MEMBERS[1].email).should('exist');
|
||||
usersSettingsPage.getters.userActionsToggle(INSTANCE_ADMIN.email).should('exist');
|
||||
});
|
||||
|
||||
it('should be able to change user role to Admin and back', () => {
|
||||
cy.enableFeature('advancedPermissions');
|
||||
|
||||
usersSettingsPage.actions.loginAndVisit(INSTANCE_OWNER.email, INSTANCE_OWNER.password, true);
|
||||
|
||||
// Change role from Member to Admin
|
||||
usersSettingsPage.getters
|
||||
.userRoleSelect(INSTANCE_MEMBERS[0].email)
|
||||
.find('input')
|
||||
.should('contain.value', 'Member');
|
||||
usersSettingsPage.getters.userRoleSelect(INSTANCE_MEMBERS[0].email).click();
|
||||
getVisibleSelect().find('li').contains('Admin').click();
|
||||
usersSettingsPage.getters
|
||||
.userRoleSelect(INSTANCE_MEMBERS[0].email)
|
||||
.find('input')
|
||||
.should('contain.value', 'Admin');
|
||||
|
||||
usersSettingsPage.actions.loginAndVisit(
|
||||
INSTANCE_MEMBERS[0].email,
|
||||
INSTANCE_MEMBERS[0].password,
|
||||
true,
|
||||
);
|
||||
|
||||
// Change role from Admin to Member, then back to Admin
|
||||
usersSettingsPage.getters
|
||||
.userRoleSelect(INSTANCE_ADMIN.email)
|
||||
.find('input')
|
||||
.should('contain.value', 'Admin');
|
||||
|
||||
usersSettingsPage.getters.userRoleSelect(INSTANCE_ADMIN.email).click();
|
||||
getVisibleSelect().find('li').contains('Member').click();
|
||||
usersSettingsPage.getters
|
||||
.userRoleSelect(INSTANCE_ADMIN.email)
|
||||
.find('input')
|
||||
.should('contain.value', 'Member');
|
||||
|
||||
usersSettingsPage.actions.loginAndVisit(INSTANCE_ADMIN.email, INSTANCE_ADMIN.password, false);
|
||||
usersSettingsPage.actions.loginAndVisit(
|
||||
INSTANCE_MEMBERS[0].email,
|
||||
INSTANCE_MEMBERS[0].password,
|
||||
true,
|
||||
);
|
||||
|
||||
usersSettingsPage.getters.userRoleSelect(INSTANCE_ADMIN.email).click();
|
||||
getVisibleSelect().find('li').contains('Admin').click();
|
||||
usersSettingsPage.getters
|
||||
.userRoleSelect(INSTANCE_ADMIN.email)
|
||||
.find('input')
|
||||
.should('contain.value', 'Admin');
|
||||
|
||||
usersSettingsPage.actions.loginAndVisit(INSTANCE_ADMIN.email, INSTANCE_ADMIN.password, true);
|
||||
usersSettingsPage.getters.userRoleSelect(INSTANCE_MEMBERS[0].email).click();
|
||||
getVisibleSelect().find('li').contains('Member').click();
|
||||
usersSettingsPage.getters
|
||||
.userRoleSelect(INSTANCE_MEMBERS[0].email)
|
||||
.find('input')
|
||||
.should('contain.value', 'Member');
|
||||
|
||||
cy.disableFeature('advancedPermissions');
|
||||
});
|
||||
|
||||
it('should be able to change theme', () => {
|
||||
personalSettingsPage.actions.loginAndVisit(INSTANCE_OWNER.email, INSTANCE_OWNER.password);
|
||||
|
||||
personalSettingsPage.actions.changeTheme('Dark');
|
||||
cy.get('body').should('have.attr', 'data-theme', 'dark');
|
||||
settingsSidebar.actions.back();
|
||||
mainSidebar.getters
|
||||
.logo()
|
||||
.should('have.attr', 'src')
|
||||
.then((src) => {
|
||||
expect(src).to.include('/n8n-dev-logo-dark-mode.svg');
|
||||
});
|
||||
|
||||
cy.visit(personalSettingsPage.url);
|
||||
personalSettingsPage.actions.changeTheme('Light');
|
||||
cy.get('body').should('have.attr', 'data-theme', 'light');
|
||||
settingsSidebar.actions.back();
|
||||
mainSidebar.getters
|
||||
.logo()
|
||||
.should('have.attr', 'src')
|
||||
.then((src) => {
|
||||
expect(src).to.include('/n8n-dev-logo.svg');
|
||||
});
|
||||
});
|
||||
|
||||
it('should delete user and their data', () => {
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import { v4 as uuid } from 'uuid';
|
||||
import { NDV, WorkflowPage as WorkflowPageClass, WorkflowsPage } from '../pages';
|
||||
import { NDV, WorkflowExecutionsTab, WorkflowPage as WorkflowPageClass } from '../pages';
|
||||
|
||||
const workflowPage = new WorkflowPageClass();
|
||||
const executionsTab = new WorkflowExecutionsTab();
|
||||
const ndv = new NDV();
|
||||
|
||||
describe('Execution', () => {
|
||||
|
@ -112,10 +113,6 @@ describe('Execution', () => {
|
|||
.canvasNodeByName('Manual')
|
||||
.within(() => cy.get('.fa-check'))
|
||||
.should('exist');
|
||||
workflowPage.getters
|
||||
.canvasNodeByName('Wait')
|
||||
.within(() => cy.get('.fa-check'))
|
||||
.should('exist');
|
||||
workflowPage.getters
|
||||
.canvasNodeByName('Wait')
|
||||
.within(() => cy.get('.fa-sync-alt').should('not.visible'));
|
||||
|
@ -191,10 +188,6 @@ describe('Execution', () => {
|
|||
.canvasNodeByName('Webhook')
|
||||
.within(() => cy.get('.fa-check'))
|
||||
.should('exist');
|
||||
workflowPage.getters
|
||||
.canvasNodeByName('Wait')
|
||||
.within(() => cy.get('.fa-check'))
|
||||
.should('exist');
|
||||
workflowPage.getters
|
||||
.canvasNodeByName('Set')
|
||||
.within(() => cy.get('.fa-check'))
|
||||
|
@ -267,10 +260,6 @@ describe('Execution', () => {
|
|||
.canvasNodeByName('Webhook')
|
||||
.within(() => cy.get('.fa-check'))
|
||||
.should('exist');
|
||||
workflowPage.getters
|
||||
.canvasNodeByName('Wait')
|
||||
.within(() => cy.get('.fa-check'))
|
||||
.should('exist');
|
||||
workflowPage.getters
|
||||
.canvasNodeByName('Wait')
|
||||
.within(() => cy.get('.fa-sync-alt').should('not.visible'));
|
||||
|
@ -286,4 +275,139 @@ describe('Execution', () => {
|
|||
// Check success toast (works because Cypress waits enough for the element to show after the http request node has finished)
|
||||
workflowPage.getters.successToast().should('be.visible');
|
||||
});
|
||||
|
||||
describe('execution preview', () => {
|
||||
it('when deleting the last execution, it should show empty state', () => {
|
||||
workflowPage.actions.addInitialNodeToCanvas('Manual Trigger');
|
||||
workflowPage.actions.executeWorkflow();
|
||||
executionsTab.actions.switchToExecutionsTab();
|
||||
|
||||
executionsTab.actions.deleteExecutionInPreview();
|
||||
|
||||
executionsTab.getters.successfulExecutionListItems().should('have.length', 0);
|
||||
workflowPage.getters.successToast().contains('Execution deleted');
|
||||
});
|
||||
});
|
||||
|
||||
describe('connections should be colored differently for pinned data', () => {
|
||||
beforeEach(() => {
|
||||
cy.createFixtureWorkflow('Schedule_pinned.json', `Schedule pinned ${uuid()}`);
|
||||
workflowPage.actions.deselectAll();
|
||||
workflowPage.getters.zoomToFitButton().click();
|
||||
|
||||
workflowPage.getters
|
||||
.getConnectionBetweenNodes('Schedule Trigger', 'Edit Fields')
|
||||
.should('have.class', 'success')
|
||||
.should('have.class', 'pinned')
|
||||
.should('not.have.class', 'has-run');
|
||||
|
||||
workflowPage.getters
|
||||
.getConnectionBetweenNodes('Schedule Trigger', 'Edit Fields1')
|
||||
.should('have.class', 'success')
|
||||
.should('have.class', 'pinned')
|
||||
.should('not.have.class', 'has-run');
|
||||
|
||||
workflowPage.getters
|
||||
.getConnectionBetweenNodes('Edit Fields5', 'Edit Fields6')
|
||||
.should('not.have.class', 'success')
|
||||
.should('not.have.class', 'pinned');
|
||||
|
||||
workflowPage.getters
|
||||
.getConnectionBetweenNodes('Edit Fields7', 'Edit Fields9')
|
||||
.should('have.class', 'success')
|
||||
.should('have.class', 'pinned')
|
||||
.should('not.have.class', 'has-run');
|
||||
|
||||
workflowPage.getters
|
||||
.getConnectionBetweenNodes('Edit Fields1', 'Edit Fields2')
|
||||
.should('not.have.class', 'success')
|
||||
.should('not.have.class', 'pinned');
|
||||
|
||||
workflowPage.getters
|
||||
.getConnectionBetweenNodes('Edit Fields2', 'Edit Fields3')
|
||||
.should('have.class', 'success')
|
||||
.should('have.class', 'pinned')
|
||||
.should('not.have.class', 'has-run');
|
||||
});
|
||||
|
||||
it('when executing the workflow', () => {
|
||||
workflowPage.actions.executeWorkflow();
|
||||
|
||||
workflowPage.getters
|
||||
.getConnectionBetweenNodes('Schedule Trigger', 'Edit Fields')
|
||||
.should('have.class', 'success')
|
||||
.should('have.class', 'pinned')
|
||||
.should('have.class', 'has-run');
|
||||
|
||||
workflowPage.getters
|
||||
.getConnectionBetweenNodes('Schedule Trigger', 'Edit Fields1')
|
||||
.should('have.class', 'success')
|
||||
.should('have.class', 'pinned')
|
||||
.should('have.class', 'has-run');
|
||||
|
||||
workflowPage.getters
|
||||
.getConnectionBetweenNodes('Edit Fields5', 'Edit Fields6')
|
||||
.should('have.class', 'success')
|
||||
.should('not.have.class', 'pinned')
|
||||
.should('not.have.class', 'has-run');
|
||||
|
||||
workflowPage.getters
|
||||
.getConnectionBetweenNodes('Edit Fields7', 'Edit Fields9')
|
||||
.should('have.class', 'success')
|
||||
.should('have.class', 'pinned')
|
||||
.should('have.class', 'has-run');
|
||||
|
||||
workflowPage.getters
|
||||
.getConnectionBetweenNodes('Edit Fields1', 'Edit Fields2')
|
||||
.should('have.class', 'success')
|
||||
.should('not.have.class', 'pinned')
|
||||
.should('not.have.class', 'has-run');
|
||||
|
||||
workflowPage.getters
|
||||
.getConnectionBetweenNodes('Edit Fields2', 'Edit Fields3')
|
||||
.should('have.class', 'success')
|
||||
.should('have.class', 'pinned')
|
||||
.should('have.class', 'has-run');
|
||||
});
|
||||
|
||||
it('when executing a node', () => {
|
||||
workflowPage.actions.executeNode('Edit Fields3');
|
||||
|
||||
workflowPage.getters
|
||||
.getConnectionBetweenNodes('Schedule Trigger', 'Edit Fields')
|
||||
.should('have.class', 'success')
|
||||
.should('have.class', 'pinned')
|
||||
.should('have.class', 'has-run');
|
||||
|
||||
workflowPage.getters
|
||||
.getConnectionBetweenNodes('Schedule Trigger', 'Edit Fields1')
|
||||
.should('have.class', 'success')
|
||||
.should('have.class', 'pinned')
|
||||
.should('have.class', 'has-run');
|
||||
|
||||
workflowPage.getters
|
||||
.getConnectionBetweenNodes('Edit Fields5', 'Edit Fields6')
|
||||
.should('not.have.class', 'success')
|
||||
.should('not.have.class', 'pinned')
|
||||
.should('not.have.class', 'has-run');
|
||||
|
||||
workflowPage.getters
|
||||
.getConnectionBetweenNodes('Edit Fields7', 'Edit Fields9')
|
||||
.should('have.class', 'success')
|
||||
.should('have.class', 'pinned')
|
||||
.should('not.have.class', 'has-run');
|
||||
|
||||
workflowPage.getters
|
||||
.getConnectionBetweenNodes('Edit Fields1', 'Edit Fields2')
|
||||
.should('have.class', 'success')
|
||||
.should('not.have.class', 'pinned')
|
||||
.should('not.have.class', 'has-run');
|
||||
|
||||
workflowPage.getters
|
||||
.getConnectionBetweenNodes('Edit Fields2', 'Edit Fields3')
|
||||
.should('have.class', 'success')
|
||||
.should('have.class', 'pinned')
|
||||
.should('have.class', 'has-run');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -42,7 +42,7 @@ describe('Credentials', () => {
|
|||
credentialsPage.getters.credentialCards().should('have.length', 1);
|
||||
});
|
||||
|
||||
it('should create a new credential using Add Credential button', () => {
|
||||
it.skip('should create a new credential using Add Credential button', () => {
|
||||
credentialsPage.getters.createCredentialButton().click();
|
||||
|
||||
credentialsModal.getters.newCredentialModal().should('be.visible');
|
||||
|
@ -60,7 +60,7 @@ describe('Credentials', () => {
|
|||
credentialsPage.getters.credentialCards().should('have.length', 2);
|
||||
});
|
||||
|
||||
it('should search credentials', () => {
|
||||
it.skip('should search credentials', () => {
|
||||
// Search by name
|
||||
credentialsPage.actions.search('Notion');
|
||||
credentialsPage.getters.credentialCards().should('have.length', 1);
|
||||
|
|
|
@ -1,18 +1,20 @@
|
|||
import { WorkflowPage } from '../pages';
|
||||
import { WorkflowExecutionsTab } from '../pages/workflow-executions-tab';
|
||||
import type { RouteHandler } from 'cypress/types/net-stubbing';
|
||||
|
||||
const workflowPage = new WorkflowPage();
|
||||
const executionsTab = new WorkflowExecutionsTab();
|
||||
const executionsRefreshInterval = 4000;
|
||||
|
||||
// Test suite for executions tab
|
||||
describe('Current Workflow Executions', () => {
|
||||
beforeEach(() => {
|
||||
workflowPage.actions.visit();
|
||||
cy.createFixtureWorkflow('Test_workflow_4_executions_view.json', `My test workflow`);
|
||||
createMockExecutions();
|
||||
});
|
||||
|
||||
it('should render executions tab correctly', () => {
|
||||
createMockExecutions();
|
||||
cy.intercept('GET', '/rest/executions?filter=*').as('getExecutions');
|
||||
cy.intercept('GET', '/rest/executions-current?filter=*').as('getCurrentExecutions');
|
||||
|
||||
|
@ -29,6 +31,45 @@ describe('Current Workflow Executions', () => {
|
|||
.invoke('attr', 'class')
|
||||
.should('match', /_active_/);
|
||||
});
|
||||
|
||||
it('should not redirect back to execution tab when request is not done before leaving the page', () => {
|
||||
cy.intercept('GET', '/rest/executions?filter=*');
|
||||
cy.intercept('GET', '/rest/executions-current?filter=*');
|
||||
|
||||
executionsTab.actions.switchToExecutionsTab();
|
||||
executionsTab.actions.switchToEditorTab();
|
||||
cy.wait(executionsRefreshInterval);
|
||||
cy.url().should('not.include', '/executions');
|
||||
executionsTab.actions.switchToExecutionsTab();
|
||||
executionsTab.actions.switchToEditorTab();
|
||||
executionsTab.actions.switchToExecutionsTab();
|
||||
executionsTab.actions.switchToEditorTab();
|
||||
executionsTab.actions.switchToExecutionsTab();
|
||||
executionsTab.actions.switchToEditorTab();
|
||||
cy.wait(executionsRefreshInterval);
|
||||
cy.url().should('not.include', '/executions');
|
||||
executionsTab.actions.switchToExecutionsTab();
|
||||
cy.wait(1000);
|
||||
executionsTab.actions.switchToEditorTab();
|
||||
cy.wait(executionsRefreshInterval);
|
||||
cy.url().should('not.include', '/executions');
|
||||
});
|
||||
|
||||
it('should not redirect back to execution tab when slow request is not done before leaving the page', () => {
|
||||
const throttleResponse: RouteHandler = (req) => {
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(() => resolve(req.continue()), 2000);
|
||||
});
|
||||
};
|
||||
|
||||
cy.intercept('GET', '/rest/executions?filter=*', throttleResponse);
|
||||
cy.intercept('GET', '/rest/executions-current?filter=*', throttleResponse);
|
||||
|
||||
executionsTab.actions.switchToExecutionsTab();
|
||||
executionsTab.actions.switchToEditorTab();
|
||||
cy.wait(executionsRefreshInterval);
|
||||
cy.url().should('not.include', '/executions');
|
||||
});
|
||||
});
|
||||
|
||||
const createMockExecutions = () => {
|
||||
|
|
|
@ -19,7 +19,7 @@ describe('NDV', () => {
|
|||
|
||||
workflowPage.actions.executeWorkflow();
|
||||
|
||||
workflowPage.actions.openNode('Item Lists');
|
||||
workflowPage.actions.openNode('Sort');
|
||||
|
||||
ndv.getters.inputPanel().contains('6 items').should('exist');
|
||||
ndv.getters.outputPanel().contains('6 items').should('exist');
|
||||
|
@ -92,7 +92,7 @@ describe('NDV', () => {
|
|||
ndv.getters.outputHoveringItem().should('have.text', '1000');
|
||||
ndv.getters.parameterExpressionPreview('value').should('include.text', '1000');
|
||||
|
||||
ndv.actions.selectInputNode('Item Lists');
|
||||
ndv.actions.selectInputNode('Sort');
|
||||
ndv.actions.changeOutputRunSelector('1 of 2 (6 items)');
|
||||
ndv.getters.backToCanvas().realHover(); // reset to default hover
|
||||
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
import { META_KEY } from '../constants';
|
||||
import { WorkflowPage as WorkflowPageClass } from '../pages/workflow';
|
||||
import { getPopper } from '../utils';
|
||||
import { Interception } from 'cypress/types/net-stubbing';
|
||||
|
||||
const workflowPage = new WorkflowPageClass();
|
||||
|
||||
|
@ -29,6 +32,19 @@ describe('Canvas Actions', () => {
|
|||
workflowPage.getters.addStickyButton().should('not.be.visible');
|
||||
|
||||
addDefaultSticky();
|
||||
workflowPage.actions.deselectAll();
|
||||
workflowPage.actions.addStickyFromContextMenu();
|
||||
workflowPage.actions.hitAddStickyShortcut();
|
||||
|
||||
workflowPage.getters.stickies().should('have.length', 3);
|
||||
|
||||
// Should not add a sticky for ctrl+shift+s
|
||||
cy.get('body')
|
||||
.type(META_KEY, { delay: 500, release: false })
|
||||
.type('{shift}', { release: false })
|
||||
.type('s');
|
||||
|
||||
workflowPage.getters.stickies().should('have.length', 3);
|
||||
workflowPage.getters
|
||||
.stickies()
|
||||
.eq(0)
|
||||
|
@ -66,6 +82,32 @@ describe('Canvas Actions', () => {
|
|||
workflowPage.getters.stickies().should('have.length', 0);
|
||||
});
|
||||
|
||||
it('change sticky color', () => {
|
||||
workflowPage.actions.addSticky();
|
||||
|
||||
workflowPage.getters.stickies().should('have.length', 1);
|
||||
|
||||
workflowPage.actions.toggleColorPalette();
|
||||
|
||||
getPopper().should('be.visible');
|
||||
|
||||
workflowPage.actions.pickColor(2);
|
||||
|
||||
workflowPage.actions.toggleColorPalette();
|
||||
|
||||
getPopper().should('not.be.visible');
|
||||
|
||||
workflowPage.actions.saveWorkflowOnButtonClick();
|
||||
|
||||
cy.wait('@createWorkflow').then((interception: Interception) => {
|
||||
const { request } = interception;
|
||||
const color = request.body?.nodes[0]?.parameters?.color;
|
||||
expect(color).to.equal(2);
|
||||
});
|
||||
|
||||
workflowPage.getters.stickies().should('have.length', 1);
|
||||
});
|
||||
|
||||
it('edits sticky and updates content as markdown', () => {
|
||||
workflowPage.actions.addSticky();
|
||||
|
||||
|
@ -84,8 +126,11 @@ describe('Canvas Actions', () => {
|
|||
|
||||
moveSticky({ top: 200, left: 200 });
|
||||
|
||||
dragRightEdge({ left: 200, top: 200, height: 160, width: 240 }, 100);
|
||||
dragRightEdge({ left: 200, top: 200, height: 160, width: 240 }, -50);
|
||||
cy.drag('[data-test-id="sticky"] [data-dir="right"]', [100, 100]);
|
||||
checkStickiesStyle(100, 20, 160, 346);
|
||||
|
||||
cy.drag('[data-test-id="sticky"] [data-dir="right"]', [-50, -50]);
|
||||
checkStickiesStyle(100, 20, 160, 302);
|
||||
});
|
||||
|
||||
it('expands/shrinks sticky from the left edge', () => {
|
||||
|
@ -205,27 +250,6 @@ type Position = {
|
|||
left: number;
|
||||
};
|
||||
|
||||
type BoundingBox = {
|
||||
height: number;
|
||||
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]);
|
||||
});
|
||||
}
|
||||
|
||||
function shouldHaveOneSticky() {
|
||||
workflowPage.getters.stickies().should('have.length', 1);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { WorkflowPage, NDV, CredentialsModal } from '../pages';
|
||||
import { getPopper, getVisiblePopper, getVisibleSelect } from '../utils';
|
||||
import { getVisiblePopper, getVisibleSelect } from '../utils';
|
||||
|
||||
const workflowPage = new WorkflowPage();
|
||||
const ndv = new NDV();
|
||||
|
@ -16,7 +16,7 @@ describe('Resource Locator', () => {
|
|||
|
||||
it('should render both RLC components in google sheets', () => {
|
||||
workflowPage.actions.addInitialNodeToCanvas('Manual');
|
||||
workflowPage.actions.addNodeToCanvas('Google Sheets', true, true);
|
||||
workflowPage.actions.addNodeToCanvas('Google Sheets', true, true, 'Update row in sheet');
|
||||
ndv.getters.resourceLocator('documentId').should('be.visible');
|
||||
ndv.getters.resourceLocator('sheetName').should('be.visible');
|
||||
ndv.getters
|
||||
|
@ -31,7 +31,7 @@ describe('Resource Locator', () => {
|
|||
|
||||
it('should show appropriate error when credentials are not set', () => {
|
||||
workflowPage.actions.addInitialNodeToCanvas('Manual');
|
||||
workflowPage.actions.addNodeToCanvas('Google Sheets', true, true);
|
||||
workflowPage.actions.addNodeToCanvas('Google Sheets', true, true, 'Update row in sheet');
|
||||
ndv.getters.resourceLocator('documentId').should('be.visible');
|
||||
ndv.getters.resourceLocatorInput('documentId').click();
|
||||
ndv.getters.resourceLocatorErrorMessage().should('contain', NO_CREDENTIALS_MESSAGE);
|
||||
|
@ -39,7 +39,7 @@ describe('Resource Locator', () => {
|
|||
|
||||
it('should show appropriate error when credentials are not valid', () => {
|
||||
workflowPage.actions.addInitialNodeToCanvas('Manual');
|
||||
workflowPage.actions.addNodeToCanvas('Google Sheets', true, true);
|
||||
workflowPage.actions.addNodeToCanvas('Google Sheets', true, true, 'Update row in sheet');
|
||||
workflowPage.getters.nodeCredentialsSelect().click();
|
||||
// Add oAuth credentials
|
||||
getVisibleSelect().find('li').last().click();
|
||||
|
@ -54,7 +54,7 @@ describe('Resource Locator', () => {
|
|||
|
||||
it('should reset resource locator when dependent field is changed', () => {
|
||||
workflowPage.actions.addInitialNodeToCanvas('Manual');
|
||||
workflowPage.actions.addNodeToCanvas('Google Sheets', true, true);
|
||||
workflowPage.actions.addNodeToCanvas('Google Sheets', true, true, 'Update row in sheet');
|
||||
ndv.actions.setRLCValue('documentId', '123');
|
||||
ndv.actions.setRLCValue('sheetName', '123');
|
||||
ndv.actions.setRLCValue('documentId', '321');
|
||||
|
@ -66,6 +66,8 @@ describe('Resource Locator', () => {
|
|||
workflowPage.actions.addInitialNodeToCanvas('E2e Test', { action: 'Resource Locator' });
|
||||
|
||||
ndv.getters.resourceLocatorInput('rlc').click();
|
||||
|
||||
cy.getByTestId('rlc-item').should('exist');
|
||||
getVisiblePopper()
|
||||
.should('have.length', 1)
|
||||
.findChildByTestId('rlc-item')
|
||||
|
@ -73,9 +75,11 @@ describe('Resource Locator', () => {
|
|||
|
||||
ndv.actions.setInvalidExpression({ fieldName: 'fieldId' });
|
||||
|
||||
ndv.getters.container().click(); // remove focus from input, hide expression preview
|
||||
ndv.getters.nodeParameters().click(); // remove focus from input, hide expression preview
|
||||
|
||||
ndv.getters.resourceLocatorInput('rlc').click();
|
||||
|
||||
cy.getByTestId('rlc-item').should('exist');
|
||||
getVisiblePopper()
|
||||
.should('have.length', 1)
|
||||
.findChildByTestId('rlc-item')
|
||||
|
|
119
cypress/e2e/27-cloud.cy.ts
Normal file
119
cypress/e2e/27-cloud.cy.ts
Normal file
|
@ -0,0 +1,119 @@
|
|||
import {
|
||||
BannerStack,
|
||||
MainSidebar,
|
||||
WorkflowPage,
|
||||
visitPublicApiPage,
|
||||
getPublicApiUpgradeCTA,
|
||||
} from '../pages';
|
||||
import planData from '../fixtures/Plan_data_opt_in_trial.json';
|
||||
import { INSTANCE_OWNER } from '../constants';
|
||||
|
||||
const mainSidebar = new MainSidebar();
|
||||
const bannerStack = new BannerStack();
|
||||
const workflowPage = new WorkflowPage();
|
||||
|
||||
describe('Cloud', { disableAutoLogin: true }, () => {
|
||||
before(() => {
|
||||
const now = new Date();
|
||||
const fiveDaysFromNow = new Date(now.getTime() + 5 * 24 * 60 * 60 * 1000);
|
||||
planData.expirationDate = fiveDaysFromNow.toJSON();
|
||||
});
|
||||
|
||||
describe('BannerStack', () => {
|
||||
it('should render trial banner for opt-in cloud user', () => {
|
||||
cy.intercept('GET', '/rest/admin/cloud-plan', {
|
||||
body: planData,
|
||||
}).as('getPlanData');
|
||||
|
||||
cy.intercept('GET', '/rest/settings', (req) => {
|
||||
req.on('response', (res) => {
|
||||
res.send({
|
||||
data: { ...res.body.data, deployment: { type: 'cloud' }, n8nMetadata: { userId: 1 } },
|
||||
});
|
||||
});
|
||||
}).as('loadSettings');
|
||||
|
||||
cy.signin({ email: INSTANCE_OWNER.email, password: INSTANCE_OWNER.password });
|
||||
|
||||
cy.visit(workflowPage.url);
|
||||
|
||||
cy.wait('@getPlanData');
|
||||
|
||||
bannerStack.getters.banner().should('be.visible');
|
||||
|
||||
mainSidebar.actions.signout();
|
||||
|
||||
bannerStack.getters.banner().should('not.be.visible');
|
||||
|
||||
cy.signin({ email: INSTANCE_OWNER.email, password: INSTANCE_OWNER.password });
|
||||
|
||||
cy.visit(workflowPage.url);
|
||||
|
||||
bannerStack.getters.banner().should('be.visible');
|
||||
|
||||
mainSidebar.actions.signout();
|
||||
});
|
||||
|
||||
it('should not render opt-in-trial banner for non cloud deployment', () => {
|
||||
cy.intercept('GET', '/rest/settings', (req) => {
|
||||
req.on('response', (res) => {
|
||||
res.send({
|
||||
data: { ...res.body.data, deployment: { type: 'default' } },
|
||||
});
|
||||
});
|
||||
}).as('loadSettings');
|
||||
|
||||
cy.signin({ email: INSTANCE_OWNER.email, password: INSTANCE_OWNER.password });
|
||||
|
||||
cy.visit(workflowPage.url);
|
||||
|
||||
bannerStack.getters.banner().should('not.be.visible');
|
||||
|
||||
mainSidebar.actions.signout();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Admin Home', () => {
|
||||
it('Should show admin button', () => {
|
||||
cy.intercept('GET', '/rest/settings', (req) => {
|
||||
req.on('response', (res) => {
|
||||
res.send({
|
||||
data: { ...res.body.data, deployment: { type: 'cloud' }, n8nMetadata: { userId: 1 } },
|
||||
});
|
||||
});
|
||||
}).as('loadSettings');
|
||||
|
||||
cy.signin({ email: INSTANCE_OWNER.email, password: INSTANCE_OWNER.password });
|
||||
|
||||
cy.visit(workflowPage.url);
|
||||
|
||||
mainSidebar.getters.adminPanel().should('be.visible');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Public API', () => {
|
||||
it('Should show upgrade CTA for Public API if user is trialing', () => {
|
||||
cy.intercept('GET', '/rest/admin/cloud-plan', {
|
||||
body: planData,
|
||||
}).as('getPlanData');
|
||||
|
||||
cy.intercept('GET', '/rest/settings', (req) => {
|
||||
req.on('response', (res) => {
|
||||
res.send({
|
||||
data: {
|
||||
...res.body.data,
|
||||
deployment: { type: 'cloud' },
|
||||
n8nMetadata: { userId: 1 },
|
||||
},
|
||||
});
|
||||
});
|
||||
}).as('loadSettings');
|
||||
|
||||
cy.signin({ email: INSTANCE_OWNER.email, password: INSTANCE_OWNER.password });
|
||||
|
||||
visitPublicApiPage();
|
||||
|
||||
getPublicApiUpgradeCTA().should('be.visible');
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,67 +0,0 @@
|
|||
import { BannerStack, MainSidebar, WorkflowPage } from '../pages';
|
||||
import planData from '../fixtures/Plan_data_opt_in_trial.json';
|
||||
import { INSTANCE_OWNER } from '../constants';
|
||||
|
||||
const mainSidebar = new MainSidebar();
|
||||
const bannerStack = new BannerStack();
|
||||
const workflowPage = new WorkflowPage();
|
||||
|
||||
describe('BannerStack', { disableAutoLogin: true }, () => {
|
||||
before(() => {
|
||||
const now = new Date();
|
||||
const fiveDaysFromNow = new Date(now.getTime() + 5 * 24 * 60 * 60 * 1000);
|
||||
planData.expirationDate = fiveDaysFromNow.toJSON();
|
||||
});
|
||||
|
||||
it('should render trial banner for opt-in cloud user', () => {
|
||||
cy.intercept('GET', '/rest/settings', (req) => {
|
||||
req.on('response', (res) => {
|
||||
res.send({
|
||||
data: { ...res.body.data, deployment: { type: 'cloud' }, n8nMetadata: { userId: 1 } },
|
||||
});
|
||||
});
|
||||
}).as('loadSettings');
|
||||
|
||||
cy.intercept('GET', '/rest/admin/cloud-plan', {
|
||||
body: planData,
|
||||
}).as('getPlanData');
|
||||
|
||||
cy.signin({ email: INSTANCE_OWNER.email, password: INSTANCE_OWNER.password });
|
||||
|
||||
cy.visit(workflowPage.url);
|
||||
|
||||
cy.wait('@getPlanData');
|
||||
|
||||
bannerStack.getters.banner().should('be.visible');
|
||||
|
||||
mainSidebar.actions.signout();
|
||||
|
||||
bannerStack.getters.banner().should('not.be.visible');
|
||||
|
||||
cy.signin({ email: INSTANCE_OWNER.email, password: INSTANCE_OWNER.password });
|
||||
|
||||
cy.visit(workflowPage.url);
|
||||
|
||||
bannerStack.getters.banner().should('be.visible');
|
||||
|
||||
mainSidebar.actions.signout();
|
||||
});
|
||||
|
||||
it('should not render opt-in-trial banner for non cloud deployment', () => {
|
||||
cy.intercept('GET', '/rest/settings', (req) => {
|
||||
req.on('response', (res) => {
|
||||
res.send({
|
||||
data: { ...res.body.data, deployment: { type: 'default' } },
|
||||
});
|
||||
});
|
||||
}).as('loadSettings');
|
||||
|
||||
cy.signin({ email: INSTANCE_OWNER.email, password: INSTANCE_OWNER.password });
|
||||
|
||||
cy.visit(workflowPage.url);
|
||||
|
||||
bannerStack.getters.banner().should('not.be.visible');
|
||||
|
||||
mainSidebar.actions.signout();
|
||||
});
|
||||
});
|
|
@ -1,5 +1,5 @@
|
|||
import { MainSidebar } from './../pages/sidebar/main-sidebar';
|
||||
import { INSTANCE_OWNER, BACKEND_BASE_URL } from '../constants';
|
||||
import { INSTANCE_OWNER, INSTANCE_ADMIN, BACKEND_BASE_URL } from '../constants';
|
||||
import { SigninPage } from '../pages';
|
||||
import { PersonalSettingsPage } from '../pages/settings-personal';
|
||||
import { MfaLoginPage } from '../pages/mfa-login';
|
||||
|
@ -19,6 +19,16 @@ const user = {
|
|||
mfaRecoveryCodes: [RECOVERY_CODE],
|
||||
};
|
||||
|
||||
const admin = {
|
||||
email: INSTANCE_ADMIN.email,
|
||||
password: INSTANCE_ADMIN.password,
|
||||
firstName: 'Admin',
|
||||
lastName: 'B',
|
||||
mfaEnabled: false,
|
||||
mfaSecret: MFA_SECRET,
|
||||
mfaRecoveryCodes: [RECOVERY_CODE],
|
||||
};
|
||||
|
||||
const mfaLoginPage = new MfaLoginPage();
|
||||
const signinPage = new SigninPage();
|
||||
const personalSettingsPage = new PersonalSettingsPage();
|
||||
|
@ -30,6 +40,7 @@ describe('Two-factor authentication', () => {
|
|||
cy.request('POST', `${BACKEND_BASE_URL}/rest/e2e/reset`, {
|
||||
owner: user,
|
||||
members: [],
|
||||
admin,
|
||||
});
|
||||
cy.on('uncaught:exception', (err, runnable) => {
|
||||
expect(err.message).to.include('Not logged in');
|
||||
|
|
|
@ -12,14 +12,11 @@ const ndv = new NDV();
|
|||
const executionsTab = new WorkflowExecutionsTab();
|
||||
|
||||
describe('Debug', () => {
|
||||
beforeEach(() => {
|
||||
cy.enableFeature('debugInEditor');
|
||||
});
|
||||
|
||||
it('should be able to debug executions', () => {
|
||||
cy.intercept('GET', '/rest/settings', (req) => {
|
||||
req.on('response', (res) => {
|
||||
res.send({
|
||||
data: { ...res.body.data, enterprise: { debugInEditor: true } },
|
||||
});
|
||||
});
|
||||
}).as('loadSettings');
|
||||
cy.intercept('GET', '/rest/executions?filter=*').as('getExecutions');
|
||||
cy.intercept('GET', '/rest/executions/*').as('getExecution');
|
||||
cy.intercept('GET', '/rest/executions-current?filter=*').as('getCurrentExecutions');
|
||||
|
@ -47,6 +44,7 @@ describe('Debug', () => {
|
|||
cy.wait(['@getExecutions', '@getCurrentExecutions']);
|
||||
|
||||
executionsTab.getters.executionDebugButton().should('have.text', 'Debug in editor').click();
|
||||
cy.url().should('include', '/debug');
|
||||
cy.get('.el-notification').contains('Execution data imported').should('be.visible');
|
||||
cy.get('.matching-pinned-nodes-confirmation').should('not.exist');
|
||||
|
||||
|
@ -56,6 +54,8 @@ describe('Debug', () => {
|
|||
ndv.actions.close();
|
||||
|
||||
workflowPage.actions.saveWorkflowUsingKeyboardShortcut();
|
||||
cy.url().should('not.include', '/debug');
|
||||
|
||||
workflowPage.actions.executeWorkflow();
|
||||
|
||||
cy.wait(['@postWorkflowRun']);
|
||||
|
@ -87,6 +87,7 @@ describe('Debug', () => {
|
|||
confirmDialog = cy.get('.matching-pinned-nodes-confirmation').filter(':visible');
|
||||
confirmDialog.find('li').should('have.length', 2);
|
||||
confirmDialog.get('.btn--confirm').click();
|
||||
cy.url().should('include', '/debug');
|
||||
|
||||
workflowPage.getters.canvasNodes().first().should('have.descendants', '.node-pin-data-icon');
|
||||
workflowPage.getters
|
||||
|
@ -104,6 +105,7 @@ describe('Debug', () => {
|
|||
workflowPage.getters.canvasNodePlusEndpointByName(EDIT_FIELDS_SET_NODE_NAME).click();
|
||||
workflowPage.actions.addNodeToCanvas(IF_NODE_NAME, false);
|
||||
workflowPage.actions.saveWorkflowUsingKeyboardShortcut();
|
||||
cy.url().should('not.include', '/debug');
|
||||
|
||||
executionsTab.actions.switchToExecutionsTab();
|
||||
cy.wait(['@getExecutions', '@getCurrentExecutions']);
|
||||
|
@ -112,6 +114,8 @@ describe('Debug', () => {
|
|||
confirmDialog = cy.get('.matching-pinned-nodes-confirmation').filter(':visible');
|
||||
confirmDialog.find('li').should('have.length', 1);
|
||||
confirmDialog.get('.btn--confirm').click();
|
||||
cy.url().should('include', '/debug');
|
||||
|
||||
workflowPage.getters.canvasNodes().last().find('.node-info-icon').should('be.empty');
|
||||
|
||||
workflowPage.getters.canvasNodes().first().dblclick();
|
||||
|
@ -119,7 +123,10 @@ describe('Debug', () => {
|
|||
ndv.actions.close();
|
||||
|
||||
workflowPage.actions.saveWorkflowUsingKeyboardShortcut();
|
||||
cy.url().should('not.include', '/debug');
|
||||
|
||||
workflowPage.actions.executeWorkflow();
|
||||
workflowPage.actions.zoomToFit();
|
||||
workflowPage.actions.deleteNode(IF_NODE_NAME);
|
||||
|
||||
executionsTab.actions.switchToExecutionsTab();
|
||||
|
@ -128,5 +135,6 @@ describe('Debug', () => {
|
|||
cy.wait(['@getExecution']);
|
||||
executionsTab.getters.executionDebugButton().should('have.text', 'Copy to editor').click();
|
||||
cy.get('.el-notification').contains("Some execution data wasn't imported").should('be.visible');
|
||||
cy.url().should('include', '/debug');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -2,11 +2,21 @@ import { TemplatesPage } from '../pages/templates';
|
|||
import { WorkflowPage } from '../pages/workflow';
|
||||
|
||||
import OnboardingWorkflow from '../fixtures/Onboarding_workflow.json';
|
||||
import WorkflowTemplate from '../fixtures/Workflow_template_write_http_query.json';
|
||||
import { TemplateWorkflowPage } from '../pages/template-workflow';
|
||||
|
||||
const templatesPage = new TemplatesPage();
|
||||
const workflowPage = new WorkflowPage();
|
||||
const templateWorkflowPage = new TemplateWorkflowPage();
|
||||
|
||||
describe('Templates', () => {
|
||||
beforeEach(() => {
|
||||
cy.intercept('GET', '**/api/templates/search?page=1&rows=20&category=&search=', { fixture: 'templates_search/all_templates_search_response.json' }).as('searchRequest');
|
||||
cy.intercept('GET', '**/api/templates/search?page=1&rows=20&category=Sales*', { fixture: 'templates_search/sales_templates_search_response.json' }).as('categorySearchRequest');
|
||||
cy.intercept('GET', '**/api/templates/workflows/*', { fixture: 'templates_search/test_template_preview.json' }).as('singleTemplateRequest');
|
||||
cy.intercept('GET', '**/api/workflows/templates/*', { fixture: 'templates_search/test_template_import.json' }).as('singleTemplateRequest');
|
||||
});
|
||||
|
||||
it('can open onboarding flow', () => {
|
||||
templatesPage.actions.openOnboardingFlow(1234, OnboardingWorkflow.name, OnboardingWorkflow);
|
||||
cy.url().then(($url) => {
|
||||
|
@ -26,9 +36,104 @@ describe('Templates', () => {
|
|||
cy.url().then(($url) => {
|
||||
expect($url).to.include('/workflow/new?templateId=1234');
|
||||
});
|
||||
|
||||
|
||||
workflowPage.getters.canvasNodes().should('have.length', 4);
|
||||
workflowPage.getters.stickies().should('have.length', 1);
|
||||
workflowPage.actions.shouldHaveWorkflowName(OnboardingWorkflow.name);
|
||||
});
|
||||
|
||||
it('should save template id with the workflow', () => {
|
||||
cy.visit(templatesPage.url);
|
||||
cy.get('.el-skeleton.n8n-loading').should('not.exist');
|
||||
templatesPage.getters.firstTemplateCard().should('exist');
|
||||
templatesPage.getters.templatesLoadingContainer().should('not.exist');
|
||||
templatesPage.getters.firstTemplateCard().click();
|
||||
cy.url().should('include', '/templates/');
|
||||
|
||||
cy.url().then(($url) => {
|
||||
const templateId = $url.split('/').pop();
|
||||
|
||||
templatesPage.getters.useTemplateButton().click();
|
||||
cy.url().should('include', '/workflow/new');
|
||||
workflowPage.actions.saveWorkflowOnButtonClick();
|
||||
|
||||
workflowPage.actions.selectAll();
|
||||
workflowPage.actions.hitCopy();
|
||||
|
||||
cy.grantBrowserPermissions('clipboardReadWrite', 'clipboardSanitizedWrite');
|
||||
// Check workflow JSON by copying it to clipboard
|
||||
cy.readClipboard().then((workflowJSON) => {
|
||||
expect(workflowJSON).to.contain(`"templateId": "${templateId}"`);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('can open template with images and hides workflow screenshots', () => {
|
||||
templateWorkflowPage.actions.openTemplate(WorkflowTemplate);
|
||||
|
||||
templateWorkflowPage.getters.description().find('img').should('have.length', 1);
|
||||
});
|
||||
|
||||
|
||||
it('renders search elements correctly', () => {
|
||||
cy.visit(templatesPage.url);
|
||||
templatesPage.getters.searchInput().should('exist');
|
||||
templatesPage.getters.allCategoriesFilter().should('exist');
|
||||
templatesPage.getters.categoryFilters().should('have.length.greaterThan', 1);
|
||||
templatesPage.getters.templateCards().should('have.length.greaterThan', 0);
|
||||
});
|
||||
|
||||
it('can filter templates by category', () => {
|
||||
cy.visit(templatesPage.url);
|
||||
templatesPage.getters.templatesLoadingContainer().should('not.exist');
|
||||
templatesPage.getters.expandCategoriesButton().click();
|
||||
templatesPage.getters.categoryFilter('sales').should('exist');
|
||||
let initialTemplateCount = 0;
|
||||
let initialCollectionCount = 0;
|
||||
|
||||
templatesPage.getters.templateCountLabel().then(($el) => {
|
||||
initialTemplateCount = parseInt($el.text().replace(/\D/g, ''), 10);
|
||||
templatesPage.getters.collectionCountLabel().then(($el) => {
|
||||
initialCollectionCount = parseInt($el.text().replace(/\D/g, ''), 10);
|
||||
|
||||
templatesPage.getters.categoryFilter('sales').click();
|
||||
templatesPage.getters.templatesLoadingContainer().should('not.exist');
|
||||
|
||||
// Should have less templates and collections after selecting a category
|
||||
templatesPage.getters.templateCountLabel().should(($el) => {
|
||||
expect(parseInt($el.text().replace(/\D/g, ''), 10)).to.be.lessThan(initialTemplateCount);
|
||||
});
|
||||
templatesPage.getters.collectionCountLabel().should(($el) => {
|
||||
expect(parseInt($el.text().replace(/\D/g, ''), 10)).to.be.lessThan(initialCollectionCount);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should preserve search query in URL', () => {
|
||||
cy.visit(templatesPage.url);
|
||||
templatesPage.getters.templatesLoadingContainer().should('not.exist');
|
||||
templatesPage.getters.expandCategoriesButton().click();
|
||||
templatesPage.getters.categoryFilter('sales').should('exist');
|
||||
templatesPage.getters.categoryFilter('sales').click();
|
||||
templatesPage.getters.searchInput().type('auto');
|
||||
|
||||
cy.url().should('include', '?categories=');
|
||||
cy.url().should('include', '&search=');
|
||||
|
||||
cy.reload();
|
||||
|
||||
// Should preserve search query in URL
|
||||
cy.url().should('include', '?categories=');
|
||||
cy.url().should('include', '&search=');
|
||||
|
||||
// Sales category should still be selected
|
||||
templatesPage.getters.categoryFilter('sales').find('label').should('have.class', 'is-checked');
|
||||
// Search input should still have the search query
|
||||
templatesPage.getters.searchInput().should('have.value', 'auto');
|
||||
// Sales checkbox should be pushed to the top
|
||||
templatesPage.getters.categoryFilters().eq(1).then(($el) => {
|
||||
expect($el.text()).to.equal('Sales');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
218
cypress/e2e/30-editor-after-route-changes.cy.ts
Normal file
218
cypress/e2e/30-editor-after-route-changes.cy.ts
Normal file
|
@ -0,0 +1,218 @@
|
|||
import {
|
||||
CODE_NODE_NAME,
|
||||
EDIT_FIELDS_SET_NODE_NAME,
|
||||
IF_NODE_NAME,
|
||||
INSTANCE_OWNER,
|
||||
SCHEDULE_TRIGGER_NODE_NAME,
|
||||
} from '../constants';
|
||||
import {
|
||||
WorkflowExecutionsTab,
|
||||
WorkflowPage as WorkflowPageClass,
|
||||
WorkflowHistoryPage,
|
||||
} from '../pages';
|
||||
|
||||
const workflowPage = new WorkflowPageClass();
|
||||
const executionsTab = new WorkflowExecutionsTab();
|
||||
const workflowHistoryPage = new WorkflowHistoryPage();
|
||||
|
||||
const createNewWorkflowAndActivate = () => {
|
||||
workflowPage.actions.visit();
|
||||
workflowPage.actions.addNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME);
|
||||
workflowPage.actions.saveWorkflowOnButtonClick();
|
||||
workflowPage.actions.activateWorkflow();
|
||||
cy.get('.el-notification .el-notification--error').should('not.exist');
|
||||
};
|
||||
|
||||
const editWorkflowAndDeactivate = () => {
|
||||
workflowPage.getters.canvasNodePlusEndpointByName(SCHEDULE_TRIGGER_NODE_NAME).click();
|
||||
workflowPage.getters.nodeCreatorSearchBar().should('be.visible');
|
||||
workflowPage.actions.addNodeToCanvas(EDIT_FIELDS_SET_NODE_NAME, false);
|
||||
cy.get('.jtk-connector').should('have.length', 1);
|
||||
workflowPage.actions.saveWorkflowOnButtonClick();
|
||||
workflowPage.getters.activatorSwitch().click();
|
||||
workflowPage.actions.zoomToFit();
|
||||
cy.get('.el-notification .el-notification--error').should('not.exist');
|
||||
};
|
||||
|
||||
const editWorkflowMoreAndActivate = () => {
|
||||
cy.drag(workflowPage.getters.getEndpointSelector('plus', EDIT_FIELDS_SET_NODE_NAME), [200, 200], {
|
||||
realMouse: true,
|
||||
});
|
||||
workflowPage.getters.nodeCreatorSearchBar().should('be.visible');
|
||||
|
||||
workflowPage.actions.addNodeToCanvas(CODE_NODE_NAME, false);
|
||||
workflowPage.getters.nodeViewBackground().click(600, 200, { force: true });
|
||||
cy.get('.jtk-connector').should('have.length', 2);
|
||||
workflowPage.actions.zoomToFit();
|
||||
workflowPage.actions.saveWorkflowOnButtonClick();
|
||||
|
||||
workflowPage.actions.addNodeToCanvas(IF_NODE_NAME);
|
||||
workflowPage.getters.nodeViewBackground().click(600, 200, { force: true });
|
||||
cy.get('.jtk-connector').should('have.length', 2);
|
||||
|
||||
const position = {
|
||||
top: 0,
|
||||
left: 0,
|
||||
};
|
||||
workflowPage.getters
|
||||
.canvasNodeByName(IF_NODE_NAME)
|
||||
.click()
|
||||
.then(($element) => {
|
||||
position.top = $element.position().top;
|
||||
position.left = $element.position().left;
|
||||
});
|
||||
|
||||
cy.drag('[data-test-id="canvas-node"].jtk-drag-selected', [50, 200], { clickToFinish: true });
|
||||
workflowPage.getters
|
||||
.canvasNodes()
|
||||
.last()
|
||||
.then(($element) => {
|
||||
const finalPosition = {
|
||||
top: $element.position().top,
|
||||
left: $element.position().left,
|
||||
};
|
||||
|
||||
expect(finalPosition.top).to.be.greaterThan(position.top);
|
||||
expect(finalPosition.left).to.be.greaterThan(position.left);
|
||||
});
|
||||
|
||||
cy.draganddrop(
|
||||
workflowPage.getters.getEndpointSelector('output', CODE_NODE_NAME),
|
||||
workflowPage.getters.getEndpointSelector('input', IF_NODE_NAME),
|
||||
);
|
||||
cy.get('.jtk-connector').should('have.length', 3);
|
||||
|
||||
workflowPage.actions.saveWorkflowOnButtonClick();
|
||||
workflowPage.getters.activatorSwitch().click();
|
||||
cy.get('.el-notification .el-notification--error').should('not.exist');
|
||||
};
|
||||
|
||||
const switchBetweenEditorAndHistory = () => {
|
||||
workflowPage.getters.workflowHistoryButton().click();
|
||||
cy.wait(['@getHistory']);
|
||||
cy.wait(['@getVersion']);
|
||||
|
||||
cy.intercept('GET', '/rest/workflows/*').as('workflowGet');
|
||||
workflowHistoryPage.getters.workflowHistoryCloseButton().click();
|
||||
cy.wait(['@workflowGet']);
|
||||
cy.wait(1000);
|
||||
|
||||
workflowPage.getters.canvasNodes().first().should('be.visible');
|
||||
workflowPage.getters.canvasNodes().last().should('be.visible');
|
||||
};
|
||||
|
||||
const switchBetweenEditorAndWorkflowlist = () => {
|
||||
cy.getByTestId('menu-item').first().click();
|
||||
cy.wait(['@getUsers', '@getWorkflows', '@getActiveWorkflows', '@getCredentials']);
|
||||
|
||||
cy.getByTestId('resources-list-item').first().click();
|
||||
|
||||
workflowPage.getters.canvasNodes().first().should('be.visible');
|
||||
workflowPage.getters.canvasNodes().last().should('be.visible');
|
||||
};
|
||||
|
||||
const zoomInAndCheckNodes = () => {
|
||||
cy.getByTestId('zoom-in-button').click();
|
||||
cy.getByTestId('zoom-in-button').click();
|
||||
cy.getByTestId('zoom-in-button').click();
|
||||
cy.getByTestId('zoom-in-button').click();
|
||||
|
||||
workflowPage.getters.canvasNodes().first().should('not.be.visible');
|
||||
workflowPage.getters.canvasNodes().last().should('not.be.visible');
|
||||
};
|
||||
|
||||
describe('Editor actions should work', () => {
|
||||
beforeEach(() => {
|
||||
cy.enableFeature('debugInEditor');
|
||||
cy.enableFeature('workflowHistory');
|
||||
cy.signin({ email: INSTANCE_OWNER.email, password: INSTANCE_OWNER.password });
|
||||
createNewWorkflowAndActivate();
|
||||
});
|
||||
|
||||
it('after saving a new workflow', () => {
|
||||
editWorkflowAndDeactivate();
|
||||
editWorkflowMoreAndActivate();
|
||||
});
|
||||
|
||||
it('after switching between Editor and Executions', () => {
|
||||
cy.intercept('GET', '/rest/executions?filter=*').as('getExecutions');
|
||||
cy.intercept('GET', '/rest/executions-current?filter=*').as('getCurrentExecutions');
|
||||
|
||||
executionsTab.actions.switchToExecutionsTab();
|
||||
cy.wait(['@getExecutions', '@getCurrentExecutions']);
|
||||
cy.wait(500);
|
||||
executionsTab.actions.switchToEditorTab();
|
||||
editWorkflowAndDeactivate();
|
||||
editWorkflowMoreAndActivate();
|
||||
});
|
||||
|
||||
it('after switching between Editor and Debug', () => {
|
||||
cy.intercept('GET', '/rest/executions?filter=*').as('getExecutions');
|
||||
cy.intercept('GET', '/rest/executions/*').as('getExecution');
|
||||
cy.intercept('GET', '/rest/executions-current?filter=*').as('getCurrentExecutions');
|
||||
cy.intercept('POST', '/rest/workflows/run').as('postWorkflowRun');
|
||||
|
||||
editWorkflowAndDeactivate();
|
||||
workflowPage.actions.executeWorkflow();
|
||||
cy.wait(['@postWorkflowRun']);
|
||||
|
||||
executionsTab.actions.switchToExecutionsTab();
|
||||
cy.wait(['@getExecutions', '@getCurrentExecutions']);
|
||||
|
||||
executionsTab.getters.executionListItems().should('have.length', 1).first().click();
|
||||
cy.wait(['@getExecution']);
|
||||
|
||||
executionsTab.getters.executionDebugButton().should('have.text', 'Copy to editor').click();
|
||||
editWorkflowMoreAndActivate();
|
||||
});
|
||||
|
||||
it('after switching between Editor and Workflow history', () => {
|
||||
cy.intercept('GET', '/rest/workflow-history/workflow/*/version/*').as('getVersion');
|
||||
cy.intercept('GET', '/rest/workflow-history/workflow/*').as('getHistory');
|
||||
|
||||
editWorkflowAndDeactivate();
|
||||
workflowPage.getters.workflowHistoryButton().click();
|
||||
cy.wait(['@getHistory']);
|
||||
cy.wait(['@getVersion']);
|
||||
|
||||
cy.intercept('GET', '/rest/workflows/*').as('workflowGet');
|
||||
workflowHistoryPage.getters.workflowHistoryCloseButton().click();
|
||||
cy.wait(['@workflowGet']);
|
||||
cy.wait(1000);
|
||||
|
||||
editWorkflowMoreAndActivate();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Editor zoom should work after route changes', () => {
|
||||
beforeEach(() => {
|
||||
cy.enableFeature('debugInEditor');
|
||||
cy.enableFeature('workflowHistory');
|
||||
cy.signin({ email: INSTANCE_OWNER.email, password: INSTANCE_OWNER.password });
|
||||
workflowPage.actions.visit();
|
||||
cy.createFixtureWorkflow('Lots_of_nodes.json', `Lots of nodes`);
|
||||
workflowPage.actions.saveWorkflowOnButtonClick();
|
||||
});
|
||||
|
||||
it('after switching between Editor and Workflow history and Workflow list', () => {
|
||||
cy.intercept('GET', '/rest/workflow-history/workflow/*/version/*').as('getVersion');
|
||||
cy.intercept('GET', '/rest/workflow-history/workflow/*').as('getHistory');
|
||||
cy.intercept('GET', '/rest/users').as('getUsers');
|
||||
cy.intercept('GET', '/rest/workflows').as('getWorkflows');
|
||||
cy.intercept('GET', '/rest/active-workflows').as('getActiveWorkflows');
|
||||
cy.intercept('GET', '/rest/credentials').as('getCredentials');
|
||||
|
||||
switchBetweenEditorAndHistory();
|
||||
zoomInAndCheckNodes();
|
||||
switchBetweenEditorAndHistory();
|
||||
switchBetweenEditorAndHistory();
|
||||
zoomInAndCheckNodes();
|
||||
switchBetweenEditorAndWorkflowlist();
|
||||
zoomInAndCheckNodes();
|
||||
switchBetweenEditorAndWorkflowlist();
|
||||
switchBetweenEditorAndWorkflowlist();
|
||||
zoomInAndCheckNodes();
|
||||
switchBetweenEditorAndHistory();
|
||||
switchBetweenEditorAndWorkflowlist();
|
||||
});
|
||||
});
|
58
cypress/e2e/30-if-node.cy.ts
Normal file
58
cypress/e2e/30-if-node.cy.ts
Normal file
|
@ -0,0 +1,58 @@
|
|||
import { IF_NODE_NAME } from '../constants';
|
||||
import { WorkflowPage, NDV } from '../pages';
|
||||
|
||||
const workflowPage = new WorkflowPage();
|
||||
const ndv = new NDV();
|
||||
|
||||
const FILTER_PARAM_NAME = 'conditions';
|
||||
|
||||
describe('If Node (filter component)', () => {
|
||||
beforeEach(() => {
|
||||
workflowPage.actions.visit();
|
||||
});
|
||||
|
||||
it('should be able to create and delete multiple conditions', () => {
|
||||
workflowPage.actions.addInitialNodeToCanvas(IF_NODE_NAME, { keepNdvOpen: true });
|
||||
|
||||
// Default state
|
||||
ndv.getters.filterComponent(FILTER_PARAM_NAME).should('exist');
|
||||
ndv.getters.filterConditions(FILTER_PARAM_NAME).should('have.length', 1);
|
||||
ndv.getters
|
||||
.filterConditionOperator(FILTER_PARAM_NAME)
|
||||
.find('input')
|
||||
.should('have.value', 'is equal to');
|
||||
|
||||
// Add
|
||||
ndv.actions.addFilterCondition(FILTER_PARAM_NAME);
|
||||
ndv.getters.filterConditionLeft(FILTER_PARAM_NAME, 0).find('input').type('first left');
|
||||
ndv.getters.filterConditionLeft(FILTER_PARAM_NAME, 1).find('input').type('second left');
|
||||
ndv.actions.addFilterCondition(FILTER_PARAM_NAME);
|
||||
ndv.getters.filterConditions(FILTER_PARAM_NAME).should('have.length', 3);
|
||||
|
||||
// Delete
|
||||
ndv.actions.removeFilterCondition(FILTER_PARAM_NAME, 0);
|
||||
ndv.getters.filterConditions(FILTER_PARAM_NAME).should('have.length', 2);
|
||||
ndv.getters
|
||||
.filterConditionLeft(FILTER_PARAM_NAME, 0)
|
||||
.find('input')
|
||||
.should('have.value', 'second left');
|
||||
ndv.actions.removeFilterCondition(FILTER_PARAM_NAME, 1);
|
||||
ndv.getters.filterConditions(FILTER_PARAM_NAME).should('have.length', 1);
|
||||
});
|
||||
|
||||
it('should correctly evaluate conditions', () => {
|
||||
cy.fixture('Test_workflow_filter.json').then((data) => {
|
||||
cy.get('body').paste(JSON.stringify(data));
|
||||
});
|
||||
|
||||
workflowPage.actions.zoomToFit();
|
||||
workflowPage.actions.executeWorkflow();
|
||||
|
||||
workflowPage.actions.openNode('Then');
|
||||
ndv.getters.outputPanel().contains('3 items').should('exist');
|
||||
ndv.actions.close();
|
||||
|
||||
workflowPage.actions.openNode('Else');
|
||||
ndv.getters.outputPanel().contains('1 item').should('exist');
|
||||
});
|
||||
});
|
278
cypress/e2e/30-langchain.cy.ts
Normal file
278
cypress/e2e/30-langchain.cy.ts
Normal file
|
@ -0,0 +1,278 @@
|
|||
import {
|
||||
AGENT_NODE_NAME,
|
||||
MANUAL_CHAT_TRIGGER_NODE_NAME,
|
||||
AI_LANGUAGE_MODEL_OPENAI_CHAT_MODEL_NODE_NAME,
|
||||
MANUAL_TRIGGER_NODE_NAME,
|
||||
AI_MEMORY_WINDOW_BUFFER_MEMORY_NODE_NAME,
|
||||
AI_TOOL_CALCULATOR_NODE_NAME,
|
||||
AI_OUTPUT_PARSER_AUTO_FIXING_NODE_NAME,
|
||||
AI_TOOL_CODE_NODE_NAME,
|
||||
AI_TOOL_WIKIPEDIA_NODE_NAME,
|
||||
BASIC_LLM_CHAIN_NODE_NAME,
|
||||
} from './../constants';
|
||||
import { createMockNodeExecutionData, runMockWorkflowExcution } from '../utils';
|
||||
import {
|
||||
addLanguageModelNodeToParent,
|
||||
addMemoryNodeToParent,
|
||||
addNodeToCanvas,
|
||||
addOutputParserNodeToParent,
|
||||
addToolNodeToParent,
|
||||
clickManualChatButton,
|
||||
navigateToNewWorkflowPage,
|
||||
openNode,
|
||||
} from '../composables/workflow';
|
||||
import {
|
||||
clickCreateNewCredential,
|
||||
clickExecuteNode,
|
||||
clickGetBackToCanvas,
|
||||
getOutputPanelTable,
|
||||
setParameterInputByName,
|
||||
} from '../composables/ndv';
|
||||
import { setCredentialValues } from '../composables/modals/credential-modal';
|
||||
import {
|
||||
closeManualChatModal,
|
||||
getManualChatMessages,
|
||||
getManualChatModalLogs,
|
||||
getManualChatModalLogsEntries,
|
||||
getManualChatModalLogsTree,
|
||||
sendManualChatMessage,
|
||||
} from '../composables/modals/chat-modal';
|
||||
|
||||
describe('Langchain Integration', () => {
|
||||
beforeEach(() => {
|
||||
navigateToNewWorkflowPage();
|
||||
});
|
||||
|
||||
it('should add nodes to all Agent node input types', () => {
|
||||
addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME, true);
|
||||
addNodeToCanvas(AGENT_NODE_NAME, true);
|
||||
|
||||
addLanguageModelNodeToParent(AI_LANGUAGE_MODEL_OPENAI_CHAT_MODEL_NODE_NAME, AGENT_NODE_NAME);
|
||||
clickGetBackToCanvas();
|
||||
|
||||
addMemoryNodeToParent(AI_MEMORY_WINDOW_BUFFER_MEMORY_NODE_NAME, AGENT_NODE_NAME);
|
||||
clickGetBackToCanvas();
|
||||
|
||||
addToolNodeToParent(AI_TOOL_CALCULATOR_NODE_NAME, AGENT_NODE_NAME);
|
||||
clickGetBackToCanvas();
|
||||
|
||||
addOutputParserNodeToParent(AI_OUTPUT_PARSER_AUTO_FIXING_NODE_NAME, AGENT_NODE_NAME);
|
||||
clickGetBackToCanvas();
|
||||
});
|
||||
|
||||
it('should add multiple tool nodes to Agent node tool input type', () => {
|
||||
addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME, true);
|
||||
addNodeToCanvas(AGENT_NODE_NAME, true);
|
||||
|
||||
[
|
||||
AI_TOOL_CALCULATOR_NODE_NAME,
|
||||
AI_TOOL_CODE_NODE_NAME,
|
||||
AI_TOOL_CODE_NODE_NAME,
|
||||
AI_TOOL_CODE_NODE_NAME,
|
||||
AI_TOOL_WIKIPEDIA_NODE_NAME,
|
||||
].forEach((tool) => {
|
||||
addToolNodeToParent(tool, AGENT_NODE_NAME);
|
||||
clickGetBackToCanvas();
|
||||
});
|
||||
});
|
||||
|
||||
it('should be able to open and execute Basic LLM Chain node', () => {
|
||||
addNodeToCanvas(MANUAL_CHAT_TRIGGER_NODE_NAME, true);
|
||||
addNodeToCanvas(BASIC_LLM_CHAIN_NODE_NAME, true);
|
||||
|
||||
addLanguageModelNodeToParent(
|
||||
AI_LANGUAGE_MODEL_OPENAI_CHAT_MODEL_NODE_NAME,
|
||||
BASIC_LLM_CHAIN_NODE_NAME,
|
||||
);
|
||||
|
||||
clickCreateNewCredential();
|
||||
setCredentialValues({
|
||||
apiKey: 'sk_test_123',
|
||||
});
|
||||
clickGetBackToCanvas();
|
||||
|
||||
openNode(BASIC_LLM_CHAIN_NODE_NAME);
|
||||
|
||||
const inputMessage = 'Hello!';
|
||||
const outputMessage = 'Hi there! How can I assist you today?';
|
||||
|
||||
setParameterInputByName('prompt', inputMessage);
|
||||
|
||||
runMockWorkflowExcution({
|
||||
trigger: () => clickExecuteNode(),
|
||||
runData: [
|
||||
createMockNodeExecutionData(BASIC_LLM_CHAIN_NODE_NAME, {
|
||||
jsonData: {
|
||||
main: { output: outputMessage },
|
||||
},
|
||||
metadata: {
|
||||
subRun: [{ node: AI_LANGUAGE_MODEL_OPENAI_CHAT_MODEL_NODE_NAME, runIndex: 0 }],
|
||||
},
|
||||
}),
|
||||
],
|
||||
lastNodeExecuted: BASIC_LLM_CHAIN_NODE_NAME,
|
||||
});
|
||||
|
||||
getOutputPanelTable().should('contain', 'output');
|
||||
getOutputPanelTable().should('contain', outputMessage);
|
||||
});
|
||||
|
||||
it('should be able to open and execute Agent node', () => {
|
||||
addNodeToCanvas(MANUAL_CHAT_TRIGGER_NODE_NAME, true);
|
||||
addNodeToCanvas(AGENT_NODE_NAME, true);
|
||||
|
||||
addLanguageModelNodeToParent(AI_LANGUAGE_MODEL_OPENAI_CHAT_MODEL_NODE_NAME, AGENT_NODE_NAME);
|
||||
|
||||
clickCreateNewCredential();
|
||||
setCredentialValues({
|
||||
apiKey: 'sk_test_123',
|
||||
});
|
||||
clickGetBackToCanvas();
|
||||
|
||||
openNode(AGENT_NODE_NAME);
|
||||
|
||||
const inputMessage = 'Hello!';
|
||||
const outputMessage = 'Hi there! How can I assist you today?';
|
||||
|
||||
setParameterInputByName('text', inputMessage);
|
||||
|
||||
runMockWorkflowExcution({
|
||||
trigger: () => clickExecuteNode(),
|
||||
runData: [
|
||||
createMockNodeExecutionData(AGENT_NODE_NAME, {
|
||||
jsonData: {
|
||||
main: { output: outputMessage },
|
||||
},
|
||||
metadata: {
|
||||
subRun: [{ node: AI_LANGUAGE_MODEL_OPENAI_CHAT_MODEL_NODE_NAME, runIndex: 0 }],
|
||||
},
|
||||
}),
|
||||
],
|
||||
lastNodeExecuted: AGENT_NODE_NAME,
|
||||
});
|
||||
|
||||
getOutputPanelTable().should('contain', 'output');
|
||||
getOutputPanelTable().should('contain', outputMessage);
|
||||
});
|
||||
|
||||
it('should add and use Manual Chat Trigger node together with Agent node', () => {
|
||||
addNodeToCanvas(MANUAL_CHAT_TRIGGER_NODE_NAME, true);
|
||||
addNodeToCanvas(AGENT_NODE_NAME, true);
|
||||
|
||||
addLanguageModelNodeToParent(AI_LANGUAGE_MODEL_OPENAI_CHAT_MODEL_NODE_NAME, AGENT_NODE_NAME);
|
||||
|
||||
clickCreateNewCredential();
|
||||
setCredentialValues({
|
||||
apiKey: 'sk_test_123',
|
||||
});
|
||||
clickGetBackToCanvas();
|
||||
|
||||
clickManualChatButton();
|
||||
|
||||
getManualChatModalLogs().should('not.exist');
|
||||
|
||||
const inputMessage = 'Hello!';
|
||||
const outputMessage = 'Hi there! How can I assist you today?';
|
||||
|
||||
runMockWorkflowExcution({
|
||||
trigger: () => {
|
||||
sendManualChatMessage(inputMessage);
|
||||
},
|
||||
runData: [
|
||||
createMockNodeExecutionData(MANUAL_CHAT_TRIGGER_NODE_NAME, {
|
||||
jsonData: {
|
||||
main: { input: inputMessage },
|
||||
},
|
||||
}),
|
||||
createMockNodeExecutionData(AI_LANGUAGE_MODEL_OPENAI_CHAT_MODEL_NODE_NAME, {
|
||||
jsonData: {
|
||||
ai_languageModel: {
|
||||
response: {
|
||||
generations: [
|
||||
{
|
||||
text: `{
|
||||
"action": "Final Answer",
|
||||
"action_input": "${outputMessage}"
|
||||
}`,
|
||||
message: {
|
||||
lc: 1,
|
||||
type: 'constructor',
|
||||
id: ['langchain', 'schema', 'AIMessage'],
|
||||
kwargs: {
|
||||
content: `{
|
||||
"action": "Final Answer",
|
||||
"action_input": "${outputMessage}"
|
||||
}`,
|
||||
additional_kwargs: {},
|
||||
},
|
||||
},
|
||||
generationInfo: { finish_reason: 'stop' },
|
||||
},
|
||||
],
|
||||
llmOutput: {
|
||||
tokenUsage: {
|
||||
completionTokens: 26,
|
||||
promptTokens: 519,
|
||||
totalTokens: 545,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
inputOverride: {
|
||||
ai_languageModel: [
|
||||
[
|
||||
{
|
||||
json: {
|
||||
messages: [
|
||||
{
|
||||
lc: 1,
|
||||
type: 'constructor',
|
||||
id: ['langchain', 'schema', 'SystemMessage'],
|
||||
kwargs: {
|
||||
content:
|
||||
'Assistant is a large language model trained by OpenAI.\n\nAssistant is designed to be able to assist with a wide range of tasks, from answering simple questions to providing in-depth explanations and discussions on a wide range of topics. As a language model, Assistant is able to generate human-like text based on the input it receives, allowing it to engage in natural-sounding conversations and provide responses that are coherent and relevant to the topic at hand.\n\nAssistant is constantly learning and improving, and its capabilities are constantly evolving. It is able to process and understand large amounts of text, and can use this knowledge to provide accurate and informative responses to a wide range of questions. Additionally, Assistant is able to generate its own text based on the input it receives, allowing it to engage in discussions and provide explanations and descriptions on a wide range of topics.\n\nOverall, Assistant is a powerful system that can help with a wide range of tasks and provide valuable insights and information on a wide range of topics. Whether you need help with a specific question or just want to have a conversation about a particular topic, Assistant is here to assist. However, above all else, all responses must adhere to the format of RESPONSE FORMAT INSTRUCTIONS.',
|
||||
additional_kwargs: {},
|
||||
},
|
||||
},
|
||||
{
|
||||
lc: 1,
|
||||
type: 'constructor',
|
||||
id: ['langchain', 'schema', 'HumanMessage'],
|
||||
kwargs: {
|
||||
content:
|
||||
'TOOLS\n------\nAssistant can ask the user to use tools to look up information that may be helpful in answering the users original question. The tools the human can use are:\n\n\n\nRESPONSE FORMAT INSTRUCTIONS\n----------------------------\n\nOutput a JSON markdown code snippet containing a valid JSON object in one of two formats:\n\n**Option 1:**\nUse this if you want the human to use a tool.\nMarkdown code snippet formatted in the following schema:\n\n```json\n{\n "action": string, // The action to take. Must be one of []\n "action_input": string // The input to the action. May be a stringified object.\n}\n```\n\n**Option #2:**\nUse this if you want to respond directly and conversationally to the human. Markdown code snippet formatted in the following schema:\n\n```json\n{\n "action": "Final Answer",\n "action_input": string // You should put what you want to return to use here and make sure to use valid json newline characters.\n}\n```\n\nFor both options, remember to always include the surrounding markdown code snippet delimiters (begin with "```json" and end with "```")!\n\n\nUSER\'S INPUT\n--------------------\nHere is the user\'s input (remember to respond with a markdown code snippet of a json blob with a single action, and NOTHING else):\n\nHello!',
|
||||
additional_kwargs: {},
|
||||
},
|
||||
},
|
||||
],
|
||||
options: { stop: ['Observation:'], promptIndex: 0 },
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
}),
|
||||
createMockNodeExecutionData(AGENT_NODE_NAME, {
|
||||
jsonData: {
|
||||
main: { output: 'Hi there! How can I assist you today?' },
|
||||
},
|
||||
metadata: {
|
||||
subRun: [{ node: AI_LANGUAGE_MODEL_OPENAI_CHAT_MODEL_NODE_NAME, runIndex: 0 }],
|
||||
},
|
||||
}),
|
||||
],
|
||||
lastNodeExecuted: AGENT_NODE_NAME,
|
||||
});
|
||||
|
||||
const messages = getManualChatMessages();
|
||||
messages.should('have.length', 2);
|
||||
messages.should('contain', inputMessage);
|
||||
messages.should('contain', outputMessage);
|
||||
|
||||
getManualChatModalLogsTree().should('be.visible');
|
||||
getManualChatModalLogsEntries().should('have.length', 1);
|
||||
|
||||
closeManualChatModal();
|
||||
});
|
||||
});
|
118
cypress/e2e/30-workflow-filters.cy.ts
Normal file
118
cypress/e2e/30-workflow-filters.cy.ts
Normal file
|
@ -0,0 +1,118 @@
|
|||
import { WorkflowsPage as WorkflowsPageClass } from '../pages/workflows';
|
||||
import { WorkflowPage as WorkflowPageClass } from '../pages/workflow';
|
||||
|
||||
import { MainSidebar } from '../pages';
|
||||
import { INSTANCE_OWNER } from '../constants';
|
||||
|
||||
const WorkflowsPage = new WorkflowsPageClass();
|
||||
const WorkflowPages = new WorkflowPageClass();
|
||||
const mainSidebar = new MainSidebar();
|
||||
|
||||
describe.skip('Workflow filters', () => {
|
||||
before(() => {
|
||||
cy.enableFeature('sharing', true);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
cy.visit(WorkflowsPage.url);
|
||||
});
|
||||
|
||||
it('Should filter by tags', () => {
|
||||
cy.visit(WorkflowsPage.url);
|
||||
WorkflowsPage.getters.newWorkflowButtonCard().click();
|
||||
cy.createFixtureWorkflow('Test_workflow_1.json', `Workflow 1`);
|
||||
cy.visit(WorkflowsPage.url);
|
||||
WorkflowsPage.getters.createWorkflowButton().click();
|
||||
cy.createFixtureWorkflow('Test_workflow_2.json', `Workflow 2`);
|
||||
cy.visit(WorkflowsPage.url);
|
||||
|
||||
WorkflowsPage.getters.workflowFilterButton().click();
|
||||
WorkflowsPage.getters.workflowTagsDropdown().click();
|
||||
WorkflowsPage.getters.workflowTagItem('other-tag-1').click();
|
||||
cy.get('body').click(0, 0);
|
||||
|
||||
WorkflowsPage.getters.workflowCards().should('have.length', 1);
|
||||
WorkflowsPage.getters.workflowCard('Workflow 2').should('contain.text', 'Workflow 2');
|
||||
mainSidebar.actions.goToSettings();
|
||||
cy.go('back');
|
||||
|
||||
WorkflowsPage.getters.workflowCards().should('have.length', 1);
|
||||
WorkflowsPage.getters.workflowCard('Workflow 2').should('contain.text', 'Workflow 2');
|
||||
WorkflowsPage.getters.workflowResetFilters().click();
|
||||
|
||||
WorkflowsPage.getters.workflowCards().each(($el) => {
|
||||
const workflowName = $el.find('[data-test-id="workflow-card-name"]').text();
|
||||
|
||||
WorkflowsPage.getters.workflowCardActions(workflowName).click();
|
||||
WorkflowsPage.getters.workflowDeleteButton().click();
|
||||
|
||||
cy.get('button').contains('delete').click();
|
||||
});
|
||||
});
|
||||
|
||||
it('Should filter by status', () => {
|
||||
cy.visit(WorkflowsPage.url);
|
||||
WorkflowsPage.getters.newWorkflowButtonCard().click();
|
||||
cy.createFixtureWorkflow('Test_workflow_1.json', `Workflow 1`);
|
||||
cy.visit(WorkflowsPage.url);
|
||||
WorkflowsPage.getters.createWorkflowButton().click();
|
||||
cy.createFixtureWorkflow('Test_workflow_3.json', `Workflow 3`);
|
||||
WorkflowPages.getters.activatorSwitch().click();
|
||||
cy.visit(WorkflowsPage.url);
|
||||
|
||||
WorkflowsPage.getters.workflowFilterButton().click();
|
||||
WorkflowsPage.getters.workflowStatusDropdown().click();
|
||||
WorkflowsPage.getters.workflowStatusItem('Active').click();
|
||||
cy.get('body').click(0, 0);
|
||||
|
||||
WorkflowsPage.getters.workflowCards().should('have.length', 1);
|
||||
WorkflowsPage.getters.workflowCard('Workflow 3').should('contain.text', 'Workflow 3');
|
||||
mainSidebar.actions.goToSettings();
|
||||
cy.go('back');
|
||||
|
||||
WorkflowsPage.getters.workflowCards().should('have.length', 1);
|
||||
WorkflowsPage.getters.workflowCard('Workflow 3').should('contain.text', 'Workflow 3');
|
||||
WorkflowsPage.getters.workflowResetFilters().click();
|
||||
|
||||
WorkflowsPage.getters.workflowCards().each(($el) => {
|
||||
const workflowName = $el.find('[data-test-id="workflow-card-name"]').text();
|
||||
|
||||
WorkflowsPage.getters.workflowCardActions(workflowName).click();
|
||||
WorkflowsPage.getters.workflowDeleteButton().click();
|
||||
|
||||
cy.get('button').contains('delete').click();
|
||||
});
|
||||
});
|
||||
|
||||
it('Should filter by owned by', () => {
|
||||
cy.visit(WorkflowsPage.url);
|
||||
|
||||
WorkflowsPage.getters.newWorkflowButtonCard().click();
|
||||
cy.createFixtureWorkflow('Test_workflow_1.json', `Workflow 1`);
|
||||
cy.visit(WorkflowsPage.url);
|
||||
WorkflowsPage.getters.createWorkflowButton().click();
|
||||
cy.createFixtureWorkflow('Test_workflow_3.json', `Workflow 3`);
|
||||
WorkflowPages.getters.activatorSwitch().click();
|
||||
cy.visit(WorkflowsPage.url);
|
||||
|
||||
WorkflowsPage.getters.workflowFilterButton().click();
|
||||
WorkflowsPage.getters.workflowOwnershipDropdown().realClick();
|
||||
WorkflowsPage.getters.workflowOwner(INSTANCE_OWNER.email).click();
|
||||
cy.get('body').click(0, 0);
|
||||
|
||||
WorkflowsPage.getters.workflowCards().should('have.length', 2);
|
||||
mainSidebar.actions.goToSettings();
|
||||
cy.go('back');
|
||||
|
||||
WorkflowsPage.getters.workflowResetFilters().click();
|
||||
|
||||
WorkflowsPage.getters.workflowCards().each(($el) => {
|
||||
const workflowName = $el.find('[data-test-id="workflow-card-name"]').text();
|
||||
|
||||
WorkflowsPage.getters.workflowCardActions(workflowName).click();
|
||||
WorkflowsPage.getters.workflowDeleteButton().click();
|
||||
|
||||
cy.get('button').contains('delete').click();
|
||||
});
|
||||
});
|
||||
});
|
23
cypress/e2e/31-demo.cy.ts
Normal file
23
cypress/e2e/31-demo.cy.ts
Normal file
|
@ -0,0 +1,23 @@
|
|||
import workflow from '../fixtures/Manual_wait_set.json';
|
||||
import { importWorkflow, vistDemoPage } from '../pages/demo';
|
||||
import { WorkflowPage } from '../pages/workflow';
|
||||
|
||||
const workflowPage = new WorkflowPage();
|
||||
|
||||
describe('Demo', () => {
|
||||
it('can import template', () => {
|
||||
vistDemoPage();
|
||||
importWorkflow(workflow);
|
||||
workflowPage.getters.canvasNodes().should('have.length', 3);
|
||||
});
|
||||
|
||||
it('can override theme to dark', () => {
|
||||
vistDemoPage('dark');
|
||||
cy.get('body').should('have.attr', 'data-theme', 'dark');
|
||||
});
|
||||
|
||||
it('can override theme to light', () => {
|
||||
vistDemoPage('light');
|
||||
cy.get('body').should('have.attr', 'data-theme', 'light');
|
||||
});
|
||||
});
|
116
cypress/e2e/32-node-io-filter.cy.ts
Normal file
116
cypress/e2e/32-node-io-filter.cy.ts
Normal file
|
@ -0,0 +1,116 @@
|
|||
import { WorkflowPage as WorkflowPageClass, NDV } from '../pages';
|
||||
|
||||
const workflowPage = new WorkflowPageClass();
|
||||
const ndv = new NDV();
|
||||
|
||||
describe('Node IO Filter', () => {
|
||||
beforeEach(() => {
|
||||
workflowPage.actions.visit();
|
||||
cy.createFixtureWorkflow('Node_IO_filter.json', `Node IO filter`);
|
||||
workflowPage.actions.saveWorkflowOnButtonClick();
|
||||
workflowPage.actions.executeWorkflow();
|
||||
});
|
||||
|
||||
it('should filter pinned data', () => {
|
||||
workflowPage.getters.canvasNodes().first().dblclick();
|
||||
ndv.actions.close();
|
||||
workflowPage.getters.canvasNodes().first().dblclick();
|
||||
cy.wait(500);
|
||||
ndv.getters.outputDataContainer().should('be.visible');
|
||||
cy.document().trigger('keyup', { key: '/' });
|
||||
|
||||
const searchInput = ndv.getters.searchInput();
|
||||
|
||||
searchInput.filter(':focus').should('exist');
|
||||
ndv.getters.pagination().find('li').should('have.length', 3);
|
||||
cy.get('.highlight').should('not.exist');
|
||||
|
||||
searchInput.type('ar');
|
||||
ndv.getters.pagination().find('li').should('have.length', 2);
|
||||
cy.get('.highlight').its('length').should('be.gt', 0);
|
||||
|
||||
searchInput.type('i');
|
||||
ndv.getters.pagination().should('not.exist');
|
||||
cy.get('.highlight').its('length').should('be.gt', 0);
|
||||
});
|
||||
|
||||
it.only('should filter input/output data separately', () => {
|
||||
workflowPage.getters.canvasNodes().eq(1).dblclick();
|
||||
cy.wait(500);
|
||||
ndv.getters.outputDataContainer().should('be.visible');
|
||||
ndv.getters.inputDataContainer().should('be.visible');
|
||||
ndv.actions.switchInputMode('Table');
|
||||
cy.document().trigger('keyup', { key: '/' });
|
||||
|
||||
ndv.getters.outputPanel().findChildByTestId('ndv-search').filter(':focus').should('not.exist');
|
||||
|
||||
let focusedInput = ndv.getters
|
||||
.inputPanel()
|
||||
.findChildByTestId('ndv-search')
|
||||
.filter(':focus')
|
||||
.should('exist');
|
||||
|
||||
const getInputPagination = () =>
|
||||
ndv.getters.inputPanel().findChildByTestId('ndv-data-pagination');
|
||||
const getInputCounter = () => ndv.getters.inputPanel().findChildByTestId('ndv-items-count');
|
||||
const getOuputPagination = () =>
|
||||
ndv.getters.outputPanel().findChildByTestId('ndv-data-pagination');
|
||||
const getOutputCounter = () => ndv.getters.outputPanel().findChildByTestId('ndv-items-count');
|
||||
|
||||
getInputPagination().find('li').should('have.length', 3);
|
||||
getInputCounter().contains('21 items').should('exist');
|
||||
getOuputPagination().find('li').should('have.length', 3);
|
||||
getOutputCounter().contains('21 items').should('exist');
|
||||
focusedInput.type('ar');
|
||||
|
||||
getInputPagination().find('li').should('have.length', 2);
|
||||
getInputCounter().should('contain', '14 of 21 items');
|
||||
getOuputPagination().find('li').should('have.length', 3);
|
||||
getOutputCounter().should('contain', '21 items');
|
||||
focusedInput.type('i');
|
||||
|
||||
getInputPagination().should('not.exist');
|
||||
getInputCounter().should('contain', '8 of 21 items');
|
||||
getOuputPagination().find('li').should('have.length', 3);
|
||||
getOutputCounter().should('contain', '21 items');
|
||||
|
||||
focusedInput.clear();
|
||||
getInputPagination().find('li').should('have.length', 3);
|
||||
getInputCounter().contains('21 items').should('exist');
|
||||
getOuputPagination().find('li').should('have.length', 3);
|
||||
getOutputCounter().contains('21 items').should('exist');
|
||||
|
||||
ndv.getters.outputDataContainer().trigger('mouseover');
|
||||
cy.document().trigger('keyup', { key: '/' });
|
||||
ndv.getters.inputPanel().findChildByTestId('ndv-search').filter(':focus').should('not.exist');
|
||||
|
||||
focusedInput = ndv.getters
|
||||
.outputPanel()
|
||||
.findChildByTestId('ndv-search')
|
||||
.filter(':focus')
|
||||
.should('exist');
|
||||
|
||||
getInputPagination().find('li').should('have.length', 3);
|
||||
getInputCounter().contains('21 items').should('exist');
|
||||
getOuputPagination().find('li').should('have.length', 3);
|
||||
getOutputCounter().contains('21 items').should('exist');
|
||||
focusedInput.type('ar');
|
||||
|
||||
getInputPagination().find('li').should('have.length', 3);
|
||||
getInputCounter().contains('21 items').should('exist');
|
||||
getOuputPagination().find('li').should('have.length', 2);
|
||||
getOutputCounter().should('contain', '14 of 21 items');
|
||||
focusedInput.type('i');
|
||||
|
||||
getInputPagination().find('li').should('have.length', 3);
|
||||
getInputCounter().contains('21 items').should('exist');
|
||||
getOuputPagination().should('not.exist');
|
||||
getOutputCounter().should('contain', '8 of 21 items');
|
||||
|
||||
focusedInput.clear();
|
||||
getInputPagination().find('li').should('have.length', 3);
|
||||
getInputCounter().contains('21 items').should('exist');
|
||||
getOuputPagination().find('li').should('have.length', 3);
|
||||
getOutputCounter().contains('21 items').should('exist');
|
||||
});
|
||||
});
|
43
cypress/e2e/32-worker-view.cy.ts
Normal file
43
cypress/e2e/32-worker-view.cy.ts
Normal file
|
@ -0,0 +1,43 @@
|
|||
import { INSTANCE_MEMBERS, INSTANCE_OWNER } from '../constants';
|
||||
import { WorkerViewPage } from '../pages';
|
||||
|
||||
const workerViewPage = new WorkerViewPage();
|
||||
|
||||
describe('Worker View (unlicensed)', () => {
|
||||
beforeEach(() => {
|
||||
cy.disableFeature('workerView');
|
||||
cy.disableQueueMode();
|
||||
});
|
||||
|
||||
it('should not show up in the menu sidebar', () => {
|
||||
cy.signin(INSTANCE_MEMBERS[0]);
|
||||
cy.visit(workerViewPage.url);
|
||||
workerViewPage.getters.menuItem().should('not.exist');
|
||||
});
|
||||
|
||||
it('should show action box', () => {
|
||||
cy.signin(INSTANCE_MEMBERS[0]);
|
||||
cy.visit(workerViewPage.url);
|
||||
workerViewPage.getters.workerViewUnlicensed().should('exist');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Worker View (licensed)', () => {
|
||||
beforeEach(() => {
|
||||
cy.enableFeature('workerView');
|
||||
cy.enableQueueMode();
|
||||
});
|
||||
|
||||
it('should show up in the menu sidebar', () => {
|
||||
cy.signin(INSTANCE_OWNER);
|
||||
cy.enableQueueMode();
|
||||
cy.visit(workerViewPage.url);
|
||||
workerViewPage.getters.menuItem().should('exist');
|
||||
});
|
||||
|
||||
it('should show worker list view', () => {
|
||||
cy.signin(INSTANCE_MEMBERS[0]);
|
||||
cy.visit(workerViewPage.url);
|
||||
workerViewPage.getters.workerViewLicensed().should('exist');
|
||||
});
|
||||
});
|
52
cypress/e2e/33-settings-personal.cy.ts
Normal file
52
cypress/e2e/33-settings-personal.cy.ts
Normal file
|
@ -0,0 +1,52 @@
|
|||
import { WorkflowPage } from "../pages";
|
||||
|
||||
const workflowPage = new WorkflowPage();
|
||||
|
||||
const INVALID_NAMES = [
|
||||
'https://n8n.io',
|
||||
'http://n8n.io',
|
||||
'www.n8n.io',
|
||||
'n8n.io',
|
||||
'n8n.бг',
|
||||
'n8n.io/home',
|
||||
'n8n.io/home?send=true',
|
||||
'<a href="#">Jack</a>',
|
||||
'<script>alert("Hello")</script>',
|
||||
];
|
||||
|
||||
const VALID_NAMES = [
|
||||
['a', 'a'],
|
||||
['alice', 'alice'],
|
||||
['Robert', 'Downey Jr.'],
|
||||
['Mia', 'Mia-Downey'],
|
||||
['Mark', "O'neil"],
|
||||
['Thomas', 'Müler'],
|
||||
['ßáçøñ', 'ßáçøñ'],
|
||||
['أحمد', 'فلسطين'],
|
||||
['Милорад', 'Филиповић'],
|
||||
];
|
||||
|
||||
describe('Personal Settings', () => {
|
||||
it ('should allow to change first and last name', () => {
|
||||
cy.visit('/settings/personal');
|
||||
VALID_NAMES.forEach((name) => {
|
||||
cy.getByTestId('personal-data-form').find('input[name="firstName"]').clear().type(name[0]);
|
||||
cy.getByTestId('personal-data-form').find('input[name="lastName"]').clear().type(name[1]);
|
||||
cy.getByTestId('save-settings-button').click();
|
||||
workflowPage.getters.successToast().should('contain', 'Personal details updated');
|
||||
workflowPage.getters.successToast().find('.el-notification__closeBtn').click();
|
||||
});
|
||||
});
|
||||
it('not allow malicious values for personal data', () => {
|
||||
cy.visit('/settings/personal');
|
||||
INVALID_NAMES.forEach((name) => {
|
||||
cy.getByTestId('personal-data-form').find('input[name="firstName"]').clear().type(name);
|
||||
cy.getByTestId('personal-data-form').find('input[name="lastName"]').clear().type(name);
|
||||
cy.getByTestId('save-settings-button').click();
|
||||
workflowPage.getters
|
||||
.errorToast()
|
||||
.should('contain', 'Malicious firstName | Malicious lastName');
|
||||
workflowPage.getters.errorToast().find('.el-notification__closeBtn').click();
|
||||
});
|
||||
});
|
||||
});
|
212
cypress/e2e/34-template-credentials-setup.cy.ts
Normal file
212
cypress/e2e/34-template-credentials-setup.cy.ts
Normal file
|
@ -0,0 +1,212 @@
|
|||
import {
|
||||
clickUseWorkflowButtonByTitle,
|
||||
visitTemplateCollectionPage,
|
||||
testData,
|
||||
} from '../pages/template-collection';
|
||||
import * as templateCredentialsSetupPage from '../pages/template-credential-setup';
|
||||
import { TemplateWorkflowPage } from '../pages/template-workflow';
|
||||
import { WorkflowPage } from '../pages/workflow';
|
||||
import * as formStep from '../composables/setup-template-form-step';
|
||||
import { getSetupWorkflowCredentialsButton } from '../composables/setup-workflow-credentials-button';
|
||||
import * as setupCredsModal from '../composables/modals/workflow-credential-setup-modal';
|
||||
|
||||
const templateWorkflowPage = new TemplateWorkflowPage();
|
||||
const workflowPage = new WorkflowPage();
|
||||
|
||||
const testTemplate = templateCredentialsSetupPage.testData.simpleTemplate;
|
||||
|
||||
// NodeView uses beforeunload listener that will show a browser
|
||||
// native popup, which will block cypress from continuing / exiting.
|
||||
// This prevent the registration of the listener.
|
||||
Cypress.on('window:before:load', (win) => {
|
||||
const origAddEventListener = win.addEventListener;
|
||||
win.addEventListener = (eventName: string, listener: any, opts: any) => {
|
||||
if (eventName === 'beforeunload') {
|
||||
return;
|
||||
}
|
||||
|
||||
return origAddEventListener.call(win, eventName, listener, opts);
|
||||
};
|
||||
});
|
||||
|
||||
describe('Template credentials setup', () => {
|
||||
beforeEach(() => {
|
||||
cy.intercept('GET', `https://api.n8n.io/api/templates/workflows/${testTemplate.id}`, {
|
||||
fixture: testTemplate.fixture,
|
||||
});
|
||||
});
|
||||
|
||||
it('can be opened from template workflow page', () => {
|
||||
templateWorkflowPage.actions.visit(testTemplate.id);
|
||||
templateCredentialsSetupPage.enableTemplateCredentialSetupFeatureFlag();
|
||||
templateWorkflowPage.getters.useTemplateButton().should('be.visible');
|
||||
templateCredentialsSetupPage.enableTemplateCredentialSetupFeatureFlag();
|
||||
templateWorkflowPage.actions.clickUseThisWorkflowButton();
|
||||
|
||||
templateCredentialsSetupPage.getters
|
||||
.title(`Set up 'Promote new Shopify products on Twitter and Telegram' template`)
|
||||
.should('be.visible');
|
||||
});
|
||||
|
||||
it('can be opened from template collection page', () => {
|
||||
visitTemplateCollectionPage(testData.ecommerceStarterPack);
|
||||
templateCredentialsSetupPage.enableTemplateCredentialSetupFeatureFlag();
|
||||
clickUseWorkflowButtonByTitle('Promote new Shopify products on Twitter and Telegram');
|
||||
|
||||
templateCredentialsSetupPage.getters
|
||||
.title(`Set up 'Promote new Shopify products on Twitter and Telegram' template`)
|
||||
.should('be.visible');
|
||||
});
|
||||
|
||||
it('has all the elements on page', () => {
|
||||
templateCredentialsSetupPage.visitTemplateCredentialSetupPage(testTemplate.id);
|
||||
|
||||
templateCredentialsSetupPage.getters
|
||||
.title(`Set up 'Promote new Shopify products on Twitter and Telegram' template`)
|
||||
.should('be.visible');
|
||||
|
||||
templateCredentialsSetupPage.getters
|
||||
.infoCallout()
|
||||
.should(
|
||||
'contain.text',
|
||||
'You need 1x Shopify, 1x X (Formerly Twitter) and 1x Telegram account to setup this template',
|
||||
);
|
||||
|
||||
const expectedAppNames = ['1. Shopify', '2. X (Formerly Twitter)', '3. Telegram'];
|
||||
const expectedAppDescriptions = [
|
||||
'The credential you select will be used in the product created node of the workflow template.',
|
||||
'The credential you select will be used in the Twitter node of the workflow template.',
|
||||
'The credential you select will be used in the Telegram node of the workflow template.',
|
||||
];
|
||||
|
||||
formStep.getFormStep().each(($el, index) => {
|
||||
formStep.getStepHeading($el).should('have.text', expectedAppNames[index]);
|
||||
formStep.getStepDescription($el).should('have.text', expectedAppDescriptions[index]);
|
||||
});
|
||||
});
|
||||
|
||||
it('can skip template creation', () => {
|
||||
templateCredentialsSetupPage.visitTemplateCredentialSetupPage(testTemplate.id);
|
||||
|
||||
templateCredentialsSetupPage.getters.skipLink().click();
|
||||
workflowPage.getters.canvasNodes().should('have.length', 3);
|
||||
});
|
||||
|
||||
it('can create credentials and workflow from the template', () => {
|
||||
templateCredentialsSetupPage.visitTemplateCredentialSetupPage(testTemplate.id);
|
||||
|
||||
// Continue button should be disabled if no credentials are created
|
||||
templateCredentialsSetupPage.getters.continueButton().should('be.disabled');
|
||||
|
||||
templateCredentialsSetupPage.fillInDummyCredentialsForApp('Shopify');
|
||||
|
||||
// Continue button should be enabled if at least one has been created
|
||||
templateCredentialsSetupPage.getters.continueButton().should('be.enabled');
|
||||
|
||||
templateCredentialsSetupPage.fillInDummyCredentialsForAppWithConfirm('X (Formerly Twitter)');
|
||||
templateCredentialsSetupPage.fillInDummyCredentialsForApp('Telegram');
|
||||
|
||||
templateCredentialsSetupPage.finishCredentialSetup();
|
||||
|
||||
workflowPage.getters.canvasNodes().should('have.length', 3);
|
||||
|
||||
// Focus the canvas so the copy to clipboard works
|
||||
workflowPage.getters.canvasNodes().eq(0).realClick();
|
||||
workflowPage.actions.selectAll();
|
||||
workflowPage.actions.hitCopy();
|
||||
|
||||
cy.grantBrowserPermissions('clipboardReadWrite', 'clipboardSanitizedWrite');
|
||||
// Check workflow JSON by copying it to clipboard
|
||||
cy.readClipboard().then((workflowJSON) => {
|
||||
const workflow = JSON.parse(workflowJSON);
|
||||
|
||||
expect(workflow.meta).to.haveOwnProperty('templateId', testTemplate.id.toString());
|
||||
workflow.nodes.forEach((node: any) => {
|
||||
expect(Object.keys(node.credentials ?? {})).to.have.lengthOf(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should work with a template that has no credentials (ADO-1603)', () => {
|
||||
const templateWithoutCreds = templateCredentialsSetupPage.testData.templateWithoutCredentials;
|
||||
cy.intercept('GET', `https://api.n8n.io/api/templates/workflows/${templateWithoutCreds.id}`, {
|
||||
fixture: templateWithoutCreds.fixture,
|
||||
});
|
||||
templateCredentialsSetupPage.visitTemplateCredentialSetupPage(templateWithoutCreds.id);
|
||||
|
||||
const expectedAppNames = ['1. Email (IMAP)', '2. Nextcloud'];
|
||||
const expectedAppDescriptions = [
|
||||
'The credential you select will be used in the IMAP Email node of the workflow template.',
|
||||
'The credential you select will be used in the Nextcloud node of the workflow template.',
|
||||
];
|
||||
|
||||
formStep.getFormStep().each(($el, index) => {
|
||||
formStep.getStepHeading($el).should('have.text', expectedAppNames[index]);
|
||||
formStep.getStepDescription($el).should('have.text', expectedAppDescriptions[index]);
|
||||
});
|
||||
|
||||
templateCredentialsSetupPage.getters.continueButton().should('be.disabled');
|
||||
|
||||
templateCredentialsSetupPage.fillInDummyCredentialsForApp('Email (IMAP)');
|
||||
templateCredentialsSetupPage.fillInDummyCredentialsForApp('Nextcloud');
|
||||
|
||||
templateCredentialsSetupPage.finishCredentialSetup();
|
||||
|
||||
workflowPage.getters.canvasNodes().should('have.length', 3);
|
||||
});
|
||||
|
||||
describe('Credential setup from workflow editor', () => {
|
||||
beforeEach(() => {
|
||||
cy.resetDatabase();
|
||||
cy.signinAsOwner();
|
||||
});
|
||||
|
||||
it('should allow credential setup from workflow editor if user skips it during template setup', () => {
|
||||
templateCredentialsSetupPage.visitTemplateCredentialSetupPage(testTemplate.id);
|
||||
templateCredentialsSetupPage.getters.skipLink().click();
|
||||
|
||||
getSetupWorkflowCredentialsButton().should('be.visible');
|
||||
});
|
||||
|
||||
it('should allow credential setup from workflow editor if user fills in credentials partially during template setup', () => {
|
||||
templateCredentialsSetupPage.visitTemplateCredentialSetupPage(testTemplate.id);
|
||||
templateCredentialsSetupPage.fillInDummyCredentialsForApp('Shopify');
|
||||
|
||||
templateCredentialsSetupPage.finishCredentialSetup();
|
||||
|
||||
getSetupWorkflowCredentialsButton().should('be.visible');
|
||||
});
|
||||
|
||||
it('should fill credentials from workflow editor', () => {
|
||||
templateCredentialsSetupPage.visitTemplateCredentialSetupPage(testTemplate.id);
|
||||
templateCredentialsSetupPage.getters.skipLink().click();
|
||||
|
||||
getSetupWorkflowCredentialsButton().click();
|
||||
setupCredsModal.getWorkflowCredentialsModal().should('be.visible');
|
||||
|
||||
templateCredentialsSetupPage.fillInDummyCredentialsForApp('Shopify');
|
||||
templateCredentialsSetupPage.fillInDummyCredentialsForAppWithConfirm('X (Formerly Twitter)');
|
||||
templateCredentialsSetupPage.fillInDummyCredentialsForApp('Telegram');
|
||||
|
||||
setupCredsModal.closeModalFromContinueButton();
|
||||
setupCredsModal.getWorkflowCredentialsModal().should('not.exist');
|
||||
|
||||
// Focus the canvas so the copy to clipboard works
|
||||
workflowPage.getters.canvasNodes().eq(0).realClick();
|
||||
workflowPage.actions.selectAll();
|
||||
workflowPage.actions.hitCopy();
|
||||
|
||||
cy.grantBrowserPermissions('clipboardReadWrite', 'clipboardSanitizedWrite');
|
||||
// Check workflow JSON by copying it to clipboard
|
||||
cy.readClipboard().then((workflowJSON) => {
|
||||
const workflow = JSON.parse(workflowJSON);
|
||||
|
||||
workflow.nodes.forEach((node: any) => {
|
||||
expect(Object.keys(node.credentials ?? {})).to.have.lengthOf(1);
|
||||
});
|
||||
});
|
||||
|
||||
getSetupWorkflowCredentialsButton().should('not.exist');
|
||||
});
|
||||
});
|
||||
});
|
23
cypress/e2e/35-admin-user-smoke-test.cy.ts
Normal file
23
cypress/e2e/35-admin-user-smoke-test.cy.ts
Normal file
|
@ -0,0 +1,23 @@
|
|||
import { INSTANCE_ADMIN, INSTANCE_OWNER } from '../constants';
|
||||
import { SettingsPage } from '../pages/settings';
|
||||
|
||||
const settingsPage = new SettingsPage();
|
||||
|
||||
describe('Admin user', { disableAutoLogin: true }, () => {
|
||||
it('should see same Settings sub menu items as instance owner', () => {
|
||||
cy.signin(INSTANCE_OWNER);
|
||||
cy.visit(settingsPage.url);
|
||||
|
||||
let ownerMenuItems = 0;
|
||||
|
||||
settingsPage.getters.menuItems().then(($el) => {
|
||||
ownerMenuItems = $el.length;
|
||||
});
|
||||
|
||||
cy.signout();
|
||||
cy.signin(INSTANCE_ADMIN);
|
||||
cy.visit(settingsPage.url);
|
||||
|
||||
settingsPage.getters.menuItems().should('have.length', ownerMenuItems);
|
||||
});
|
||||
});
|
143
cypress/e2e/36-suggested-templates.cy.ts
Normal file
143
cypress/e2e/36-suggested-templates.cy.ts
Normal file
|
@ -0,0 +1,143 @@
|
|||
import { WorkflowsPage as WorkflowsPageClass } from '../pages/workflows';
|
||||
import { WorkflowPage as WorkflowPageClass } from '../pages/workflow';
|
||||
|
||||
type SuggestedTemplatesStub = {
|
||||
sections: SuggestedTemplatesSectionStub[];
|
||||
}
|
||||
|
||||
type SuggestedTemplatesSectionStub = {
|
||||
name: string;
|
||||
title: string;
|
||||
description: string;
|
||||
workflows: Array<Object>;
|
||||
};
|
||||
|
||||
const WorkflowsListPage = new WorkflowsPageClass();
|
||||
const WorkflowPage = new WorkflowPageClass();
|
||||
|
||||
let fixtureSections: SuggestedTemplatesStub = { sections: [] };;
|
||||
|
||||
describe('Suggested templates - Should render', () => {
|
||||
|
||||
before(() => {
|
||||
cy.fixture('Suggested_Templates.json').then((data) => {
|
||||
fixtureSections = data;
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
localStorage.removeItem('SHOW_N8N_SUGGESTED_TEMPLATES');
|
||||
cy.intercept('GET', '/rest/settings', (req) => {
|
||||
req.on('response', (res) => {
|
||||
res.send({
|
||||
data: { ...res.body.data, deployment: { type: 'cloud' } },
|
||||
});
|
||||
});
|
||||
}).as('loadSettings');
|
||||
cy.intercept('GET', '/rest/cloud/proxy/templates', {
|
||||
fixture: 'Suggested_Templates.json',
|
||||
});
|
||||
cy.visit(WorkflowsListPage.url);
|
||||
cy.wait('@loadSettings');
|
||||
});
|
||||
|
||||
it('should render suggested templates page in empty workflow list', () => {
|
||||
WorkflowsListPage.getters.suggestedTemplatesPageContainer().should('exist');
|
||||
WorkflowsListPage.getters.suggestedTemplatesCards().should('have.length', fixtureSections.sections[0].workflows.length);
|
||||
WorkflowsListPage.getters.suggestedTemplatesSectionDescription().should('contain', fixtureSections.sections[0].description);
|
||||
});
|
||||
|
||||
it('should render suggested templates when there are workflows in the list', () => {
|
||||
WorkflowsListPage.getters.suggestedTemplatesNewWorkflowButton().click();
|
||||
cy.createFixtureWorkflow('Test_workflow_1.json', 'Test Workflow');
|
||||
cy.visit(WorkflowsListPage.url);
|
||||
WorkflowsListPage.getters.suggestedTemplatesSectionContainer().should('exist');
|
||||
cy.contains(`Explore ${fixtureSections.sections[0].name.toLocaleLowerCase()} workflow templates`).should('exist');
|
||||
WorkflowsListPage.getters.suggestedTemplatesCards().should('have.length', fixtureSections.sections[0].workflows.length);
|
||||
});
|
||||
|
||||
it('should enable users to signup for suggested templates templates', () => {
|
||||
// Test the whole flow
|
||||
WorkflowsListPage.getters.suggestedTemplatesCards().first().click();
|
||||
WorkflowsListPage.getters.suggestedTemplatesPreviewModal().should('exist');
|
||||
WorkflowsListPage.getters.suggestedTemplatesUseTemplateButton().click();
|
||||
cy.url().should('include', '/workflow/new');
|
||||
WorkflowPage.getters.infoToast().should('contain', 'Template coming soon!');
|
||||
WorkflowPage.getters.infoToast().contains('Notify me when it\'s available').click();
|
||||
WorkflowPage.getters.successToast().should('contain', 'We will contact you via email once this template is released.');
|
||||
cy.visit(WorkflowsListPage.url);
|
||||
// Once users have signed up for a template, suggestions should not be shown again
|
||||
WorkflowsListPage.getters.suggestedTemplatesSectionContainer().should('not.exist');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('Suggested templates - Should not render', () => {
|
||||
beforeEach(() => {
|
||||
localStorage.removeItem('SHOW_N8N_SUGGESTED_TEMPLATES');
|
||||
cy.visit(WorkflowsListPage.url);
|
||||
});
|
||||
|
||||
it('should not render suggested templates templates if not in cloud deployment', () => {
|
||||
cy.intercept('GET', '/rest/settings', (req) => {
|
||||
req.on('response', (res) => {
|
||||
res.send({
|
||||
data: { ...res.body.data, deployment: { type: 'notCloud' } },
|
||||
});
|
||||
});
|
||||
});
|
||||
WorkflowsListPage.getters.suggestedTemplatesPageContainer().should('not.exist');
|
||||
WorkflowsListPage.getters.suggestedTemplatesSectionContainer().should('not.exist');
|
||||
});
|
||||
|
||||
it('should not render suggested templates templates if endpoint throws error', () => {
|
||||
cy.intercept('GET', '/rest/settings', (req) => {
|
||||
req.on('response', (res) => {
|
||||
res.send({
|
||||
data: { ...res.body.data, deployment: { type: 'cloud' } },
|
||||
});
|
||||
});
|
||||
});
|
||||
cy.intercept('GET', '/rest/cloud/proxy/templates', { statusCode: 500 }).as('loadTemplates');
|
||||
WorkflowsListPage.getters.suggestedTemplatesPageContainer().should('not.exist');
|
||||
WorkflowsListPage.getters.suggestedTemplatesSectionContainer().should('not.exist');
|
||||
});
|
||||
|
||||
it('should not render suggested templates templates if endpoint returns empty list', () => {
|
||||
cy.intercept('GET', '/rest/settings', (req) => {
|
||||
req.on('response', (res) => {
|
||||
res.send({
|
||||
data: { ...res.body.data, deployment: { type: 'cloud' } },
|
||||
});
|
||||
});
|
||||
});
|
||||
cy.intercept('GET', '/rest/cloud/proxy/templates', (req) => {
|
||||
req.on('response', (res) => {
|
||||
res.send({
|
||||
data: { collections: [] },
|
||||
});
|
||||
});
|
||||
});
|
||||
WorkflowsListPage.getters.suggestedTemplatesPageContainer().should('not.exist');
|
||||
WorkflowsListPage.getters.suggestedTemplatesSectionContainer().should('not.exist');
|
||||
});
|
||||
|
||||
it('should not render suggested templates templates if endpoint returns invalid response', () => {
|
||||
cy.intercept('GET', '/rest/settings', (req) => {
|
||||
req.on('response', (res) => {
|
||||
res.send({
|
||||
data: { ...res.body.data, deployment: { type: 'cloud' } },
|
||||
});
|
||||
});
|
||||
});
|
||||
cy.intercept('GET', '/rest/cloud/proxy/templates', (req) => {
|
||||
req.on('response', (res) => {
|
||||
res.send({
|
||||
data: { somethingElse: [] },
|
||||
});
|
||||
});
|
||||
});
|
||||
WorkflowsListPage.getters.suggestedTemplatesPageContainer().should('not.exist');
|
||||
WorkflowsListPage.getters.suggestedTemplatesSectionContainer().should('not.exist');
|
||||
});
|
||||
});
|
66
cypress/e2e/36-versions.cy.ts
Normal file
66
cypress/e2e/36-versions.cy.ts
Normal file
|
@ -0,0 +1,66 @@
|
|||
import { INSTANCE_OWNER } from '../constants';
|
||||
import { WorkflowsPage } from '../pages/workflows';
|
||||
import {
|
||||
closeVersionUpdatesPanel,
|
||||
getVersionCard,
|
||||
getVersionUpdatesPanelOpenButton,
|
||||
openVersionUpdatesPanel,
|
||||
} from '../composables/versions';
|
||||
|
||||
const workflowsPage = new WorkflowsPage();
|
||||
|
||||
describe('Versions', () => {
|
||||
it('should open updates panel', () => {
|
||||
cy.intercept('GET', '/rest/settings', (req) => {
|
||||
req.continue((res) => {
|
||||
if (res.body.hasOwnProperty('data')) {
|
||||
res.body.data = {
|
||||
...res.body.data,
|
||||
releaseChannel: 'stable',
|
||||
versionCli: '1.0.0',
|
||||
versionNotifications: {
|
||||
enabled: true,
|
||||
endpoint: 'https://api.n8n.io/api/versions/',
|
||||
infoUrl: 'https://docs.n8n.io/getting-started/installation/updating.html',
|
||||
},
|
||||
};
|
||||
}
|
||||
});
|
||||
}).as('settings');
|
||||
|
||||
cy.intercept('GET', 'https://api.n8n.io/api/versions/1.0.0', [
|
||||
{
|
||||
name: '1.3.1',
|
||||
createdAt: '2023-08-18T11:53:12.857Z',
|
||||
hasSecurityIssue: null,
|
||||
hasSecurityFix: null,
|
||||
securityIssueFixVersion: null,
|
||||
hasBreakingChange: null,
|
||||
documentationUrl: 'https://docs.n8n.io/release-notes/#n8n131',
|
||||
nodes: [],
|
||||
description: 'Includes <strong>bug fixes</strong>',
|
||||
},
|
||||
{
|
||||
name: '1.0.5',
|
||||
createdAt: '2023-07-24T10:54:56.097Z',
|
||||
hasSecurityIssue: false,
|
||||
hasSecurityFix: null,
|
||||
securityIssueFixVersion: null,
|
||||
hasBreakingChange: true,
|
||||
documentationUrl: 'https://docs.n8n.io/release-notes/#n8n104',
|
||||
nodes: [],
|
||||
description: 'Includes <strong>core functionality</strong> and <strong>bug fixes</strong>',
|
||||
},
|
||||
]);
|
||||
|
||||
cy.signin(INSTANCE_OWNER);
|
||||
|
||||
cy.visit(workflowsPage.url);
|
||||
cy.wait('@settings');
|
||||
|
||||
getVersionUpdatesPanelOpenButton().should('contain', '2 updates');
|
||||
openVersionUpdatesPanel();
|
||||
getVersionCard().should('have.length', 2);
|
||||
closeVersionUpdatesPanel();
|
||||
});
|
||||
});
|
|
@ -2,6 +2,7 @@ import { NodeCreator } from '../pages/features/node-creator';
|
|||
import { WorkflowPage as WorkflowPageClass } from '../pages/workflow';
|
||||
import { NDV } from '../pages/ndv';
|
||||
import { getVisibleSelect } from '../utils';
|
||||
import { IF_NODE_NAME } from '../constants';
|
||||
|
||||
const nodeCreatorFeature = new NodeCreator();
|
||||
const WorkflowPage = new WorkflowPageClass();
|
||||
|
@ -34,7 +35,7 @@ describe('Node Creator', () => {
|
|||
nodeCreatorFeature.actions.openNodeCreator();
|
||||
|
||||
nodeCreatorFeature.getters.searchBar().find('input').type('manual');
|
||||
nodeCreatorFeature.getters.creatorItem().should('have.length', 1);
|
||||
nodeCreatorFeature.getters.creatorItem().should('have.length', 2);
|
||||
nodeCreatorFeature.getters.searchBar().find('input').clear().type('manual123');
|
||||
nodeCreatorFeature.getters.creatorItem().should('have.length', 0);
|
||||
nodeCreatorFeature.getters
|
||||
|
@ -101,15 +102,15 @@ describe('Node Creator', () => {
|
|||
nodeCreatorFeature.getters.searchBar().find('input').type('{rightarrow}');
|
||||
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}{rightarrow}');
|
||||
// The 1st trigger is selected, up 1x to the collapsable header, up 2x to the last action (rename)
|
||||
nodeCreatorFeature.getters.searchBar().find('input').type('{uparrow}{uparrow}{rightarrow}');
|
||||
NDVModal.getters.parameterInput('operation').find('input').should('have.value', 'Rename');
|
||||
});
|
||||
|
||||
it('should not show actions for single action nodes', () => {
|
||||
const singleActionNodes = [
|
||||
'DHL',
|
||||
'iCalendar',
|
||||
'Edit Fields',
|
||||
'LingvaNex',
|
||||
'Mailcheck',
|
||||
'MSG91',
|
||||
|
@ -307,7 +308,7 @@ 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.actions.deleteNode('When clicking "Test Workflow"');
|
||||
WorkflowPage.getters.canvasNodePlusEndpointByName('n8n').click();
|
||||
nodeCreatorFeature.getters.searchBar().find('input').clear().type('n8n');
|
||||
nodeCreatorFeature.getters.getCreatorItem('n8n').click();
|
||||
|
@ -316,7 +317,7 @@ 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', 'Summarize');
|
||||
WorkflowPage.getters.canvasNodes().should('have.length', 3);
|
||||
});
|
||||
});
|
||||
|
@ -360,7 +361,7 @@ describe('Node Creator', () => {
|
|||
nodeCreatorFeature.getters.nodeItemName().first().should('have.text', 'Edit Fields (Set)');
|
||||
|
||||
nodeCreatorFeature.getters.searchBar().find('input').clear().type('i');
|
||||
nodeCreatorFeature.getters.nodeItemName().first().should('have.text', 'IF');
|
||||
nodeCreatorFeature.getters.nodeItemName().first().should('have.text', IF_NODE_NAME);
|
||||
nodeCreatorFeature.getters.nodeItemName().eq(1).should('have.text', 'Switch');
|
||||
|
||||
nodeCreatorFeature.getters.searchBar().find('input').clear().type('sw');
|
||||
|
@ -368,11 +369,11 @@ describe('Node Creator', () => {
|
|||
nodeCreatorFeature.getters.nodeItemName().first().should('have.text', 'Edit Fields (Set)');
|
||||
|
||||
nodeCreatorFeature.getters.searchBar().find('input').clear().type('i');
|
||||
nodeCreatorFeature.getters.nodeItemName().first().should('have.text', 'IF');
|
||||
nodeCreatorFeature.getters.nodeItemName().first().should('have.text', IF_NODE_NAME);
|
||||
nodeCreatorFeature.getters.nodeItemName().eq(1).should('have.text', 'Switch');
|
||||
|
||||
nodeCreatorFeature.getters.searchBar().find('input').clear().type('IF');
|
||||
nodeCreatorFeature.getters.nodeItemName().first().should('have.text', 'IF');
|
||||
nodeCreatorFeature.getters.nodeItemName().first().should('have.text', IF_NODE_NAME);
|
||||
nodeCreatorFeature.getters.nodeItemName().eq(1).should('have.text', 'Switch');
|
||||
|
||||
nodeCreatorFeature.getters.searchBar().find('input').clear().type('sw');
|
||||
|
@ -410,7 +411,7 @@ describe('Node Creator', () => {
|
|||
|
||||
nodeCreatorFeature.getters.searchBar().find('input').clear().type('js');
|
||||
nodeCreatorFeature.getters.nodeItemName().first().should('have.text', 'Code');
|
||||
nodeCreatorFeature.getters.nodeItemName().eq(1).should('have.text', 'Item Lists');
|
||||
nodeCreatorFeature.getters.nodeItemName().eq(1).should('have.text', 'Edit Fields (Set)');
|
||||
|
||||
nodeCreatorFeature.getters.searchBar().find('input').clear().type('fi');
|
||||
nodeCreatorFeature.getters.nodeItemName().first().should('have.text', 'Filter');
|
||||
|
@ -478,15 +479,14 @@ describe('Node Creator', () => {
|
|||
|
||||
nodeCreatorFeature.getters.searchBar().find('input').clear().type('wa');
|
||||
nodeCreatorFeature.getters.nodeItemName().first().should('have.text', 'Wait');
|
||||
nodeCreatorFeature.getters.nodeItemName().eq(1).should('have.text', 'Merge');
|
||||
|
||||
nodeCreatorFeature.getters.searchBar().find('input').clear().type('wait');
|
||||
nodeCreatorFeature.getters.nodeItemName().first().should('have.text', 'Wait');
|
||||
nodeCreatorFeature.getters.nodeItemName().eq(1).should('have.text', 'Merge');
|
||||
|
||||
nodeCreatorFeature.getters.searchBar().find('input').clear().type('spreadsheet');
|
||||
nodeCreatorFeature.getters.nodeItemName().first().should('have.text', 'Spreadsheet File');
|
||||
nodeCreatorFeature.getters.nodeItemName().eq(1).should('have.text', 'Google Sheets');
|
||||
nodeCreatorFeature.getters.nodeItemName().first().should('have.text', 'Convert to File');
|
||||
nodeCreatorFeature.getters.nodeItemName().eq(1).should('have.text', 'Extract From File');
|
||||
nodeCreatorFeature.getters.nodeItemName().eq(2).should('have.text', 'Google Sheets');
|
||||
|
||||
nodeCreatorFeature.getters.searchBar().find('input').clear().type('sheets');
|
||||
nodeCreatorFeature.getters.nodeItemName().first().should('have.text', 'Google Sheets');
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { WorkflowPage, NDV } from '../pages';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import { getPopper, getVisiblePopper, getVisibleSelect } from '../utils';
|
||||
import { META_KEY } from '../constants';
|
||||
import { getVisibleSelect } from '../utils';
|
||||
import { MANUAL_TRIGGER_NODE_DISPLAY_NAME } from '../constants';
|
||||
import { NDV, WorkflowPage } from '../pages';
|
||||
|
||||
const workflowPage = new WorkflowPage();
|
||||
const ndv = new NDV();
|
||||
|
@ -42,7 +42,7 @@ describe('NDV', () => {
|
|||
ndv.getters.outputDisplayMode().should('have.length.at.least', 1).and('be.visible');
|
||||
});
|
||||
|
||||
it('should change input', () => {
|
||||
it('should change input and go back to canvas', () => {
|
||||
cy.createFixtureWorkflow('NDV-test-select-input.json', `NDV test select input ${uuid()}`);
|
||||
workflowPage.actions.zoomToFit();
|
||||
workflowPage.getters.canvasNodes().last().dblclick();
|
||||
|
@ -50,6 +50,9 @@ describe('NDV', () => {
|
|||
ndv.getters.inputOption().last().click();
|
||||
ndv.getters.inputDataContainer().find('[class*=schema_]').should('exist');
|
||||
ndv.getters.inputDataContainer().should('contain', 'start');
|
||||
ndv.getters.backToCanvas().click();
|
||||
ndv.getters.container().should('not.be.visible');
|
||||
cy.shouldNotHaveConsoleErrors();
|
||||
});
|
||||
|
||||
it('should show correct validation state for resource locator params', () => {
|
||||
|
@ -68,10 +71,10 @@ describe('NDV', () => {
|
|||
workflowPage.actions.addNodeToCanvas('Manual');
|
||||
workflowPage.actions.addNodeToCanvas('Airtable', true, true, 'Search records');
|
||||
ndv.getters.container().should('be.visible');
|
||||
// cy.get('.has-issues').should('have.length', 0);
|
||||
cy.get('.has-issues').should('have.length', 0);
|
||||
ndv.getters.parameterInput('table').find('input').eq(1).focus().blur();
|
||||
ndv.getters.parameterInput('base').find('input').eq(1).focus().blur();
|
||||
cy.get('.has-issues').should('have.length', 0);
|
||||
cy.get('.has-issues').should('have.length', 2);
|
||||
ndv.getters.backToCanvas().click();
|
||||
workflowPage.actions.openNode('Airtable');
|
||||
cy.get('.has-issues').should('have.length', 2);
|
||||
|
@ -299,11 +302,11 @@ describe('NDV', () => {
|
|||
|
||||
ndv.actions.setInvalidExpression({ fieldName: 'fieldId', delay: 200 });
|
||||
|
||||
ndv.getters.container().click(); // remove focus from input, hide expression preview
|
||||
ndv.getters.nodeParameters().click(); // remove focus from input, hide expression preview
|
||||
|
||||
ndv.getters.parameterInput('remoteOptions').click();
|
||||
|
||||
ndv.getters.parameterInputIssues('remoteOptions').realHover();
|
||||
ndv.getters.parameterInputIssues('remoteOptions').realHover({ scrollBehavior: false});
|
||||
// Remote options dropdown should not be visible
|
||||
ndv.getters.parameterInput('remoteOptions').find('.el-select').should('not.exist');
|
||||
});
|
||||
|
@ -317,7 +320,7 @@ describe('NDV', () => {
|
|||
|
||||
ndv.actions.setInvalidExpression({ fieldName: 'otherField', delay: 50 });
|
||||
|
||||
ndv.getters.container().click(); // remove focus from input, hide expression preview
|
||||
ndv.getters.nodeParameters().click(); // remove focus from input, hide expression preview
|
||||
|
||||
ndv.getters.parameterInput('remoteOptions').click();
|
||||
getVisibleSelect().find('.el-select-dropdown__item').should('have.length', 3);
|
||||
|
@ -357,12 +360,253 @@ describe('NDV', () => {
|
|||
ndv.getters.nodeExecuteButton().should('be.visible');
|
||||
});
|
||||
|
||||
it('should allow editing code in fullscreen in the Code node', () => {
|
||||
workflowPage.actions.addInitialNodeToCanvas('Code', { keepNdvOpen: true });
|
||||
ndv.actions.openCodeEditorFullscreen();
|
||||
|
||||
ndv.getters.codeEditorFullscreen().type('{selectall}').type('{backspace}').type('foo()');
|
||||
ndv.getters.codeEditorFullscreen().should('contain.text', 'foo()');
|
||||
cy.wait(200);
|
||||
ndv.getters.codeEditorDialog().find('.el-dialog__close').click();
|
||||
ndv.getters.parameterInput('jsCode').get('.cm-content').should('contain.text', 'foo()');
|
||||
});
|
||||
|
||||
it('should not retrieve remote options when a parameter value changes', () => {
|
||||
cy.intercept('/rest/node-parameter-options?**', cy.spy().as('fetchParameterOptions'));
|
||||
cy.intercept('/rest/dynamic-node-parameters/options?**', cy.spy().as('fetchParameterOptions'));
|
||||
workflowPage.actions.addInitialNodeToCanvas('E2e Test', { action: 'Remote Options' });
|
||||
// Type something into the field
|
||||
ndv.actions.typeIntoParameterInput('otherField', 'test');
|
||||
// Should call the endpoint only once (on mount), not for every keystroke
|
||||
cy.get('@fetchParameterOptions').should('have.been.calledOnce');
|
||||
});
|
||||
|
||||
describe('floating nodes', () => {
|
||||
function getFloatingNodeByPosition(
|
||||
position: 'inputMain' | 'outputMain' | 'outputSub' | 'inputSub',
|
||||
) {
|
||||
return cy.get(`[data-node-placement=${position}]`);
|
||||
}
|
||||
beforeEach(() => {
|
||||
cy.createFixtureWorkflow('Floating_Nodes.json', `Floating Nodes`);
|
||||
workflowPage.getters.canvasNodes().first().dblclick();
|
||||
getFloatingNodeByPosition('inputMain').should('not.exist');
|
||||
getFloatingNodeByPosition('outputMain').should('exist');
|
||||
});
|
||||
|
||||
it('should traverse floating nodes with mouse', () => {
|
||||
// Traverse 4 connected node forwards
|
||||
Array.from(Array(4).keys()).forEach((i) => {
|
||||
getFloatingNodeByPosition('outputMain').click({ force: true });
|
||||
ndv.getters.nodeNameContainer().should('contain', `Node ${i + 1}`);
|
||||
getFloatingNodeByPosition('inputMain').should('exist');
|
||||
getFloatingNodeByPosition('outputMain').should('exist');
|
||||
ndv.actions.close();
|
||||
workflowPage.getters.selectedNodes().should('have.length', 1);
|
||||
workflowPage.getters
|
||||
.selectedNodes()
|
||||
.first()
|
||||
.should('contain', `Node ${i + 1}`);
|
||||
workflowPage.getters.selectedNodes().first().dblclick();
|
||||
});
|
||||
|
||||
getFloatingNodeByPosition('outputMain').click({ force: true });
|
||||
ndv.getters.nodeNameContainer().should('contain', 'Chain');
|
||||
getFloatingNodeByPosition('inputSub').should('exist');
|
||||
getFloatingNodeByPosition('inputSub').click({ force: true });
|
||||
ndv.getters.nodeNameContainer().should('contain', 'Model');
|
||||
getFloatingNodeByPosition('inputSub').should('not.exist');
|
||||
getFloatingNodeByPosition('inputMain').should('not.exist');
|
||||
getFloatingNodeByPosition('outputMain').should('not.exist');
|
||||
getFloatingNodeByPosition('outputSub').should('exist');
|
||||
ndv.actions.close();
|
||||
workflowPage.getters.selectedNodes().should('have.length', 1);
|
||||
workflowPage.getters.selectedNodes().first().should('contain', 'Model');
|
||||
workflowPage.getters.selectedNodes().first().dblclick();
|
||||
getFloatingNodeByPosition('outputSub').click({ force: true });
|
||||
ndv.getters.nodeNameContainer().should('contain', 'Chain');
|
||||
|
||||
// Traverse 4 connected node backwards
|
||||
Array.from(Array(4).keys()).forEach((i) => {
|
||||
getFloatingNodeByPosition('inputMain').click({ force: true });
|
||||
ndv.getters.nodeNameContainer().should('contain', `Node ${4 - i}`);
|
||||
getFloatingNodeByPosition('outputMain').should('exist');
|
||||
getFloatingNodeByPosition('inputMain').should('exist');
|
||||
});
|
||||
getFloatingNodeByPosition('inputMain').click({ force: true });
|
||||
workflowPage.getters
|
||||
.selectedNodes()
|
||||
.first()
|
||||
.should('contain', MANUAL_TRIGGER_NODE_DISPLAY_NAME);
|
||||
getFloatingNodeByPosition('inputMain').should('not.exist');
|
||||
getFloatingNodeByPosition('inputSub').should('not.exist');
|
||||
getFloatingNodeByPosition('outputSub').should('not.exist');
|
||||
ndv.actions.close();
|
||||
workflowPage.getters.selectedNodes().should('have.length', 1);
|
||||
workflowPage.getters
|
||||
.selectedNodes()
|
||||
.first()
|
||||
.should('contain', MANUAL_TRIGGER_NODE_DISPLAY_NAME);
|
||||
});
|
||||
|
||||
it('should traverse floating nodes with mouse', () => {
|
||||
// Traverse 4 connected node forwards
|
||||
Array.from(Array(4).keys()).forEach((i) => {
|
||||
cy.realPress(['ShiftLeft', 'Meta', 'AltLeft', 'ArrowRight']);
|
||||
ndv.getters.nodeNameContainer().should('contain', `Node ${i + 1}`);
|
||||
getFloatingNodeByPosition('inputMain').should('exist');
|
||||
getFloatingNodeByPosition('outputMain').should('exist');
|
||||
ndv.actions.close();
|
||||
workflowPage.getters.selectedNodes().should('have.length', 1);
|
||||
workflowPage.getters
|
||||
.selectedNodes()
|
||||
.first()
|
||||
.should('contain', `Node ${i + 1}`);
|
||||
workflowPage.getters.selectedNodes().first().dblclick();
|
||||
});
|
||||
|
||||
cy.realPress(['ShiftLeft', 'Meta', 'AltLeft', 'ArrowRight']);
|
||||
ndv.getters.nodeNameContainer().should('contain', 'Chain');
|
||||
getFloatingNodeByPosition('inputSub').should('exist');
|
||||
cy.realPress(['ShiftLeft', 'Meta', 'AltLeft', 'ArrowDown']);
|
||||
ndv.getters.nodeNameContainer().should('contain', 'Model');
|
||||
getFloatingNodeByPosition('inputSub').should('not.exist');
|
||||
getFloatingNodeByPosition('inputMain').should('not.exist');
|
||||
getFloatingNodeByPosition('outputMain').should('not.exist');
|
||||
getFloatingNodeByPosition('outputSub').should('exist');
|
||||
ndv.actions.close();
|
||||
workflowPage.getters.selectedNodes().should('have.length', 1);
|
||||
workflowPage.getters.selectedNodes().first().should('contain', 'Model');
|
||||
workflowPage.getters.selectedNodes().first().dblclick();
|
||||
cy.realPress(['ShiftLeft', 'Meta', 'AltLeft', 'ArrowUp']);
|
||||
ndv.getters.nodeNameContainer().should('contain', 'Chain');
|
||||
|
||||
// Traverse 4 connected node backwards
|
||||
Array.from(Array(4).keys()).forEach((i) => {
|
||||
cy.realPress(['ShiftLeft', 'Meta', 'AltLeft', 'ArrowLeft']);
|
||||
ndv.getters.nodeNameContainer().should('contain', `Node ${4 - i}`);
|
||||
getFloatingNodeByPosition('outputMain').should('exist');
|
||||
getFloatingNodeByPosition('inputMain').should('exist');
|
||||
});
|
||||
cy.realPress(['ShiftLeft', 'Meta', 'AltLeft', 'ArrowLeft']);
|
||||
workflowPage.getters
|
||||
.selectedNodes()
|
||||
.first()
|
||||
.should('contain', MANUAL_TRIGGER_NODE_DISPLAY_NAME);
|
||||
getFloatingNodeByPosition('inputMain').should('not.exist');
|
||||
getFloatingNodeByPosition('inputSub').should('not.exist');
|
||||
getFloatingNodeByPosition('outputSub').should('not.exist');
|
||||
ndv.actions.close();
|
||||
workflowPage.getters.selectedNodes().should('have.length', 1);
|
||||
workflowPage.getters
|
||||
.selectedNodes()
|
||||
.first()
|
||||
.should('contain', MANUAL_TRIGGER_NODE_DISPLAY_NAME);
|
||||
});
|
||||
});
|
||||
|
||||
it('should show node name and version in settings', () => {
|
||||
cy.createFixtureWorkflow('Test_workflow_ndv_version.json', `NDV test version ${uuid()}`);
|
||||
|
||||
workflowPage.actions.openNode('Edit Fields (old)');
|
||||
ndv.actions.openSettings();
|
||||
ndv.getters.nodeVersion().should('have.text', 'Set node version 2 (Latest version: 3.2)');
|
||||
ndv.actions.close();
|
||||
|
||||
workflowPage.actions.openNode('Edit Fields (latest)');
|
||||
ndv.actions.openSettings();
|
||||
ndv.getters.nodeVersion().should('have.text', 'Edit Fields (Set) node version 3.2 (Latest)');
|
||||
ndv.actions.close();
|
||||
|
||||
workflowPage.actions.openNode('Function');
|
||||
ndv.actions.openSettings();
|
||||
ndv.getters.nodeVersion().should('have.text', 'Function node version 1 (Deprecated)');
|
||||
ndv.actions.close();
|
||||
});
|
||||
|
||||
it('Should render xml and html tags as strings and can search', () => {
|
||||
cy.createFixtureWorkflow('Test_workflow_xml_output.json', `test`);
|
||||
|
||||
workflowPage.actions.executeWorkflow();
|
||||
|
||||
workflowPage.actions.openNode('Edit Fields');
|
||||
|
||||
ndv.getters.outputDisplayMode().find('[class*=active]').should('contain', 'Table');
|
||||
|
||||
ndv.getters
|
||||
.outputTableRow(1)
|
||||
.should('include.text', '<?xml version="1.0" encoding="UTF-8"?> <library>');
|
||||
|
||||
cy.document().trigger('keyup', { key: '/' });
|
||||
ndv.getters.searchInput().filter(':focus').type('<lib');
|
||||
|
||||
ndv.getters.outputTableRow(1).find('mark').should('have.text', '<lib');
|
||||
|
||||
ndv.getters.outputDisplayMode().find('label').eq(1).should('include.text', 'JSON');
|
||||
ndv.getters.outputDisplayMode().find('label').eq(1).click();
|
||||
|
||||
ndv.getters.outputDataContainer().find('.json-data').should('exist');
|
||||
ndv.getters
|
||||
.outputDataContainer()
|
||||
.should(
|
||||
'have.text',
|
||||
'[{"body": "<?xml version="1.0" encoding="UTF-8"?> <library> <book> <title>Introduction to XML</title> <author>John Doe</author> <publication_year>2020</publication_year> <isbn>1234567890</isbn> </book> <book> <title>Data Science Basics</title> <author>Jane Smith</author> <publication_year>2019</publication_year> <isbn>0987654321</isbn> </book> <book> <title>Programming in Python</title> <author>Bob Johnson</author> <publication_year>2021</publication_year> <isbn>5432109876</isbn> </book> </library>"}]',
|
||||
);
|
||||
ndv.getters.outputDataContainer().find('mark').should('have.text', '<lib');
|
||||
|
||||
ndv.getters.outputDisplayMode().find('label').eq(2).should('include.text', 'Schema');
|
||||
ndv.getters.outputDisplayMode().find('label').eq(2).click({ force: true });
|
||||
ndv.getters
|
||||
.outputDataContainer()
|
||||
.findChildByTestId('run-data-schema-item')
|
||||
.find('> span')
|
||||
.should('include.text', '<?xml version="1.0" encoding="UTF-8"?>');
|
||||
});
|
||||
|
||||
it('should properly show node execution indicator', () => {
|
||||
workflowPage.actions.addInitialNodeToCanvas('Code');
|
||||
workflowPage.actions.openNode('Code');
|
||||
// Should not show run info before execution
|
||||
ndv.getters.nodeRunSuccessIndicator().should('not.exist');
|
||||
ndv.getters.nodeRunErrorIndicator().should('not.exist');
|
||||
ndv.getters.nodeExecuteButton().click();
|
||||
ndv.getters.nodeRunSuccessIndicator().should('exist');
|
||||
});
|
||||
|
||||
it('should properly show node execution indicator for multiple nodes', () => {
|
||||
workflowPage.actions.addInitialNodeToCanvas('Code');
|
||||
workflowPage.actions.openNode('Code');
|
||||
ndv.actions.typeIntoParameterInput('jsCode', 'testets');
|
||||
ndv.getters.backToCanvas().click();
|
||||
workflowPage.actions.executeWorkflow();
|
||||
// Manual tigger node should show success indicator
|
||||
workflowPage.actions.openNode('When clicking "Test Workflow"');
|
||||
ndv.getters.nodeRunSuccessIndicator().should('exist');
|
||||
// Code node should show error
|
||||
ndv.getters.backToCanvas().click();
|
||||
workflowPage.actions.openNode('Code');
|
||||
ndv.getters.nodeRunErrorIndicator().should('exist');
|
||||
});
|
||||
|
||||
it('Should handle mismatched option attributes', () => {
|
||||
workflowPage.actions.addInitialNodeToCanvas('LDAP', {
|
||||
keepNdvOpen: true,
|
||||
action: 'Create a new entry',
|
||||
});
|
||||
// Add some attributes in Create operation
|
||||
cy.getByTestId('parameter-item').contains('Add Attributes').click();
|
||||
ndv.actions.changeNodeOperation('Update');
|
||||
// Attributes should be empty after operation change
|
||||
cy.getByTestId('parameter-item').contains('Currently no items exist').should('exist');
|
||||
});
|
||||
|
||||
it('Should keep RLC values after operation change', () => {
|
||||
const TEST_DOC_ID = '1111';
|
||||
workflowPage.actions.addInitialNodeToCanvas('Google Sheets', {
|
||||
keepNdvOpen: true,
|
||||
action: 'Append row in sheet',
|
||||
});
|
||||
ndv.actions.setRLCValue('documentId', TEST_DOC_ID);
|
||||
ndv.actions.changeNodeOperation('Update Row');
|
||||
ndv.getters.resourceLocatorInput('documentId').find('input').should('have.value', TEST_DOC_ID);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -4,6 +4,8 @@ import {
|
|||
META_KEY,
|
||||
SCHEDULE_TRIGGER_NODE_NAME,
|
||||
EDIT_FIELDS_SET_NODE_NAME,
|
||||
INSTANCE_MEMBERS,
|
||||
INSTANCE_OWNER,
|
||||
} from '../constants';
|
||||
import { WorkflowPage as WorkflowPageClass } from '../pages/workflow';
|
||||
import { WorkflowsPage as WorkflowsPageClass } from '../pages/workflows';
|
||||
|
@ -100,6 +102,7 @@ describe('Workflow Actions', () => {
|
|||
cy.get('body').type(META_KEY, { release: false }).type('s');
|
||||
cy.get('body').type(META_KEY, { release: false }).type('s');
|
||||
cy.wrap(null).then(() => expect(interceptCalledCount).to.eq(0));
|
||||
cy.waitForLoad();
|
||||
WorkflowPage.actions.addNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME);
|
||||
cy.get('body').type(META_KEY, { release: false }).type('s');
|
||||
cy.wait('@saveWorkflow');
|
||||
|
@ -275,3 +278,19 @@ describe('Workflow Actions', () => {
|
|||
WorkflowPage.getters.nodeCreatorSearchBar().should('be.visible');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Menu entry Push To Git', () => {
|
||||
it('should not show up in the menu for members', () => {
|
||||
cy.signin(INSTANCE_MEMBERS[0]);
|
||||
cy.visit(WorkflowPages.url);
|
||||
WorkflowPage.actions.visit();
|
||||
WorkflowPage.getters.workflowMenuItemGitPush().should('not.exist');
|
||||
});
|
||||
|
||||
it('should show up for owners', () => {
|
||||
cy.signin(INSTANCE_OWNER);
|
||||
cy.visit(WorkflowPages.url);
|
||||
WorkflowPage.actions.visit();
|
||||
WorkflowPage.getters.workflowMenuItemGitPush().should('exist');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import { WorkflowPage, NDV } from '../pages';
|
||||
import { NodeCreator } from '../pages/features/node-creator';
|
||||
|
||||
const workflowPage = new WorkflowPage();
|
||||
const nodeCreatorFeature = new NodeCreator();
|
||||
const ndv = new NDV();
|
||||
|
||||
describe('HTTP Request node', () => {
|
||||
|
@ -18,4 +20,40 @@ describe('HTTP Request node', () => {
|
|||
|
||||
ndv.getters.outputPanel().contains('fact');
|
||||
});
|
||||
|
||||
describe('Credential-only HTTP Request Node variants', () => {
|
||||
it('should render a modified HTTP Request Node', () => {
|
||||
workflowPage.actions.addInitialNodeToCanvas('Manual');
|
||||
|
||||
workflowPage.getters.nodeCreatorPlusButton().click();
|
||||
workflowPage.getters.nodeCreatorSearchBar().type('VirusTotal');
|
||||
|
||||
expect(nodeCreatorFeature.getters.nodeItemName().first().should('have.text', 'VirusTotal'));
|
||||
expect(
|
||||
nodeCreatorFeature.getters
|
||||
.nodeItemDescription()
|
||||
.first()
|
||||
.should('have.text', 'HTTP request'),
|
||||
);
|
||||
|
||||
nodeCreatorFeature.actions.selectNode('VirusTotal');
|
||||
expect(ndv.getters.nodeNameContainer().should('contain.text', 'VirusTotal HTTP Request'));
|
||||
expect(
|
||||
ndv.getters
|
||||
.parameterInput('url')
|
||||
.find('input')
|
||||
.should('contain.value', 'https://www.virustotal.com/api/v3/'),
|
||||
);
|
||||
|
||||
// These parameters exist for normal HTTP Request Node, but are hidden for credential-only variants
|
||||
expect(ndv.getters.parameterInput('authentication').should('not.exist'));
|
||||
expect(ndv.getters.parameterInput('nodeCredentialType').should('not.exist'));
|
||||
|
||||
expect(
|
||||
workflowPage.getters
|
||||
.nodeCredentialsLabel()
|
||||
.should('contain.text', 'Credential for VirusTotal'),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -57,6 +57,6 @@ describe('Expression editor modal', () => {
|
|||
it('should resolve $parameter[]', () => {
|
||||
WorkflowPage.getters.expressionModalInput().clear();
|
||||
WorkflowPage.getters.expressionModalInput().type('{{ $parameter["operation"]');
|
||||
WorkflowPage.getters.expressionModalOutput().contains(/^get$/);
|
||||
WorkflowPage.getters.expressionModalOutput().should('have.text', 'getAll');
|
||||
});
|
||||
});
|
||||
|
|
File diff suppressed because one or more lines are too long
176
cypress/fixtures/Floating_Nodes.json
Normal file
176
cypress/fixtures/Floating_Nodes.json
Normal file
|
@ -0,0 +1,176 @@
|
|||
{
|
||||
"name": "Floating Nodes",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "d0eda550-2526-42a1-aa19-dee411c8acf9",
|
||||
"name": "When clicking \"Test Workflow\"",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
700,
|
||||
560
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"options": {}
|
||||
},
|
||||
"id": "30412165-1229-4b21-9890-05bfbd9952ab",
|
||||
"name": "Node 1",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.2,
|
||||
"position": [
|
||||
920,
|
||||
560
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"options": {}
|
||||
},
|
||||
"id": "201cc8fc-3124-47a3-bc08-b3917c1ddcd9",
|
||||
"name": "Node 2",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.2,
|
||||
"position": [
|
||||
1100,
|
||||
560
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"options": {}
|
||||
},
|
||||
"id": "a29802bb-a284-495d-9917-6c6e42fef01e",
|
||||
"name": "Node 3",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.2,
|
||||
"position": [
|
||||
1280,
|
||||
560
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"options": {}
|
||||
},
|
||||
"id": "a95a72b3-8b39-44e2-a05b-d8d677741c80",
|
||||
"name": "Node 4",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.2,
|
||||
"position": [
|
||||
1440,
|
||||
560
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "4674f10d-6144-4a17-bbbb-350c3974438e",
|
||||
"name": "Chain",
|
||||
"type": "@n8n/n8n-nodes-langchain.chainLlm",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
1580,
|
||||
560
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"options": {}
|
||||
},
|
||||
"id": "58e12ea5-bd3e-4abf-abec-fcfb5c0a7955",
|
||||
"name": "Model",
|
||||
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
1600,
|
||||
740
|
||||
]
|
||||
}
|
||||
],
|
||||
"pinData": {},
|
||||
"connections": {
|
||||
"When clicking \"Test Workflow\"": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Node 1",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Node 1": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Node 2",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Node 3": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Node 4",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Node 2": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Node 3",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Chain": {
|
||||
"main": [
|
||||
[]
|
||||
]
|
||||
},
|
||||
"Model": {
|
||||
"ai_languageModel": [
|
||||
[
|
||||
{
|
||||
"node": "Chain",
|
||||
"type": "ai_languageModel",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Node 4": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Chain",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": false,
|
||||
"settings": {
|
||||
"executionOrder": "v1"
|
||||
},
|
||||
"versionId": "2730d156-a98a-4ac8-b481-5c16361fdba2",
|
||||
"id": "6bzXMGxHuxeEaqsA",
|
||||
"meta": {
|
||||
"instanceId": "1838be0fa0389fbaf5e2e4aaedab4ddc79abc4175b433401abb22a281001b853"
|
||||
},
|
||||
"tags": []
|
||||
}
|
1051
cypress/fixtures/Lots_of_nodes.json
Normal file
1051
cypress/fixtures/Lots_of_nodes.json
Normal file
File diff suppressed because it is too large
Load diff
653
cypress/fixtures/Node_IO_filter.json
Normal file
653
cypress/fixtures/Node_IO_filter.json
Normal file
|
@ -0,0 +1,653 @@
|
|||
{
|
||||
"name": "Node IO filter",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "46770685-44d1-4aad-9107-1d790cf26b50",
|
||||
"name": "When clicking \"Test Workflow\"",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
840,
|
||||
180
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"options": {}
|
||||
},
|
||||
"id": "480e3832-2ce4-4118-9f7b-a8aed6017174",
|
||||
"name": "Edit Fields",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.2,
|
||||
"position": [
|
||||
1080,
|
||||
180
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"conditions": {
|
||||
"string": [
|
||||
{
|
||||
"value1": "={{ $json.profile.name }}",
|
||||
"operation": "contains",
|
||||
"value2": "an"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"id": "4773d460-6ed9-49e1-a688-7e480f0fbacf",
|
||||
"name": "IF",
|
||||
"type": "n8n-nodes-base.if",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
1300,
|
||||
180
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"options": {}
|
||||
},
|
||||
"id": "d17dffe6-e29c-4c1a-8b4c-9e374dcd70ea",
|
||||
"name": "True",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.2,
|
||||
"position": [
|
||||
1560,
|
||||
60
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"options": {}
|
||||
},
|
||||
"id": "893d6e79-feb4-4752-a6f8-e2e5f5163787",
|
||||
"name": "False",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.2,
|
||||
"position": [
|
||||
1560,
|
||||
240
|
||||
]
|
||||
}
|
||||
],
|
||||
"pinData": {
|
||||
"When clicking \"Test Workflow\"": [
|
||||
{
|
||||
"json": {
|
||||
"id": "654cfa05fa51480dcb543b1a",
|
||||
"email": "reese_hahn@kidgrease.coach",
|
||||
"username": "reese94",
|
||||
"profile": {
|
||||
"name": "Reese Hahn",
|
||||
"company": "Kidgrease",
|
||||
"dob": "1994-06-18",
|
||||
"address": "3 Richmond Street, Norfolk, Delaware",
|
||||
"location": {
|
||||
"lat": 22.507436,
|
||||
"long": -50.812775
|
||||
},
|
||||
"about": "Cupidatat voluptate reprehenderit commodo mollit tempor sint id. Id exercitation id eiusmod dolore non non anim voluptate anim eu consectetur."
|
||||
},
|
||||
"apiKey": "a18592bf-1147-4b61-a70f-2ab90b60bb6e",
|
||||
"roles": [
|
||||
"guest"
|
||||
],
|
||||
"createdAt": "2010-10-04T09:57:59.240Z",
|
||||
"updatedAt": "2010-10-05T09:57:59.240Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": "654cfa055bea471bc4853158",
|
||||
"email": "jeanne_boyd@hatology.gratis",
|
||||
"username": "jeanne91",
|
||||
"profile": {
|
||||
"name": "Jeanne Boyd",
|
||||
"company": "Hatology",
|
||||
"dob": "1991-02-21",
|
||||
"address": "81 Kingsway Place, Blairstown, Vermont",
|
||||
"location": {
|
||||
"lat": -57.665234,
|
||||
"long": -41.301893
|
||||
},
|
||||
"about": "Proident pariatur non consequat cupidatat Lorem nisi est consequat dolor id eiusmod id. Amet culpa ex Lorem nostrud labore laboris culpa mollit dolor culpa ut."
|
||||
},
|
||||
"apiKey": "8a6056a6-0197-4920-858d-cb26f8c8a1e2",
|
||||
"roles": [
|
||||
"owner",
|
||||
"admin"
|
||||
],
|
||||
"createdAt": "2011-11-06T09:05:41.945Z",
|
||||
"updatedAt": "2011-11-07T09:05:41.945Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": "654cfa05b012921c060dc5a5",
|
||||
"email": "roslyn_underwood@portico.melbourne",
|
||||
"username": "roslyn88",
|
||||
"profile": {
|
||||
"name": "Roslyn Underwood",
|
||||
"company": "Portico",
|
||||
"dob": "1988-04-30",
|
||||
"address": "24 Schenck Street, Drytown, New Jersey",
|
||||
"location": {
|
||||
"lat": 11.797141,
|
||||
"long": 10.751804
|
||||
},
|
||||
"about": "Duis excepteur minim consequat exercitation. Laboris occaecat cupidatat aliqua consequat occaecat."
|
||||
},
|
||||
"apiKey": "72d629f3-d613-4fd0-bbfe-3f67c8ad7af2",
|
||||
"roles": [
|
||||
"member",
|
||||
"owner"
|
||||
],
|
||||
"createdAt": "2012-11-17T22:09:10.911Z",
|
||||
"updatedAt": "2012-11-18T22:09:10.911Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": "654cfa05df7b35968507efe6",
|
||||
"email": "combs_hardy@acrodance.domains",
|
||||
"username": "combs91",
|
||||
"profile": {
|
||||
"name": "Combs Hardy",
|
||||
"company": "Acrodance",
|
||||
"dob": "1991-04-30",
|
||||
"address": "58 Pineapple Street, Falconaire, New Mexico",
|
||||
"location": {
|
||||
"lat": -62.922443,
|
||||
"long": -159.493799
|
||||
},
|
||||
"about": "Magna qui minim velit magna est eiusmod aliquip elit aliquip excepteur. Laborum labore do ut et ut in incididunt do elit nostrud."
|
||||
},
|
||||
"apiKey": "d9807b9e-aee9-486d-9826-4e6c166bfbe4",
|
||||
"roles": [
|
||||
"owner",
|
||||
"member"
|
||||
],
|
||||
"createdAt": "2014-04-13T13:02:09.319Z",
|
||||
"updatedAt": "2014-04-14T13:02:09.319Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": "654cfa05f2d4a0508a7c59c4",
|
||||
"email": "terrell_peters@vantage.international",
|
||||
"username": "terrell94",
|
||||
"profile": {
|
||||
"name": "Terrell Peters",
|
||||
"company": "Vantage",
|
||||
"dob": "1994-01-31",
|
||||
"address": "10 Lafayette Walk, Vincent, Virginia",
|
||||
"location": {
|
||||
"lat": -62.267913,
|
||||
"long": 29.682121
|
||||
},
|
||||
"about": "Eiusmod fugiat nulla ea tempor incididunt nulla nulla consectetur officia incididunt proident sint. Sunt duis non excepteur non."
|
||||
},
|
||||
"apiKey": "20b96df1-d882-4dea-a505-84d7ff296a6e",
|
||||
"roles": [
|
||||
"admin",
|
||||
"guest"
|
||||
],
|
||||
"createdAt": "2010-12-09T08:24:56.517Z",
|
||||
"updatedAt": "2010-12-10T08:24:56.517Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": "654cfa0599fbabf3a05c7b14",
|
||||
"email": "shari_winters@powernet.supply",
|
||||
"username": "shari93",
|
||||
"profile": {
|
||||
"name": "Shari Winters",
|
||||
"company": "Powernet",
|
||||
"dob": "1993-03-10",
|
||||
"address": "89 Aviation Road, Leyner, Indiana",
|
||||
"location": {
|
||||
"lat": 40.404704,
|
||||
"long": -141.216235
|
||||
},
|
||||
"about": "Occaecat sit laboris elit laboris do anim culpa dolore exercitation enim. Non veniam sint exercitation irure."
|
||||
},
|
||||
"apiKey": "2b869ce9-3431-4edb-944d-9d9336b1eb4a",
|
||||
"roles": [
|
||||
"guest",
|
||||
"admin"
|
||||
],
|
||||
"createdAt": "2014-10-15T15:56:55.873Z",
|
||||
"updatedAt": "2014-10-16T15:56:55.873Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": "654cfa050df18b4798ec95be",
|
||||
"email": "rena_beasley@bitrex.ma",
|
||||
"username": "rena90",
|
||||
"profile": {
|
||||
"name": "Rena Beasley",
|
||||
"company": "Bitrex",
|
||||
"dob": "1990-01-09",
|
||||
"address": "78 Forbell Street, Homeland, Maine",
|
||||
"location": {
|
||||
"lat": 46.047548,
|
||||
"long": 4.128049
|
||||
},
|
||||
"about": "Lorem aliqua veniam duis ut cillum ad sunt mollit incididunt elit. Ipsum incididunt et magna incididunt quis duis amet duis occaecat laborum nulla et commodo nisi."
|
||||
},
|
||||
"apiKey": "17e350f8-1020-4344-bbd7-ceb62cd44edb",
|
||||
"roles": [
|
||||
"member",
|
||||
"owner"
|
||||
],
|
||||
"createdAt": "2010-04-22T13:35:24.838Z",
|
||||
"updatedAt": "2010-04-23T13:35:24.838Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": "654cfa0595243d2b7b1ea22a",
|
||||
"email": "sally_gentry@eventex.maif",
|
||||
"username": "sally93",
|
||||
"profile": {
|
||||
"name": "Sally Gentry",
|
||||
"company": "Eventex",
|
||||
"dob": "1993-04-03",
|
||||
"address": "54 Plaza Street, Greenbackville, North Carolina",
|
||||
"location": {
|
||||
"lat": -20.529121,
|
||||
"long": 73.533118
|
||||
},
|
||||
"about": "Laborum sit exercitation sint laborum. Fugiat sit ipsum ullamco sint do dolore in sunt incididunt adipisicing magna ullamco aute."
|
||||
},
|
||||
"apiKey": "746b6ab3-c63f-44df-bb99-9de48f8e43c4",
|
||||
"roles": [
|
||||
"owner",
|
||||
"guest"
|
||||
],
|
||||
"createdAt": "2011-09-18T13:18:49.655Z",
|
||||
"updatedAt": "2011-09-19T13:18:49.655Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": "654cfa05cdea66c87bb01439",
|
||||
"email": "battle_duran@jasper.property",
|
||||
"username": "battle88",
|
||||
"profile": {
|
||||
"name": "Battle Duran",
|
||||
"company": "Jasper",
|
||||
"dob": "1988-11-04",
|
||||
"address": "34 Amherst Street, Corriganville, Nevada",
|
||||
"location": {
|
||||
"lat": 74.391489,
|
||||
"long": -98.421464
|
||||
},
|
||||
"about": "Nostrud occaecat laborum aliquip sint est minim id aliquip adipisicing dolor. Aute velit amet officia anim sint anim aliquip."
|
||||
},
|
||||
"apiKey": "b22a3ddd-d540-4df0-9ce5-e837bc6a6a10",
|
||||
"roles": [
|
||||
"member"
|
||||
],
|
||||
"createdAt": "2012-08-31T19:14:37.463Z",
|
||||
"updatedAt": "2012-09-01T19:14:37.463Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": "654cfa05e9c13e25d41d4135",
|
||||
"email": "petty_moore@neurocell.shriram",
|
||||
"username": "petty91",
|
||||
"profile": {
|
||||
"name": "Petty Moore",
|
||||
"company": "Neurocell",
|
||||
"dob": "1991-03-10",
|
||||
"address": "78 Interborough Parkway, Grill, Texas",
|
||||
"location": {
|
||||
"lat": -79.817761,
|
||||
"long": -36.728201
|
||||
},
|
||||
"about": "Dolor occaecat anim est Lorem culpa fugiat id aliqua sint. Sit nisi do exercitation do voluptate exercitation in."
|
||||
},
|
||||
"apiKey": "4b341cfb-a83c-4f2a-9f4d-11cd747b8783",
|
||||
"roles": [
|
||||
"admin"
|
||||
],
|
||||
"createdAt": "2012-01-02T21:28:22.431Z",
|
||||
"updatedAt": "2012-01-03T21:28:22.431Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": "654cfa052890c7b4d510d3d4",
|
||||
"email": "matilda_kelley@senmei.in",
|
||||
"username": "matilda93",
|
||||
"profile": {
|
||||
"name": "Matilda Kelley",
|
||||
"company": "Senmei",
|
||||
"dob": "1993-02-04",
|
||||
"address": "29 Stuart Street, Henrietta, New York",
|
||||
"location": {
|
||||
"lat": 40.788206,
|
||||
"long": -135.821558
|
||||
},
|
||||
"about": "Dolor veniam ex ullamco deserunt reprehenderit nostrud sunt culpa cupidatat qui labore deserunt. In ad anim laboris amet labore duis consequat nostrud eiusmod."
|
||||
},
|
||||
"apiKey": "dcf40383-a00a-43ef-8bd0-4af7e70413bd",
|
||||
"roles": [
|
||||
"owner",
|
||||
"guest"
|
||||
],
|
||||
"createdAt": "2014-03-28T22:07:39.636Z",
|
||||
"updatedAt": "2014-03-29T22:07:39.636Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": "654cfa05af129db469473bf1",
|
||||
"email": "savannah_hardin@exoblue.kn",
|
||||
"username": "savannah89",
|
||||
"profile": {
|
||||
"name": "Savannah Hardin",
|
||||
"company": "Exoblue",
|
||||
"dob": "1989-07-01",
|
||||
"address": "44 Navy Walk, Fresno, Kentucky",
|
||||
"location": {
|
||||
"lat": 75.679679,
|
||||
"long": -58.534947
|
||||
},
|
||||
"about": "Id eiusmod eu elit consequat quis anim veniam officia anim ipsum. Sunt ex sit ipsum id est eu."
|
||||
},
|
||||
"apiKey": "98d6abb7-e4aa-4b3b-8958-ff3c4d672f1d",
|
||||
"roles": [
|
||||
"guest",
|
||||
"member"
|
||||
],
|
||||
"createdAt": "2011-04-15T00:55:02.325Z",
|
||||
"updatedAt": "2011-04-16T00:55:02.325Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": "654cfa055dfa731b01573a67",
|
||||
"email": "abbott_gallegos@katakana.dad",
|
||||
"username": "abbott91",
|
||||
"profile": {
|
||||
"name": "Abbott Gallegos",
|
||||
"company": "Katakana",
|
||||
"dob": "1991-03-04",
|
||||
"address": "85 Indiana Place, Forestburg, Michigan",
|
||||
"location": {
|
||||
"lat": -5.417414,
|
||||
"long": -4.557904
|
||||
},
|
||||
"about": "Adipisicing amet ullamco aliquip velit nostrud qui non pariatur Lorem. Culpa ut deserunt esse quis magna."
|
||||
},
|
||||
"apiKey": "3cf92c24-6193-4cc9-85fc-78e4ad9d6e13",
|
||||
"roles": [
|
||||
"guest",
|
||||
"owner"
|
||||
],
|
||||
"createdAt": "2011-06-01T16:38:39.316Z",
|
||||
"updatedAt": "2011-06-02T16:38:39.316Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": "654cfa05386de2e6d75c1694",
|
||||
"email": "short_brennan@hyplex.tc",
|
||||
"username": "short92",
|
||||
"profile": {
|
||||
"name": "Short Brennan",
|
||||
"company": "Hyplex",
|
||||
"dob": "1992-04-19",
|
||||
"address": "21 Irving Place, Hinsdale, Northern Mariana Islands",
|
||||
"location": {
|
||||
"lat": 57.340225,
|
||||
"long": -7.021582
|
||||
},
|
||||
"about": "Mollit dolor dolore deserunt anim minim adipisicing eiusmod velit tempor id veniam cupidatat. Magna veniam consequat incididunt ut quis culpa excepteur tempor eiusmod consectetur excepteur."
|
||||
},
|
||||
"apiKey": "07bf533d-4a31-4e78-9d6e-d46160479069",
|
||||
"roles": [
|
||||
"admin",
|
||||
"member"
|
||||
],
|
||||
"createdAt": "2014-03-10T19:25:02.217Z",
|
||||
"updatedAt": "2014-03-11T19:25:02.217Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": "654cfa05fd2a878d43bb45cd",
|
||||
"email": "bowers_cooke@iplax.ci",
|
||||
"username": "bowers92",
|
||||
"profile": {
|
||||
"name": "Bowers Cooke",
|
||||
"company": "Iplax",
|
||||
"dob": "1992-07-05",
|
||||
"address": "83 Greenpoint Avenue, Marion, Georgia",
|
||||
"location": {
|
||||
"lat": 64.261022,
|
||||
"long": -58.493714
|
||||
},
|
||||
"about": "Deserunt ipsum fugiat tempor sunt eu ea laboris ad magna ex laborum laboris. Ullamco nostrud qui exercitation aute consectetur irure."
|
||||
},
|
||||
"apiKey": "a3ecc58b-f292-4de1-b6e5-014345a76a7a",
|
||||
"roles": [
|
||||
"member",
|
||||
"owner"
|
||||
],
|
||||
"createdAt": "2010-06-20T16:34:56.467Z",
|
||||
"updatedAt": "2010-06-21T16:34:56.467Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": "654cfa05a6de547367990f9c",
|
||||
"email": "tara_rutledge@escenta.lc",
|
||||
"username": "tara90",
|
||||
"profile": {
|
||||
"name": "Tara Rutledge",
|
||||
"company": "Escenta",
|
||||
"dob": "1990-08-11",
|
||||
"address": "25 Butler Place, Frierson, Missouri",
|
||||
"location": {
|
||||
"lat": -32.176783,
|
||||
"long": 67.345415
|
||||
},
|
||||
"about": "Aute sunt laborum anim ex non pariatur nisi minim tempor adipisicing. Excepteur irure non amet eiusmod et excepteur."
|
||||
},
|
||||
"apiKey": "22da9647-a7b7-4815-91bb-d5101fc90e55",
|
||||
"roles": [
|
||||
"member"
|
||||
],
|
||||
"createdAt": "2013-09-06T21:41:53.287Z",
|
||||
"updatedAt": "2013-09-07T21:41:53.287Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": "654cfa053778601ad57f22cd",
|
||||
"email": "elva_chapman@bytrex.gg",
|
||||
"username": "elva90",
|
||||
"profile": {
|
||||
"name": "Elva Chapman",
|
||||
"company": "Bytrex",
|
||||
"dob": "1990-05-31",
|
||||
"address": "4 Royce Place, Advance, New Hampshire",
|
||||
"location": {
|
||||
"lat": -28.393464,
|
||||
"long": -28.622091
|
||||
},
|
||||
"about": "Est sit deserunt Lorem amet voluptate elit reprehenderit occaecat est eiusmod eu reprehenderit laborum. Pariatur magna occaecat et excepteur est excepteur consectetur ad nulla."
|
||||
},
|
||||
"apiKey": "4d242fa4-ac69-42f1-8f12-ec19d9c6d632",
|
||||
"roles": [
|
||||
"owner",
|
||||
"admin"
|
||||
],
|
||||
"createdAt": "2011-04-05T04:04:31.524Z",
|
||||
"updatedAt": "2011-04-06T04:04:31.524Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": "654cfa054c6abbc57efcb100",
|
||||
"email": "pitts_meyer@unisure.tui",
|
||||
"username": "pitts93",
|
||||
"profile": {
|
||||
"name": "Pitts Meyer",
|
||||
"company": "Unisure",
|
||||
"dob": "1993-06-12",
|
||||
"address": "47 Columbus Place, Cade, Alaska",
|
||||
"location": {
|
||||
"lat": 56.723675,
|
||||
"long": 158.093389
|
||||
},
|
||||
"about": "Non ea pariatur excepteur nostrud elit quis qui. Dolore aute velit ipsum officia ea pariatur incididunt non elit tempor duis consequat."
|
||||
},
|
||||
"apiKey": "82a88344-d289-447c-81b5-1ae10cd1994b",
|
||||
"roles": [
|
||||
"guest",
|
||||
"admin"
|
||||
],
|
||||
"createdAt": "2014-05-15T06:38:59.269Z",
|
||||
"updatedAt": "2014-05-16T06:38:59.269Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": "654cfa0527e7ce14e421d9cd",
|
||||
"email": "delia_figueroa@overplex.um",
|
||||
"username": "delia89",
|
||||
"profile": {
|
||||
"name": "Delia Figueroa",
|
||||
"company": "Overplex",
|
||||
"dob": "1989-04-22",
|
||||
"address": "12 Nova Court, Taft, Ohio",
|
||||
"location": {
|
||||
"lat": -32.990583,
|
||||
"long": -4.598863
|
||||
},
|
||||
"about": "Cupidatat fugiat veniam eu proident excepteur deserunt ad esse fugiat deserunt. Non velit cillum velit veniam ex minim eiusmod tempor excepteur voluptate adipisicing nostrud."
|
||||
},
|
||||
"apiKey": "b3a7747b-24a0-4039-8a21-56e83441a660",
|
||||
"roles": [
|
||||
"admin",
|
||||
"guest"
|
||||
],
|
||||
"createdAt": "2014-09-20T03:40:10.190Z",
|
||||
"updatedAt": "2014-09-21T03:40:10.190Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": "654cfa05cf60000cbca6dca4",
|
||||
"email": "kristina_fulton@portaline.engineer",
|
||||
"username": "kristina88",
|
||||
"profile": {
|
||||
"name": "Kristina Fulton",
|
||||
"company": "Portaline",
|
||||
"dob": "1988-07-25",
|
||||
"address": "50 Laurel Avenue, Greenwich, Palau",
|
||||
"location": {
|
||||
"lat": 44.118984,
|
||||
"long": 41.518949
|
||||
},
|
||||
"about": "Id incididunt officia exercitation ipsum id cillum consectetur. Veniam enim voluptate ut proident ex."
|
||||
},
|
||||
"apiKey": "c106dbf0-bfc0-461d-b1d7-1840fe8e1cbc",
|
||||
"roles": [
|
||||
"admin",
|
||||
"member"
|
||||
],
|
||||
"createdAt": "2010-04-10T08:06:27.028Z",
|
||||
"updatedAt": "2010-04-11T08:06:27.028Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": "654cfa0501fe5691d620f570",
|
||||
"email": "gould_noel@gonkle.gmx",
|
||||
"username": "gould91",
|
||||
"profile": {
|
||||
"name": "Gould Noel",
|
||||
"company": "Gonkle",
|
||||
"dob": "1991-10-08",
|
||||
"address": "33 Crooke Avenue, Idamay, Oklahoma",
|
||||
"location": {
|
||||
"lat": -11.398731,
|
||||
"long": 34.706948
|
||||
},
|
||||
"about": "Veniam esse tempor aute quis mollit consequat Lorem. Nostrud ea dolore laboris Lorem elit est do nisi Lorem minim reprehenderit culpa."
|
||||
},
|
||||
"apiKey": "1089783d-32ae-4102-8ac5-1e7f6cebe3c1",
|
||||
"roles": [
|
||||
"guest",
|
||||
"admin"
|
||||
],
|
||||
"createdAt": "2011-12-30T20:24:19.620Z",
|
||||
"updatedAt": "2011-12-31T20:24:19.620Z"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"connections": {
|
||||
"When clicking \"Test Workflow\"": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Edit Fields",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Edit Fields": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "IF",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"IF": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "True",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"node": "False",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": false,
|
||||
"settings": {
|
||||
"executionOrder": "v1"
|
||||
},
|
||||
"versionId": "9812dda2-cc1b-4458-97d8-21ccb18c90d1",
|
||||
"id": "WNq486x7DpV1MPRH",
|
||||
"meta": {
|
||||
"instanceId": "8a47b83b4479b11330fdf21ccc96d4a8117035a968612e452b4c87bfd09c16c7"
|
||||
},
|
||||
"tags": []
|
||||
}
|
|
@ -13,8 +13,7 @@
|
|||
"feat:advancedExecutionFilters": true,
|
||||
"quota:users": -1,
|
||||
"quota:maxVariables": -1,
|
||||
"feat:variables": true,
|
||||
"feat:apiDisabled": true
|
||||
"feat:variables": true
|
||||
},
|
||||
"metadata": {
|
||||
"version": "v1",
|
||||
|
|
313
cypress/fixtures/Schedule_pinned.json
Normal file
313
cypress/fixtures/Schedule_pinned.json
Normal file
|
@ -0,0 +1,313 @@
|
|||
{
|
||||
"name": "Schedule + pinned",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {
|
||||
"rule": {
|
||||
"interval": [
|
||||
{}
|
||||
]
|
||||
}
|
||||
},
|
||||
"id": "66358c29-b263-43dd-be25-3b068b0a88eb",
|
||||
"name": "Schedule Trigger",
|
||||
"type": "n8n-nodes-base.scheduleTrigger",
|
||||
"typeVersion": 1.1,
|
||||
"position": [
|
||||
660,
|
||||
340
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"options": {}
|
||||
},
|
||||
"id": "6d903354-4e59-4032-81fe-426a5d6ec33c",
|
||||
"name": "Edit Fields",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.2,
|
||||
"position": [
|
||||
860,
|
||||
240
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"options": {}
|
||||
},
|
||||
"id": "d8a1e9cf-81d3-400f-97d4-ad6167e7b236",
|
||||
"name": "Edit Fields1",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.2,
|
||||
"position": [
|
||||
860,
|
||||
440
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"options": {}
|
||||
},
|
||||
"id": "bdc41148-067e-4649-8f21-5707b128d877",
|
||||
"name": "Edit Fields2",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.2,
|
||||
"position": [
|
||||
1080,
|
||||
440
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"options": {}
|
||||
},
|
||||
"id": "d5a4337f-a6b3-4b51-9b02-e668593d9ae8",
|
||||
"name": "Edit Fields3",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.2,
|
||||
"position": [
|
||||
1300,
|
||||
440
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"options": {}
|
||||
},
|
||||
"id": "fbc23f60-e7f6-4423-9329-33b0e4809a9a",
|
||||
"name": "Edit Fields4",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.2,
|
||||
"position": [
|
||||
1500,
|
||||
440
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"options": {}
|
||||
},
|
||||
"id": "eaee47b0-94ec-4137-bfeb-a6c1a2c63f81",
|
||||
"name": "Edit Fields5",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.2,
|
||||
"position": [
|
||||
1080,
|
||||
240
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"options": {}
|
||||
},
|
||||
"id": "eabb6308-21e9-4e59-8f74-9220a03c3186",
|
||||
"name": "Edit Fields6",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.2,
|
||||
"position": [
|
||||
1300,
|
||||
240
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"options": {}
|
||||
},
|
||||
"id": "8812a45b-5545-4080-aad8-8e9f7b17ecd7",
|
||||
"name": "Edit Fields7",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.2,
|
||||
"position": [
|
||||
1500,
|
||||
240
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"options": {}
|
||||
},
|
||||
"id": "d5ea3c5b-0b3e-4514-93e1-9c88563bab5c",
|
||||
"name": "Edit Fields9",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.2,
|
||||
"position": [
|
||||
1700,
|
||||
240
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"options": {}
|
||||
},
|
||||
"id": "7af34474-5cd0-40b1-abea-850858e3b495",
|
||||
"name": "Edit Fields10",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.2,
|
||||
"position": [
|
||||
1700,
|
||||
440
|
||||
]
|
||||
}
|
||||
],
|
||||
"pinData": {
|
||||
"Schedule Trigger": [
|
||||
{
|
||||
"json": {
|
||||
"name": "First item",
|
||||
"code": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"name": "Second item",
|
||||
"code": 2
|
||||
}
|
||||
}
|
||||
],
|
||||
"Edit Fields7": [
|
||||
{
|
||||
"json": {
|
||||
"name": "First item",
|
||||
"code": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"name": "Second item",
|
||||
"code": 2
|
||||
}
|
||||
}
|
||||
],
|
||||
"Edit Fields2": [
|
||||
{
|
||||
"json": {
|
||||
"name": "First item",
|
||||
"code": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"name": "Second item",
|
||||
"code": 2
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"connections": {
|
||||
"Schedule Trigger": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Edit Fields",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Edit Fields1",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Edit Fields1": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Edit Fields2",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Edit Fields2": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Edit Fields3",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Edit Fields3": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Edit Fields4",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Edit Fields5": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Edit Fields6",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Edit Fields6": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Edit Fields7",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Edit Fields": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Edit Fields5",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Edit Fields7": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Edit Fields9",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Edit Fields4": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Edit Fields10",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": false,
|
||||
"settings": {
|
||||
"executionOrder": "v1"
|
||||
},
|
||||
"versionId": "9b6c68c0-f94f-45bc-a604-bf97d17a47ac",
|
||||
"meta": {
|
||||
"templateCredsSetupCompleted": true,
|
||||
"instanceId": "8a47b83b4479b11330fdf21ccc96d4a8117035a968612e452b4c87bfd09c16c7"
|
||||
},
|
||||
"id": "nWzcnYUb3AVaZpHG",
|
||||
"tags": []
|
||||
}
|
655
cypress/fixtures/Suggested_Templates.json
Normal file
655
cypress/fixtures/Suggested_Templates.json
Normal file
|
@ -0,0 +1,655 @@
|
|||
{
|
||||
"sections": [
|
||||
{
|
||||
"name": "Lead enrichment",
|
||||
"description": "Explore curated lead enrichment workflows or start fresh with a blank canvas",
|
||||
"workflows": [
|
||||
{
|
||||
"title": "Score new leads with AI from Facebook Lead Ads with AI and get notifications for high scores on Slack",
|
||||
"description": "This workflow will help you save tons of time and will notify you fully automatically about the most important incoming leads from Facebook Lead Ads. The workflow will automatically fire for every submission. It will then take the name, company, and email information, enrich the submitter via AI, and score it based on metrics that you can easily set.",
|
||||
"preview": {
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "create",
|
||||
"base": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": ""
|
||||
},
|
||||
"table": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": ""
|
||||
},
|
||||
"columns": {
|
||||
"mappingMode": "defineBelow",
|
||||
"value": {},
|
||||
"matchingColumns": [],
|
||||
"schema": []
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"id": "b09d4f4d-19fa-43de-8148-2d430a04956f",
|
||||
"name": "Airtable",
|
||||
"type": "n8n-nodes-base.airtable",
|
||||
"typeVersion": 2,
|
||||
"position": [
|
||||
1800,
|
||||
740
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "551313bb-1e01-4133-9956-e6f09968f2ce",
|
||||
"name": "When clicking \"Test Workflow\"",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
920,
|
||||
740
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"options": {}
|
||||
},
|
||||
"id": "b4c089ee-2adb-435e-8d48-47012c981a11",
|
||||
"name": "Get image",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.1,
|
||||
"position": [
|
||||
1140,
|
||||
740
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "extractHtmlContent",
|
||||
"options": {}
|
||||
},
|
||||
"id": "04ca2f61-b930-4fbc-b467-3470c0d93d64",
|
||||
"name": "Extract Information",
|
||||
"type": "n8n-nodes-base.html",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
1360,
|
||||
740
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"options": {}
|
||||
},
|
||||
"id": "d1a77493-c579-4ac4-b6a7-708eea2bf8ce",
|
||||
"name": "Set Information",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.2,
|
||||
"position": [
|
||||
1580,
|
||||
740
|
||||
]
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"When clicking \"Test Workflow\"": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Get image",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Get image": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Extract Information",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Extract Information": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Set Information",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Set Information": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Airtable",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"nodes": [
|
||||
{
|
||||
"id": 24,
|
||||
"icon": "fa:code-branch",
|
||||
"defaults": {
|
||||
"color": "#00bbcc"
|
||||
},
|
||||
"iconData": {
|
||||
"icon": "code-branch",
|
||||
"type": "icon"
|
||||
},
|
||||
"displayName": "Merge"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Verify the email address every time a contact is created in HubSpot",
|
||||
"description": "This workflow will help you save tons of time and will notify you fully automatically about the most important incoming leads from Facebook Lead Ads. The workflow will automatically fire for every submission. It will then take the name, company, and email information, enrich the submitter via AI, and score it based on metrics that you can easily set.",
|
||||
"preview": {
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "create",
|
||||
"base": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": ""
|
||||
},
|
||||
"table": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": ""
|
||||
},
|
||||
"columns": {
|
||||
"mappingMode": "defineBelow",
|
||||
"value": {},
|
||||
"matchingColumns": [],
|
||||
"schema": []
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"id": "b09d4f4d-19fa-43de-8148-2d430a04956f",
|
||||
"name": "Airtable",
|
||||
"type": "n8n-nodes-base.airtable",
|
||||
"typeVersion": 2,
|
||||
"position": [
|
||||
1800,
|
||||
740
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "551313bb-1e01-4133-9956-e6f09968f2ce",
|
||||
"name": "When clicking \"Test Workflow\"",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
920,
|
||||
740
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"options": {}
|
||||
},
|
||||
"id": "b4c089ee-2adb-435e-8d48-47012c981a11",
|
||||
"name": "Get image",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.1,
|
||||
"position": [
|
||||
1140,
|
||||
740
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "extractHtmlContent",
|
||||
"options": {}
|
||||
},
|
||||
"id": "04ca2f61-b930-4fbc-b467-3470c0d93d64",
|
||||
"name": "Extract Information",
|
||||
"type": "n8n-nodes-base.html",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
1360,
|
||||
740
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"options": {}
|
||||
},
|
||||
"id": "d1a77493-c579-4ac4-b6a7-708eea2bf8ce",
|
||||
"name": "Set Information",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.2,
|
||||
"position": [
|
||||
1580,
|
||||
740
|
||||
]
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"When clicking \"Test Workflow\"": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Get image",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Get image": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Extract Information",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Extract Information": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Set Information",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Set Information": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Airtable",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"nodes": [
|
||||
{
|
||||
"id": 14,
|
||||
"icon": "fa:code",
|
||||
"name": "n8n-nodes-base.function",
|
||||
"defaults": {
|
||||
"name": "Function",
|
||||
"color": "#FF9922"
|
||||
},
|
||||
"iconData": {
|
||||
"icon": "code",
|
||||
"type": "icon"
|
||||
},
|
||||
"categories": [
|
||||
{
|
||||
"id": 5,
|
||||
"name": "Development"
|
||||
},
|
||||
{
|
||||
"id": 9,
|
||||
"name": "Core Nodes"
|
||||
}
|
||||
],
|
||||
"displayName": "Function",
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": 24,
|
||||
"icon": "fa:code-branch",
|
||||
"name": "n8n-nodes-base.merge",
|
||||
"defaults": {
|
||||
"name": "Merge",
|
||||
"color": "#00bbcc"
|
||||
},
|
||||
"iconData": {
|
||||
"icon": "code-branch",
|
||||
"type": "icon"
|
||||
},
|
||||
"categories": [
|
||||
{
|
||||
"id": 9,
|
||||
"name": "Core Nodes"
|
||||
}
|
||||
],
|
||||
"displayName": "Merge",
|
||||
"typeVersion": 2
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Enrich leads from HubSpot with company information via OpenAi",
|
||||
"description": "This workflow will help you save tons of time and will notify you fully automatically about the most important incoming leads from Facebook Lead Ads. The workflow will automatically fire for every submission. It will then take the name, company, and email information, enrich the submitter via AI, and score it based on metrics that you can easily set.",
|
||||
"preview": {
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "create",
|
||||
"base": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": ""
|
||||
},
|
||||
"table": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": ""
|
||||
},
|
||||
"columns": {
|
||||
"mappingMode": "defineBelow",
|
||||
"value": {},
|
||||
"matchingColumns": [],
|
||||
"schema": []
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"id": "b09d4f4d-19fa-43de-8148-2d430a04956f",
|
||||
"name": "Airtable",
|
||||
"type": "n8n-nodes-base.airtable",
|
||||
"typeVersion": 2,
|
||||
"position": [
|
||||
1800,
|
||||
740
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "551313bb-1e01-4133-9956-e6f09968f2ce",
|
||||
"name": "When clicking \"Test Workflow\"",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
920,
|
||||
740
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"options": {}
|
||||
},
|
||||
"id": "b4c089ee-2adb-435e-8d48-47012c981a11",
|
||||
"name": "Get image",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.1,
|
||||
"position": [
|
||||
1140,
|
||||
740
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "extractHtmlContent",
|
||||
"options": {}
|
||||
},
|
||||
"id": "04ca2f61-b930-4fbc-b467-3470c0d93d64",
|
||||
"name": "Extract Information",
|
||||
"type": "n8n-nodes-base.html",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
1360,
|
||||
740
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"options": {}
|
||||
},
|
||||
"id": "d1a77493-c579-4ac4-b6a7-708eea2bf8ce",
|
||||
"name": "Set Information",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.2,
|
||||
"position": [
|
||||
1580,
|
||||
740
|
||||
]
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"When clicking \"Test Workflow\"": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Get image",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Get image": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Extract Information",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Extract Information": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Set Information",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Set Information": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Airtable",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"nodes": [
|
||||
{
|
||||
"id": 14,
|
||||
"icon": "fa:code",
|
||||
"defaults": {
|
||||
"name": "Function",
|
||||
"color": "#FF9922"
|
||||
},
|
||||
"iconData": {
|
||||
"icon": "code",
|
||||
"type": "icon"
|
||||
},
|
||||
"displayName": "Function"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Score new lead submissions from Facebook Lead Ads with AI and notify me on Slack when it is a high score lead",
|
||||
"description": "This workflow will help you save tons of time and will notify you fully automatically about the most important incoming leads from Facebook Lead Ads. The workflow will automatically fire for every submission. It will then take the name, company, and email information, enrich the submitter via AI, and score it based on metrics that you can easily set.",
|
||||
"preview": {
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "create",
|
||||
"base": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": ""
|
||||
},
|
||||
"table": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": ""
|
||||
},
|
||||
"columns": {
|
||||
"mappingMode": "defineBelow",
|
||||
"value": {},
|
||||
"matchingColumns": [],
|
||||
"schema": []
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"id": "b09d4f4d-19fa-43de-8148-2d430a04956f",
|
||||
"name": "Airtable",
|
||||
"type": "n8n-nodes-base.airtable",
|
||||
"typeVersion": 2,
|
||||
"position": [
|
||||
1800,
|
||||
740
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "551313bb-1e01-4133-9956-e6f09968f2ce",
|
||||
"name": "When clicking \"Test Workflow\"",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
920,
|
||||
740
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"options": {}
|
||||
},
|
||||
"id": "b4c089ee-2adb-435e-8d48-47012c981a11",
|
||||
"name": "Get image",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.1,
|
||||
"position": [
|
||||
1140,
|
||||
740
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "extractHtmlContent",
|
||||
"options": {}
|
||||
},
|
||||
"id": "04ca2f61-b930-4fbc-b467-3470c0d93d64",
|
||||
"name": "Extract Information",
|
||||
"type": "n8n-nodes-base.html",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
1360,
|
||||
740
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"options": {}
|
||||
},
|
||||
"id": "d1a77493-c579-4ac4-b6a7-708eea2bf8ce",
|
||||
"name": "Set Information",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.2,
|
||||
"position": [
|
||||
1580,
|
||||
740
|
||||
]
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"When clicking \"Test Workflow\"": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Get image",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Get image": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Extract Information",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Extract Information": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Set Information",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Set Information": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Airtable",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"nodes": [
|
||||
{
|
||||
"id": 14,
|
||||
"icon": "fa:code",
|
||||
"defaults": {
|
||||
"name": "Function",
|
||||
"color": "#FF9922"
|
||||
},
|
||||
"iconData": {
|
||||
"icon": "code",
|
||||
"type": "icon"
|
||||
},
|
||||
"displayName": "Function"
|
||||
},
|
||||
{
|
||||
"id": 24,
|
||||
"icon": "fa:code-branch",
|
||||
"defaults": {
|
||||
"name": "Merge",
|
||||
"color": "#00bbcc"
|
||||
},
|
||||
"iconData": {
|
||||
"icon": "code-branch",
|
||||
"type": "icon"
|
||||
},
|
||||
"displayName": "Merge"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
177
cypress/fixtures/Test_Template_1.json
Normal file
177
cypress/fixtures/Test_Template_1.json
Normal file
|
@ -0,0 +1,177 @@
|
|||
{
|
||||
"workflow": {
|
||||
"id": 1205,
|
||||
"name": "Promote new Shopify products on Twitter and Telegram",
|
||||
"views": 478,
|
||||
"recentViews": 9880,
|
||||
"totalViews": 478,
|
||||
"createdAt": "2021-08-24T10:40:50.007Z",
|
||||
"description": "This workflow automatically promotes your new Shopify products on Twitter and Telegram. This workflow is also featured in the blog post [*6 e-commerce workflows to power up your Shopify store*](https://n8n.io/blog/no-code-ecommerce-workflow-automations/#promote-your-new-products-on-social-media).\n\n## Prerequisites\n\n- A Shopify account and [credentials](https://docs.n8n.io/integrations/credentials/shopify/)\n- A Twitter account and [credentials](https://docs.n8n.io/integrations/credentials/twitter/)\n- A Telegram account and [credentials](https://docs.n8n.io/integrations/credentials/telegram/) for the channel you want to send messages to.\n\n## Nodes\n\n- [Shopify Trigger node](https://docs.n8n.io/integrations/trigger-nodes/n8n-nodes-base.shopifytrigger/) triggers the workflow when you create a new product in Shopify.\n- [Twitter node](https://docs.n8n.io/integrations/nodes/n8n-nodes-base.twitter/) posts a tweet with the text \"Hey there, my design is now on a new product! Visit my {shop name} to get this cool {product title} (and check out more {product type})\".\n- [Telegram node](https://docs.n8n.io/integrations/nodes/n8n-nodes-base.telegram/) posts a message with the same text as above in a Telegram channel.",
|
||||
"workflow": {
|
||||
"nodes": [
|
||||
{
|
||||
"name": "Twitter",
|
||||
"type": "n8n-nodes-base.twitter",
|
||||
"position": [
|
||||
720,
|
||||
-220
|
||||
],
|
||||
"parameters": {
|
||||
"text": "=Hey there, my design is now on a new product ✨\nVisit my {{$json[\"vendor\"]}} shop to get this cool{{$json[\"title\"]}} (and check out more {{$json[\"product_type\"]}}) 🛍️",
|
||||
"additionalFields": {}
|
||||
},
|
||||
"credentials": {
|
||||
"twitterOAuth1Api": "twitter"
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"name": "Telegram",
|
||||
"type": "n8n-nodes-base.telegram",
|
||||
"position": [
|
||||
720,
|
||||
-20
|
||||
],
|
||||
"parameters": {
|
||||
"text": "=Hey there, my design is now on a new product!\nVisit my {{$json[\"vendor\"]}} shop to get this cool{{$json[\"title\"]}} (and check out more {{$json[\"product_type\"]}})",
|
||||
"chatId": "123456",
|
||||
"additionalFields": {}
|
||||
},
|
||||
"credentials": {
|
||||
"telegramApi": "telegram_habot"
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"name": "product created",
|
||||
"type": "n8n-nodes-base.shopifyTrigger",
|
||||
"position": [
|
||||
540,
|
||||
-110
|
||||
],
|
||||
"webhookId": "2a7e0e50-8f09-4a2b-bf54-a849a6ac4fe0",
|
||||
"parameters": {
|
||||
"topic": "products/create"
|
||||
},
|
||||
"credentials": {
|
||||
"shopifyApi": "shopify_nodeqa"
|
||||
},
|
||||
"typeVersion": 1
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"product created": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Twitter",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Telegram",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"workflowInfo": {
|
||||
"nodeCount": 3,
|
||||
"nodeTypes": {
|
||||
"n8n-nodes-base.twitter": {
|
||||
"count": 1
|
||||
},
|
||||
"n8n-nodes-base.telegram": {
|
||||
"count": 1
|
||||
},
|
||||
"n8n-nodes-base.shopifyTrigger": {
|
||||
"count": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"username": "lorenanda"
|
||||
},
|
||||
"nodes": [
|
||||
{
|
||||
"id": 49,
|
||||
"icon": "file:telegram.svg",
|
||||
"name": "n8n-nodes-base.telegram",
|
||||
"defaults": {
|
||||
"name": "Telegram"
|
||||
},
|
||||
"iconData": {
|
||||
"type": "file",
|
||||
"fileBuffer": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgNjYgNjYiIGZpbGw9IiNmZmYiIGZpbGwtcnVsZT0iZXZlbm9kZCIgc3Ryb2tlPSIjMDAwIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjx1c2UgeGxpbms6aHJlZj0iI2EiIHg9Ii41IiB5PSIuNSIvPjxzeW1ib2wgaWQ9ImEiIG92ZXJmbG93PSJ2aXNpYmxlIj48ZyBzdHJva2U9Im5vbmUiIGZpbGwtcnVsZT0ibm9uemVybyI+PHBhdGggZD0iTTAgMzJjMCAxNy42NzMgMTQuMzI3IDMyIDMyIDMyczMyLTE0LjMyNyAzMi0zMlM0OS42NzMgMCAzMiAwIDAgMTQuMzI3IDAgMzIiIGZpbGw9IiMzN2FlZTIiLz48cGF0aCBkPSJNMjEuNjYxIDM0LjMzOGwzLjc5NyAxMC41MDhzLjQ3NS45ODMuOTgzLjk4MyA4LjA2OC03Ljg2NCA4LjA2OC03Ljg2NGw4LjQwNy0xNi4yMzctMjEuMTE5IDkuODk4eiIgZmlsbD0iI2M4ZGFlYSIvPjxwYXRoIGQ9Ik0yNi42OTUgMzcuMDM0bC0uNzI5IDcuNzQ2cy0uMzA1IDIuMzczIDIuMDY4IDBsNC42NDQtNC4yMDMiIGZpbGw9IiNhOWM2ZDgiLz48cGF0aCBkPSJNMjEuNzMgMzQuNzEybC03LjgwOS0yLjU0NXMtLjkzMi0uMzc4LS42MzMtMS4yMzdjLjA2Mi0uMTc3LjE4Ni0uMzI4LjU1OS0uNTg4IDEuNzMxLTEuMjA2IDMyLjAyOC0xMi4wOTYgMzIuMDI4LTEyLjA5NnMuODU2LS4yODggMS4zNjEtLjA5N2MuMjMxLjA4OC4zNzguMTg3LjUwMy41NDguMDQ1LjEzMi4wNzEuNDExLjA2OC42ODktLjAwMy4yMDEtLjAyNy4zODYtLjA0NS42NzgtLjE4NCAyLjk3OC01LjcwNiAyNS4xOTgtNS43MDYgMjUuMTk4cy0uMzMgMS4zLTEuNTE0IDEuMzQ1Yy0uNDMyLjAxNi0uOTU2LS4wNzEtMS41ODItLjYxLTIuMzIzLTEuOTk4LTEwLjM1Mi03LjM5NC0xMi4xMjYtOC41OGEuMzQuMzQgMCAwMS0uMTQ2LS4yMzljLS4wMjUtLjEyNS4xMDgtLjI4LjEwOC0uMjhzMTMuOTgtMTIuNDI3IDE0LjM1Mi0xMy43MzFjLjAyOS0uMTAxLS4wNzktLjE1MS0uMjI2LS4xMDctLjkyOS4zNDItMTcuMDI1IDEwLjUwNi0xOC44MDEgMTEuNjI5LS4xMDQuMDY2LS4zOTUuMDIzLS4zOTUuMDIzIi8+PC9nPjwvc3ltYm9sPjwvc3ZnPg=="
|
||||
},
|
||||
"categories": [
|
||||
{
|
||||
"id": 6,
|
||||
"name": "Communication"
|
||||
}
|
||||
],
|
||||
"displayName": "Telegram",
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": 107,
|
||||
"icon": "file:shopify.svg",
|
||||
"name": "n8n-nodes-base.shopifyTrigger",
|
||||
"defaults": {
|
||||
"name": "Shopify Trigger"
|
||||
},
|
||||
"iconData": {
|
||||
"type": "file",
|
||||
"fileBuffer": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgNTggNjYiIGZpbGw9IiNmZmYiIGZpbGwtcnVsZT0iZXZlbm9kZCIgc3Ryb2tlPSIjMDAwIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjx1c2UgeGxpbms6aHJlZj0iI2EiIHg9Ii41IiB5PSIuNSIvPjxzeW1ib2wgaWQ9ImEiIG92ZXJmbG93PSJ2aXNpYmxlIj48ZyBzdHJva2U9Im5vbmUiIGZpbGwtcnVsZT0ibm9uemVybyI+PHBhdGggZD0iTTQ5LjI1NSAxMi40ODRhLjYzMy42MzMgMCAwMC0uNTY0LS41MjdjLS4yMjUtLjAzNy01LjE3LS4zNzYtNS4xNy0uMzc2bC0zLjc3LTMuNzdjLS4zNC0uMzc2LTEuMDkyLS4yNjYtMS4zNzYtLjE4OC0uMDM3IDAtLjc1Mi4yMjUtMS45MjIuNjA1LTEuMTM3LTMuMy0zLjE1LTYuMzA2LTYuNjk2LTYuMzA2aC0uMzAzQzI4LjQzOC42MDUgMjcuMTk0IDAgMjYuMTQ0IDBjLTguMjU2LjAzNy0xMi4yIDEwLjMzMy0xMy40MzQgMTUuNTk0bC01Ljc3IDEuNzdjLTEuNzcuNTY0LTEuODM1LjYwNS0yLjA3MyAyLjI5M0wwIDU3LjE3NSAzNi40NjggNjRsMTkuNzYzLTQuMjZjMC0uMDM3LTYuOTQtNDYuODk3LTYuOTc2LTQ3LjI1NXpNMzQuNDMxIDguODZjLS45MTcuMzAzLTEuOTYzLjYwNS0zLjEuOTQ1di0uNjhhMTUuMDMgMTUuMDMgMCAwMC0uNzUyLTQuOTk5YzEuODQ4LjI4NCAzLjEgMi4zNTcgMy44NDMgNC43MzN6bS02LjA2OC00LjI5OGMuNjAzIDEuNzc4Ljg4MyAzLjY1LjgyNiA1LjUyN3YuMzRsLTYuMzc1IDEuOTYzYzEuMjQ4LTQuNjYgMy41NS02Ljk2MiA1LjU1LTcuODN6bS0yLjQ1LTIuMjkzYTEuOTQgMS45NCAwIDAxMS4wNTUuMzM5Yy0yLjY2IDEuMjM4LTUuNDcyIDQuMzY2LTYuNjc4IDEwLjYyN2wtNS4wNDUgMS41NDZDMTYuNjY4IDEwLjAzIDE5Ljk4OCAyLjI2IDI1LjkxIDIuMjZ6IiBmaWxsPSIjOTViZjQ3Ii8+PHBhdGggZD0iTTQ4LjY5MSAxMS45NTdjLS4yMjUtLjAzNy01LjE3LS4zNzYtNS4xNy0uMzc2bC0zLjc3LTMuNzdhLjc1My43NTMgMCAwMC0uNTI3LS4yMjVMMzYuNDcyIDY0bDE5Ljc2My00LjI2LTYuOTgtNDcuMjE4YS42OC42OCAwIDAwLS41NjQtLjU2NHoiIGZpbGw9IiM1ZThlM2UiLz48cGF0aCBkPSJNMjkuNzU4IDIyLjlsLTIuNDU0IDcuMjQyYTExLjM2IDExLjM2IDAgMDAtNC43NTItMS4xMzNjLTMuODQ4IDAtNC4wMzYgMi40MTItNC4wMzYgMy4wMTggMCAzLjI5OCA4LjYzNiA0LjU2NCA4LjYzNiAxMi4zMzMgMCA2LjEtMy44ODUgMTAuMDMtOS4xIDEwLjAzLTYuMjYgMC05LjQ2Ny0zLjg4NS05LjQ2Ny0zLjg4NWwxLjY2NS01LjU1czMuMjggMi44MyA2LjA3MyAyLjgzYTIuNDcgMi40NyAwIDAwMi41NjQtMi40OWMwLTQuMzQtNy4xLTQuNTI3LTcuMS0xMS42MTggMC01Ljk2MiA0LjI5OC0xMS43NyAxMi45MzQtMTEuNzcgMy4zOTQuMDUgNS4wMTggMSA1LjAxOCAxeiIvPjwvZz48L3N5bWJvbD48L3N2Zz4="
|
||||
},
|
||||
"categories": [
|
||||
{
|
||||
"id": 2,
|
||||
"name": "Sales"
|
||||
}
|
||||
],
|
||||
"displayName": "Shopify Trigger",
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": 325,
|
||||
"icon": "file:x.svg",
|
||||
"name": "n8n-nodes-base.twitter",
|
||||
"defaults": {
|
||||
"name": "X"
|
||||
},
|
||||
"iconData": {
|
||||
"type": "file",
|
||||
"fileBuffer": "data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjQgMjQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTE4LjI0NCAyLjI1aDMuMzA4bC03LjIyNyA4LjI2IDguNTAyIDExLjI0SDE2LjE3bC01LjIxNC02LjgxN0w0Ljk5IDIxLjc1SDEuNjhsNy43My04LjgzNUwxLjI1NCAyLjI1SDguMDhsNC43MTMgNi4yMzF6bS0xLjE2MSAxNy41MmgxLjgzM0w3LjA4NCA0LjEyNkg1LjExN3oiPjwvcGF0aD48L3N2Zz4K"
|
||||
},
|
||||
"categories": [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "Marketing & Content"
|
||||
}
|
||||
],
|
||||
"displayName": "X (Formerly Twitter)",
|
||||
"typeVersion": 2
|
||||
}
|
||||
],
|
||||
"categories": [
|
||||
{
|
||||
"id": 2,
|
||||
"name": "Sales"
|
||||
},
|
||||
{
|
||||
"id": 19,
|
||||
"name": "Marketing & Growth"
|
||||
}
|
||||
],
|
||||
"image": [
|
||||
{
|
||||
"id": 527,
|
||||
"url": "https://n8niostorageaccount.blob.core.windows.net/n8nio-strapi-blobs-prod/assets/89a078b208fe4c6181902608b1cd1332.png"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
182
cypress/fixtures/Test_Template_2.json
Normal file
182
cypress/fixtures/Test_Template_2.json
Normal file
File diff suppressed because one or more lines are too long
|
@ -40,7 +40,7 @@
|
|||
{
|
||||
"parameters": {},
|
||||
"id": "ef63cdc5-50bc-4525-9873-7e7f7589a60e",
|
||||
"name": "When clicking \"Execute Workflow\"",
|
||||
"name": "When clicking \"Test Workflow\"",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
|
@ -50,7 +50,6 @@
|
|||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "sort",
|
||||
"sortFieldsUi": {
|
||||
"sortField": [
|
||||
{
|
||||
|
@ -61,9 +60,9 @@
|
|||
"options": {}
|
||||
},
|
||||
"id": "555a150c-d735-4331-b628-c1f1cfed2da1",
|
||||
"name": "Item Lists",
|
||||
"type": "n8n-nodes-base.itemLists",
|
||||
"typeVersion": 2,
|
||||
"name": "Sort",
|
||||
"type": "n8n-nodes-base.sort",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
-280,
|
||||
580
|
||||
|
@ -182,7 +181,7 @@
|
|||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Item Lists",
|
||||
"node": "Sort",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
|
@ -200,7 +199,7 @@
|
|||
]
|
||||
]
|
||||
},
|
||||
"When clicking \"Execute Workflow\"": {
|
||||
"When clicking \"Test Workflow\"": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
|
@ -216,7 +215,7 @@
|
|||
]
|
||||
]
|
||||
},
|
||||
"Item Lists": {
|
||||
"Sort": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
|
@ -289,4 +288,4 @@
|
|||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
153
cypress/fixtures/Test_workflow_filter.json
Normal file
153
cypress/fixtures/Test_workflow_filter.json
Normal file
|
@ -0,0 +1,153 @@
|
|||
{
|
||||
"name": "Filter test",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "f332a7d1-31b4-4e78-b31e-9e8db945bf3f",
|
||||
"name": "When clicking \"Test Workflow\"",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
-60,
|
||||
480
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "return [\n {\n \"label\": \"Apple\",\n tags: [],\n meta: {foo: 'bar'}\n },\n {\n \"label\": \"Banana\",\n tags: ['exotic'],\n meta: {}\n },\n {\n \"label\": \"Pear\",\n tags: ['other'],\n meta: {}\n },\n {\n \"label\": \"Orange\",\n meta: {}\n }\n]"
|
||||
},
|
||||
"id": "60697c7f-3948-4790-97ba-8aba03d02ac2",
|
||||
"name": "Code",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [
|
||||
160,
|
||||
480
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"conditions": {
|
||||
"options": {
|
||||
"caseSensitive": true,
|
||||
"leftValue": ""
|
||||
},
|
||||
"conditions": [
|
||||
{
|
||||
"leftValue": "={{ $json.tags }}",
|
||||
"rightValue": "exotic",
|
||||
"operator": {
|
||||
"type": "array",
|
||||
"operation": "contains",
|
||||
"rightType": "any"
|
||||
}
|
||||
},
|
||||
{
|
||||
"leftValue": "={{ $json.meta }}",
|
||||
"rightValue": "",
|
||||
"operator": {
|
||||
"type": "object",
|
||||
"operation": "notEmpty",
|
||||
"singleValue": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"leftValue": "={{ $json.label }}",
|
||||
"rightValue": "Pea",
|
||||
"operator": {
|
||||
"type": "string",
|
||||
"operation": "startsWith",
|
||||
"rightType": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"combinator": "or"
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"id": "7531191b-5ac3-45dc-8afb-27ae83d8f33a",
|
||||
"name": "If",
|
||||
"type": "n8n-nodes-base.if",
|
||||
"typeVersion": 2,
|
||||
"position": [
|
||||
380,
|
||||
480
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "d8c614ea-0bbf-4b12-ad7d-c9ebe09ce583",
|
||||
"name": "Then",
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
600,
|
||||
400
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "69364770-60d2-4ef4-9f29-9570718a9a10",
|
||||
"name": "Else",
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
600,
|
||||
580
|
||||
]
|
||||
}
|
||||
],
|
||||
"pinData": {},
|
||||
"connections": {
|
||||
"When clicking \"Test Workflow\"": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Code",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Code": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "If",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"If": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Then",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"node": "Else",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": false,
|
||||
"settings": {
|
||||
"executionOrder": "v1"
|
||||
},
|
||||
"versionId": "a6249f48-d88f-4b80-9ed9-79555e522d48",
|
||||
"id": "BWUTRs5RHxVgQ4uT",
|
||||
"meta": {
|
||||
"instanceId": "78577815012af39cf16dad7a787b0898c42fb7514b8a7f99b2136862c2af502c"
|
||||
},
|
||||
"tags": []
|
||||
}
|
78
cypress/fixtures/Test_workflow_form_switch.json
Normal file
78
cypress/fixtures/Test_workflow_form_switch.json
Normal file
|
@ -0,0 +1,78 @@
|
|||
{
|
||||
"name": "My workflow 8",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {
|
||||
"path": "d1cba915-ca18-4425-bcfb-133205fc815a",
|
||||
"formTitle": "test",
|
||||
"formFields": {
|
||||
"values": [
|
||||
{
|
||||
"fieldLabel": "test"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"id": "9e685367-fb94-4376-a9a4-7f311d9f7e2d",
|
||||
"name": "n8n Form Trigger",
|
||||
"type": "n8n-nodes-base.formTrigger",
|
||||
"typeVersion": 2,
|
||||
"position": [
|
||||
620,
|
||||
580
|
||||
],
|
||||
"webhookId": "d1cba915-ca18-4425-bcfb-133205fc815a"
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "0f4dfe66-51c0-4378-9eab-680f8140a572",
|
||||
"name": "Switch",
|
||||
"type": "n8n-nodes-base.switch",
|
||||
"typeVersion": 2,
|
||||
"position": [
|
||||
800,
|
||||
580
|
||||
]
|
||||
}
|
||||
],
|
||||
"pinData": {
|
||||
"n8n Form Trigger": [
|
||||
{
|
||||
"json": {
|
||||
"name": "First item",
|
||||
"code": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"name": "Second item",
|
||||
"code": 2
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"connections": {
|
||||
"n8n Form Trigger": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Switch",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": false,
|
||||
"settings": {
|
||||
"executionOrder": "v1"
|
||||
},
|
||||
"versionId": "d6c14bc8-a69f-47bb-b5ba-fe6e9db0a3a4",
|
||||
"id": "UQSimcMQJGbTeTLG",
|
||||
"meta": {
|
||||
"instanceId": "a786b722078489c1fa382391a9f3476c2784761624deb2dfb4634827256d51a0"
|
||||
},
|
||||
"tags": []
|
||||
}
|
49
cypress/fixtures/Test_workflow_ndv_version.json
Normal file
49
cypress/fixtures/Test_workflow_ndv_version.json
Normal file
|
@ -0,0 +1,49 @@
|
|||
{
|
||||
"name": "Node versions",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "aadaed66-84ed-4cf8-bf21-082e9a65db76",
|
||||
"name": "When clicking \"Test Workflow\"",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
1540,
|
||||
780
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "93d73a85-82f0-4380-a032-713d5dc82b32",
|
||||
"name": "Function",
|
||||
"type": "n8n-nodes-base.function",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
2040,
|
||||
780
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "50f322d9-c622-4dd0-8d38-e851502739dd",
|
||||
"name": "Edit Fields (old)",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 2,
|
||||
"position": [
|
||||
1880,
|
||||
780
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "93aaadac-55fe-4618-b1eb-f63e61d1446a",
|
||||
"name": "Edit Fields (latest)",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.2,
|
||||
"position": [
|
||||
1720,
|
||||
780
|
||||
]
|
||||
}
|
||||
],
|
||||
"pinData": {},
|
||||
"connections": {}
|
||||
}
|
|
@ -47,7 +47,7 @@
|
|||
{
|
||||
"parameters": {},
|
||||
"id": "58512a93-dabf-4584-817f-27c608c1bdd5",
|
||||
"name": "When clicking \"Execute Workflow\"",
|
||||
"name": "When clicking \"Test Workflow\"",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
|
@ -69,7 +69,7 @@
|
|||
]
|
||||
]
|
||||
},
|
||||
"When clicking \"Execute Workflow\"": {
|
||||
"When clicking \"Test Workflow\"": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
|
|
|
@ -47,7 +47,7 @@
|
|||
{
|
||||
"parameters": {},
|
||||
"id": "3dc7cf26-ff25-4437-b9fd-0e8b127ebec9",
|
||||
"name": "When clicking \"Execute Workflow\"",
|
||||
"name": "When clicking \"Test Workflow\"",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
|
@ -552,7 +552,7 @@
|
|||
]
|
||||
]
|
||||
},
|
||||
"When clicking \"Execute Workflow\"": {
|
||||
"When clicking \"Test Workflow\"": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
|
|
151
cypress/fixtures/Test_workflow_webhook_with_pin_data.json
Normal file
151
cypress/fixtures/Test_workflow_webhook_with_pin_data.json
Normal file
|
@ -0,0 +1,151 @@
|
|||
{
|
||||
"name": "PinData Test",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "0a60e507-7f34-41c0-a0f9-697d852033b6",
|
||||
"name": "When clicking \"Test Workflow\"",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
780,
|
||||
320
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"path": "b0d79ddb-df2d-49b1-8555-9fa2b482608f",
|
||||
"responseMode": "lastNode",
|
||||
"options": {}
|
||||
},
|
||||
"id": "66425ce3-450d-4aa6-a53b-a701ab89c2de",
|
||||
"name": "Webhook",
|
||||
"type": "n8n-nodes-base.webhook",
|
||||
"typeVersion": 1.1,
|
||||
"position": [
|
||||
780,
|
||||
540
|
||||
],
|
||||
"webhookId": "b0d79ddb-df2d-49b1-8555-9fa2b482608f"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"fields": {
|
||||
"values": [
|
||||
{
|
||||
"name": "nodeData",
|
||||
"stringValue": "init"
|
||||
}
|
||||
]
|
||||
},
|
||||
"include": "none",
|
||||
"options": {}
|
||||
},
|
||||
"id": "3211b3c5-49e9-4694-8f86-7a5783bc653a",
|
||||
"name": "Init Data",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.2,
|
||||
"position": [
|
||||
1000,
|
||||
320
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"fields": {
|
||||
"values": [
|
||||
{
|
||||
"name": "nodeData",
|
||||
"stringValue": "pin"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"id": "97b31120-4720-4632-9d35-356f345119f7",
|
||||
"name": "Pin Data",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.2,
|
||||
"position": [
|
||||
1240,
|
||||
320
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "1ee7be4f-7006-43bf-bb0c-29db3058a399",
|
||||
"name": "End",
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
1460,
|
||||
320
|
||||
]
|
||||
}
|
||||
],
|
||||
"pinData": {
|
||||
"Pin Data": [
|
||||
{
|
||||
"json": {
|
||||
"nodeData": "pin-overwritten"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"connections": {
|
||||
"When clicking \"Test Workflow\"": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Init Data",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Webhook": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Init Data",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Init Data": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Pin Data",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Pin Data": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "End",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": false,
|
||||
"settings": {
|
||||
"executionOrder": "v1"
|
||||
},
|
||||
"versionId": "ded8577a-3ed2-4611-842c-a7922ec58b98",
|
||||
"id": "weofVLZo0ssmPDrV",
|
||||
"meta": {
|
||||
"instanceId": "021d3c82ba2d3bc090cbf4fc81c9312668bcc34297e022bb3438c5c88a43a5ff"
|
||||
},
|
||||
"tags": []
|
||||
}
|
53
cypress/fixtures/Test_workflow_xml_output.json
Normal file
53
cypress/fixtures/Test_workflow_xml_output.json
Normal file
|
@ -0,0 +1,53 @@
|
|||
{
|
||||
"meta": {
|
||||
"instanceId": "2d1cf27f75b18bb9e146336f791c37884f4fc7ddb97c2def27c0444d106778bf"
|
||||
},
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "8108d313-8b03-4aa4-963d-cd1c0fe8f85c",
|
||||
"name": "When clicking \"Test Workflow\"",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
420,
|
||||
220
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"fields": {
|
||||
"values": [
|
||||
{
|
||||
"name": "body",
|
||||
"stringValue": "<?xml version=\"1.0\" encoding=\"UTF-8\"?> <library> <book> <title>Introduction to XML</title> <author>John Doe</author> <publication_year>2020</publication_year> <isbn>1234567890</isbn> </book> <book> <title>Data Science Basics</title> <author>Jane Smith</author> <publication_year>2019</publication_year> <isbn>0987654321</isbn> </book> <book> <title>Programming in Python</title> <author>Bob Johnson</author> <publication_year>2021</publication_year> <isbn>5432109876</isbn> </book> </library>"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"id": "45888152-7c5f-4d88-9039-660c594da084",
|
||||
"name": "Edit Fields",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.2,
|
||||
"position": [
|
||||
640,
|
||||
220
|
||||
]
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"When clicking \"Test Workflow\"": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Edit Fields",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"pinData": {}
|
||||
}
|
188
cypress/fixtures/Workflow_template_write_http_query.json
Normal file
188
cypress/fixtures/Workflow_template_write_http_query.json
Normal file
File diff suppressed because one or more lines are too long
|
@ -6,7 +6,7 @@
|
|||
{
|
||||
"parameters": {},
|
||||
"id": "bcb6abdf-d34b-4ea7-a8ed-58155b708c43",
|
||||
"name": "When clicking \"Execute Workflow\"",
|
||||
"name": "When clicking \"Test Workflow\"",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
|
@ -90,7 +90,7 @@
|
|||
}
|
||||
],
|
||||
"connections": {
|
||||
"When clicking \"Execute Workflow\"": {
|
||||
"When clicking \"Test Workflow\"": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
|
|
1071
cypress/fixtures/templates_search/all_templates_search_response.json
Normal file
1071
cypress/fixtures/templates_search/all_templates_search_response.json
Normal file
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
19
cypress/fixtures/templates_search/test_template_import.json
Normal file
19
cypress/fixtures/templates_search/test_template_import.json
Normal file
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"id": 60,
|
||||
"name": "test1 test1",
|
||||
"workflow": {
|
||||
"nodes": [
|
||||
{
|
||||
"name": "Start",
|
||||
"type": "n8n-nodes-base.start",
|
||||
"position": [
|
||||
250,
|
||||
300
|
||||
],
|
||||
"parameters": {},
|
||||
"typeVersion": 1
|
||||
}
|
||||
],
|
||||
"connections": {}
|
||||
}
|
||||
}
|
150
cypress/fixtures/templates_search/test_template_preview.json
Normal file
150
cypress/fixtures/templates_search/test_template_preview.json
Normal file
|
@ -0,0 +1,150 @@
|
|||
{
|
||||
"workflow": {
|
||||
"id": 60,
|
||||
"name": "test1 test1",
|
||||
"views": 120000000,
|
||||
"recentViews": 0,
|
||||
"totalViews": 120000000,
|
||||
"createdAt": "2019-08-30T16:39:31.362Z",
|
||||
"description": "here is a description. here is a description. here is a description. \n\n![Screenshot from 20190806 091433.png](fileId:88)",
|
||||
"workflow": {
|
||||
"nodes": [
|
||||
{
|
||||
"name": "Start",
|
||||
"type": "n8n-nodes-base.start",
|
||||
"position": [
|
||||
250,
|
||||
300
|
||||
],
|
||||
"parameters": {},
|
||||
"typeVersion": 1
|
||||
}
|
||||
],
|
||||
"connections": {}
|
||||
},
|
||||
"lastUpdatedBy": null,
|
||||
"workflowInfo": {
|
||||
"nodeCount": 1,
|
||||
"nodeTypes": {
|
||||
"n8n-nodes-base.start": {
|
||||
"count": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"username": "admin"
|
||||
},
|
||||
"nodes": [
|
||||
{
|
||||
"id": 11,
|
||||
"icon": "file:amqp.png",
|
||||
"name": "n8n-nodes-base.amqpTrigger",
|
||||
"defaults": {
|
||||
"name": "AMQP Trigger"
|
||||
},
|
||||
"iconData": {
|
||||
"type": "file",
|
||||
"fileBuffer": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAA8CAYAAAA6/NlyAAAB7UlEQVRoge2W4W3CMBCFj26QjkBHSEdIR4AR6Ah0BBgBRqAjhBFgBBghHaEVlV29PN0lDr+o9D7JEjhn+975bJ8JIYQQQgghhBBCCCGEEA9CY2bf0NaBW2uyu7UN2XSOzTyY60J2BzNbObbsH7eTmS2mhHJHE1wmCD7A93ngEAquHaHc2omCcysSXQW74g32BHfwfTEiuCoQm9vuDsEndPYpELxKjjBj0foCEXX6XdM3by3c7aOZPZvZzMzeaBzbIh9pzIuZXaG/RqNIMAq7Ur8XCHQ2kx3LC56DMQ39X4LI23zbAd88ruRHD09wTVF5p+/eBZI5g7O8w5FgXOvsZAI7PxRwS4HGIPbm8wRjBL/Sgp/QNyQYHWySmOxgJBgFeGnPfZHgDVyufET+YMEVCdo7gziCTBbGmRKlGQpCMXOnj+1L6B0JFsxndO3cjjZyjo6OnZeqGb5gqhTQS3qKeK1SwbesfB3IrF/awqu+g8Dgs5SLE37SciHiPUv8rLVp7k2wdl63tDDqgTs8lqpINWGXbSTKe9rlJgXME7C9I6V7oGAWsEzv2gzeN2TstkbCZyIJWBYKWUwtF4foKGU9TpRGdZDSdVDpDNXSVVBLt5TeucS9K6X/E3USX3rshBBCCCGEEEIIIYQQ4tExsx8PuuPnwhCIbgAAAABJRU5ErkJggg=="
|
||||
},
|
||||
"categories": [
|
||||
{
|
||||
"id": 5,
|
||||
"name": "Development"
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"name": "Communication"
|
||||
}
|
||||
],
|
||||
"displayName": "AMQP Trigger",
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": 18,
|
||||
"icon": "file:autopilot.svg",
|
||||
"name": "n8n-nodes-base.autopilot",
|
||||
"defaults": {
|
||||
"name": "Autopilot"
|
||||
},
|
||||
"iconData": {
|
||||
"type": "file",
|
||||
"fileBuffer": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjM4IDI2IDM1IDM1Ij48Y2lyY2xlIGN4PSI1MCIgY3k9IjUwIiByPSI0MCIgc3Ryb2tlPSIjMThkNGIyIiBzdHJva2Utd2lkdGg9IjMiIGZpbGw9IiMxOGQ0YjIiLz48cGF0aCBmaWxsPSIjZmZmIiBkPSJNNDUuNCA0Mi42aDE5LjlsMy40LTQuOEg0MmwzLjQgNC44em0zLjEgOC4zaDEzLjFsMy40LTQuOEg0NS40bDMuMSA0Ljh6bTU0LS43Ii8+PC9zdmc+"
|
||||
},
|
||||
"categories": [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "Marketing"
|
||||
}
|
||||
],
|
||||
"displayName": "Autopilot",
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": 20,
|
||||
"icon": "file:lambda.svg",
|
||||
"name": "n8n-nodes-base.awsLambda",
|
||||
"defaults": {
|
||||
"name": "AWS Lambda"
|
||||
},
|
||||
"iconData": {
|
||||
"type": "file",
|
||||
"fileBuffer": "data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjI1MDAiIHZpZXdCb3g9Ii0zLjAyMyAtMC4yMiA0MjAuOTIzIDQzMy41NCIgd2lkdGg9IjI0NDMiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTIwOC40NSAyMjcuODljLTEuNTkgMi4yNi0yLjkzIDQuMTItNC4yMiA2cS0zMC44NiA0NS40Mi02MS43IDkwLjgzLTI4LjY5IDQyLjI0LTU3LjQ0IDg0LjQzYTMuODggMy44OCAwIDAxLTIuNzMgMS41OXEtNDAuNTktLjM1LTgxLjE2LS44OGMtLjMgMC0uNjEtLjA5LTEuMi0uMThhMTQuNDQgMTQuNDQgMCAwMS43Ni0xLjY1cTI4LjMxLTQzLjg5IDU2LjYyLTg3Ljc2IDI1LjExLTM4Ljg4IDUwLjI1LTc3Ljc0IDI3Ljg2LTQzLjE4IDU1LjY5LTg2LjQyYzIuNzQtNC4yNSA1LjU5LTguNDIgOC4xOS0xMi43NWE1LjI2IDUuMjYgMCAwMC41Ni0zLjgzYy01LTE1Ljk0LTEwLjEtMzEuODQtMTUuMTktNDcuNzQtMi4xOC02LjgxLTQuNDYtMTMuNTgtNi41LTIwLjQzLS42Ni0yLjItMS43NS0yLjg3LTQtMi44Ni0xNyAuMDctMzMuOS4wNS01MC44NS4wNS0zLjIyIDAtMy4yMyAwLTMuMjMtMy4xOCAwLTIwLjg0IDAtNDEuNjgtLjA2LTYyLjUyIDAtMi4zMi43Ni0yLjg0IDIuOTQtMi44NHE1MS4xOS4wOSAxMDIuNCAwYTMuMjkgMy4yOSAwIDAxMy42IDIuNDNxMjcgNjcuOTEgNTQgMTM1Ljc3IDMxLjUgNzkuMTQgNjMgMTU4LjNjNi41MiAxNi4zOCAxMy4wOSAzMi43NSAxOS41NCA0OS4xNy43NyAyIDEuNTcgMi4zOCAzLjU5IDEuNzYgMTcuODktNS41MyAzNS44Mi0xMC45MSA1My43LTE2LjQ1IDIuMjUtLjcgMy4wNy0uMjMgMy43NyAyIDYuMSAxOS4xNyAxMi4zMiAzOC4zIDE4LjUgNTcuNDUuMjEuNjYuMzcgMS4zMy42MiAyLjI1LTEuMjguNDctMi40OCAxLTMuNzEgMS4zNHEtNjEgMTkuMzMtMTIxLjkzIDM4LjY4Yy0xLjk0LjYxLTIuNTItLjA1LTMuMTctMS42OHEtMTguNjEtNDcuMTYtMzcuMzEtOTQuMjgtMTguMjktNDYuMTQtMzYuNi05Mi4yOGMtMS44My00LjYyLTMuNjMtOS4yNi01LjQ2LTEzLjg4LS4yOS0uNzktLjY5LTEuNDgtMS4yNy0yLjd6IiBmaWxsPSIjZmE3ZTE0Ii8+PC9zdmc+"
|
||||
},
|
||||
"categories": [
|
||||
{
|
||||
"id": 5,
|
||||
"name": "Development"
|
||||
}
|
||||
],
|
||||
"displayName": "AWS Lambda",
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": 40,
|
||||
"icon": "file:clearbit.svg",
|
||||
"name": "n8n-nodes-base.clearbit",
|
||||
"defaults": {
|
||||
"name": "Clearbit"
|
||||
},
|
||||
"iconData": {
|
||||
"type": "file",
|
||||
"fileBuffer": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3MiIgaGVpZ2h0PSI3MiI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJhIiB4MT0iNTAlIiB4Mj0iMTAwJSIgeTE9IjAlIiB5Mj0iMTAwJSI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI0RFRjJGRSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI0RCRjFGRSIvPjwvbGluZWFyR3JhZGllbnQ+PGxpbmVhckdyYWRpZW50IGlkPSJiIiB4MT0iMCUiIHgyPSI1MCUiIHkxPSIwJSIgeTI9IjEwMCUiPjxzdG9wIG9mZnNldD0iMCUiIHN0b3AtY29sb3I9IiM1N0JDRkQiLz48c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiM1MUI1RkQiLz48L2xpbmVhckdyYWRpZW50PjxsaW5lYXJHcmFkaWVudCBpZD0iYyIgeDE9IjM3LjUlIiB4Mj0iNjIuNSUiIHkxPSIwJSIgeTI9IjEwMCUiPjxzdG9wIG9mZnNldD0iMCUiIHN0b3AtY29sb3I9IiMxQ0E3RkQiLz48c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiMxNDhDRkMiLz48L2xpbmVhckdyYWRpZW50PjwvZGVmcz48ZyBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxwYXRoIGZpbGw9InVybCgjYSkiIGQ9Ik03MiAzNnYxNi43N2wtLjAwNC44NjhjLS4wNiA2LjAzNS0uNzUgOC4zNTMtMiAxMC42ODhhMTMuNjMgMTMuNjMgMCAwMS01LjY3IDUuNjdsLS4zMjYuMTcxQzYxLjY1OCA3MS4zNjQgNTkuMTYgNzIgNTIuNzcgNzJIMzZWMzZoMzZ6Ii8+PHBhdGggZmlsbD0idXJsKCNiKSIgZD0iTTY0LjMyNiAyLjAwM2ExMy42MyAxMy42MyAwIDAxNS42NyA1LjY3bC4xNzEuMzI3QzcxLjM2NCAxMC4zNDIgNzIgMTIuODQgNzIgMTkuMjNWMzZIMzZWMGgxNi43N2M2LjY4NyAwIDkuMTEyLjY5NiAxMS41NTYgMi4wMDN6Ii8+PHBhdGggZmlsbD0idXJsKCNjKSIgZD0iTTM2IDB2NzJIMTkuMjNsLS44NjgtLjAwNGMtNi4wMzUtLjA2LTguMzUzLS43NS0xMC42ODgtMmExMy42MyAxMy42MyAwIDAxLTUuNjctNS42N0wxLjgzMiA2NEMuNjM2IDYxLjY1OCAwIDU5LjE2IDAgNTIuNzdWMTkuMjNjMC02LjY4Ny42OTYtOS4xMTIgMi4wMDMtMTEuNTU2YTEzLjYzIDEzLjYzIDAgMDE1LjY3LTUuNjdMOCAxLjgzMkMxMC4zNDIuNjM2IDEyLjg0IDAgMTkuMjMgMEgzNnoiLz48L2c+PC9zdmc+"
|
||||
},
|
||||
"categories": [
|
||||
{
|
||||
"id": 2,
|
||||
"name": "Sales"
|
||||
}
|
||||
],
|
||||
"displayName": "Clearbit",
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": 51,
|
||||
"icon": "file:convertKit.svg",
|
||||
"name": "n8n-nodes-base.convertKitTrigger",
|
||||
"defaults": {
|
||||
"name": "ConvertKit Trigger"
|
||||
},
|
||||
"iconData": {
|
||||
"type": "file",
|
||||
"fileBuffer": "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTcyIiBoZWlnaHQ9IjE2MCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJNODIuNzIgMTI2LjMxNmMyOS43NyAwIDUyLjc4LTIyLjYyMiA1Mi43OC01MC41MjYgMC0yNi4xNDMtMjEuNjE3LTQyLjEwNi0zNS45MzUtNDIuMTA2LTE5Ljk0NSAwLTM1LjkzIDE0LjA4NC0zOC4xOTggMzQuOTg4LS40MTggMy44NTYtMy40NzYgNy4wOS03LjM1NSA3LjA2MS02LjQyMy0uMDQ2LTE1Ljc0Ni0uMS0yMS42NTgtLjA4LTIuNTU1LjAwOC00LjY2OS0yLjA2NS00LjU0My00LjYxOC44OS0xOC4xMjMgNi45MTQtMzUuMDcgMTguNDAyLTQ4LjA4N0M1OC45NzYgOC40ODggNzcuNTYxIDAgOTkuNTY1IDBjMzYuOTY5IDAgNzEuODY5IDMzLjc4NiA3MS44NjkgNzUuNzkgMCA0Ni41MDgtMzguMzEyIDg0LjIxLTg3LjkyNyA4NC4yMS0zNS4zODQgMC03MS4wMjEtMjMuMjU4LTgzLjQ2NC01NS43NzVhLjcwMi43MDIgMCAwMS0uMDMtLjM3N2MuMTY1LS45NjIuNDk0LTEuODQxLjgxOC0yLjcwNy40NzEtMS4yNTguOTMxLTIuNDg4Ljg2NC0zLjkwNmwtLjIxNS00LjUyOWE1LjUyMyA1LjUyMyAwIDAxMy4xOC01LjI2M2wxLjc5OC0uODQyYTYuOTgyIDYuOTgyIDAgMDAzLjkxMi01LjA3NSA2Ljk5MyA2Ljk5MyAwIDAxNi44ODctNS43MzZjNS4yODIgMCA5Ljg3NSAzLjUxNSAxMS41OSA4LjUxMiA4LjMwNyAyNC4yMTIgMjEuNTExIDQyLjAxNCA1My44NzMgNDIuMDE0eiIgZmlsbD0iI0ZCNjk3MCIvPjwvc3ZnPg=="
|
||||
},
|
||||
"categories": [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "Marketing"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "Sales"
|
||||
}
|
||||
],
|
||||
"displayName": "ConvertKit Trigger",
|
||||
"typeVersion": 1
|
||||
}
|
||||
],
|
||||
"categories": [],
|
||||
"image": []
|
||||
}
|
||||
}
|
52
cypress/fixtures/workflow-with-unknown-credentials.json
Normal file
52
cypress/fixtures/workflow-with-unknown-credentials.json
Normal file
|
@ -0,0 +1,52 @@
|
|||
{
|
||||
"meta": {
|
||||
"instanceId": "123"
|
||||
},
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {
|
||||
"resource": "credential",
|
||||
"name": "123",
|
||||
"credentialTypeName": "123"
|
||||
},
|
||||
"id": "a01f79f6-e8c3-44c5-be5e-4bc482e23172",
|
||||
"name": "n8n",
|
||||
"type": "n8n-nodes-base.n8n",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
540,
|
||||
240
|
||||
],
|
||||
"credentials": {
|
||||
"n8nApi": {
|
||||
"id": "10",
|
||||
"name": "n8n account"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "acdd1bdc-c642-4ea6-ad67-f4201b640cfa",
|
||||
"name": "When clicking \"Test Workflow\"",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
300,
|
||||
240
|
||||
]
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"When clicking \"Test Workflow\"": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "n8n",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
90
cypress/fixtures/workflow-with-unknown-nodes.json
Normal file
90
cypress/fixtures/workflow-with-unknown-nodes.json
Normal file
|
@ -0,0 +1,90 @@
|
|||
{
|
||||
"meta": {
|
||||
"instanceId": "15bbf37b6a515ccc2f534cabcd8bd171ca33583ff7744b1e9420e5ce68e615bb"
|
||||
},
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "40720511-19b6-4421-bdb0-3fb6efef4bc5",
|
||||
"name": "When clicking \"Test Workflow\"",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
280,
|
||||
320
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "acdd1bdc-c642-4ea6-ad67-f4201b640cfa",
|
||||
"name": "Unknown node 1",
|
||||
"type": "n8n-nodes-base.thisNodeDoesntExist",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
400,
|
||||
500
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "acdd1bdc-c642-4ea6-ad67-f4201b640ffa",
|
||||
"name": "Unknown node 2",
|
||||
"type": "n8n-nodes-base.thisNodeDoesntExistEither",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
600,
|
||||
500
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"options": {}
|
||||
},
|
||||
"id": "fbe5163b-7474-4741-980a-e4956789be0a",
|
||||
"name": "Edit Fields",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.2,
|
||||
"position": [
|
||||
500,
|
||||
320
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"options": {}
|
||||
},
|
||||
"id": "163313b9-64ff-4ffc-b00f-09b267d8132c",
|
||||
"name": "Edit Fields1",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3.2,
|
||||
"position": [
|
||||
720,
|
||||
320
|
||||
]
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"When clicking \"Test Workflow\"": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Edit Fields",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Edit Fields": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Edit Fields1",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
21
cypress/pages/demo.ts
Normal file
21
cypress/pages/demo.ts
Normal file
|
@ -0,0 +1,21 @@
|
|||
/**
|
||||
* Actions
|
||||
*/
|
||||
|
||||
export function vistDemoPage(theme?: 'dark' | 'light') {
|
||||
const query = theme ? `?theme=${theme}` : '';
|
||||
cy.visit('/workflows/demo' + query);
|
||||
cy.waitForLoad();
|
||||
cy.window().then((win) => {
|
||||
// @ts-ignore
|
||||
win.preventNodeViewBeforeUnload = true;
|
||||
});
|
||||
}
|
||||
|
||||
export function importWorkflow(workflow: object) {
|
||||
const OPEN_WORKFLOW = {command: 'openWorkflow', workflow};
|
||||
cy.window().then($window => {
|
||||
const message = JSON.stringify(OPEN_WORKFLOW);
|
||||
$window.postMessage(message, '*')
|
||||
});
|
||||
}
|
|
@ -20,6 +20,7 @@ export class NodeCreator extends BasePage {
|
|||
communityNodeTooltip: () => cy.getByTestId('node-item-community-tooltip'),
|
||||
noResults: () => cy.getByTestId('node-creator-no-results'),
|
||||
nodeItemName: () => cy.getByTestId('node-creator-item-name'),
|
||||
nodeItemDescription: () => cy.getByTestId('node-creator-item-description'),
|
||||
activeSubcategory: () => cy.getByTestId('nodes-list-header'),
|
||||
expandedCategories: () =>
|
||||
this.getters.creatorItem().find('>div').filter('.active').invoke('text'),
|
||||
|
|
|
@ -10,3 +10,6 @@ export * from './ndv';
|
|||
export * from './bannerStack';
|
||||
export * from './workflow-executions-tab';
|
||||
export * from './signin';
|
||||
export * from './workflow-history';
|
||||
export * from './workerView';
|
||||
export * from './settings-public-api';
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue