Compare commits
74 Commits
badge-indi
...
backport-6
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
910d1ab3aa | ||
|
|
55d8e41d20 | ||
|
|
61660a8128 | ||
|
|
b575a8d7a2 | ||
|
|
750a9d882a | ||
|
|
2febb24d6c | ||
|
|
d42e38300d | ||
|
|
20687c2945 | ||
|
|
efb19eac80 | ||
|
|
2f73262ca6 | ||
|
|
ae01f8d51a | ||
|
|
b32a1e9ce8 | ||
|
|
94cb6bf294 | ||
|
|
379f27a001 | ||
|
|
17ae4cbf53 | ||
|
|
97949c61fb | ||
|
|
84ce6c183d | ||
|
|
9adf0c179f | ||
|
|
84189a208e | ||
|
|
3b38e4353a | ||
|
|
5e972d8512 | ||
|
|
bce26f646a | ||
|
|
bb11639b75 | ||
|
|
0afc6995d2 | ||
|
|
2d98008942 | ||
|
|
96d76f0052 | ||
|
|
5229a48ef5 | ||
|
|
072b234a13 | ||
|
|
065b848e58 | ||
|
|
aa5a8fcb95 | ||
|
|
09bad9c1e8 | ||
|
|
76bd9ab43e | ||
|
|
088a57a43c | ||
|
|
718655ae65 | ||
|
|
4aa45f1259 | ||
|
|
3954ac8584 | ||
|
|
71e851acfa | ||
|
|
570f51e60c | ||
|
|
938ea6b81b | ||
|
|
eabc7ec19a | ||
|
|
c067fcc27f | ||
|
|
bed58a09c0 | ||
|
|
1a019437ee | ||
|
|
648190bf65 | ||
|
|
ecc809c5c0 | ||
|
|
dcf4454343 | ||
|
|
5e131372e2 | ||
|
|
fcb01815ac | ||
|
|
63c91a62fd | ||
|
|
797b1c5bae | ||
|
|
dd1af641db | ||
|
|
f0a19ebb1d | ||
|
|
415589261e | ||
|
|
3a5ed57f50 | ||
|
|
fd2a52500c | ||
|
|
fb07de4c38 | ||
|
|
0d4d68fec9 | ||
|
|
b708ebf540 | ||
|
|
b210e63f3c | ||
|
|
a9db25ecc3 | ||
|
|
ed8b17e777 | ||
|
|
32e6cfa95f | ||
|
|
8b71058c1f | ||
|
|
e827138f6f | ||
|
|
31eb9ea640 | ||
|
|
d31df54f57 | ||
|
|
5e97614aa1 | ||
|
|
07ce463302 | ||
|
|
0239a83da2 | ||
|
|
ab312ce3d7 | ||
|
|
f7e4e4f1b8 | ||
|
|
d1577bf18f | ||
|
|
388fdcbfde | ||
|
|
963741f554 |
55
.github/actions/setup-comfyui-server/action.yml
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
name: Setup ComfyUI Server
|
||||
description: 'Setup ComfyUI server for continuous integration (with ComfyUI_devtools node installed)'
|
||||
inputs:
|
||||
extra_server_params:
|
||||
description: 'Additional parameters to pass to ComfyUI server'
|
||||
required: false
|
||||
default: ''
|
||||
launch_server:
|
||||
description: 'Whether to launch the server after setup'
|
||||
required: false
|
||||
default: 'false'
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
# Note: this workflow assume frontend repo is checked out and is built in ../dist
|
||||
|
||||
# Checkout ComfyUI repo, install the dev_tools node and start server
|
||||
- name: Checkout ComfyUI
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
repository: 'comfyanonymous/ComfyUI'
|
||||
path: 'ComfyUI'
|
||||
|
||||
- name: Install ComfyUI_devtools from frontend repo
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p ComfyUI/custom_nodes/ComfyUI_devtools
|
||||
if ! cp -r ./tools/devtools/* ComfyUI/custom_nodes/ComfyUI_devtools/; then
|
||||
echo "::error::Failed to copy ComfyUI_devtools from ./tools/devtools/"
|
||||
echo "::error::This action assumes the ComfyUI_frontend repository is checked out in the current working directory."
|
||||
echo "::error::Please ensure you have run 'actions/checkout@v5' before calling this action."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.10'
|
||||
|
||||
- name: Install Python requirements
|
||||
shell: bash
|
||||
working-directory: ComfyUI
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu
|
||||
pip install -r requirements.txt
|
||||
pip install wait-for-it
|
||||
|
||||
- name: Start ComfyUI server
|
||||
if: ${{ inputs.launch_server == 'true' }}
|
||||
shell: bash
|
||||
working-directory: ComfyUI
|
||||
run: |
|
||||
python main.py --cpu --multi-user --front-end-root ../dist ${{ inputs.extra_server_params }} &
|
||||
wait-for-it --service 127.0.0.1:8188 -t 600
|
||||
68
.github/actions/setup-frontend/action.yml
vendored
@@ -1,31 +1,16 @@
|
||||
name: Setup Frontend
|
||||
description: 'Setup ComfyUI frontend development environment'
|
||||
name: Setup ComfyUI Frontend
|
||||
description: 'Install nodejs/pnpm/dependencies and optionally build ComfyUI_frontend'
|
||||
inputs:
|
||||
extra_server_params:
|
||||
description: 'Additional parameters to pass to ComfyUI server'
|
||||
include_build_step:
|
||||
description: 'Include the build step to build the frontend. Set to true for workflows that need a built frontend'
|
||||
required: false
|
||||
default: ''
|
||||
default: 'false'
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- name: Checkout ComfyUI
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: 'comfyanonymous/ComfyUI'
|
||||
path: 'ComfyUI'
|
||||
|
||||
- name: Checkout ComfyUI_frontend
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: 'Comfy-Org/ComfyUI_frontend'
|
||||
path: 'ComfyUI_frontend'
|
||||
|
||||
- name: Copy ComfyUI_devtools from frontend repo
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p ComfyUI/custom_nodes/ComfyUI_devtools
|
||||
cp -r ComfyUI_frontend/tools/devtools/* ComfyUI/custom_nodes/ComfyUI_devtools/
|
||||
# Note: this workflow assume frontend repo is checked out in the root of the workspace
|
||||
|
||||
# Install pnpm, Node.js, build frontend
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
@@ -36,32 +21,25 @@ runs:
|
||||
with:
|
||||
node-version: 'lts/*'
|
||||
cache: 'pnpm'
|
||||
cache-dependency-path: 'ComfyUI_frontend/pnpm-lock.yaml'
|
||||
cache-dependency-path: './pnpm-lock.yaml'
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v4
|
||||
# Restore tool caches before running any build/lint operations
|
||||
- name: Restore tool output cache
|
||||
uses: actions/cache/restore@v4
|
||||
with:
|
||||
python-version: '3.10'
|
||||
path: |
|
||||
./.cache
|
||||
./tsconfig.tsbuildinfo
|
||||
key: tool-cache-${{ runner.os }}-${{ hashFiles('./pnpm-lock.yaml') }}-${{ hashFiles('./src/**/*.{ts,vue,js,mts}', './*.config.*') }}
|
||||
restore-keys: |
|
||||
tool-cache-${{ runner.os }}-${{ hashFiles('./pnpm-lock.yaml') }}-
|
||||
tool-cache-${{ runner.os }}-
|
||||
|
||||
- name: Install Python requirements
|
||||
- name: Install dependencies
|
||||
shell: bash
|
||||
working-directory: ComfyUI
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu
|
||||
pip install -r requirements.txt
|
||||
pip install wait-for-it
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Build & Install ComfyUI_frontend
|
||||
- name: Build ComfyUI_frontend
|
||||
if: ${{ inputs.include_build_step == 'true' }}
|
||||
shell: bash
|
||||
working-directory: ComfyUI_frontend
|
||||
run: |
|
||||
pnpm install --frozen-lockfile
|
||||
pnpm build
|
||||
|
||||
- name: Start ComfyUI server
|
||||
shell: bash
|
||||
working-directory: ComfyUI
|
||||
run: |
|
||||
python main.py --cpu --multi-user --front-end-root ../ComfyUI_frontend/dist ${{ inputs.extra_server_params }} &
|
||||
wait-for-it --service 127.0.0.1:8188 -t 600
|
||||
run: pnpm build
|
||||
|
||||
3
.github/actions/setup-playwright/action.yml
vendored
@@ -6,7 +6,6 @@ runs:
|
||||
- name: Detect Playwright version
|
||||
id: detect-version
|
||||
shell: bash
|
||||
working-directory: ComfyUI_frontend
|
||||
run: |
|
||||
PLAYWRIGHT_VERSION=$(pnpm ls @playwright/test --json | jq --raw-output '.[0].devDependencies["@playwright/test"].version')
|
||||
echo "playwright-version=$PLAYWRIGHT_VERSION" >> $GITHUB_OUTPUT
|
||||
@@ -22,10 +21,8 @@ runs:
|
||||
if: steps.cache-playwright-browsers.outputs.cache-hit != 'true'
|
||||
shell: bash
|
||||
run: pnpm exec playwright install chromium --with-deps
|
||||
working-directory: ComfyUI_frontend
|
||||
|
||||
- name: Install Playwright Browsers (operating system dependencies)
|
||||
if: steps.cache-playwright-browsers.outputs.cache-hit == 'true'
|
||||
shell: bash
|
||||
run: pnpm exec playwright install-deps
|
||||
working-directory: ComfyUI_frontend
|
||||
18
.github/workflows/claude-pr-review.yml
vendored
@@ -11,6 +11,10 @@ on:
|
||||
pull_request:
|
||||
types: [labeled]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
wait-for-ci:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -73,10 +77,10 @@ jobs:
|
||||
with:
|
||||
label_trigger: "claude-review"
|
||||
prompt: |
|
||||
Read the file .claude/commands/comprehensive-pr-review.md and follow ALL the instructions exactly.
|
||||
|
||||
CRITICAL: You must post individual inline comments using the gh api commands shown in the file.
|
||||
DO NOT create a summary comment.
|
||||
Read the file .claude/commands/comprehensive-pr-review.md and follow ALL the instructions exactly.
|
||||
|
||||
CRITICAL: You must post individual inline comments using the gh api commands shown in the file.
|
||||
DO NOT create a summary comment.
|
||||
Each issue must be posted as a separate inline comment on the specific line of code.
|
||||
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
claude_args: "--max-turns 256 --allowedTools 'Bash(git:*),Bash(gh api:*),Bash(gh pr:*),Bash(gh repo:*),Bash(jq:*),Bash(echo:*),Read,Write,Edit,Glob,Grep,WebFetch'"
|
||||
@@ -86,3 +90,9 @@ jobs:
|
||||
COMMIT_SHA: ${{ github.event.pull_request.head.sha }}
|
||||
BASE_SHA: ${{ github.event.pull_request.base.sha }}
|
||||
REPOSITORY: ${{ github.repository }}
|
||||
|
||||
- name: Remove claude-review label
|
||||
if: always()
|
||||
run: gh pr edit ${{ github.event.pull_request.number }} --remove-label "claude-review"
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
4
.github/workflows/lint-and-format.yaml
vendored
@@ -4,6 +4,10 @@ on:
|
||||
pull_request:
|
||||
branches-ignore: [wip/*, draft/*, temp/*]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
203
.github/workflows/tests-ci.yaml
vendored
@@ -7,70 +7,37 @@ on:
|
||||
branches-ignore:
|
||||
[wip/*, draft/*, temp/*, vue-nodes-migration, sno-playwright-*]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
setup:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
cache-key: ${{ steps.cache-key.outputs.key }}
|
||||
steps:
|
||||
- name: Checkout ComfyUI
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
|
||||
# Setup Test Environment, build frontend but do not start server yet
|
||||
- name: Setup ComfyUI server
|
||||
uses: ./.github/actions/setup-comfyui-server
|
||||
- name: Setup frontend
|
||||
uses: ./.github/actions/setup-frontend
|
||||
with:
|
||||
repository: 'comfyanonymous/ComfyUI'
|
||||
path: 'ComfyUI'
|
||||
ref: master
|
||||
|
||||
- name: Checkout ComfyUI_frontend
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
repository: 'Comfy-Org/ComfyUI_frontend'
|
||||
path: 'ComfyUI_frontend'
|
||||
|
||||
- name: Copy ComfyUI_devtools from frontend repo
|
||||
run: |
|
||||
mkdir -p ComfyUI/custom_nodes/ComfyUI_devtools
|
||||
cp -r ComfyUI_frontend/tools/devtools/* ComfyUI/custom_nodes/ComfyUI_devtools/
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 10
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: lts/*
|
||||
cache: 'pnpm'
|
||||
cache-dependency-path: 'ComfyUI_frontend/pnpm-lock.yaml'
|
||||
|
||||
- name: Cache tool outputs
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
ComfyUI_frontend/.cache
|
||||
ComfyUI_frontend/tsconfig.tsbuildinfo
|
||||
key: playwright-setup-cache-${{ runner.os }}-${{ hashFiles('ComfyUI_frontend/pnpm-lock.yaml') }}-${{ hashFiles('ComfyUI_frontend/src/**/*.{ts,vue,js}', 'ComfyUI_frontend/*.config.*') }}
|
||||
restore-keys: |
|
||||
playwright-setup-cache-${{ runner.os }}-${{ hashFiles('ComfyUI_frontend/pnpm-lock.yaml') }}-
|
||||
playwright-setup-cache-${{ runner.os }}-
|
||||
playwright-tools-cache-${{ runner.os }}-
|
||||
|
||||
- name: Build ComfyUI_frontend
|
||||
run: |
|
||||
pnpm install --frozen-lockfile
|
||||
pnpm build
|
||||
working-directory: ComfyUI_frontend
|
||||
include_build_step: true
|
||||
- name: Setup Playwright
|
||||
uses: ./.github/actions/setup-playwright # Setup Playwright and cache browsers
|
||||
|
||||
# Save the entire workspace as cache for later test jobs to restore
|
||||
- name: Generate cache key
|
||||
id: cache-key
|
||||
run: echo "key=$(date +%s)" >> $GITHUB_OUTPUT
|
||||
|
||||
|
||||
- name: Save cache
|
||||
uses: actions/cache/save@5a3ec84eff668545956fd18022155c47e93e2684
|
||||
with:
|
||||
path: |
|
||||
ComfyUI
|
||||
ComfyUI_frontend
|
||||
path: .
|
||||
key: comfyui-setup-${{ steps.cache-key.outputs.key }}
|
||||
|
||||
# Sharded chromium tests
|
||||
@@ -85,54 +52,35 @@ jobs:
|
||||
shardIndex: [1, 2, 3, 4, 5, 6, 7, 8]
|
||||
shardTotal: [8]
|
||||
steps:
|
||||
# download built frontend repo from setup job
|
||||
- name: Wait for cache propagation
|
||||
run: sleep 10
|
||||
|
||||
- name: Restore cached setup
|
||||
uses: actions/cache/restore@v4
|
||||
uses: actions/cache/restore@5a3ec84eff668545956fd18022155c47e93e2684
|
||||
with:
|
||||
fail-on-cache-miss: true
|
||||
path: |
|
||||
ComfyUI
|
||||
ComfyUI_frontend
|
||||
path: .
|
||||
key: comfyui-setup-${{ needs.setup.outputs.cache-key }}
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
# Setup Test Environment for this runner, start server, use cached built frontend ./dist from 'setup' job
|
||||
- name: Setup ComfyUI server
|
||||
uses: ./.github/actions/setup-comfyui-server
|
||||
with:
|
||||
version: 10
|
||||
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.10'
|
||||
cache: 'pip'
|
||||
|
||||
- name: Install requirements
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu
|
||||
pip install -r requirements.txt
|
||||
pip install wait-for-it
|
||||
working-directory: ComfyUI
|
||||
|
||||
|
||||
launch_server: true
|
||||
- name: Setup nodejs, pnpm, reuse built frontend
|
||||
uses: ./.github/actions/setup-frontend
|
||||
- name: Setup Playwright
|
||||
uses: ./ComfyUI_frontend/.github/actions/setup-playwright
|
||||
|
||||
- name: Start ComfyUI server
|
||||
run: |
|
||||
python main.py --cpu --multi-user --front-end-root ../ComfyUI_frontend/dist &
|
||||
wait-for-it --service 127.0.0.1:8188 -t 600
|
||||
working-directory: ComfyUI
|
||||
uses: ./.github/actions/setup-playwright
|
||||
|
||||
# Run sharded tests and upload sharded reports
|
||||
- name: Run Playwright tests (Shard ${{ matrix.shardIndex }}/${{ matrix.shardTotal }})
|
||||
id: playwright
|
||||
run: pnpm exec playwright test --project=chromium --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }} --reporter=blob
|
||||
working-directory: ComfyUI_frontend
|
||||
env:
|
||||
PLAYWRIGHT_BLOB_OUTPUT_DIR: ../blob-report
|
||||
PLAYWRIGHT_BLOB_OUTPUT_DIR: ./blob-report
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
- name: Upload blob report
|
||||
uses: actions/upload-artifact@v4
|
||||
if: ${{ !cancelled() }}
|
||||
with:
|
||||
name: blob-report-chromium-${{ matrix.shardIndex }}
|
||||
@@ -151,45 +99,27 @@ jobs:
|
||||
matrix:
|
||||
browser: [chromium-2x, chromium-0.5x, mobile-chrome]
|
||||
steps:
|
||||
# download built frontend repo from setup job
|
||||
- name: Wait for cache propagation
|
||||
run: sleep 10
|
||||
|
||||
- name: Restore cached setup
|
||||
uses: actions/cache/restore@v4
|
||||
uses: actions/cache/restore@5a3ec84eff668545956fd18022155c47e93e2684
|
||||
with:
|
||||
fail-on-cache-miss: true
|
||||
path: |
|
||||
ComfyUI
|
||||
ComfyUI_frontend
|
||||
path: .
|
||||
key: comfyui-setup-${{ needs.setup.outputs.cache-key }}
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
# Setup Test Environment for this runner, start server, use cached built frontend ./dist from 'setup' job
|
||||
- name: Setup ComfyUI server
|
||||
uses: ./.github/actions/setup-comfyui-server
|
||||
with:
|
||||
version: 10
|
||||
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.10'
|
||||
cache: 'pip'
|
||||
|
||||
- name: Install requirements
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu
|
||||
pip install -r requirements.txt
|
||||
pip install wait-for-it
|
||||
working-directory: ComfyUI
|
||||
|
||||
launch_server: true
|
||||
- name: Setup nodejs, pnpm, reuse built frontend
|
||||
uses: ./.github/actions/setup-frontend
|
||||
- name: Setup Playwright
|
||||
uses: ./ComfyUI_frontend/.github/actions/setup-playwright
|
||||
|
||||
- name: Start ComfyUI server
|
||||
run: |
|
||||
python main.py --cpu --multi-user --front-end-root ../ComfyUI_frontend/dist &
|
||||
wait-for-it --service 127.0.0.1:8188 -t 600
|
||||
working-directory: ComfyUI
|
||||
uses: ./.github/actions/setup-playwright
|
||||
|
||||
# Run tests and upload reports
|
||||
- name: Run Playwright tests (${{ matrix.browser }})
|
||||
id: playwright
|
||||
run: |
|
||||
@@ -199,13 +129,13 @@ jobs:
|
||||
--reporter=list \
|
||||
--reporter=html \
|
||||
--reporter=json
|
||||
working-directory: ComfyUI_frontend
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
- name: Upload Playwright report
|
||||
uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: playwright-report-${{ matrix.browser }}
|
||||
path: ComfyUI_frontend/playwright-report/
|
||||
path: ./playwright-report/
|
||||
retention-days: 30
|
||||
|
||||
# Merge sharded test reports
|
||||
@@ -214,32 +144,19 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ !cancelled() }}
|
||||
steps:
|
||||
- name: Checkout ComfyUI_frontend
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
repository: 'Comfy-Org/ComfyUI_frontend'
|
||||
path: 'ComfyUI_frontend'
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 10
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: lts/*
|
||||
cache: 'pnpm'
|
||||
cache-dependency-path: 'ComfyUI_frontend/pnpm-lock.yaml'
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
pnpm install --frozen-lockfile
|
||||
working-directory: ComfyUI_frontend
|
||||
# Setup Test Environment, we only need playwright to merge reports
|
||||
- name: Setup frontend
|
||||
uses: ./.github/actions/setup-frontend
|
||||
- name: Setup Playwright
|
||||
uses: ./.github/actions/setup-playwright
|
||||
|
||||
- name: Download blob reports
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: ComfyUI_frontend/all-blob-reports
|
||||
path: ./all-blob-reports
|
||||
pattern: blob-report-chromium-*
|
||||
merge-multiple: true
|
||||
|
||||
@@ -250,13 +167,12 @@ jobs:
|
||||
# Generate JSON report separately with explicit output path
|
||||
PLAYWRIGHT_JSON_OUTPUT_NAME=playwright-report/report.json \
|
||||
pnpm exec playwright merge-reports --reporter=json ./all-blob-reports
|
||||
working-directory: ComfyUI_frontend
|
||||
|
||||
- name: Upload HTML report
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: playwright-report-chromium
|
||||
path: ComfyUI_frontend/playwright-report/
|
||||
path: ./playwright-report/
|
||||
retention-days: 30
|
||||
|
||||
#### BEGIN Deployment and commenting (non-forked PRs only)
|
||||
@@ -272,11 +188,11 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
|
||||
|
||||
- name: Get start time
|
||||
id: start-time
|
||||
run: echo "time=$(date -u '+%m/%d/%Y, %I:%M:%S %p')" >> $GITHUB_OUTPUT
|
||||
|
||||
|
||||
- name: Post starting comment
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
@@ -287,7 +203,7 @@ jobs:
|
||||
"${{ github.head_ref }}" \
|
||||
"starting" \
|
||||
"${{ steps.start-time.outputs.time }}"
|
||||
|
||||
|
||||
# Deploy and comment for non-forked PRs only
|
||||
deploy-and-comment:
|
||||
needs: [playwright-tests, merge-reports]
|
||||
@@ -299,23 +215,20 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
|
||||
|
||||
- name: Download all playwright reports
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
pattern: playwright-report-*
|
||||
path: reports
|
||||
|
||||
- name: Make deployment script executable
|
||||
run: chmod +x scripts/cicd/pr-playwright-deploy-and-comment.sh
|
||||
|
||||
|
||||
- name: Deploy reports and comment on PR
|
||||
env:
|
||||
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
./scripts/cicd/pr-playwright-deploy-and-comment.sh \
|
||||
bash ./scripts/cicd/pr-playwright-deploy-and-comment.sh \
|
||||
"${{ github.event.pull_request.number }}" \
|
||||
"${{ github.head_ref }}" \
|
||||
"completed"
|
||||
|
||||
@@ -21,90 +21,64 @@ jobs:
|
||||
update-locales:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout ComfyUI
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
|
||||
# Setup playwright environment with custom node repository
|
||||
- name: Setup ComfyUI Server (without launching)
|
||||
uses: ./.github/actions/setup-comfyui-server
|
||||
- name: Setup frontend
|
||||
uses: ./.github/actions/setup-frontend
|
||||
with:
|
||||
repository: comfyanonymous/ComfyUI
|
||||
path: ComfyUI
|
||||
ref: master
|
||||
- name: Checkout ComfyUI_frontend
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
repository: Comfy-Org/ComfyUI_frontend
|
||||
path: ComfyUI_frontend
|
||||
- name: Copy ComfyUI_devtools from frontend repo
|
||||
run: |
|
||||
mkdir -p ComfyUI/custom_nodes/ComfyUI_devtools
|
||||
cp -r ComfyUI_frontend/tools/devtools/* ComfyUI/custom_nodes/ComfyUI_devtools/
|
||||
include_build_step: 'true'
|
||||
- name: Setup Playwright
|
||||
uses: ./.github/actions/setup-playwright
|
||||
|
||||
# Install the custom node repository
|
||||
- name: Checkout custom node repository
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
repository: ${{ inputs.owner }}/${{ inputs.repository }}
|
||||
path: 'ComfyUI/custom_nodes/${{ inputs.repository }}'
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 10
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 'lts/*'
|
||||
cache: 'pnpm'
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.10'
|
||||
- name: Install ComfyUI requirements
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu
|
||||
pip install -r requirements.txt
|
||||
pip install wait-for-it
|
||||
working-directory: ComfyUI
|
||||
- name: Install custom node requirements
|
||||
- name: Install custom node Python requirements
|
||||
working-directory: ComfyUI/custom_nodes/${{ inputs.repository }}
|
||||
run: |
|
||||
if [ -f "requirements.txt" ]; then
|
||||
pip install -r requirements.txt
|
||||
fi
|
||||
working-directory: ComfyUI/custom_nodes/${{ inputs.repository }}
|
||||
- name: Build & Install ComfyUI_frontend
|
||||
run: |
|
||||
pnpm install --frozen-lockfile
|
||||
pnpm build
|
||||
rm -rf ../ComfyUI/web/*
|
||||
mv dist/* ../ComfyUI/web/
|
||||
working-directory: ComfyUI_frontend
|
||||
- name: Start ComfyUI server
|
||||
run: |
|
||||
python main.py --cpu --multi-user &
|
||||
wait-for-it --service 127.0.0.1:8188 -t 600
|
||||
|
||||
# Start ComfyUI Server
|
||||
- name: Start ComfyUI Server
|
||||
shell: bash
|
||||
working-directory: ComfyUI
|
||||
- name: Setup Playwright
|
||||
uses: ./ComfyUI_frontend/.github/actions/setup-playwright
|
||||
run: |
|
||||
python main.py --cpu --multi-user --front-end-root ../dist --custom-node-path ../ComfyUI/custom_nodes/${{ inputs.repository }} &
|
||||
wait-for-it --service
|
||||
|
||||
- name: Start dev server
|
||||
# Run electron dev server as it is a superset of the web dev server
|
||||
# We do want electron specific UIs to be translated.
|
||||
run: pnpm dev:electron &
|
||||
working-directory: ComfyUI_frontend
|
||||
|
||||
- name: Capture base i18n
|
||||
run: pnpm exec tsx scripts/diff-i18n capture
|
||||
working-directory: ComfyUI_frontend
|
||||
- name: Update en.json
|
||||
run: pnpm collect-i18n
|
||||
env:
|
||||
PLAYWRIGHT_TEST_URL: http://localhost:5173
|
||||
working-directory: ComfyUI_frontend
|
||||
- name: Update translations
|
||||
run: pnpm locale
|
||||
env:
|
||||
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
||||
working-directory: ComfyUI_frontend
|
||||
- name: Diff base vs updated i18n
|
||||
run: pnpm exec tsx scripts/diff-i18n diff
|
||||
working-directory: ComfyUI_frontend
|
||||
- name: Update i18n in custom node repository
|
||||
run: |
|
||||
LOCALE_DIR=ComfyUI/custom_nodes/${{ inputs.repository }}/locales/
|
||||
install -d "$LOCALE_DIR"
|
||||
cp -rf ComfyUI_frontend/temp/diff/* "$LOCALE_DIR"
|
||||
|
||||
# Git ops for pushing changes and creating PR
|
||||
- name: Check and create fork of custom node repository
|
||||
run: |
|
||||
# Try to fork the repository
|
||||
|
||||
27
.github/workflows/update-locales.yaml
vendored
@@ -14,35 +14,35 @@ jobs:
|
||||
if: github.event_name == 'workflow_dispatch' || (github.event.pull_request.head.repo.full_name == github.repository && startsWith(github.head_ref, 'version-bump-'))
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Setup Frontend
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
|
||||
# Setup playwright environment
|
||||
- name: Setup ComfyUI Frontend
|
||||
uses: ./.github/actions/setup-frontend
|
||||
|
||||
- name: Cache tool outputs
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
ComfyUI_frontend/.cache
|
||||
ComfyUI_frontend/.cache
|
||||
key: i18n-tools-cache-${{ runner.os }}-${{ hashFiles('ComfyUI_frontend/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
i18n-tools-cache-${{ runner.os }}-
|
||||
include_build_step: true
|
||||
- name: Setup ComfyUI Server
|
||||
uses: ./.github/actions/setup-comfyui-server
|
||||
with:
|
||||
launch_server: true
|
||||
- name: Setup Playwright
|
||||
uses: ./.github/actions/setup-playwright
|
||||
|
||||
- name: Start dev server
|
||||
# Run electron dev server as it is a superset of the web dev server
|
||||
# We do want electron specific UIs to be translated.
|
||||
run: pnpm dev:electron &
|
||||
working-directory: ComfyUI_frontend
|
||||
|
||||
# Update locales, collect new strings and update translations using OpenAI, then commit changes
|
||||
- name: Update en.json
|
||||
run: pnpm collect-i18n
|
||||
env:
|
||||
PLAYWRIGHT_TEST_URL: http://localhost:5173
|
||||
working-directory: ComfyUI_frontend
|
||||
- name: Update translations
|
||||
run: pnpm locale
|
||||
env:
|
||||
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
||||
working-directory: ComfyUI_frontend
|
||||
- name: Commit updated locales
|
||||
run: |
|
||||
git config --global user.name 'github-actions'
|
||||
@@ -56,4 +56,3 @@ jobs:
|
||||
git add src/locales/
|
||||
git diff --staged --quiet || git commit -m "Update locales [skip ci]"
|
||||
git push origin HEAD:${{ github.head_ref }}
|
||||
working-directory: ComfyUI_frontend
|
||||
|
||||
@@ -13,24 +13,32 @@ jobs:
|
||||
update-locales:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: Comfy-Org/ComfyUI_frontend_setup_action@v3
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
# Setup playwright environment
|
||||
- name: Setup ComfyUI Server (and start)
|
||||
uses: ./.github/actions/setup-comfyui-server
|
||||
with:
|
||||
launch_server: true
|
||||
- name: Setup frontend
|
||||
uses: ./.github/actions/setup-frontend
|
||||
with:
|
||||
include_build_step: true
|
||||
- name: Setup Playwright
|
||||
uses: ./.github/actions/setup-playwright
|
||||
|
||||
- name: Start dev server
|
||||
# Run electron dev server as it is a superset of the web dev server
|
||||
# We do want electron specific UIs to be translated.
|
||||
run: pnpm dev:electron &
|
||||
working-directory: ComfyUI_frontend
|
||||
- name: Update en.json
|
||||
run: pnpm collect-i18n -- scripts/collect-i18n-node-defs.ts
|
||||
env:
|
||||
PLAYWRIGHT_TEST_URL: http://localhost:5173
|
||||
working-directory: ComfyUI_frontend
|
||||
- name: Update translations
|
||||
run: pnpm locale
|
||||
env:
|
||||
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
||||
working-directory: ComfyUI_frontend
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e
|
||||
with:
|
||||
@@ -44,4 +52,3 @@ jobs:
|
||||
branch: update-locales-node-defs-${{ github.event.inputs.trigger_type }}-${{ github.run_id }}
|
||||
base: main
|
||||
labels: dependencies
|
||||
path: ComfyUI_frontend
|
||||
@@ -3,42 +3,106 @@ name: Update Playwright Expectations
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [ labeled ]
|
||||
types: [labeled]
|
||||
issue_comment:
|
||||
types: [created]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event.label.name == 'New Browser Test Expectations'
|
||||
if: >
|
||||
( github.event_name == 'pull_request' && github.event.label.name == 'New Browser Test Expectations' ) ||
|
||||
( github.event.issue.pull_request &&
|
||||
github.event_name == 'issue_comment' &&
|
||||
(
|
||||
github.event.comment.author_association == 'OWNER' ||
|
||||
github.event.comment.author_association == 'MEMBER' ||
|
||||
github.event.comment.author_association == 'COLLABORATOR'
|
||||
) &&
|
||||
startsWith(github.event.comment.body, '/update-playwright') )
|
||||
steps:
|
||||
- name: Checkout workflow repo
|
||||
uses: actions/checkout@v5
|
||||
- name: Setup Frontend
|
||||
uses: ./.github/actions/setup-frontend
|
||||
- name: Setup Playwright
|
||||
uses: ./.github/actions/setup-playwright
|
||||
- name: Run Playwright tests and update snapshots
|
||||
id: playwright-tests
|
||||
run: pnpm exec playwright test --update-snapshots
|
||||
continue-on-error: true
|
||||
working-directory: ComfyUI_frontend
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: playwright-report
|
||||
path: ComfyUI_frontend/playwright-report/
|
||||
retention-days: 30
|
||||
- name: Debugging info
|
||||
run: |
|
||||
echo "Branch: ${{ github.head_ref }}"
|
||||
git status
|
||||
working-directory: ComfyUI_frontend
|
||||
- name: Commit updated expectations
|
||||
run: |
|
||||
git config --global user.name 'github-actions'
|
||||
git config --global user.email 'github-actions@github.com'
|
||||
git fetch origin ${{ github.head_ref }}
|
||||
git checkout -B ${{ github.head_ref }} origin/${{ github.head_ref }}
|
||||
git add browser_tests
|
||||
git commit -m "[automated] Update test expectations"
|
||||
git push origin HEAD:${{ github.head_ref }}
|
||||
working-directory: ComfyUI_frontend
|
||||
- name: Find Update Comment
|
||||
uses: peter-evans/find-comment@b30e6a3c0ed37e7c023ccd3f1db5c6c0b0c23aad
|
||||
id: "find-update-comment"
|
||||
with:
|
||||
issue-number: ${{ github.event.number || github.event.issue.number }}
|
||||
comment-author: "github-actions[bot]"
|
||||
body-includes: "Updating Playwright Expectations"
|
||||
|
||||
- name: Add Starting Reaction
|
||||
uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9
|
||||
with:
|
||||
comment-id: ${{ steps.find-update-comment.outputs.comment-id }}
|
||||
issue-number: ${{ github.event.number || github.event.issue.number }}
|
||||
body: |
|
||||
Updating Playwright Expectations
|
||||
edit-mode: replace
|
||||
reactions: eyes
|
||||
|
||||
- name: Get Branch SHA
|
||||
id: "get-branch"
|
||||
run: echo ::set-output name=branch::$(gh pr view $PR_NO --repo $REPO --json headRefName --jq '.headRefName')
|
||||
env:
|
||||
REPO: ${{ github.repository }}
|
||||
PR_NO: ${{ github.event.number || github.event.issue.number }}
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Initial Checkout
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
ref: ${{ steps.get-branch.outputs.branch }}
|
||||
- name: Setup Frontend
|
||||
uses: ./.github/actions/setup-frontend
|
||||
with:
|
||||
include_build_step: true
|
||||
- name: Setup ComfyUI Server
|
||||
uses: ./.github/actions/setup-comfyui-server
|
||||
with:
|
||||
launch_server: true
|
||||
- name: Setup Playwright
|
||||
uses: ./.github/actions/setup-playwright
|
||||
- name: Run Playwright tests and update snapshots
|
||||
id: playwright-tests
|
||||
run: pnpm exec playwright test --update-snapshots
|
||||
continue-on-error: true
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: playwright-report
|
||||
path: ./playwright-report/
|
||||
retention-days: 30
|
||||
- name: Debugging info
|
||||
run: |
|
||||
echo "PR: ${{ github.event.issue.number }}"
|
||||
echo "Branch: ${{ steps.get-branch.outputs.branch }}"
|
||||
git status
|
||||
- name: Commit updated expectations
|
||||
run: |
|
||||
git config --global user.name 'github-actions'
|
||||
git config --global user.email 'github-actions@github.com'
|
||||
git add browser_tests
|
||||
if git diff --cached --quiet; then
|
||||
echo "No changes to commit"
|
||||
else
|
||||
git commit -m "[automated] Update test expectations"
|
||||
git push origin ${{ steps.get-branch.outputs.branch }}
|
||||
fi
|
||||
|
||||
- name: Add Done Reaction
|
||||
uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9
|
||||
if: github.event_name == 'issue_comment'
|
||||
with:
|
||||
comment-id: ${{ steps.find-update-comment.outputs.comment-id }}
|
||||
issue-number: ${{ github.event.number || github.event.issue.number }}
|
||||
reactions: +1
|
||||
reactions-edit-mode: replace
|
||||
|
||||
- name: Remove New Browser Test Expectations label
|
||||
if: always() && github.event_name == 'pull_request'
|
||||
run: gh pr edit ${{ github.event.pull_request.number }} --remove-label "New Browser Test Expectations"
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
4
.github/workflows/vitest-tests.yaml
vendored
@@ -6,6 +6,10 @@ on:
|
||||
pull_request:
|
||||
branches-ignore: [wip/*, draft/*, temp/*]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
@@ -211,18 +211,17 @@ This Storybook setup includes:
|
||||
|
||||
## Icon Usage in Storybook
|
||||
|
||||
In this project, the `<i-lucide:... />` syntax from unplugin-icons is not supported in Storybook.
|
||||
In this project, only the `<i class="icon-[lucide--folder]" />` syntax from unplugin-icons is supported in Storybook.
|
||||
|
||||
**Example:**
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
import { Trophy, Settings } from 'lucide-vue-next'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Trophy :size="16" class="text-neutral" />
|
||||
<Settings :size="16" class="text-neutral" />
|
||||
<i class="icon-[lucide--trophy] text-neutral size-4" />
|
||||
<i class="icon-[lucide--settings] text-neutral size-4" />
|
||||
</template>
|
||||
```
|
||||
|
||||
|
||||
@@ -7,15 +7,15 @@
|
||||
}
|
||||
],
|
||||
"rules": {
|
||||
"import-notation": "url",
|
||||
"import-notation": "string",
|
||||
"font-family-no-missing-generic-family-keyword": true,
|
||||
"declaration-block-no-redundant-longhand-properties": true,
|
||||
"declaration-property-value-no-unknown": [
|
||||
true,
|
||||
{
|
||||
"ignoreProperties": {
|
||||
"speak": ["none"],
|
||||
"app-region": ["drag", "no-drag"]
|
||||
"app-region": ["drag", "no-drag"],
|
||||
"/^(width|height)$/": ["/^v-bind/"]
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -35,7 +35,7 @@
|
||||
"selector-max-type": 2,
|
||||
"declaration-block-no-duplicate-properties": true,
|
||||
"block-no-empty": true,
|
||||
"no-descending-specificity": true,
|
||||
"no-descending-specificity": null,
|
||||
"no-duplicate-at-import-rules": true,
|
||||
"at-rule-no-unknown": [
|
||||
true,
|
||||
@@ -57,7 +57,8 @@
|
||||
true,
|
||||
{
|
||||
"ignoreFunctions": [
|
||||
"theme"
|
||||
"theme",
|
||||
"v-bind"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
- Routing/i18n/entry: `src/router.ts`, `src/i18n.ts`, `src/main.ts`.
|
||||
- Tests: unit/component in `tests-ui/` and `src/components/**/*.{test,spec}.ts`; E2E in `browser_tests/`.
|
||||
- Public assets: `public/`. Build output: `dist/`.
|
||||
- Config: `vite.config.mts`, `vitest.config.ts`, `playwright.config.ts`, `eslint.config.js`, `.prettierrc`.
|
||||
- Config: `vite.config.mts`, `vitest.config.ts`, `playwright.config.ts`, `eslint.config.ts`, `.prettierrc`.
|
||||
|
||||
## Build, Test, and Development Commands
|
||||
- `pnpm dev`: Start Vite dev server.
|
||||
|
||||
@@ -126,6 +126,5 @@ const value = api.getServerFeature('config_name', defaultValue) // Get config
|
||||
- NEVER use `--no-verify` flag when committing
|
||||
- NEVER delete or disable tests to make them pass
|
||||
- NEVER circumvent quality checks
|
||||
- NEVER use `dark:` prefix - always use `dark-theme:` for dark mode styles, for example: `dark-theme:text-white dark-theme:bg-black`
|
||||
- NEVER use `:class="[]"` to merge class names - always use `import { cn } from '@/utils/tailwindUtil'`, for example: `<div :class="cn('bg-red-500', { 'bg-blue-500': condition })" />`
|
||||
|
||||
- NEVER use `dark:` or `dark-theme:` tailwind variants. Instead use a semantic value from the `style.css` theme, e.g. `bg-node-component-surface`
|
||||
- NEVER use `:class="[]"` to merge class names - always use `import { cn } from '@/utils/tailwindUtil'`, for example: `<div :class="cn('text-node-component-header-icon', hasError && 'text-danger')" />`
|
||||
|
||||
@@ -54,3 +54,10 @@
|
||||
|
||||
# Translations
|
||||
/src/locales/ @Yorha4D @KarryCharon @shinshin86 @Comfy-Org/comfy_maintainer
|
||||
|
||||
# LLM Instructions (blank on purpose)
|
||||
.claude/
|
||||
.cursor/
|
||||
.cursorrules
|
||||
**/AGENTS.md
|
||||
**/CLAUDE.md
|
||||
@@ -255,7 +255,7 @@ pnpm format
|
||||
The project supports three types of icons, all with automatic imports (no manual imports needed):
|
||||
|
||||
1. **PrimeIcons** - Built-in PrimeVue icons using CSS classes: `<i class="pi pi-plus" />`
|
||||
2. **Iconify Icons** - 200,000+ icons from various libraries: `<i-lucide:settings />`, `<i-mdi:folder />`
|
||||
2. **Iconify Icons** - 200,000+ icons from various libraries: `<i class="icon-[lucide--settings]" />`, `<i class="icon-[mdi--folder]" />`
|
||||
3. **Custom Icons** - Your own SVG icons: `<i-comfy:workflow />`
|
||||
|
||||
Icons are powered by the unplugin-icons system, which automatically discovers and imports icons as Vue components. Custom icons are stored in `packages/design-system/src/icons/` and processed by `packages/design-system/src/iconCollection.ts` with automatic validation.
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
:value="$t('install.gpuPicker.recommended')"
|
||||
class="bg-neutral-300 text-neutral-900 rounded-full text-sm font-bold px-2 py-[1px]"
|
||||
/>
|
||||
<i-lucide:badge-check class="text-neutral-300 text-lg" />
|
||||
<i class="icon-[lucide--badge-check] text-neutral-300 text-lg" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -286,6 +286,12 @@ const onFocus = async () => {
|
||||
.p-accordionheader {
|
||||
@apply rounded-t-xl rounded-b-none;
|
||||
}
|
||||
|
||||
.p-accordionheader-toggle-icon {
|
||||
&::before {
|
||||
content: '\e902';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.p-accordioncontent {
|
||||
@@ -302,13 +308,5 @@ const onFocus = async () => {
|
||||
content: '\e933';
|
||||
}
|
||||
}
|
||||
|
||||
.p-accordionpanel-active {
|
||||
.p-accordionheader-toggle-icon {
|
||||
&::before {
|
||||
content: '\e902';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
<template>
|
||||
<div
|
||||
class="task-div max-w-48 min-h-52 grid relative"
|
||||
class="task-div relative grid min-h-52 max-w-48"
|
||||
:class="{ 'opacity-75': isLoading }"
|
||||
>
|
||||
<Card
|
||||
class="max-w-48 relative h-full overflow-hidden"
|
||||
class="relative h-full max-w-48 overflow-hidden"
|
||||
:class="{ 'opacity-65': runner.state !== 'error' }"
|
||||
v-bind="(({ onClick, ...rest }) => rest)($attrs)"
|
||||
>
|
||||
<template #header>
|
||||
<i
|
||||
v-if="runner.state === 'error'"
|
||||
class="pi pi-exclamation-triangle text-red-500 absolute m-2 top-0 -right-14 opacity-15"
|
||||
class="pi pi-exclamation-triangle absolute top-0 -right-14 m-2 text-red-500 opacity-15"
|
||||
style="font-size: 10rem"
|
||||
/>
|
||||
<img
|
||||
v-if="task.headerImg"
|
||||
:src="task.headerImg"
|
||||
class="object-contain w-full h-full opacity-25 pt-4 px-4"
|
||||
class="h-full w-full object-contain px-4 pt-4 opacity-25"
|
||||
/>
|
||||
</template>
|
||||
<template #title>
|
||||
@@ -27,7 +27,7 @@
|
||||
{{ description }}
|
||||
</template>
|
||||
<template #footer>
|
||||
<div class="flex gap-4 mt-1">
|
||||
<div class="mt-1 flex gap-4">
|
||||
<Button
|
||||
:icon="task.button?.icon"
|
||||
:label="task.button?.text"
|
||||
@@ -73,7 +73,7 @@ defineEmits<{
|
||||
// Bindings
|
||||
const description = computed(() =>
|
||||
runner.value.state === 'error'
|
||||
? props.task.errorDescription ?? props.task.shortDescription
|
||||
? (props.task.errorDescription ?? props.task.shortDescription)
|
||||
: props.task.shortDescription
|
||||
)
|
||||
|
||||
|
||||
@@ -1,67 +1,163 @@
|
||||
import arCommands from '@frontend-locales/ar/commands.json' with { type: 'json' }
|
||||
import ar from '@frontend-locales/ar/main.json' with { type: 'json' }
|
||||
import arNodes from '@frontend-locales/ar/nodeDefs.json' with { type: 'json' }
|
||||
import arSettings from '@frontend-locales/ar/settings.json' with { type: 'json' }
|
||||
// Import only English locale eagerly as the default/fallback
|
||||
// ESLint cannot statically resolve dynamic imports with path aliases (@frontend-locales/*),
|
||||
// but these are properly configured in tsconfig.json and resolved by Vite at build time.
|
||||
// eslint-disable-next-line import-x/no-unresolved
|
||||
import enCommands from '@frontend-locales/en/commands.json' with { type: 'json' }
|
||||
// eslint-disable-next-line import-x/no-unresolved
|
||||
import en from '@frontend-locales/en/main.json' with { type: 'json' }
|
||||
// eslint-disable-next-line import-x/no-unresolved
|
||||
import enNodes from '@frontend-locales/en/nodeDefs.json' with { type: 'json' }
|
||||
// eslint-disable-next-line import-x/no-unresolved
|
||||
import enSettings from '@frontend-locales/en/settings.json' with { type: 'json' }
|
||||
import esCommands from '@frontend-locales/es/commands.json' with { type: 'json' }
|
||||
import es from '@frontend-locales/es/main.json' with { type: 'json' }
|
||||
import esNodes from '@frontend-locales/es/nodeDefs.json' with { type: 'json' }
|
||||
import esSettings from '@frontend-locales/es/settings.json' with { type: 'json' }
|
||||
import frCommands from '@frontend-locales/fr/commands.json' with { type: 'json' }
|
||||
import fr from '@frontend-locales/fr/main.json' with { type: 'json' }
|
||||
import frNodes from '@frontend-locales/fr/nodeDefs.json' with { type: 'json' }
|
||||
import frSettings from '@frontend-locales/fr/settings.json' with { type: 'json' }
|
||||
import jaCommands from '@frontend-locales/ja/commands.json' with { type: 'json' }
|
||||
import ja from '@frontend-locales/ja/main.json' with { type: 'json' }
|
||||
import jaNodes from '@frontend-locales/ja/nodeDefs.json' with { type: 'json' }
|
||||
import jaSettings from '@frontend-locales/ja/settings.json' with { type: 'json' }
|
||||
import koCommands from '@frontend-locales/ko/commands.json' with { type: 'json' }
|
||||
import ko from '@frontend-locales/ko/main.json' with { type: 'json' }
|
||||
import koNodes from '@frontend-locales/ko/nodeDefs.json' with { type: 'json' }
|
||||
import koSettings from '@frontend-locales/ko/settings.json' with { type: 'json' }
|
||||
import ruCommands from '@frontend-locales/ru/commands.json' with { type: 'json' }
|
||||
import ru from '@frontend-locales/ru/main.json' with { type: 'json' }
|
||||
import ruNodes from '@frontend-locales/ru/nodeDefs.json' with { type: 'json' }
|
||||
import ruSettings from '@frontend-locales/ru/settings.json' with { type: 'json' }
|
||||
import trCommands from '@frontend-locales/tr/commands.json' with { type: 'json' }
|
||||
import tr from '@frontend-locales/tr/main.json' with { type: 'json' }
|
||||
import trNodes from '@frontend-locales/tr/nodeDefs.json' with { type: 'json' }
|
||||
import trSettings from '@frontend-locales/tr/settings.json' with { type: 'json' }
|
||||
import zhTWCommands from '@frontend-locales/zh-TW/commands.json' with { type: 'json' }
|
||||
import zhTW from '@frontend-locales/zh-TW/main.json' with { type: 'json' }
|
||||
import zhTWNodes from '@frontend-locales/zh-TW/nodeDefs.json' with { type: 'json' }
|
||||
import zhTWSettings from '@frontend-locales/zh-TW/settings.json' with { type: 'json' }
|
||||
import zhCommands from '@frontend-locales/zh/commands.json' with { type: 'json' }
|
||||
import zh from '@frontend-locales/zh/main.json' with { type: 'json' }
|
||||
import zhNodes from '@frontend-locales/zh/nodeDefs.json' with { type: 'json' }
|
||||
import zhSettings from '@frontend-locales/zh/settings.json' with { type: 'json' }
|
||||
import { createI18n } from 'vue-i18n'
|
||||
|
||||
function buildLocale<M, N, C, S>(main: M, nodes: N, commands: C, settings: S) {
|
||||
function buildLocale<
|
||||
M extends Record<string, unknown>,
|
||||
N extends Record<string, unknown>,
|
||||
C extends Record<string, unknown>,
|
||||
S extends Record<string, unknown>
|
||||
>(main: M, nodes: N, commands: C, settings: S) {
|
||||
return {
|
||||
...main,
|
||||
nodeDefs: nodes,
|
||||
commands: commands,
|
||||
settings: settings
|
||||
}
|
||||
} as M & { nodeDefs: N; commands: C; settings: S }
|
||||
}
|
||||
|
||||
const messages = {
|
||||
en: buildLocale(en, enNodes, enCommands, enSettings),
|
||||
zh: buildLocale(zh, zhNodes, zhCommands, zhSettings),
|
||||
'zh-TW': buildLocale(zhTW, zhTWNodes, zhTWCommands, zhTWSettings),
|
||||
ru: buildLocale(ru, ruNodes, ruCommands, ruSettings),
|
||||
ja: buildLocale(ja, jaNodes, jaCommands, jaSettings),
|
||||
ko: buildLocale(ko, koNodes, koCommands, koSettings),
|
||||
fr: buildLocale(fr, frNodes, frCommands, frSettings),
|
||||
es: buildLocale(es, esNodes, esCommands, esSettings),
|
||||
ar: buildLocale(ar, arNodes, arCommands, arSettings),
|
||||
tr: buildLocale(tr, trNodes, trCommands, trSettings)
|
||||
// Locale loader map - dynamically import locales only when needed
|
||||
// ESLint cannot statically resolve these dynamic imports, but they are valid at build time
|
||||
/* eslint-disable import-x/no-unresolved */
|
||||
const localeLoaders: Record<
|
||||
string,
|
||||
() => Promise<{ default: Record<string, unknown> }>
|
||||
> = {
|
||||
ar: () => import('@frontend-locales/ar/main.json'),
|
||||
es: () => import('@frontend-locales/es/main.json'),
|
||||
fr: () => import('@frontend-locales/fr/main.json'),
|
||||
ja: () => import('@frontend-locales/ja/main.json'),
|
||||
ko: () => import('@frontend-locales/ko/main.json'),
|
||||
ru: () => import('@frontend-locales/ru/main.json'),
|
||||
tr: () => import('@frontend-locales/tr/main.json'),
|
||||
zh: () => import('@frontend-locales/zh/main.json'),
|
||||
'zh-TW': () => import('@frontend-locales/zh-TW/main.json')
|
||||
}
|
||||
|
||||
const nodeDefsLoaders: Record<
|
||||
string,
|
||||
() => Promise<{ default: Record<string, unknown> }>
|
||||
> = {
|
||||
ar: () => import('@frontend-locales/ar/nodeDefs.json'),
|
||||
es: () => import('@frontend-locales/es/nodeDefs.json'),
|
||||
fr: () => import('@frontend-locales/fr/nodeDefs.json'),
|
||||
ja: () => import('@frontend-locales/ja/nodeDefs.json'),
|
||||
ko: () => import('@frontend-locales/ko/nodeDefs.json'),
|
||||
ru: () => import('@frontend-locales/ru/nodeDefs.json'),
|
||||
tr: () => import('@frontend-locales/tr/nodeDefs.json'),
|
||||
zh: () => import('@frontend-locales/zh/nodeDefs.json'),
|
||||
'zh-TW': () => import('@frontend-locales/zh-TW/nodeDefs.json')
|
||||
}
|
||||
|
||||
const commandsLoaders: Record<
|
||||
string,
|
||||
() => Promise<{ default: Record<string, unknown> }>
|
||||
> = {
|
||||
ar: () => import('@frontend-locales/ar/commands.json'),
|
||||
es: () => import('@frontend-locales/es/commands.json'),
|
||||
fr: () => import('@frontend-locales/fr/commands.json'),
|
||||
ja: () => import('@frontend-locales/ja/commands.json'),
|
||||
ko: () => import('@frontend-locales/ko/commands.json'),
|
||||
ru: () => import('@frontend-locales/ru/commands.json'),
|
||||
tr: () => import('@frontend-locales/tr/commands.json'),
|
||||
zh: () => import('@frontend-locales/zh/commands.json'),
|
||||
'zh-TW': () => import('@frontend-locales/zh-TW/commands.json')
|
||||
}
|
||||
|
||||
const settingsLoaders: Record<
|
||||
string,
|
||||
() => Promise<{ default: Record<string, unknown> }>
|
||||
> = {
|
||||
ar: () => import('@frontend-locales/ar/settings.json'),
|
||||
es: () => import('@frontend-locales/es/settings.json'),
|
||||
fr: () => import('@frontend-locales/fr/settings.json'),
|
||||
ja: () => import('@frontend-locales/ja/settings.json'),
|
||||
ko: () => import('@frontend-locales/ko/settings.json'),
|
||||
ru: () => import('@frontend-locales/ru/settings.json'),
|
||||
tr: () => import('@frontend-locales/tr/settings.json'),
|
||||
zh: () => import('@frontend-locales/zh/settings.json'),
|
||||
'zh-TW': () => import('@frontend-locales/zh-TW/settings.json')
|
||||
}
|
||||
|
||||
// Track which locales have been loaded
|
||||
const loadedLocales = new Set<string>(['en'])
|
||||
|
||||
// Track locales currently being loaded to prevent race conditions
|
||||
const loadingLocales = new Map<string, Promise<void>>()
|
||||
|
||||
/**
|
||||
* Dynamically load a locale and its associated files (nodeDefs, commands, settings)
|
||||
*/
|
||||
export async function loadLocale(locale: string): Promise<void> {
|
||||
if (loadedLocales.has(locale)) {
|
||||
return
|
||||
}
|
||||
|
||||
// If already loading, return the existing promise to prevent duplicate loads
|
||||
const existingLoad = loadingLocales.get(locale)
|
||||
if (existingLoad) {
|
||||
return existingLoad
|
||||
}
|
||||
|
||||
const loader = localeLoaders[locale]
|
||||
const nodeDefsLoader = nodeDefsLoaders[locale]
|
||||
const commandsLoader = commandsLoaders[locale]
|
||||
const settingsLoader = settingsLoaders[locale]
|
||||
|
||||
if (!loader || !nodeDefsLoader || !commandsLoader || !settingsLoader) {
|
||||
console.warn(`Locale "${locale}" is not supported`)
|
||||
return
|
||||
}
|
||||
|
||||
// Create and track the loading promise
|
||||
const loadPromise = (async () => {
|
||||
try {
|
||||
const [main, nodes, commands, settings] = await Promise.all([
|
||||
loader(),
|
||||
nodeDefsLoader(),
|
||||
commandsLoader(),
|
||||
settingsLoader()
|
||||
])
|
||||
|
||||
const messages = buildLocale(
|
||||
main.default,
|
||||
nodes.default,
|
||||
commands.default,
|
||||
settings.default
|
||||
)
|
||||
|
||||
i18n.global.setLocaleMessage(locale, messages as LocaleMessages)
|
||||
loadedLocales.add(locale)
|
||||
} catch (error) {
|
||||
console.error(`Failed to load locale "${locale}":`, error)
|
||||
throw error
|
||||
} finally {
|
||||
// Clean up the loading promise once complete
|
||||
loadingLocales.delete(locale)
|
||||
}
|
||||
})()
|
||||
|
||||
loadingLocales.set(locale, loadPromise)
|
||||
return loadPromise
|
||||
}
|
||||
|
||||
// Only include English in the initial bundle
|
||||
const messages = {
|
||||
en: buildLocale(en, enNodes, enCommands, enSettings)
|
||||
}
|
||||
|
||||
// Type for locale messages - inferred from the English locale structure
|
||||
type LocaleMessages = typeof messages.en
|
||||
|
||||
export const i18n = createI18n({
|
||||
// Must set `false`, as Vue I18n Legacy API is for Vue 2
|
||||
legacy: false,
|
||||
|
||||
@@ -65,12 +65,12 @@ onUnmounted(() => electron.Validation.dispose())
|
||||
|
||||
.download-bg::before {
|
||||
@apply m-0 absolute text-muted;
|
||||
font-family: 'primeicons';
|
||||
font-family: 'primeicons', sans-serif;
|
||||
top: -2rem;
|
||||
right: 2rem;
|
||||
speak: none;
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
font-weight: 400;
|
||||
font-variant: normal;
|
||||
text-transform: none;
|
||||
line-height: 1;
|
||||
|
||||
@@ -186,12 +186,12 @@ onUnmounted(() => electron.Validation.dispose())
|
||||
|
||||
.backspan::before {
|
||||
@apply m-0 absolute text-muted;
|
||||
font-family: 'primeicons';
|
||||
font-family: 'primeicons', sans-serif;
|
||||
top: -2rem;
|
||||
right: -2rem;
|
||||
speak: none;
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
font-weight: 400;
|
||||
font-variant: normal;
|
||||
text-transform: none;
|
||||
line-height: 1;
|
||||
|
||||
@@ -18,16 +18,16 @@
|
||||
style="
|
||||
background: radial-gradient(
|
||||
ellipse 800px 600px at center,
|
||||
rgba(23, 23, 23, 0.95) 0%,
|
||||
rgba(23, 23, 23, 0.93) 10%,
|
||||
rgba(23, 23, 23, 0.9) 20%,
|
||||
rgba(23, 23, 23, 0.85) 30%,
|
||||
rgba(23, 23, 23, 0.75) 40%,
|
||||
rgba(23, 23, 23, 0.6) 50%,
|
||||
rgba(23, 23, 23, 0.4) 60%,
|
||||
rgba(23, 23, 23, 0.2) 70%,
|
||||
rgba(23, 23, 23, 0.1) 80%,
|
||||
rgba(23, 23, 23, 0.05) 90%,
|
||||
rgb(23 23 23 / 0.95) 0%,
|
||||
rgb(23 23 23 / 0.93) 10%,
|
||||
rgb(23 23 23 / 0.9) 20%,
|
||||
rgb(23 23 23 / 0.85) 30%,
|
||||
rgb(23 23 23 / 0.75) 40%,
|
||||
rgb(23 23 23 / 0.6) 50%,
|
||||
rgb(23 23 23 / 0.4) 60%,
|
||||
rgb(23 23 23 / 0.2) 70%,
|
||||
rgb(23 23 23 / 0.1) 80%,
|
||||
rgb(23 23 23 / 0.05) 90%,
|
||||
transparent 100%
|
||||
);
|
||||
"
|
||||
|
||||
90
browser_tests/assets/vueNodes/linked-int-widget.json
Normal file
@@ -0,0 +1,90 @@
|
||||
{
|
||||
"id": "95ea19ba-456c-46e8-aa40-dc3ff135b746",
|
||||
"revision": 0,
|
||||
"last_node_id": 11,
|
||||
"last_link_id": 10,
|
||||
"nodes": [
|
||||
{
|
||||
"id": 10,
|
||||
"type": "KSampler",
|
||||
"pos": [494.3333740234375, 142.3333282470703],
|
||||
"size": [444, 399],
|
||||
"flags": {},
|
||||
"order": 1,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "model",
|
||||
"type": "MODEL",
|
||||
"link": null
|
||||
},
|
||||
{
|
||||
"name": "positive",
|
||||
"type": "CONDITIONING",
|
||||
"link": null
|
||||
},
|
||||
{
|
||||
"name": "negative",
|
||||
"type": "CONDITIONING",
|
||||
"link": null
|
||||
},
|
||||
{
|
||||
"name": "latent_image",
|
||||
"type": "LATENT",
|
||||
"link": null
|
||||
},
|
||||
{
|
||||
"name": "seed",
|
||||
"type": "INT",
|
||||
"widget": {
|
||||
"name": "seed"
|
||||
},
|
||||
"link": 10
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "LATENT",
|
||||
"type": "LATENT",
|
||||
"links": null
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "KSampler"
|
||||
},
|
||||
"widgets_values": [67, "randomize", 20, 8, "euler", "simple", 1]
|
||||
},
|
||||
{
|
||||
"id": 11,
|
||||
"type": "PrimitiveInt",
|
||||
"pos": [24.333343505859375, 149.6666717529297],
|
||||
"size": [444, 125],
|
||||
"flags": {},
|
||||
"order": 0,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "INT",
|
||||
"type": "INT",
|
||||
"links": [10]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "PrimitiveInt"
|
||||
},
|
||||
"widgets_values": [67, "randomize"]
|
||||
}
|
||||
],
|
||||
"links": [[10, 11, 0, 10, 4, "INT"]],
|
||||
"groups": [],
|
||||
"config": {},
|
||||
"extra": {
|
||||
"ds": {
|
||||
"scale": 1,
|
||||
"offset": [0, 0]
|
||||
},
|
||||
"frontendVersion": "1.28.6"
|
||||
},
|
||||
"version": 0.4
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
import type { APIRequestContext, Locator, Page } from '@playwright/test'
|
||||
import { expect } from '@playwright/test'
|
||||
import { test as base } from '@playwright/test'
|
||||
import { test as base, expect } from '@playwright/test'
|
||||
import dotenv from 'dotenv'
|
||||
import * as fs from 'fs'
|
||||
|
||||
@@ -130,7 +129,8 @@ export class ComfyPage {
|
||||
|
||||
// Buttons
|
||||
public readonly resetViewButton: Locator
|
||||
public readonly queueButton: Locator
|
||||
public readonly queueButton: Locator // Run button in Legacy UI
|
||||
public readonly runButton: Locator // Run button (renamed "Queue" -> "Run")
|
||||
|
||||
// Inputs
|
||||
public readonly workflowUploadInput: Locator
|
||||
@@ -165,6 +165,9 @@ export class ComfyPage {
|
||||
this.widgetTextBox = page.getByPlaceholder('text').nth(1)
|
||||
this.resetViewButton = page.getByRole('button', { name: 'Reset View' })
|
||||
this.queueButton = page.getByRole('button', { name: 'Queue Prompt' })
|
||||
this.runButton = page
|
||||
.getByTestId('queue-button')
|
||||
.getByRole('button', { name: 'Run' })
|
||||
this.workflowUploadInput = page.locator('#comfy-file-input')
|
||||
this.visibleToasts = page.locator('.p-toast-message:visible')
|
||||
|
||||
@@ -282,6 +285,33 @@ export class ComfyPage {
|
||||
} = {}) {
|
||||
await this.goto()
|
||||
|
||||
// Mock remote config endpoint for cloud builds
|
||||
// Cloud builds (rh-test) call /api/features on startup, which blocks initialization
|
||||
// if the endpoint doesn't exist or times out. Try real backend first, fallback to empty config.
|
||||
await this.page.route('**/api/features', async (route) => {
|
||||
try {
|
||||
// Try to get response from real backend
|
||||
const response = await route.fetch()
|
||||
if (response.ok()) {
|
||||
await route.fulfill({ response })
|
||||
} else {
|
||||
// Backend doesn't have endpoint, return empty config
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
body: JSON.stringify({})
|
||||
})
|
||||
}
|
||||
} catch {
|
||||
// Network error, return empty config
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
body: JSON.stringify({})
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// Mock release endpoint to prevent changelog popups
|
||||
if (mockReleases) {
|
||||
await this.page.route('**/releases**', async (route) => {
|
||||
@@ -1086,12 +1116,6 @@ export class ComfyPage {
|
||||
|
||||
const targetPosition = await targetSlot.getPosition()
|
||||
|
||||
// Debug: Log the positions we're trying to use
|
||||
console.log('Drag positions:', {
|
||||
source: sourcePosition,
|
||||
target: targetPosition
|
||||
})
|
||||
|
||||
await this.dragAndDrop(sourcePosition, targetPosition)
|
||||
await this.nextFrame()
|
||||
}
|
||||
|
||||
@@ -119,4 +119,24 @@ export class VueNodeHelpers {
|
||||
await this.page.waitForSelector('[data-node-id]')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific widget by node title and widget name
|
||||
*/
|
||||
getWidgetByName(nodeTitle: string, widgetName: string): Locator {
|
||||
return this.getNodeByTitle(nodeTitle).locator(
|
||||
`_vue=[widget.name="${widgetName}"]`
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get controls for input number widgets (increment/decrement buttons and input)
|
||||
*/
|
||||
getInputNumberControls(widget: Locator) {
|
||||
return {
|
||||
input: widget.locator('input'),
|
||||
incrementButton: widget.locator('button').first(),
|
||||
decrementButton: widget.locator('button').last()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,9 @@ test.describe('Bottom Panel Shortcuts', () => {
|
||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Top')
|
||||
})
|
||||
|
||||
test('should toggle shortcuts panel visibility', async ({ comfyPage }) => {
|
||||
test.skip('should toggle shortcuts panel visibility', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
// Initially shortcuts panel should be hidden
|
||||
await expect(comfyPage.page.locator('.bottom-panel')).not.toBeVisible()
|
||||
|
||||
@@ -28,7 +30,9 @@ test.describe('Bottom Panel Shortcuts', () => {
|
||||
await expect(comfyPage.page.locator('.bottom-panel')).not.toBeVisible()
|
||||
})
|
||||
|
||||
test('should display essentials shortcuts tab', async ({ comfyPage }) => {
|
||||
test.skip('should display essentials shortcuts tab', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
// Open shortcuts panel
|
||||
await comfyPage.page
|
||||
.locator('button[aria-label*="Keyboard Shortcuts"]')
|
||||
@@ -62,7 +66,9 @@ test.describe('Bottom Panel Shortcuts', () => {
|
||||
).toBeVisible()
|
||||
})
|
||||
|
||||
test('should display view controls shortcuts tab', async ({ comfyPage }) => {
|
||||
test.skip('should display view controls shortcuts tab', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
// Open shortcuts panel
|
||||
await comfyPage.page
|
||||
.locator('button[aria-label*="Keyboard Shortcuts"]')
|
||||
@@ -88,7 +94,7 @@ test.describe('Bottom Panel Shortcuts', () => {
|
||||
).toBeVisible()
|
||||
})
|
||||
|
||||
test('should switch between shortcuts tabs', async ({ comfyPage }) => {
|
||||
test.skip('should switch between shortcuts tabs', async ({ comfyPage }) => {
|
||||
// Open shortcuts panel
|
||||
await comfyPage.page
|
||||
.locator('button[aria-label*="Keyboard Shortcuts"]')
|
||||
@@ -122,7 +128,9 @@ test.describe('Bottom Panel Shortcuts', () => {
|
||||
).not.toHaveAttribute('aria-selected', 'true')
|
||||
})
|
||||
|
||||
test('should display formatted keyboard shortcuts', async ({ comfyPage }) => {
|
||||
test.skip('should display formatted keyboard shortcuts', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
// Open shortcuts panel
|
||||
await comfyPage.page
|
||||
.locator('button[aria-label*="Keyboard Shortcuts"]')
|
||||
@@ -144,7 +152,7 @@ test.describe('Bottom Panel Shortcuts', () => {
|
||||
expect(hasModifiers).toBeTruthy()
|
||||
})
|
||||
|
||||
test('should maintain panel state when switching to terminal', async ({
|
||||
test.skip('should maintain panel state when switching to terminal', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
// Open shortcuts panel first
|
||||
@@ -172,7 +180,7 @@ test.describe('Bottom Panel Shortcuts', () => {
|
||||
).toBeVisible()
|
||||
})
|
||||
|
||||
test('should handle keyboard navigation', async ({ comfyPage }) => {
|
||||
test.skip('should handle keyboard navigation', async ({ comfyPage }) => {
|
||||
// Open shortcuts panel
|
||||
await comfyPage.page
|
||||
.locator('button[aria-label*="Keyboard Shortcuts"]')
|
||||
@@ -198,7 +206,7 @@ test.describe('Bottom Panel Shortcuts', () => {
|
||||
).toHaveAttribute('aria-selected', 'true')
|
||||
})
|
||||
|
||||
test('should close panel by clicking shortcuts button again', async ({
|
||||
test.skip('should close panel by clicking shortcuts button again', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
// Open shortcuts panel
|
||||
@@ -216,7 +224,7 @@ test.describe('Bottom Panel Shortcuts', () => {
|
||||
await expect(comfyPage.page.locator('.bottom-panel')).not.toBeVisible()
|
||||
})
|
||||
|
||||
test('should display shortcuts in organized columns', async ({
|
||||
test.skip('should display shortcuts in organized columns', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
// Open shortcuts panel
|
||||
@@ -251,7 +259,7 @@ test.describe('Bottom Panel Shortcuts', () => {
|
||||
).toHaveAttribute('aria-selected', 'true')
|
||||
})
|
||||
|
||||
test('should open settings dialog when clicking manage shortcuts button', async ({
|
||||
test.skip('should open settings dialog when clicking manage shortcuts button', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
// Open shortcuts panel
|
||||
|
||||
@@ -65,7 +65,7 @@ test.describe('Change Tracker', () => {
|
||||
})
|
||||
})
|
||||
|
||||
test('Can group multiple change actions into a single transaction', async ({
|
||||
test.skip('Can group multiple change actions into a single transaction', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const node = (await comfyPage.getFirstNodeRef())!
|
||||
@@ -153,7 +153,7 @@ test.describe('Change Tracker', () => {
|
||||
await expect(node).toBeCollapsed()
|
||||
})
|
||||
|
||||
test('Can detect changes in workflow.extra', async ({ comfyPage }) => {
|
||||
test.skip('Can detect changes in workflow.extra', async ({ comfyPage }) => {
|
||||
expect(await comfyPage.getUndoQueueSize()).toBe(0)
|
||||
await comfyPage.page.evaluate(() => {
|
||||
window['app'].graph.extra.foo = 'bar'
|
||||
|
||||
@@ -152,7 +152,7 @@ const customColorPalettes: Record<string, Palette> = {
|
||||
}
|
||||
|
||||
test.describe('Color Palette', () => {
|
||||
test('Can show custom color palette', async ({ comfyPage }) => {
|
||||
test.skip('Can show custom color palette', async ({ comfyPage }) => {
|
||||
await comfyPage.setSetting('Comfy.CustomColorPalettes', customColorPalettes)
|
||||
// Reload to apply the new setting. Setting Comfy.CustomColorPalettes directly
|
||||
// doesn't update the store immediately.
|
||||
@@ -174,7 +174,7 @@ test.describe('Color Palette', () => {
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('default-color-palette.png')
|
||||
})
|
||||
|
||||
test('Can add custom color palette', async ({ comfyPage }) => {
|
||||
test.skip('Can add custom color palette', async ({ comfyPage }) => {
|
||||
await comfyPage.page.evaluate((p) => {
|
||||
window['app'].extensionManager.colorPalette.addCustomColorPalette(p)
|
||||
}, customColorPalettes.obsidian_dark)
|
||||
@@ -199,7 +199,7 @@ test.describe('Node Color Adjustments', () => {
|
||||
await comfyPage.loadWorkflow('nodes/every_node_color')
|
||||
})
|
||||
|
||||
test('should adjust opacity via node opacity setting', async ({
|
||||
test.skip('should adjust opacity via node opacity setting', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.setSetting('Comfy.Node.Opacity', 0.5)
|
||||
@@ -217,7 +217,7 @@ test.describe('Node Color Adjustments', () => {
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('node-opacity-1.png')
|
||||
})
|
||||
|
||||
test('should persist color adjustments when changing themes', async ({
|
||||
test.skip('should persist color adjustments when changing themes', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.setSetting('Comfy.Node.Opacity', 0.2)
|
||||
@@ -245,7 +245,7 @@ test.describe('Node Color Adjustments', () => {
|
||||
}
|
||||
})
|
||||
|
||||
test('should lighten node colors when switching to light theme', async ({
|
||||
test.skip('should lighten node colors when switching to light theme', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.setSetting('Comfy.ColorPalette', 'light')
|
||||
@@ -261,7 +261,7 @@ test.describe('Node Color Adjustments', () => {
|
||||
await node?.clickContextMenuOption('Colors')
|
||||
})
|
||||
|
||||
test('should persist color adjustments when changing custom node colors', async ({
|
||||
test.skip('should persist color adjustments when changing custom node colors', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.page
|
||||
@@ -272,7 +272,7 @@ test.describe('Node Color Adjustments', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test('should persist color adjustments when removing custom node color', async ({
|
||||
test.skip('should persist color adjustments when removing custom node color', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.page
|
||||
|
||||
@@ -7,7 +7,7 @@ test.beforeEach(async ({ comfyPage }) => {
|
||||
})
|
||||
|
||||
test.describe('Copy Paste', () => {
|
||||
test('Can copy and paste node', async ({ comfyPage }) => {
|
||||
test.skip('Can copy and paste node', async ({ comfyPage }) => {
|
||||
await comfyPage.clickEmptyLatentNode()
|
||||
await comfyPage.page.mouse.move(10, 10)
|
||||
await comfyPage.ctrlC()
|
||||
@@ -15,7 +15,7 @@ test.describe('Copy Paste', () => {
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('copied-node.png')
|
||||
})
|
||||
|
||||
test('Can copy and paste node with link', async ({ comfyPage }) => {
|
||||
test.skip('Can copy and paste node with link', async ({ comfyPage }) => {
|
||||
await comfyPage.clickTextEncodeNode1()
|
||||
await comfyPage.page.mouse.move(10, 10)
|
||||
await comfyPage.ctrlC()
|
||||
@@ -35,7 +35,7 @@ test.describe('Copy Paste', () => {
|
||||
expect(resultString).toBe(originalString + originalString)
|
||||
})
|
||||
|
||||
test('Can copy and paste widget value', async ({ comfyPage }) => {
|
||||
test.skip('Can copy and paste widget value', async ({ comfyPage }) => {
|
||||
// Copy width value (512) from empty latent node to KSampler's seed.
|
||||
// KSampler's seed
|
||||
await comfyPage.canvas.click({
|
||||
@@ -60,7 +60,7 @@ test.describe('Copy Paste', () => {
|
||||
/**
|
||||
* https://github.com/Comfy-Org/ComfyUI_frontend/issues/98
|
||||
*/
|
||||
test('Paste in text area with node previously copied', async ({
|
||||
test.skip('Paste in text area with node previously copied', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.clickEmptyLatentNode()
|
||||
@@ -77,7 +77,7 @@ test.describe('Copy Paste', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test('Copy text area does not copy node', async ({ comfyPage }) => {
|
||||
test.skip('Copy text area does not copy node', async ({ comfyPage }) => {
|
||||
const textBox = comfyPage.widgetTextBox
|
||||
await textBox.click()
|
||||
await textBox.inputValue()
|
||||
@@ -89,7 +89,7 @@ test.describe('Copy Paste', () => {
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('no-node-copied.png')
|
||||
})
|
||||
|
||||
test('Copy node by dragging + alt', async ({ comfyPage }) => {
|
||||
test.skip('Copy node by dragging + alt', async ({ comfyPage }) => {
|
||||
// TextEncodeNode1
|
||||
await comfyPage.page.mouse.move(618, 191)
|
||||
await comfyPage.page.keyboard.down('Alt')
|
||||
|
||||
@@ -27,7 +27,7 @@ test.describe('Custom Icons', () => {
|
||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Top')
|
||||
})
|
||||
|
||||
test('sidebar tab icons use custom SVGs', async ({ comfyPage }) => {
|
||||
test.skip('sidebar tab icons use custom SVGs', async ({ comfyPage }) => {
|
||||
// Find the icon in the sidebar
|
||||
const icon = comfyPage.page.locator(
|
||||
'.icon-\\[comfy--ai-model\\].side-bar-button-icon'
|
||||
|
||||
@@ -35,7 +35,7 @@ test.describe('Load workflow warning', () => {
|
||||
})
|
||||
})
|
||||
|
||||
test('Does not report warning on undo/redo', async ({ comfyPage }) => {
|
||||
test.skip('Does not report warning on undo/redo', async ({ comfyPage }) => {
|
||||
await comfyPage.setSetting('Comfy.NodeSearchBoxImpl', 'default')
|
||||
|
||||
await comfyPage.loadWorkflow('missing/missing_nodes')
|
||||
@@ -301,7 +301,9 @@ test.describe('Settings', () => {
|
||||
})
|
||||
|
||||
test.describe('Support', () => {
|
||||
test('Should open external zendesk link', async ({ comfyPage }) => {
|
||||
test('Should open external zendesk link with OSS tag', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Top')
|
||||
const pagePromise = comfyPage.page.context().waitForEvent('page')
|
||||
await comfyPage.menu.topbar.triggerTopbarCommand(['Help', 'Support'])
|
||||
@@ -309,6 +311,10 @@ test.describe('Support', () => {
|
||||
|
||||
await newPage.waitForLoadState('networkidle')
|
||||
await expect(newPage).toHaveURL(/.*support\.comfy\.org.*/)
|
||||
|
||||
const url = new URL(newPage.url())
|
||||
expect(url.searchParams.get('tf_42243568391700')).toBe('oss')
|
||||
|
||||
await newPage.close()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -29,7 +29,9 @@ test.describe('DOM Widget', () => {
|
||||
await expect(lastMultiline).not.toBeVisible()
|
||||
})
|
||||
|
||||
test('Position update when entering focus mode', async ({ comfyPage }) => {
|
||||
test.skip('Position update when entering focus mode', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Top')
|
||||
await comfyPage.executeCommand('Workspace.ToggleFocusMode')
|
||||
await comfyPage.nextFrame()
|
||||
|
||||
|
Before Width: | Height: | Size: 88 KiB After Width: | Height: | Size: 82 KiB |
@@ -7,7 +7,7 @@ test.beforeEach(async ({ comfyPage }) => {
|
||||
})
|
||||
|
||||
test.describe('Execution', () => {
|
||||
test('Report error on unconnected slot', async ({ comfyPage }) => {
|
||||
test.skip('Report error on unconnected slot', async ({ comfyPage }) => {
|
||||
await comfyPage.disconnectEdge()
|
||||
await comfyPage.clickEmptySpace()
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ test.describe('Graph Canvas Menu', () => {
|
||||
await comfyPage.setSetting('Comfy.Graph.CanvasMenu', true)
|
||||
})
|
||||
|
||||
test('Can toggle link visibility', async ({ comfyPage }) => {
|
||||
test.skip('Can toggle link visibility', async ({ comfyPage }) => {
|
||||
const button = comfyPage.page.getByTestId('toggle-link-visibility-button')
|
||||
await button.click()
|
||||
await comfyPage.nextFrame()
|
||||
@@ -39,7 +39,7 @@ test.describe('Graph Canvas Menu', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test('Focus mode button is clickable and has correct test id', async ({
|
||||
test.skip('Focus mode button is clickable and has correct test id', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const focusButton = comfyPage.page.getByTestId('focus-mode-button')
|
||||
@@ -51,7 +51,7 @@ test.describe('Graph Canvas Menu', () => {
|
||||
await comfyPage.nextFrame()
|
||||
})
|
||||
|
||||
test('Zoom controls popup opens and closes', async ({ comfyPage }) => {
|
||||
test.skip('Zoom controls popup opens and closes', async ({ comfyPage }) => {
|
||||
// Find the zoom button by its percentage text content
|
||||
const zoomButton = comfyPage.page.locator('button').filter({
|
||||
hasText: '%'
|
||||
|
||||
|
Before Width: | Height: | Size: 86 KiB After Width: | Height: | Size: 85 KiB |
|
Before Width: | Height: | Size: 100 KiB After Width: | Height: | Size: 99 KiB |
@@ -22,11 +22,11 @@ test.describe('Group Node', () => {
|
||||
await libraryTab.open()
|
||||
})
|
||||
|
||||
test('Is added to node library sidebar', async ({ comfyPage }) => {
|
||||
test.skip('Is added to node library sidebar', async ({ comfyPage }) => {
|
||||
expect(await libraryTab.getFolder('group nodes').count()).toBe(1)
|
||||
})
|
||||
|
||||
test('Can be added to canvas using node library sidebar', async ({
|
||||
test.skip('Can be added to canvas using node library sidebar', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const initialNodeCount = await comfyPage.getGraphNodesCount()
|
||||
@@ -39,7 +39,7 @@ test.describe('Group Node', () => {
|
||||
expect(await comfyPage.getGraphNodesCount()).toBe(initialNodeCount + 1)
|
||||
})
|
||||
|
||||
test('Can be bookmarked and unbookmarked', async ({ comfyPage }) => {
|
||||
test.skip('Can be bookmarked and unbookmarked', async ({ comfyPage }) => {
|
||||
await libraryTab.getFolder(groupNodeCategory).click()
|
||||
await libraryTab
|
||||
.getNode(groupNodeName)
|
||||
@@ -66,7 +66,7 @@ test.describe('Group Node', () => {
|
||||
).toHaveLength(0)
|
||||
})
|
||||
|
||||
test('Displays preview on bookmark hover', async ({ comfyPage }) => {
|
||||
test.skip('Displays preview on bookmark hover', async ({ comfyPage }) => {
|
||||
await libraryTab.getFolder(groupNodeCategory).click()
|
||||
await libraryTab
|
||||
.getNode(groupNodeName)
|
||||
@@ -261,14 +261,14 @@ test.describe('Group Node', () => {
|
||||
await groupNode.copy()
|
||||
})
|
||||
|
||||
test('Copies and pastes group node within the same workflow', async ({
|
||||
test.skip('Copies and pastes group node within the same workflow', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.ctrlV()
|
||||
await verifyNodeLoaded(comfyPage, 2)
|
||||
})
|
||||
|
||||
test('Copies and pastes group node after clearing workflow', async ({
|
||||
test.skip('Copies and pastes group node after clearing workflow', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
// Set setting
|
||||
@@ -281,7 +281,7 @@ test.describe('Group Node', () => {
|
||||
await verifyNodeLoaded(comfyPage, 1)
|
||||
})
|
||||
|
||||
test('Copies and pastes group node into a newly created blank workflow', async ({
|
||||
test.skip('Copies and pastes group node into a newly created blank workflow', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.menu.topbar.triggerTopbarCommand(['New'])
|
||||
@@ -289,7 +289,7 @@ test.describe('Group Node', () => {
|
||||
await verifyNodeLoaded(comfyPage, 1)
|
||||
})
|
||||
|
||||
test('Copies and pastes group node across different workflows', async ({
|
||||
test.skip('Copies and pastes group node across different workflows', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.loadWorkflow('default')
|
||||
@@ -297,7 +297,7 @@ test.describe('Group Node', () => {
|
||||
await verifyNodeLoaded(comfyPage, 1)
|
||||
})
|
||||
|
||||
test('Serializes group node after copy and paste across workflows', async ({
|
||||
test.skip('Serializes group node after copy and paste across workflows', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.menu.topbar.triggerTopbarCommand(['New'])
|
||||
|
||||
@@ -38,7 +38,7 @@ test.describe('History API v2', () => {
|
||||
expect(historyItem.prompt.extra_data).toHaveProperty('client_id')
|
||||
})
|
||||
|
||||
test('Can load workflow from history using history_v2 endpoint', async ({
|
||||
test.skip('Can load workflow from history using history_v2 endpoint', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
// Simple mock workflow for testing
|
||||
|
||||
@@ -3,10 +3,10 @@ import { expect } from '@playwright/test'
|
||||
import type { Position } from '@vueuse/core'
|
||||
|
||||
import {
|
||||
type ComfyPage,
|
||||
comfyPageFixture as test,
|
||||
testComfySnapToGridGridSize
|
||||
} from '../fixtures/ComfyPage'
|
||||
import type { ComfyPage } from '../fixtures/ComfyPage'
|
||||
import type { NodeReference } from '../fixtures/utils/litegraphUtils'
|
||||
|
||||
test.beforeEach(async ({ comfyPage }) => {
|
||||
@@ -14,7 +14,7 @@ test.beforeEach(async ({ comfyPage }) => {
|
||||
})
|
||||
|
||||
test.describe('Item Interaction', () => {
|
||||
test('Can select/delete all items', async ({ comfyPage }) => {
|
||||
test.skip('Can select/delete all items', async ({ comfyPage }) => {
|
||||
await comfyPage.loadWorkflow('groups/mixed_graph_items')
|
||||
await comfyPage.canvas.press('Control+a')
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('selected-all.png')
|
||||
@@ -22,7 +22,9 @@ test.describe('Item Interaction', () => {
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('deleted-all.png')
|
||||
})
|
||||
|
||||
test('Can pin/unpin items with keyboard shortcut', async ({ comfyPage }) => {
|
||||
test.skip('Can pin/unpin items with keyboard shortcut', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.loadWorkflow('groups/mixed_graph_items')
|
||||
await comfyPage.canvas.press('Control+a')
|
||||
await comfyPage.canvas.press('KeyP')
|
||||
@@ -60,7 +62,7 @@ test.describe('Node Interaction', () => {
|
||||
})
|
||||
})
|
||||
|
||||
test('@2x Can highlight selected', async ({ comfyPage }) => {
|
||||
test.skip('@2x Can highlight selected', async ({ comfyPage }) => {
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('default.png')
|
||||
await comfyPage.clickTextEncodeNode1()
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('selected-node1.png')
|
||||
@@ -150,7 +152,7 @@ test.describe('Node Interaction', () => {
|
||||
})
|
||||
})
|
||||
|
||||
test('Can drag node', async ({ comfyPage }) => {
|
||||
test.skip('Can drag node', async ({ comfyPage }) => {
|
||||
await comfyPage.dragNode2()
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('dragged-node1.png')
|
||||
})
|
||||
@@ -163,7 +165,7 @@ test.describe('Node Interaction', () => {
|
||||
|
||||
// Test both directions of edge connection.
|
||||
;[{ reverse: false }, { reverse: true }].forEach(({ reverse }) => {
|
||||
test(`Can disconnect/connect edge ${reverse ? 'reverse' : 'normal'}`, async ({
|
||||
test.skip(`Can disconnect/connect edge ${reverse ? 'reverse' : 'normal'}`, async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.disconnectEdge()
|
||||
@@ -178,7 +180,7 @@ test.describe('Node Interaction', () => {
|
||||
})
|
||||
})
|
||||
|
||||
test('Can move link', async ({ comfyPage }) => {
|
||||
test.skip('Can move link', async ({ comfyPage }) => {
|
||||
await comfyPage.dragAndDrop(
|
||||
comfyPage.clipTextEncodeNode1InputSlot,
|
||||
comfyPage.emptySpace
|
||||
@@ -209,7 +211,7 @@ test.describe('Node Interaction', () => {
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('copied-link.png')
|
||||
})
|
||||
|
||||
test('Auto snap&highlight when dragging link over node', async ({
|
||||
test.skip('Auto snap&highlight when dragging link over node', async ({
|
||||
comfyPage,
|
||||
comfyMouse
|
||||
}) => {
|
||||
@@ -222,12 +224,12 @@ test.describe('Node Interaction', () => {
|
||||
})
|
||||
})
|
||||
|
||||
test('Can adjust widget value', async ({ comfyPage }) => {
|
||||
test.skip('Can adjust widget value', async ({ comfyPage }) => {
|
||||
await comfyPage.adjustWidgetValue()
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('adjusted-widget-value.png')
|
||||
})
|
||||
|
||||
test('Link snap to slot', async ({ comfyPage }) => {
|
||||
test.skip('Link snap to slot', async ({ comfyPage }) => {
|
||||
await comfyPage.loadWorkflow('links/snap_to_slot')
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('snap_to_slot.png')
|
||||
|
||||
@@ -244,7 +246,9 @@ test.describe('Node Interaction', () => {
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('snap_to_slot_linked.png')
|
||||
})
|
||||
|
||||
test('Can batch move links by drag with shift', async ({ comfyPage }) => {
|
||||
test.skip('Can batch move links by drag with shift', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.loadWorkflow('links/batch_move_links')
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('batch_move_links.png')
|
||||
|
||||
@@ -266,7 +270,7 @@ test.describe('Node Interaction', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test('Can batch disconnect links with ctrl+alt+click', async ({
|
||||
test.skip('Can batch disconnect links with ctrl+alt+click', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const loadCheckpointClipSlotPos = {
|
||||
@@ -283,7 +287,7 @@ test.describe('Node Interaction', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test('Can toggle dom widget node open/closed', async ({ comfyPage }) => {
|
||||
test.skip('Can toggle dom widget node open/closed', async ({ comfyPage }) => {
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('default.png')
|
||||
await comfyPage.clickTextEncodeNodeToggler()
|
||||
await expect(comfyPage.canvas).toHaveScreenshot(
|
||||
@@ -296,7 +300,7 @@ test.describe('Node Interaction', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test('Can close prompt dialog with canvas click (number widget)', async ({
|
||||
test.skip('Can close prompt dialog with canvas click (number widget)', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const numberWidgetPos = {
|
||||
@@ -318,7 +322,7 @@ test.describe('Node Interaction', () => {
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('prompt-dialog-closed.png')
|
||||
})
|
||||
|
||||
test('Can close prompt dialog with canvas click (text widget)', async ({
|
||||
test.skip('Can close prompt dialog with canvas click (text widget)', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const textWidgetPos = {
|
||||
@@ -344,7 +348,7 @@ test.describe('Node Interaction', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test('Can double click node title to edit', async ({ comfyPage }) => {
|
||||
test.skip('Can double click node title to edit', async ({ comfyPage }) => {
|
||||
await comfyPage.loadWorkflow('nodes/single_ksampler')
|
||||
await comfyPage.canvas.dblclick({
|
||||
position: {
|
||||
@@ -372,7 +376,7 @@ test.describe('Node Interaction', () => {
|
||||
expect(await comfyPage.page.locator('.node-title-editor').count()).toBe(0)
|
||||
})
|
||||
|
||||
test('Can group selected nodes', async ({ comfyPage }) => {
|
||||
test.skip('Can group selected nodes', async ({ comfyPage }) => {
|
||||
await comfyPage.setSetting('Comfy.GroupSelectedNodes.Padding', 10)
|
||||
await comfyPage.select2Nodes()
|
||||
await comfyPage.page.keyboard.down('Control')
|
||||
@@ -385,7 +389,7 @@ test.describe('Node Interaction', () => {
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('group-selected-nodes.png')
|
||||
})
|
||||
|
||||
test('Can fit group to contents', async ({ comfyPage }) => {
|
||||
test.skip('Can fit group to contents', async ({ comfyPage }) => {
|
||||
await comfyPage.loadWorkflow('groups/oversized_group')
|
||||
await comfyPage.ctrlA()
|
||||
await comfyPage.nextFrame()
|
||||
@@ -394,7 +398,7 @@ test.describe('Node Interaction', () => {
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('group-fit-to-contents.png')
|
||||
})
|
||||
|
||||
test('Can pin/unpin nodes', async ({ comfyPage }) => {
|
||||
test.skip('Can pin/unpin nodes', async ({ comfyPage }) => {
|
||||
await comfyPage.select2Nodes()
|
||||
await comfyPage.executeCommand('Comfy.Canvas.ToggleSelectedNodes.Pin')
|
||||
await comfyPage.nextFrame()
|
||||
@@ -404,7 +408,7 @@ test.describe('Node Interaction', () => {
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('nodes-unpinned.png')
|
||||
})
|
||||
|
||||
test('Can bypass/unbypass nodes with keyboard shortcut', async ({
|
||||
test.skip('Can bypass/unbypass nodes with keyboard shortcut', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.select2Nodes()
|
||||
@@ -418,7 +422,7 @@ test.describe('Node Interaction', () => {
|
||||
})
|
||||
|
||||
test.describe('Group Interaction', () => {
|
||||
test('Can double click group title to edit', async ({ comfyPage }) => {
|
||||
test.skip('Can double click group title to edit', async ({ comfyPage }) => {
|
||||
await comfyPage.loadWorkflow('groups/single_group')
|
||||
await comfyPage.canvas.dblclick({
|
||||
position: {
|
||||
@@ -434,21 +438,21 @@ test.describe('Group Interaction', () => {
|
||||
})
|
||||
|
||||
test.describe('Canvas Interaction', () => {
|
||||
test('Can zoom in/out', async ({ comfyPage }) => {
|
||||
test.skip('Can zoom in/out', async ({ comfyPage }) => {
|
||||
await comfyPage.zoom(-100)
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('zoomed-in.png')
|
||||
await comfyPage.zoom(200)
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('zoomed-out.png')
|
||||
})
|
||||
|
||||
test('Can zoom very far out', async ({ comfyPage }) => {
|
||||
test.skip('Can zoom very far out', async ({ comfyPage }) => {
|
||||
await comfyPage.zoom(100, 12)
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('zoomed-very-far-out.png')
|
||||
await comfyPage.zoom(-100, 12)
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('zoomed-back-in.png')
|
||||
})
|
||||
|
||||
test('Can zoom in/out with ctrl+shift+vertical-drag', async ({
|
||||
test.skip('Can zoom in/out with ctrl+shift+vertical-drag', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.page.keyboard.down('Control')
|
||||
@@ -465,7 +469,7 @@ test.describe('Canvas Interaction', () => {
|
||||
await comfyPage.page.keyboard.up('Shift')
|
||||
})
|
||||
|
||||
test('Can zoom in/out after decreasing canvas zoom speed setting', async ({
|
||||
test.skip('Can zoom in/out after decreasing canvas zoom speed setting', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.setSetting('Comfy.Graph.ZoomSpeed', 1.05)
|
||||
@@ -480,7 +484,7 @@ test.describe('Canvas Interaction', () => {
|
||||
await comfyPage.setSetting('Comfy.Graph.ZoomSpeed', 1.1)
|
||||
})
|
||||
|
||||
test('Can zoom in/out after increasing canvas zoom speed', async ({
|
||||
test.skip('Can zoom in/out after increasing canvas zoom speed', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.setSetting('Comfy.Graph.ZoomSpeed', 1.5)
|
||||
@@ -495,12 +499,12 @@ test.describe('Canvas Interaction', () => {
|
||||
await comfyPage.setSetting('Comfy.Graph.ZoomSpeed', 1.1)
|
||||
})
|
||||
|
||||
test('Can pan', async ({ comfyPage }) => {
|
||||
test.skip('Can pan', async ({ comfyPage }) => {
|
||||
await comfyPage.pan({ x: 200, y: 200 })
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('panned.png')
|
||||
})
|
||||
|
||||
test('Cursor style changes when panning', async ({ comfyPage }) => {
|
||||
test.skip('Cursor style changes when panning', async ({ comfyPage }) => {
|
||||
const getCursorStyle = async () => {
|
||||
return await comfyPage.page.evaluate(() => {
|
||||
return (
|
||||
@@ -530,7 +534,7 @@ test.describe('Canvas Interaction', () => {
|
||||
})
|
||||
|
||||
// https://github.com/Comfy-Org/litegraph.js/pull/424
|
||||
test('Properly resets dragging state after pan mode sequence', async ({
|
||||
test.skip('Properly resets dragging state after pan mode sequence', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const getCursorStyle = async () => {
|
||||
@@ -566,7 +570,10 @@ test.describe('Canvas Interaction', () => {
|
||||
expect(await getCursorStyle()).toBe('default')
|
||||
})
|
||||
|
||||
test('Can pan when dragging a link', async ({ comfyPage, comfyMouse }) => {
|
||||
test.skip('Can pan when dragging a link', async ({
|
||||
comfyPage,
|
||||
comfyMouse
|
||||
}) => {
|
||||
const posSlot1 = comfyPage.clipTextEncodeNode1InputSlot
|
||||
await comfyMouse.move(posSlot1)
|
||||
const posEmpty = comfyPage.emptySpace
|
||||
@@ -586,7 +593,7 @@ test.describe('Canvas Interaction', () => {
|
||||
await comfyMouse.drop()
|
||||
})
|
||||
|
||||
test('Can pan very far and back', async ({ comfyPage }) => {
|
||||
test.skip('Can pan very far and back', async ({ comfyPage }) => {
|
||||
// intentionally slice the edge of where the clip text encode dom widgets are
|
||||
await comfyPage.pan({ x: -800, y: -300 }, { x: 1000, y: 10 })
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('panned-step-one.png')
|
||||
@@ -602,7 +609,7 @@ test.describe('Canvas Interaction', () => {
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('panned-back-to-one.png')
|
||||
})
|
||||
|
||||
test('@mobile Can pan with touch', async ({ comfyPage }) => {
|
||||
test.skip('@mobile Can pan with touch', async ({ comfyPage }) => {
|
||||
await comfyPage.closeMenu()
|
||||
await comfyPage.panWithTouch({ x: 200, y: 200 })
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('panned-touch.png')
|
||||
@@ -636,19 +643,19 @@ test.describe('Widget Interaction', () => {
|
||||
})
|
||||
|
||||
test.describe('Load workflow', () => {
|
||||
test('Can load workflow with string node id', async ({ comfyPage }) => {
|
||||
test.skip('Can load workflow with string node id', async ({ comfyPage }) => {
|
||||
await comfyPage.loadWorkflow('nodes/string_node_id')
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('string_node_id.png')
|
||||
})
|
||||
|
||||
test('Can load workflow with ("STRING",) input node', async ({
|
||||
test.skip('Can load workflow with ("STRING",) input node', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.loadWorkflow('inputs/string_input')
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('string_input.png')
|
||||
})
|
||||
|
||||
test('Restore workflow on reload (switch workflow)', async ({
|
||||
test.skip('Restore workflow on reload (switch workflow)', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.loadWorkflow('nodes/single_ksampler')
|
||||
@@ -657,7 +664,7 @@ test.describe('Load workflow', () => {
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('single_ksampler.png')
|
||||
})
|
||||
|
||||
test('Restore workflow on reload (modify workflow)', async ({
|
||||
test.skip('Restore workflow on reload (modify workflow)', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.loadWorkflow('nodes/single_ksampler')
|
||||
@@ -714,7 +721,9 @@ test.describe('Load workflow', () => {
|
||||
expect(activeWorkflowName).toEqual(workflowB)
|
||||
})
|
||||
|
||||
test('Restores sidebar workflows after reload', async ({ comfyPage }) => {
|
||||
test.skip('Restores sidebar workflows after reload', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.setSetting(
|
||||
'Comfy.Workflow.WorkflowTabsPosition',
|
||||
'Sidebar'
|
||||
@@ -737,7 +746,7 @@ test.describe('Load workflow', () => {
|
||||
})
|
||||
})
|
||||
|
||||
test('Auto fit view after loading workflow', async ({ comfyPage }) => {
|
||||
test.skip('Auto fit view after loading workflow', async ({ comfyPage }) => {
|
||||
await comfyPage.setSetting('Comfy.EnableWorkflowViewRestore', false)
|
||||
await comfyPage.loadWorkflow('nodes/single_ksampler')
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('single_ksampler_fit.png')
|
||||
@@ -749,7 +758,7 @@ test.describe('Load duplicate workflow', () => {
|
||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Top')
|
||||
})
|
||||
|
||||
test('A workflow can be loaded multiple times in a row', async ({
|
||||
test.skip('A workflow can be loaded multiple times in a row', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.loadWorkflow('nodes/single_ksampler')
|
||||
@@ -838,7 +847,7 @@ test.describe('Canvas Navigation', () => {
|
||||
await comfyPage.setSetting('Comfy.Canvas.NavigationMode', 'legacy')
|
||||
})
|
||||
|
||||
test('Left-click drag in empty area should pan canvas', async ({
|
||||
test.skip('Left-click drag in empty area should pan canvas', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.dragAndDrop({ x: 50, y: 50 }, { x: 150, y: 150 })
|
||||
@@ -847,7 +856,7 @@ test.describe('Canvas Navigation', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test('Middle-click drag should pan canvas', async ({ comfyPage }) => {
|
||||
test.skip('Middle-click drag should pan canvas', async ({ comfyPage }) => {
|
||||
await comfyPage.page.mouse.move(50, 50)
|
||||
await comfyPage.page.mouse.down({ button: 'middle' })
|
||||
await comfyPage.page.mouse.move(150, 150)
|
||||
@@ -858,7 +867,7 @@ test.describe('Canvas Navigation', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test('Mouse wheel should zoom in/out', async ({ comfyPage }) => {
|
||||
test.skip('Mouse wheel should zoom in/out', async ({ comfyPage }) => {
|
||||
await comfyPage.page.mouse.move(400, 300)
|
||||
await comfyPage.page.mouse.wheel(0, -120)
|
||||
await comfyPage.nextFrame()
|
||||
@@ -873,7 +882,9 @@ test.describe('Canvas Navigation', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test('Left-click on node should not pan canvas', async ({ comfyPage }) => {
|
||||
test.skip('Left-click on node should not pan canvas', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.clickTextEncodeNode1()
|
||||
const selectedCount = await comfyPage.getSelectedGraphNodesCount()
|
||||
expect(selectedCount).toBe(1)
|
||||
@@ -888,7 +899,7 @@ test.describe('Canvas Navigation', () => {
|
||||
await comfyPage.setSetting('Comfy.Canvas.NavigationMode', 'standard')
|
||||
})
|
||||
|
||||
test('Left-click drag in empty area should select nodes', async ({
|
||||
test.skip('Left-click drag in empty area should select nodes', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const clipNodes = await comfyPage.getNodeRefsByType('CLIPTextEncode')
|
||||
@@ -914,7 +925,7 @@ test.describe('Canvas Navigation', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test('Middle-click drag should pan canvas', async ({ comfyPage }) => {
|
||||
test.skip('Middle-click drag should pan canvas', async ({ comfyPage }) => {
|
||||
await comfyPage.page.mouse.move(50, 50)
|
||||
await comfyPage.page.mouse.down({ button: 'middle' })
|
||||
await comfyPage.page.mouse.move(150, 150)
|
||||
@@ -925,7 +936,9 @@ test.describe('Canvas Navigation', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test('Ctrl + mouse wheel should zoom in/out', async ({ comfyPage }) => {
|
||||
test.skip('Ctrl + mouse wheel should zoom in/out', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.page.mouse.move(400, 300)
|
||||
await comfyPage.page.keyboard.down('Control')
|
||||
await comfyPage.page.mouse.wheel(0, -120)
|
||||
@@ -944,7 +957,7 @@ test.describe('Canvas Navigation', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test('Left-click on node should select node (not start selection box)', async ({
|
||||
test.skip('Left-click on node should select node (not start selection box)', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.clickTextEncodeNode1()
|
||||
@@ -955,7 +968,9 @@ test.describe('Canvas Navigation', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test('Space + left-click drag should pan canvas', async ({ comfyPage }) => {
|
||||
test.skip('Space + left-click drag should pan canvas', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
// Click canvas to focus it
|
||||
await comfyPage.page.click('canvas')
|
||||
await comfyPage.nextFrame()
|
||||
@@ -968,7 +983,7 @@ test.describe('Canvas Navigation', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test('Space key overrides default left-click behavior', async ({
|
||||
test.skip('Space key overrides default left-click behavior', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const clipNodes = await comfyPage.getNodeRefsByType('CLIPTextEncode')
|
||||
@@ -1014,7 +1029,7 @@ test.describe('Canvas Navigation', () => {
|
||||
})
|
||||
})
|
||||
|
||||
test('Shift + mouse wheel should pan canvas horizontally', async ({
|
||||
test.skip('Shift + mouse wheel should pan canvas horizontally', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.setSetting('Comfy.Canvas.MouseWheelScroll', 'panning')
|
||||
@@ -1052,7 +1067,7 @@ test.describe('Canvas Navigation', () => {
|
||||
})
|
||||
|
||||
test.describe('Edge Cases', () => {
|
||||
test('Multiple modifier keys work correctly in legacy mode', async ({
|
||||
test.skip('Multiple modifier keys work correctly in legacy mode', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.setSetting('Comfy.Canvas.NavigationMode', 'legacy')
|
||||
|
||||
@@ -27,7 +27,9 @@ test.describe('Canvas Event', () => {
|
||||
// See https://github.com/microsoft/playwright/issues/31580
|
||||
})
|
||||
|
||||
test('Emit litegraph:canvas empty-double-click', async ({ comfyPage }) => {
|
||||
test.skip('Emit litegraph:canvas empty-double-click', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const eventPromise = comfyPage.page.evaluate(listenForEvent)
|
||||
const doubleClickPromise = comfyPage.doubleClickCanvas()
|
||||
const event = await eventPromise
|
||||
|
||||
@@ -25,7 +25,7 @@ test.describe('Load Workflow in Media', () => {
|
||||
// 'workflow.avif'
|
||||
]
|
||||
fileNames.forEach(async (fileName) => {
|
||||
test(`Load workflow in ${fileName} (drop from filesystem)`, async ({
|
||||
test.skip(`Load workflow in ${fileName} (drop from filesystem)`, async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.dragAndDropFile(`workflowInMedia/${fileName}`)
|
||||
@@ -37,7 +37,7 @@ test.describe('Load Workflow in Media', () => {
|
||||
'https://comfyanonymous.github.io/ComfyUI_examples/hidream/hidream_dev_example.png'
|
||||
]
|
||||
urls.forEach(async (url) => {
|
||||
test(`Load workflow from URL ${url} (drop from different browser tabs)`, async ({
|
||||
test.skip(`Load workflow from URL ${url} (drop from different browser tabs)`, async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.dragAndDropURL(url)
|
||||
|
||||
|
Before Width: | Height: | Size: 108 KiB After Width: | Height: | Size: 108 KiB |
@@ -7,7 +7,7 @@ test.beforeEach(async ({ comfyPage }) => {
|
||||
})
|
||||
|
||||
test.describe('LOD Threshold', () => {
|
||||
test('Should switch to low quality mode at correct zoom threshold', async ({
|
||||
test.skip('Should switch to low quality mode at correct zoom threshold', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
// Load a workflow with some nodes to render
|
||||
@@ -81,7 +81,7 @@ test.describe('LOD Threshold', () => {
|
||||
expect(zoomedInState.lowQuality).toBe(false)
|
||||
})
|
||||
|
||||
test('Should update threshold when font size setting changes', async ({
|
||||
test.skip('Should update threshold when font size setting changes', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.loadWorkflow('default')
|
||||
@@ -122,7 +122,7 @@ test.describe('LOD Threshold', () => {
|
||||
expect(afterZoom.lowQuality).toBe(true)
|
||||
})
|
||||
|
||||
test('Should disable LOD when font size is set to 0', async ({
|
||||
test.skip('Should disable LOD when font size is set to 0', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.loadWorkflow('default')
|
||||
@@ -149,7 +149,7 @@ test.describe('LOD Threshold', () => {
|
||||
expect(state.scale).toBeLessThan(0.2) // Very zoomed out
|
||||
})
|
||||
|
||||
test('Should show visual difference between LOD on and off', async ({
|
||||
test.skip('Should show visual difference between LOD on and off', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
// Load a workflow with text-heavy nodes for clear visual difference
|
||||
|
||||
@@ -7,7 +7,7 @@ test.describe('Menu', () => {
|
||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Top')
|
||||
})
|
||||
|
||||
test('Can register sidebar tab', async ({ comfyPage }) => {
|
||||
test.skip('Can register sidebar tab', async ({ comfyPage }) => {
|
||||
const initialChildrenCount = await comfyPage.menu.sideToolbar.evaluate(
|
||||
(el) => el.children.length
|
||||
)
|
||||
|
||||
@@ -66,12 +66,22 @@ test.describe('Minimap', () => {
|
||||
await comfyPage.nextFrame()
|
||||
|
||||
await expect(minimapContainer).not.toBeVisible()
|
||||
|
||||
// Open zoom controls dropdown again
|
||||
await zoomControlsButton.click()
|
||||
await comfyPage.nextFrame()
|
||||
|
||||
await expect(toggleButton).toContainText('Show Minimap')
|
||||
|
||||
await toggleButton.click()
|
||||
await comfyPage.nextFrame()
|
||||
|
||||
await expect(minimapContainer).toBeVisible()
|
||||
|
||||
// Open zoom controls dropdown again to verify button text
|
||||
await zoomControlsButton.click()
|
||||
await comfyPage.nextFrame()
|
||||
|
||||
await expect(toggleButton).toContainText('Hide Minimap')
|
||||
})
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ test.beforeEach(async ({ comfyPage }) => {
|
||||
})
|
||||
|
||||
test.describe('Node Badge', () => {
|
||||
test('Can add badge', async ({ comfyPage }) => {
|
||||
test.skip('Can add badge', async ({ comfyPage }) => {
|
||||
await comfyPage.page.evaluate(() => {
|
||||
const LGraphBadge = window['LGraphBadge']
|
||||
const app = window['app'] as ComfyApp
|
||||
@@ -26,7 +26,7 @@ test.describe('Node Badge', () => {
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('node-badge.png')
|
||||
})
|
||||
|
||||
test('Can add multiple badges', async ({ comfyPage }) => {
|
||||
test.skip('Can add multiple badges', async ({ comfyPage }) => {
|
||||
await comfyPage.page.evaluate(() => {
|
||||
const LGraphBadge = window['LGraphBadge']
|
||||
const app = window['app'] as ComfyApp
|
||||
@@ -46,7 +46,7 @@ test.describe('Node Badge', () => {
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('node-badge-multiple.png')
|
||||
})
|
||||
|
||||
test('Can add badge left-side', async ({ comfyPage }) => {
|
||||
test.skip('Can add badge left-side', async ({ comfyPage }) => {
|
||||
await comfyPage.page.evaluate(() => {
|
||||
const LGraphBadge = window['LGraphBadge']
|
||||
const app = window['app'] as ComfyApp
|
||||
@@ -68,7 +68,7 @@ test.describe('Node Badge', () => {
|
||||
|
||||
test.describe('Node source badge', () => {
|
||||
Object.values(NodeBadgeMode).forEach(async (mode) => {
|
||||
test(`Shows node badges (${mode})`, async ({ comfyPage }) => {
|
||||
test.skip(`Shows node badges (${mode})`, async ({ comfyPage }) => {
|
||||
// Execution error workflow has both custom node and core node.
|
||||
await comfyPage.loadWorkflow('nodes/execution_error')
|
||||
await comfyPage.setSetting('Comfy.NodeBadge.NodeSourceBadgeMode', mode)
|
||||
@@ -81,7 +81,7 @@ test.describe('Node source badge', () => {
|
||||
})
|
||||
|
||||
test.describe('Node badge color', () => {
|
||||
test('Can show node badge with unknown color palette', async ({
|
||||
test.skip('Can show node badge with unknown color palette', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.setSetting(
|
||||
@@ -97,7 +97,7 @@ test.describe('Node badge color', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test('Can show node badge with light color palette', async ({
|
||||
test.skip('Can show node badge with light color palette', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.setSetting(
|
||||
|
||||
|
Before Width: | Height: | Size: 105 KiB After Width: | Height: | Size: 105 KiB |
@@ -9,27 +9,27 @@ test.beforeEach(async ({ comfyPage }) => {
|
||||
// If an input is optional by node definition, it should be shown as
|
||||
// a hollow circle no matter what shape it was defined in the workflow JSON.
|
||||
test.describe('Optional input', () => {
|
||||
test('No shape specified', async ({ comfyPage }) => {
|
||||
test.skip('No shape specified', async ({ comfyPage }) => {
|
||||
await comfyPage.loadWorkflow('inputs/optional_input_no_shape')
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('optional_input.png')
|
||||
})
|
||||
|
||||
test('Wrong shape specified', async ({ comfyPage }) => {
|
||||
test.skip('Wrong shape specified', async ({ comfyPage }) => {
|
||||
await comfyPage.loadWorkflow('inputs/optional_input_wrong_shape')
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('optional_input.png')
|
||||
})
|
||||
|
||||
test('Correct shape specified', async ({ comfyPage }) => {
|
||||
test.skip('Correct shape specified', async ({ comfyPage }) => {
|
||||
await comfyPage.loadWorkflow('inputs/optional_input_correct_shape')
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('optional_input.png')
|
||||
})
|
||||
|
||||
test('Force input', async ({ comfyPage }) => {
|
||||
test.skip('Force input', async ({ comfyPage }) => {
|
||||
await comfyPage.loadWorkflow('inputs/force_input')
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('force_input.png')
|
||||
})
|
||||
|
||||
test('Default input', async ({ comfyPage }) => {
|
||||
test.skip('Default input', async ({ comfyPage }) => {
|
||||
await comfyPage.loadWorkflow('inputs/default_input')
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('default_input.png')
|
||||
})
|
||||
@@ -65,18 +65,18 @@ test.describe('Optional input', () => {
|
||||
const renamedInput = inputs.find((w) => w.name === 'breadth')
|
||||
expect(renamedInput).toBeUndefined()
|
||||
})
|
||||
test('slider', async ({ comfyPage }) => {
|
||||
test.skip('slider', async ({ comfyPage }) => {
|
||||
await comfyPage.loadWorkflow('inputs/simple_slider')
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('simple_slider.png')
|
||||
})
|
||||
test('unknown converted widget', async ({ comfyPage }) => {
|
||||
test.skip('unknown converted widget', async ({ comfyPage }) => {
|
||||
await comfyPage.setSetting('Comfy.Workflow.ShowMissingNodesWarning', false)
|
||||
await comfyPage.loadWorkflow('missing/missing_nodes_converted_widget')
|
||||
await expect(comfyPage.canvas).toHaveScreenshot(
|
||||
'missing_nodes_converted_widget.png'
|
||||
)
|
||||
})
|
||||
test('dynamically added input', async ({ comfyPage }) => {
|
||||
test.skip('dynamically added input', async ({ comfyPage }) => {
|
||||
await comfyPage.loadWorkflow('inputs/dynamically_added_input')
|
||||
await expect(comfyPage.canvas).toHaveScreenshot(
|
||||
'dynamically_added_input.png'
|
||||
|
||||
@@ -27,7 +27,9 @@ test.describe('Node Help', () => {
|
||||
})
|
||||
|
||||
test.describe('Selection Toolbox', () => {
|
||||
test('Should open help menu for selected node', async ({ comfyPage }) => {
|
||||
test.skip('Should open help menu for selected node', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
// Load a workflow with a node
|
||||
await comfyPage.setSetting('Comfy.Canvas.SelectionToolbox', true)
|
||||
await comfyPage.loadWorkflow('default')
|
||||
@@ -64,7 +66,9 @@ test.describe('Node Help', () => {
|
||||
})
|
||||
|
||||
test.describe('Node Library Sidebar', () => {
|
||||
test('Should open help menu from node library', async ({ comfyPage }) => {
|
||||
test.skip('Should open help menu from node library', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
// Open the node library sidebar
|
||||
await comfyPage.menu.nodeLibraryTab.open()
|
||||
|
||||
@@ -97,7 +101,7 @@ test.describe('Node Help', () => {
|
||||
await expect(helpPage.locator('.node-help-content')).toBeVisible()
|
||||
})
|
||||
|
||||
test('Should show node library tab when clicking back from help page', async ({
|
||||
test.skip('Should show node library tab when clicking back from help page', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
// Open the node library sidebar
|
||||
@@ -145,7 +149,7 @@ test.describe('Node Help', () => {
|
||||
await comfyPage.setSetting('Comfy.Canvas.SelectionToolbox', true)
|
||||
})
|
||||
|
||||
test('Should display loading state while fetching help', async ({
|
||||
test.skip('Should display loading state while fetching help', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
// Mock slow network response
|
||||
@@ -176,7 +180,7 @@ test.describe('Node Help', () => {
|
||||
await expect(helpPage).toContainText('Test Help Content')
|
||||
})
|
||||
|
||||
test('Should display fallback content when help file not found', async ({
|
||||
test.skip('Should display fallback content when help file not found', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
// Mock 404 response for help files
|
||||
@@ -205,7 +209,7 @@ test.describe('Node Help', () => {
|
||||
await expect(helpPage).toContainText('Outputs')
|
||||
})
|
||||
|
||||
test('Should render markdown with images correctly', async ({
|
||||
test.skip('Should render markdown with images correctly', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
// Mock response with markdown containing images
|
||||
@@ -251,7 +255,7 @@ test.describe('Node Help', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test('Should render video elements with source tags in markdown', async ({
|
||||
test.skip('Should render video elements with source tags in markdown', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
// Mock response with video elements
|
||||
@@ -312,7 +316,7 @@ test.describe('Node Help', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test('Should handle custom node documentation paths', async ({
|
||||
test.skip('Should handle custom node documentation paths', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
// First load workflow with custom node
|
||||
@@ -365,7 +369,9 @@ This is documentation for a custom node.
|
||||
}
|
||||
})
|
||||
|
||||
test('Should sanitize dangerous HTML content', async ({ comfyPage }) => {
|
||||
test.skip('Should sanitize dangerous HTML content', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
// Mock response with potentially dangerous content
|
||||
await comfyPage.page.route('**/docs/KSampler/en.md', async (route) => {
|
||||
await route.fulfill({
|
||||
@@ -424,7 +430,7 @@ This is documentation for a custom node.
|
||||
await expect(helpPage.locator('img[alt="Safe Image"]')).toBeVisible()
|
||||
})
|
||||
|
||||
test('Should handle locale-specific documentation', async ({
|
||||
test.skip('Should handle locale-specific documentation', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
// Mock different responses for different locales
|
||||
@@ -468,7 +474,9 @@ This is English documentation.
|
||||
await comfyPage.setSetting('Comfy.Locale', 'en')
|
||||
})
|
||||
|
||||
test('Should handle network errors gracefully', async ({ comfyPage }) => {
|
||||
test.skip('Should handle network errors gracefully', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
// Mock network error
|
||||
await comfyPage.page.route('**/docs/**/*.md', async (route) => {
|
||||
await route.abort('failed')
|
||||
@@ -494,7 +502,7 @@ This is English documentation.
|
||||
expect(content).toBeTruthy()
|
||||
})
|
||||
|
||||
test('Should update help content when switching between nodes', async ({
|
||||
test.skip('Should update help content when switching between nodes', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
// Mock different help content for different nodes
|
||||
|
||||
@@ -14,7 +14,9 @@ test.describe('Node search box', () => {
|
||||
await comfyPage.setSetting('Comfy.NodeSearchBoxImpl', 'default')
|
||||
})
|
||||
|
||||
test(`Can trigger on empty canvas double click`, async ({ comfyPage }) => {
|
||||
test.skip(`Can trigger on empty canvas double click`, async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.doubleClickCanvas()
|
||||
await expect(comfyPage.searchBox.input).toHaveCount(1)
|
||||
})
|
||||
@@ -46,14 +48,14 @@ test.describe('Node search box', () => {
|
||||
await expect(comfyPage.searchBox.input).toBeVisible()
|
||||
})
|
||||
|
||||
test('Can add node', async ({ comfyPage }) => {
|
||||
test.skip('Can add node', async ({ comfyPage }) => {
|
||||
await comfyPage.doubleClickCanvas()
|
||||
await expect(comfyPage.searchBox.input).toHaveCount(1)
|
||||
await comfyPage.searchBox.fillAndSelectFirstNode('KSampler')
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('added-node.png')
|
||||
})
|
||||
|
||||
test('Can auto link node', async ({ comfyPage }) => {
|
||||
test.skip('Can auto link node', async ({ comfyPage }) => {
|
||||
await comfyPage.disconnectEdge()
|
||||
// Select the second item as the first item is always reroute
|
||||
await comfyPage.searchBox.fillAndSelectFirstNode('CLIPTextEncode', {
|
||||
@@ -62,7 +64,7 @@ test.describe('Node search box', () => {
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('auto-linked-node.png')
|
||||
})
|
||||
|
||||
test('Can auto link batch moved node', async ({ comfyPage }) => {
|
||||
test.skip('Can auto link batch moved node', async ({ comfyPage }) => {
|
||||
await comfyPage.loadWorkflow('links/batch_move_links')
|
||||
|
||||
const outputSlot1Pos = {
|
||||
@@ -86,7 +88,7 @@ test.describe('Node search box', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test('Link release connecting to node with no slots', async ({
|
||||
test.skip('Link release connecting to node with no slots', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.disconnectEdge()
|
||||
@@ -98,7 +100,9 @@ test.describe('Node search box', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test('Has correct aria-labels on search results', async ({ comfyPage }) => {
|
||||
test.skip('Has correct aria-labels on search results', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const node = 'Load Checkpoint'
|
||||
await comfyPage.doubleClickCanvas()
|
||||
await comfyPage.searchBox.input.waitFor({ state: 'visible' })
|
||||
@@ -150,7 +154,7 @@ test.describe('Node search box', () => {
|
||||
await comfyPage.doubleClickCanvas()
|
||||
})
|
||||
|
||||
test('Can add filter', async ({ comfyPage }) => {
|
||||
test.skip('Can add filter', async ({ comfyPage }) => {
|
||||
await comfyPage.searchBox.addFilter('MODEL', 'Input Type')
|
||||
await expectFilterChips(comfyPage, ['MODEL'])
|
||||
})
|
||||
@@ -197,13 +201,13 @@ test.describe('Node search box', () => {
|
||||
await expect(comfyPage.searchBox.input).toBeVisible()
|
||||
})
|
||||
|
||||
test('Can add multiple filters', async ({ comfyPage }) => {
|
||||
test.skip('Can add multiple filters', async ({ comfyPage }) => {
|
||||
await comfyPage.searchBox.addFilter('MODEL', 'Input Type')
|
||||
await comfyPage.searchBox.addFilter('CLIP', 'Output Type')
|
||||
await expectFilterChips(comfyPage, ['MODEL', 'CLIP'])
|
||||
})
|
||||
|
||||
test('Can remove filter', async ({ comfyPage }) => {
|
||||
test.skip('Can remove filter', async ({ comfyPage }) => {
|
||||
await comfyPage.searchBox.addFilter('MODEL', 'Input Type')
|
||||
await comfyPage.searchBox.removeFilter(0)
|
||||
await expectFilterChips(comfyPage, [])
|
||||
@@ -216,7 +220,7 @@ test.describe('Node search box', () => {
|
||||
await comfyPage.searchBox.addFilter('utils', 'Category')
|
||||
})
|
||||
|
||||
test('Can remove first filter', async ({ comfyPage }) => {
|
||||
test.skip('Can remove first filter', async ({ comfyPage }) => {
|
||||
await comfyPage.searchBox.removeFilter(0)
|
||||
await expectFilterChips(comfyPage, ['CLIP', 'utils'])
|
||||
await comfyPage.searchBox.removeFilter(0)
|
||||
@@ -225,12 +229,12 @@ test.describe('Node search box', () => {
|
||||
await expectFilterChips(comfyPage, [])
|
||||
})
|
||||
|
||||
test('Can remove middle filter', async ({ comfyPage }) => {
|
||||
test.skip('Can remove middle filter', async ({ comfyPage }) => {
|
||||
await comfyPage.searchBox.removeFilter(1)
|
||||
await expectFilterChips(comfyPage, ['MODEL', 'utils'])
|
||||
})
|
||||
|
||||
test('Can remove last filter', async ({ comfyPage }) => {
|
||||
test.skip('Can remove last filter', async ({ comfyPage }) => {
|
||||
await comfyPage.searchBox.removeFilter(2)
|
||||
await expectFilterChips(comfyPage, ['MODEL', 'CLIP'])
|
||||
})
|
||||
@@ -242,12 +246,14 @@ test.describe('Node search box', () => {
|
||||
await comfyPage.doubleClickCanvas()
|
||||
})
|
||||
|
||||
test('focuses input after adding a filter', async ({ comfyPage }) => {
|
||||
test.skip('focuses input after adding a filter', async ({ comfyPage }) => {
|
||||
await comfyPage.searchBox.addFilter('MODEL', 'Input Type')
|
||||
await expect(comfyPage.searchBox.input).toHaveFocus()
|
||||
})
|
||||
|
||||
test('focuses input after removing a filter', async ({ comfyPage }) => {
|
||||
test.skip('focuses input after removing a filter', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.searchBox.addFilter('MODEL', 'Input Type')
|
||||
await comfyPage.searchBox.removeFilter(0)
|
||||
await expect(comfyPage.searchBox.input).toHaveFocus()
|
||||
@@ -262,7 +268,7 @@ test.describe('Release context menu', () => {
|
||||
await comfyPage.setSetting('Comfy.NodeSearchBoxImpl', 'default')
|
||||
})
|
||||
|
||||
test('Can trigger on link release', async ({ comfyPage }) => {
|
||||
test.skip('Can trigger on link release', async ({ comfyPage }) => {
|
||||
await comfyPage.disconnectEdge()
|
||||
await comfyPage.page.mouse.move(10, 10)
|
||||
await comfyPage.nextFrame()
|
||||
@@ -271,7 +277,7 @@ test.describe('Release context menu', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test('Can search and add node from context menu', async ({
|
||||
test.skip('Can search and add node from context menu', async ({
|
||||
comfyPage,
|
||||
comfyMouse
|
||||
}) => {
|
||||
|
||||
@@ -7,7 +7,7 @@ test.beforeEach(async ({ comfyPage }) => {
|
||||
})
|
||||
|
||||
test.describe('Note Node', () => {
|
||||
test('Can load node nodes', async ({ comfyPage }) => {
|
||||
test.skip('Can load node nodes', async ({ comfyPage }) => {
|
||||
await comfyPage.loadWorkflow('nodes/note_nodes')
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('note_nodes.png')
|
||||
})
|
||||
|
||||
@@ -8,14 +8,14 @@ test.beforeEach(async ({ comfyPage }) => {
|
||||
})
|
||||
|
||||
test.describe('Primitive Node', () => {
|
||||
test('Can load with correct size', async ({ comfyPage }) => {
|
||||
test.skip('Can load with correct size', async ({ comfyPage }) => {
|
||||
await comfyPage.loadWorkflow('primitive/primitive_node')
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('primitive_node.png')
|
||||
})
|
||||
|
||||
// When link is dropped on widget, it should automatically convert the widget
|
||||
// to input.
|
||||
test('Can connect to widget', async ({ comfyPage }) => {
|
||||
test.skip('Can connect to widget', async ({ comfyPage }) => {
|
||||
await comfyPage.loadWorkflow('primitive/primitive_node_unconnected')
|
||||
const primitiveNode: NodeReference = await comfyPage.getNodeRefById(1)
|
||||
const ksamplerNode: NodeReference = await comfyPage.getNodeRefById(2)
|
||||
@@ -26,7 +26,7 @@ test.describe('Primitive Node', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test('Can connect to dom widget', async ({ comfyPage }) => {
|
||||
test.skip('Can connect to dom widget', async ({ comfyPage }) => {
|
||||
await comfyPage.loadWorkflow(
|
||||
'primitive/primitive_node_unconnected_dom_widget'
|
||||
)
|
||||
@@ -38,7 +38,7 @@ test.describe('Primitive Node', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test('Can connect to static primitive node', async ({ comfyPage }) => {
|
||||
test.skip('Can connect to static primitive node', async ({ comfyPage }) => {
|
||||
await comfyPage.loadWorkflow('primitive/static_primitive_unconnected')
|
||||
const primitiveNode: NodeReference = await comfyPage.getNodeRefById(1)
|
||||
const ksamplerNode: NodeReference = await comfyPage.getNodeRefById(2)
|
||||
|
||||
|
After Width: | Height: | Size: 100 KiB |
@@ -7,7 +7,7 @@ test.describe('Release Notifications', () => {
|
||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Top')
|
||||
})
|
||||
|
||||
test('should show help center with release information', async ({
|
||||
test.skip('should show help center with release information', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
// Mock release API with test data instead of empty array
|
||||
@@ -63,7 +63,7 @@ test.describe('Release Notifications', () => {
|
||||
await expect(helpMenu).not.toBeVisible()
|
||||
})
|
||||
|
||||
test('should not show release notifications when mocked (default behavior)', async ({
|
||||
test.skip('should not show release notifications when mocked (default behavior)', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
// Use default setup (mockReleases: true)
|
||||
@@ -94,7 +94,9 @@ test.describe('Release Notifications', () => {
|
||||
).not.toBeVisible()
|
||||
})
|
||||
|
||||
test('should handle release API errors gracefully', async ({ comfyPage }) => {
|
||||
test.skip('should handle release API errors gracefully', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
// Mock API to return an error
|
||||
await comfyPage.page.route('**/releases**', async (route) => {
|
||||
const url = route.request().url()
|
||||
@@ -131,7 +133,7 @@ test.describe('Release Notifications', () => {
|
||||
).toBeVisible()
|
||||
})
|
||||
|
||||
test('should hide "What\'s New" section when notifications are disabled', async ({
|
||||
test.skip('should hide "What\'s New" section when notifications are disabled', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
// Disable version update notifications
|
||||
@@ -219,7 +221,7 @@ test.describe('Release Notifications', () => {
|
||||
expect(apiCallCount).toBe(0)
|
||||
})
|
||||
|
||||
test('should show "What\'s New" section when notifications are enabled', async ({
|
||||
test.skip('should show "What\'s New" section when notifications are enabled', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
// Enable version update notifications (default behavior)
|
||||
@@ -272,7 +274,7 @@ test.describe('Release Notifications', () => {
|
||||
).toBeVisible()
|
||||
})
|
||||
|
||||
test('should toggle "What\'s New" section when setting changes', async ({
|
||||
test.skip('should toggle "What\'s New" section when setting changes', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
// Mock release API with test data
|
||||
@@ -327,7 +329,7 @@ test.describe('Release Notifications', () => {
|
||||
await expect(whatsNewSection).not.toBeVisible()
|
||||
})
|
||||
|
||||
test('should handle edge case with empty releases and disabled notifications', async ({
|
||||
test.skip('should handle edge case with empty releases and disabled notifications', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
// Disable notifications
|
||||
|
||||
@@ -77,7 +77,7 @@ test.describe('Remote COMBO Widget', () => {
|
||||
await comfyPage.page.unroute('**/api/models/checkpoints**')
|
||||
})
|
||||
|
||||
test('lazy loads options when widget is added from node library', async ({
|
||||
test.skip('lazy loads options when widget is added from node library', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const nodeName = 'Remote Widget Node'
|
||||
@@ -104,7 +104,9 @@ test.describe('Remote COMBO Widget', () => {
|
||||
expect(widgetOptions).toEqual(mockOptions)
|
||||
})
|
||||
|
||||
test('applies query parameters from input spec', async ({ comfyPage }) => {
|
||||
test.skip('applies query parameters from input spec', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const nodeName = 'Remote Widget Node With Sort Query Param'
|
||||
await addRemoteWidgetNode(comfyPage, nodeName)
|
||||
await waitForWidgetUpdate(comfyPage)
|
||||
@@ -113,7 +115,7 @@ test.describe('Remote COMBO Widget', () => {
|
||||
expect(widgetOptions).toEqual([...mockOptions].sort())
|
||||
})
|
||||
|
||||
test('handles empty list of options', async ({ comfyPage }) => {
|
||||
test.skip('handles empty list of options', async ({ comfyPage }) => {
|
||||
await comfyPage.page.route(
|
||||
'**/api/models/checkpoints**',
|
||||
async (route) => {
|
||||
@@ -128,7 +130,7 @@ test.describe('Remote COMBO Widget', () => {
|
||||
expect(widgetOptions).toEqual([])
|
||||
})
|
||||
|
||||
test('falls back to default value when non-200 response', async ({
|
||||
test.skip('falls back to default value when non-200 response', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.page.route(
|
||||
@@ -165,7 +167,7 @@ test.describe('Remote COMBO Widget', () => {
|
||||
expect(requestWasMade).toBe(false)
|
||||
})
|
||||
|
||||
test('fetches options immediately after widget is added to graph', async ({
|
||||
test.skip('fetches options immediately after widget is added to graph', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const requestPromise = comfyPage.page.waitForRequest((request) =>
|
||||
@@ -178,7 +180,7 @@ test.describe('Remote COMBO Widget', () => {
|
||||
})
|
||||
|
||||
test.describe('Refresh Behavior', () => {
|
||||
test('refresh button is visible in selection toolbar when node is selected', async ({
|
||||
test.skip('refresh button is visible in selection toolbar when node is selected', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.setSetting('Comfy.Canvas.SelectionToolbox', true)
|
||||
@@ -197,7 +199,7 @@ test.describe('Remote COMBO Widget', () => {
|
||||
).toBeVisible()
|
||||
})
|
||||
|
||||
test('refreshes options when TTL expires', async ({ comfyPage }) => {
|
||||
test.skip('refreshes options when TTL expires', async ({ comfyPage }) => {
|
||||
// Fulfill each request with a unique timestamp
|
||||
await comfyPage.page.route(
|
||||
'**/api/models/checkpoints**',
|
||||
@@ -228,7 +230,7 @@ test.describe('Remote COMBO Widget', () => {
|
||||
expect(refreshedOptions).not.toEqual(initialOptions)
|
||||
})
|
||||
|
||||
test('does not refresh when TTL is not set', async ({ comfyPage }) => {
|
||||
test.skip('does not refresh when TTL is not set', async ({ comfyPage }) => {
|
||||
let requestCount = 0
|
||||
await comfyPage.page.route(
|
||||
'**/api/models/checkpoints**',
|
||||
@@ -251,7 +253,7 @@ test.describe('Remote COMBO Widget', () => {
|
||||
expect(requestCount).toBe(1) // Should only make initial request
|
||||
})
|
||||
|
||||
test('retries failed requests with backoff', async ({ comfyPage }) => {
|
||||
test.skip('retries failed requests with backoff', async ({ comfyPage }) => {
|
||||
const timestamps: number[] = []
|
||||
await comfyPage.page.route(
|
||||
'**/api/models/checkpoints**',
|
||||
@@ -278,7 +280,9 @@ test.describe('Remote COMBO Widget', () => {
|
||||
expect(intervals[1]).toBeGreaterThan(intervals[0])
|
||||
})
|
||||
|
||||
test('clicking refresh button forces a refresh', async ({ comfyPage }) => {
|
||||
test.skip('clicking refresh button forces a refresh', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.page.route(
|
||||
'**/api/models/checkpoints**',
|
||||
async (route) => {
|
||||
@@ -304,7 +308,7 @@ test.describe('Remote COMBO Widget', () => {
|
||||
expect(refreshedOptions).not.toEqual(initialOptions)
|
||||
})
|
||||
|
||||
test('control_after_refresh is applied after refresh', async ({
|
||||
test.skip('control_after_refresh is applied after refresh', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const options = [
|
||||
@@ -340,7 +344,7 @@ test.describe('Remote COMBO Widget', () => {
|
||||
})
|
||||
|
||||
test.describe('Cache Behavior', () => {
|
||||
test('reuses cached data between widgets with same params', async ({
|
||||
test.skip('reuses cached data between widgets with same params', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
let requestCount = 0
|
||||
|
||||
@@ -12,7 +12,7 @@ test.describe('Reroute Node', () => {
|
||||
await comfyPage.setupWorkflowsDirectory({})
|
||||
})
|
||||
|
||||
test('loads from inserted workflow', async ({ comfyPage }) => {
|
||||
test.skip('loads from inserted workflow', async ({ comfyPage }) => {
|
||||
const workflowName = 'single_connected_reroute_node.json'
|
||||
await comfyPage.setupWorkflowsDirectory({
|
||||
[workflowName]: 'links/single_connected_reroute_node.json'
|
||||
@@ -44,12 +44,12 @@ test.describe('LiteGraph Native Reroute Node', () => {
|
||||
await comfyPage.setSetting('LiteGraph.Reroute.SplineOffset', 80)
|
||||
})
|
||||
|
||||
test('loads from workflow', async ({ comfyPage }) => {
|
||||
test.skip('loads from workflow', async ({ comfyPage }) => {
|
||||
await comfyPage.loadWorkflow('reroute/native_reroute')
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('native_reroute.png')
|
||||
})
|
||||
|
||||
test('@2x @0.5x Can add reroute by alt clicking on link', async ({
|
||||
test.skip('@2x @0.5x Can add reroute by alt clicking on link', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const loadCheckpointNode = (
|
||||
@@ -75,7 +75,7 @@ test.describe('LiteGraph Native Reroute Node', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test('Can add reroute by clicking middle of link context menu', async ({
|
||||
test.skip('Can add reroute by clicking middle of link context menu', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const loadCheckpointNode = (
|
||||
@@ -102,7 +102,7 @@ test.describe('LiteGraph Native Reroute Node', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test('Can delete link that is connected to two reroutes', async ({
|
||||
test.skip('Can delete link that is connected to two reroutes', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
// https://github.com/Comfy-Org/ComfyUI_frontend/issues/4695
|
||||
|
||||
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 24 KiB |
@@ -8,7 +8,7 @@ test.beforeEach(async ({ comfyPage }) => {
|
||||
})
|
||||
|
||||
test.describe('Canvas Right Click Menu', () => {
|
||||
test('Can add node', async ({ comfyPage }) => {
|
||||
test.skip('Can add node', async ({ comfyPage }) => {
|
||||
await comfyPage.rightClickCanvas()
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('right-click-menu.png')
|
||||
await comfyPage.page.getByText('Add Node').click()
|
||||
@@ -20,7 +20,7 @@ test.describe('Canvas Right Click Menu', () => {
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('add-node-node-added.png')
|
||||
})
|
||||
|
||||
test('Can add group', async ({ comfyPage }) => {
|
||||
test.skip('Can add group', async ({ comfyPage }) => {
|
||||
await comfyPage.rightClickCanvas()
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('right-click-menu.png')
|
||||
await comfyPage.page.getByText('Add Group', { exact: true }).click()
|
||||
@@ -28,7 +28,7 @@ test.describe('Canvas Right Click Menu', () => {
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('add-group-group-added.png')
|
||||
})
|
||||
|
||||
test('Can convert to group node', async ({ comfyPage }) => {
|
||||
test.skip('Can convert to group node', async ({ comfyPage }) => {
|
||||
await comfyPage.select2Nodes()
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('selected-2-nodes.png')
|
||||
await comfyPage.rightClickCanvas()
|
||||
@@ -44,7 +44,7 @@ test.describe('Canvas Right Click Menu', () => {
|
||||
})
|
||||
|
||||
test.describe('Node Right Click Menu', () => {
|
||||
test('Can open properties panel', async ({ comfyPage }) => {
|
||||
test.skip('Can open properties panel', async ({ comfyPage }) => {
|
||||
await comfyPage.rightClickEmptyLatentNode()
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('right-click-node.png')
|
||||
await comfyPage.page.getByText('Properties Panel').click()
|
||||
@@ -54,7 +54,7 @@ test.describe('Node Right Click Menu', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test('Can collapse', async ({ comfyPage }) => {
|
||||
test.skip('Can collapse', async ({ comfyPage }) => {
|
||||
await comfyPage.rightClickEmptyLatentNode()
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('right-click-node.png')
|
||||
await comfyPage.page.getByText('Collapse').click()
|
||||
@@ -64,7 +64,7 @@ test.describe('Node Right Click Menu', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test('Can collapse (Node Badge)', async ({ comfyPage }) => {
|
||||
test.skip('Can collapse (Node Badge)', async ({ comfyPage }) => {
|
||||
await comfyPage.setSetting(
|
||||
'Comfy.NodeBadge.NodeIdBadgeMode',
|
||||
NodeBadgeMode.ShowAll
|
||||
@@ -82,7 +82,7 @@ test.describe('Node Right Click Menu', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test('Can bypass', async ({ comfyPage }) => {
|
||||
test.skip('Can bypass', async ({ comfyPage }) => {
|
||||
await comfyPage.rightClickEmptyLatentNode()
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('right-click-node.png')
|
||||
await comfyPage.page.getByText('Bypass').click()
|
||||
@@ -92,7 +92,7 @@ test.describe('Node Right Click Menu', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test('Can pin and unpin', async ({ comfyPage }) => {
|
||||
test.skip('Can pin and unpin', async ({ comfyPage }) => {
|
||||
await comfyPage.rightClickEmptyLatentNode()
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('right-click-node.png')
|
||||
await comfyPage.page.click('.litemenu-entry:has-text("Pin")')
|
||||
@@ -111,7 +111,7 @@ test.describe('Node Right Click Menu', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test('Can move after unpin', async ({ comfyPage }) => {
|
||||
test.skip('Can move after unpin', async ({ comfyPage }) => {
|
||||
await comfyPage.rightClickEmptyLatentNode()
|
||||
await comfyPage.page.click('.litemenu-entry:has-text("Pin")')
|
||||
await comfyPage.nextFrame()
|
||||
@@ -125,7 +125,7 @@ test.describe('Node Right Click Menu', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test('Can pin/unpin selected nodes', async ({ comfyPage }) => {
|
||||
test.skip('Can pin/unpin selected nodes', async ({ comfyPage }) => {
|
||||
await comfyPage.select2Nodes()
|
||||
await comfyPage.page.keyboard.down('Control')
|
||||
await comfyPage.rightClickEmptyLatentNode()
|
||||
|
||||
@@ -15,7 +15,7 @@ test.describe('Selection Toolbox', () => {
|
||||
await comfyPage.setSetting('Comfy.Canvas.SelectionToolbox', true)
|
||||
})
|
||||
|
||||
test('shows selection toolbox', async ({ comfyPage }) => {
|
||||
test.skip('shows selection toolbox', async ({ comfyPage }) => {
|
||||
// By default, selection toolbox should be enabled
|
||||
await expect(comfyPage.selectionToolbox).not.toBeVisible()
|
||||
|
||||
@@ -30,7 +30,7 @@ test.describe('Selection Toolbox', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test('shows at correct position when node is pasted', async ({
|
||||
test.skip('shows at correct position when node is pasted', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.loadWorkflow('nodes/single_ksampler')
|
||||
@@ -66,7 +66,9 @@ test.describe('Selection Toolbox', () => {
|
||||
await expect(comfyPage.selectionToolbox).not.toBeVisible()
|
||||
})
|
||||
|
||||
test('shows border only with multiple selections', async ({ comfyPage }) => {
|
||||
test.skip('shows border only with multiple selections', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
// Select single node
|
||||
await comfyPage.selectNodes(['KSampler'])
|
||||
|
||||
@@ -94,7 +96,7 @@ test.describe('Selection Toolbox', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test('displays bypass button in toolbox when nodes are selected', async ({
|
||||
test.skip('displays bypass button in toolbox when nodes are selected', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
// A group + a KSampler node
|
||||
|
||||
|
Before Width: | Height: | Size: 102 KiB After Width: | Height: | Size: 102 KiB |
|
Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 104 KiB |
|
Before Width: | Height: | Size: 99 KiB After Width: | Height: | Size: 99 KiB |
@@ -72,7 +72,7 @@ test.describe('Selection Toolbox - More Options Submenus', () => {
|
||||
throw new Error('Could not open More Options menu - popover not showing')
|
||||
}
|
||||
|
||||
test('opens Node Info from More Options menu', async ({ comfyPage }) => {
|
||||
test.skip('opens Node Info from More Options menu', async ({ comfyPage }) => {
|
||||
await openMoreOptions(comfyPage)
|
||||
const nodeInfoButton = comfyPage.page.getByText('Node Info', {
|
||||
exact: true
|
||||
@@ -82,7 +82,7 @@ test.describe('Selection Toolbox - More Options Submenus', () => {
|
||||
await comfyPage.nextFrame()
|
||||
})
|
||||
|
||||
test('changes node shape via Shape submenu', async ({ comfyPage }) => {
|
||||
test.skip('changes node shape via Shape submenu', async ({ comfyPage }) => {
|
||||
const nodeRef = (await comfyPage.getNodeRefsByTitle('KSampler'))[0]
|
||||
const initialShape = await nodeRef.getProperty<number>('shape')
|
||||
|
||||
@@ -99,7 +99,9 @@ test.describe('Selection Toolbox - More Options Submenus', () => {
|
||||
expect(newShape).toBe(1)
|
||||
})
|
||||
|
||||
test('changes node color via Color submenu swatch', async ({ comfyPage }) => {
|
||||
test.skip('changes node color via Color submenu swatch', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const nodeRef = (await comfyPage.getNodeRefsByTitle('KSampler'))[0]
|
||||
const initialColor = await nodeRef.getProperty<string | undefined>('color')
|
||||
|
||||
@@ -117,7 +119,7 @@ test.describe('Selection Toolbox - More Options Submenus', () => {
|
||||
}
|
||||
})
|
||||
|
||||
test('renames a node using Rename action', async ({ comfyPage }) => {
|
||||
test.skip('renames a node using Rename action', async ({ comfyPage }) => {
|
||||
const nodeRef = (await comfyPage.getNodeRefsByTitle('KSampler'))[0]
|
||||
await openMoreOptions(comfyPage)
|
||||
await comfyPage.page
|
||||
@@ -134,7 +136,7 @@ test.describe('Selection Toolbox - More Options Submenus', () => {
|
||||
expect(newTitle).toBe('RenamedNode')
|
||||
})
|
||||
|
||||
test('closes More Options menu when clicking outside', async ({
|
||||
test.skip('closes More Options menu when clicking outside', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await openMoreOptions(comfyPage)
|
||||
@@ -151,7 +153,7 @@ test.describe('Selection Toolbox - More Options Submenus', () => {
|
||||
).not.toBeVisible()
|
||||
})
|
||||
|
||||
test('closes More Options menu when clicking the button again (toggle)', async ({
|
||||
test.skip('closes More Options menu when clicking the button again (toggle)', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await openMoreOptions(comfyPage)
|
||||
|
||||
@@ -12,7 +12,7 @@ test.describe('Node library sidebar', () => {
|
||||
await tab.open()
|
||||
})
|
||||
|
||||
test('Node preview and drag to canvas', async ({ comfyPage }) => {
|
||||
test.skip('Node preview and drag to canvas', async ({ comfyPage }) => {
|
||||
const tab = comfyPage.menu.nodeLibraryTab
|
||||
await tab.getFolder('sampling').click()
|
||||
|
||||
@@ -49,7 +49,7 @@ test.describe('Node library sidebar', () => {
|
||||
expect(await comfyPage.getGraphNodesCount()).toBe(count + 1)
|
||||
})
|
||||
|
||||
test('Bookmark node', async ({ comfyPage }) => {
|
||||
test.skip('Bookmark node', async ({ comfyPage }) => {
|
||||
const tab = comfyPage.menu.nodeLibraryTab
|
||||
await tab.getFolder('sampling').click()
|
||||
|
||||
@@ -68,7 +68,7 @@ test.describe('Node library sidebar', () => {
|
||||
expect(await comfyPage.page.isVisible('.node-lib-node-preview')).toBe(true)
|
||||
})
|
||||
|
||||
test('Ignores unrecognized node', async ({ comfyPage }) => {
|
||||
test.skip('Ignores unrecognized node', async ({ comfyPage }) => {
|
||||
await comfyPage.setSetting('Comfy.NodeLibrary.Bookmarks.V2', ['foo'])
|
||||
|
||||
const tab = comfyPage.menu.nodeLibraryTab
|
||||
@@ -76,13 +76,13 @@ test.describe('Node library sidebar', () => {
|
||||
expect(await tab.getNode('foo').count()).toBe(0)
|
||||
})
|
||||
|
||||
test('Displays empty bookmarks folder', async ({ comfyPage }) => {
|
||||
test.skip('Displays empty bookmarks folder', async ({ comfyPage }) => {
|
||||
await comfyPage.setSetting('Comfy.NodeLibrary.Bookmarks.V2', ['foo/'])
|
||||
const tab = comfyPage.menu.nodeLibraryTab
|
||||
expect(await tab.getFolder('foo').count()).toBe(1)
|
||||
})
|
||||
|
||||
test('Can add new bookmark folder', async ({ comfyPage }) => {
|
||||
test.skip('Can add new bookmark folder', async ({ comfyPage }) => {
|
||||
const tab = comfyPage.menu.nodeLibraryTab
|
||||
await tab.newFolderButton.click()
|
||||
const textInput = comfyPage.page.locator('.editable-text input')
|
||||
@@ -95,7 +95,7 @@ test.describe('Node library sidebar', () => {
|
||||
).toEqual(['New Folder/'])
|
||||
})
|
||||
|
||||
test('Can add nested bookmark folder', async ({ comfyPage }) => {
|
||||
test.skip('Can add nested bookmark folder', async ({ comfyPage }) => {
|
||||
await comfyPage.setSetting('Comfy.NodeLibrary.Bookmarks.V2', ['foo/'])
|
||||
const tab = comfyPage.menu.nodeLibraryTab
|
||||
|
||||
@@ -112,7 +112,7 @@ test.describe('Node library sidebar', () => {
|
||||
).toEqual(['foo/', 'foo/bar/'])
|
||||
})
|
||||
|
||||
test('Can delete bookmark folder', async ({ comfyPage }) => {
|
||||
test.skip('Can delete bookmark folder', async ({ comfyPage }) => {
|
||||
await comfyPage.setSetting('Comfy.NodeLibrary.Bookmarks.V2', ['foo/'])
|
||||
const tab = comfyPage.menu.nodeLibraryTab
|
||||
|
||||
@@ -124,7 +124,7 @@ test.describe('Node library sidebar', () => {
|
||||
).toEqual([])
|
||||
})
|
||||
|
||||
test('Can rename bookmark folder', async ({ comfyPage }) => {
|
||||
test.skip('Can rename bookmark folder', async ({ comfyPage }) => {
|
||||
await comfyPage.setSetting('Comfy.NodeLibrary.Bookmarks.V2', ['foo/'])
|
||||
const tab = comfyPage.menu.nodeLibraryTab
|
||||
|
||||
@@ -140,7 +140,7 @@ test.describe('Node library sidebar', () => {
|
||||
).toEqual(['bar/'])
|
||||
})
|
||||
|
||||
test('Can add bookmark by dragging node to bookmark folder', async ({
|
||||
test.skip('Can add bookmark by dragging node to bookmark folder', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.setSetting('Comfy.NodeLibrary.Bookmarks.V2', ['foo/'])
|
||||
@@ -155,7 +155,7 @@ test.describe('Node library sidebar', () => {
|
||||
).toEqual(['foo/', 'foo/KSamplerAdvanced'])
|
||||
})
|
||||
|
||||
test('Can add bookmark by clicking bookmark button', async ({
|
||||
test.skip('Can add bookmark by clicking bookmark button', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const tab = comfyPage.menu.nodeLibraryTab
|
||||
@@ -166,7 +166,9 @@ test.describe('Node library sidebar', () => {
|
||||
).toEqual(['KSamplerAdvanced'])
|
||||
})
|
||||
|
||||
test('Can unbookmark node (Top level bookmark)', async ({ comfyPage }) => {
|
||||
test.skip('Can unbookmark node (Top level bookmark)', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.setSetting('Comfy.NodeLibrary.Bookmarks.V2', [
|
||||
'KSamplerAdvanced'
|
||||
])
|
||||
@@ -177,7 +179,9 @@ test.describe('Node library sidebar', () => {
|
||||
).toEqual([])
|
||||
})
|
||||
|
||||
test('Can unbookmark node (Library node bookmark)', async ({ comfyPage }) => {
|
||||
test.skip('Can unbookmark node (Library node bookmark)', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.setSetting('Comfy.NodeLibrary.Bookmarks.V2', [
|
||||
'KSamplerAdvanced'
|
||||
])
|
||||
@@ -192,7 +196,7 @@ test.describe('Node library sidebar', () => {
|
||||
await comfyPage.getSetting('Comfy.NodeLibrary.Bookmarks.V2')
|
||||
).toEqual([])
|
||||
})
|
||||
test('Can customize icon', async ({ comfyPage }) => {
|
||||
test.skip('Can customize icon', async ({ comfyPage }) => {
|
||||
await comfyPage.setSetting('Comfy.NodeLibrary.Bookmarks.V2', ['foo/'])
|
||||
const tab = comfyPage.menu.nodeLibraryTab
|
||||
await tab.getFolder('foo').click({ button: 'right' })
|
||||
@@ -215,7 +219,7 @@ test.describe('Node library sidebar', () => {
|
||||
})
|
||||
})
|
||||
// If color is left as default, it should not be saved
|
||||
test('Can customize icon (default field)', async ({ comfyPage }) => {
|
||||
test.skip('Can customize icon (default field)', async ({ comfyPage }) => {
|
||||
await comfyPage.setSetting('Comfy.NodeLibrary.Bookmarks.V2', ['foo/'])
|
||||
const tab = comfyPage.menu.nodeLibraryTab
|
||||
await tab.getFolder('foo').click({ button: 'right' })
|
||||
@@ -234,7 +238,7 @@ test.describe('Node library sidebar', () => {
|
||||
})
|
||||
})
|
||||
|
||||
test('Can customize bookmark color after interacting with color options', async ({
|
||||
test.skip('Can customize bookmark color after interacting with color options', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
// Open customization dialog
|
||||
@@ -274,7 +278,7 @@ test.describe('Node library sidebar', () => {
|
||||
await expect(setting['foo/'].color).not.toBe('')
|
||||
})
|
||||
|
||||
test('Can rename customized bookmark folder', async ({ comfyPage }) => {
|
||||
test.skip('Can rename customized bookmark folder', async ({ comfyPage }) => {
|
||||
await comfyPage.setSetting('Comfy.NodeLibrary.Bookmarks.V2', ['foo/'])
|
||||
await comfyPage.setSetting('Comfy.NodeLibrary.BookmarksCustomization', {
|
||||
'foo/': {
|
||||
@@ -303,7 +307,7 @@ test.describe('Node library sidebar', () => {
|
||||
})
|
||||
})
|
||||
|
||||
test('Can delete customized bookmark folder', async ({ comfyPage }) => {
|
||||
test.skip('Can delete customized bookmark folder', async ({ comfyPage }) => {
|
||||
await comfyPage.setSetting('Comfy.NodeLibrary.Bookmarks.V2', ['foo/'])
|
||||
await comfyPage.setSetting('Comfy.NodeLibrary.BookmarksCustomization', {
|
||||
'foo/': {
|
||||
@@ -323,7 +327,7 @@ test.describe('Node library sidebar', () => {
|
||||
).toEqual({})
|
||||
})
|
||||
|
||||
test('Can filter nodes in both trees', async ({ comfyPage }) => {
|
||||
test.skip('Can filter nodes in both trees', async ({ comfyPage }) => {
|
||||
await comfyPage.setSetting('Comfy.NodeLibrary.Bookmarks.V2', [
|
||||
'foo/',
|
||||
'foo/KSamplerAdvanced',
|
||||
|
||||
@@ -16,7 +16,7 @@ test.describe('Workflows sidebar', () => {
|
||||
await comfyPage.setupWorkflowsDirectory({})
|
||||
})
|
||||
|
||||
test('Can create new blank workflow', async ({ comfyPage }) => {
|
||||
test.skip('Can create new blank workflow', async ({ comfyPage }) => {
|
||||
const tab = comfyPage.menu.workflowsTab
|
||||
expect(await tab.getOpenedWorkflowNames()).toEqual([
|
||||
'*Unsaved Workflow.json'
|
||||
@@ -29,7 +29,7 @@ test.describe('Workflows sidebar', () => {
|
||||
])
|
||||
})
|
||||
|
||||
test('Can show top level saved workflows', async ({ comfyPage }) => {
|
||||
test.skip('Can show top level saved workflows', async ({ comfyPage }) => {
|
||||
await comfyPage.setupWorkflowsDirectory({
|
||||
'workflow1.json': 'default.json',
|
||||
'workflow2.json': 'default.json'
|
||||
@@ -42,7 +42,7 @@ test.describe('Workflows sidebar', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test('Can duplicate workflow', async ({ comfyPage }) => {
|
||||
test.skip('Can duplicate workflow', async ({ comfyPage }) => {
|
||||
const tab = comfyPage.menu.workflowsTab
|
||||
await comfyPage.menu.topbar.saveWorkflow('workflow1.json')
|
||||
|
||||
@@ -72,7 +72,7 @@ test.describe('Workflows sidebar', () => {
|
||||
])
|
||||
})
|
||||
|
||||
test('Can open workflow after insert', async ({ comfyPage }) => {
|
||||
test.skip('Can open workflow after insert', async ({ comfyPage }) => {
|
||||
await comfyPage.setupWorkflowsDirectory({
|
||||
'workflow1.json': 'nodes/single_ksampler.json'
|
||||
})
|
||||
@@ -91,7 +91,7 @@ test.describe('Workflows sidebar', () => {
|
||||
expect((await comfyPage.getNodes()).length).toEqual(1)
|
||||
})
|
||||
|
||||
test('Can rename nested workflow from opened workflow item', async ({
|
||||
test.skip('Can rename nested workflow from opened workflow item', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.setupWorkflowsDirectory({
|
||||
@@ -117,7 +117,7 @@ test.describe('Workflows sidebar', () => {
|
||||
])
|
||||
})
|
||||
|
||||
test('Can save workflow as', async ({ comfyPage }) => {
|
||||
test.skip('Can save workflow as', async ({ comfyPage }) => {
|
||||
await comfyPage.executeCommand('Comfy.NewBlankWorkflow')
|
||||
await comfyPage.menu.topbar.saveWorkflowAs('workflow3.json')
|
||||
expect(await comfyPage.menu.workflowsTab.getOpenedWorkflowNames()).toEqual([
|
||||
@@ -133,7 +133,7 @@ test.describe('Workflows sidebar', () => {
|
||||
])
|
||||
})
|
||||
|
||||
test('Exported workflow does not contain localized slot names', async ({
|
||||
test.skip('Exported workflow does not contain localized slot names', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.loadWorkflow('default')
|
||||
@@ -153,7 +153,7 @@ test.describe('Workflows sidebar', () => {
|
||||
}
|
||||
})
|
||||
|
||||
test('Can export same workflow with different locales', async ({
|
||||
test.skip('Can export same workflow with different locales', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.loadWorkflow('default')
|
||||
@@ -185,7 +185,7 @@ test.describe('Workflows sidebar', () => {
|
||||
expect(downloadedContent).toEqual(downloadedContentZh)
|
||||
})
|
||||
|
||||
test('Can save workflow as with same name', async ({ comfyPage }) => {
|
||||
test.skip('Can save workflow as with same name', async ({ comfyPage }) => {
|
||||
await comfyPage.menu.topbar.saveWorkflow('workflow5.json')
|
||||
await comfyPage.nextFrame()
|
||||
expect(await comfyPage.menu.workflowsTab.getOpenedWorkflowNames()).toEqual([
|
||||
@@ -200,7 +200,7 @@ test.describe('Workflows sidebar', () => {
|
||||
])
|
||||
})
|
||||
|
||||
test('Can save temporary workflow with unmodified name', async ({
|
||||
test.skip('Can save temporary workflow with unmodified name', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
expect(await comfyPage.isCurrentWorkflowModified()).toBe(false)
|
||||
@@ -214,7 +214,9 @@ test.describe('Workflows sidebar', () => {
|
||||
expect(await comfyPage.isCurrentWorkflowModified()).toBe(false)
|
||||
})
|
||||
|
||||
test('Can overwrite other workflows with save as', async ({ comfyPage }) => {
|
||||
test.skip('Can overwrite other workflows with save as', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const topbar = comfyPage.menu.topbar
|
||||
await topbar.saveWorkflow('workflow1.json')
|
||||
await topbar.saveWorkflowAs('workflow2.json')
|
||||
@@ -240,7 +242,7 @@ test.describe('Workflows sidebar', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test('Does not report warning when switching between opened workflows', async ({
|
||||
test.skip('Does not report warning when switching between opened workflows', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.loadWorkflow('missing/missing_nodes')
|
||||
@@ -258,7 +260,7 @@ test.describe('Workflows sidebar', () => {
|
||||
).not.toBeVisible()
|
||||
})
|
||||
|
||||
test('Can close saved-workflows from the open workflows section', async ({
|
||||
test.skip('Can close saved-workflows from the open workflows section', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.menu.topbar.saveWorkflow(
|
||||
@@ -273,7 +275,7 @@ test.describe('Workflows sidebar', () => {
|
||||
])
|
||||
})
|
||||
|
||||
test('Can close saved workflow with command', async ({ comfyPage }) => {
|
||||
test.skip('Can close saved workflow with command', async ({ comfyPage }) => {
|
||||
const tab = comfyPage.menu.workflowsTab
|
||||
await comfyPage.menu.topbar.saveWorkflow('workflow1.json')
|
||||
await comfyPage.executeCommand('Workspace.CloseWorkflow')
|
||||
@@ -282,7 +284,9 @@ test.describe('Workflows sidebar', () => {
|
||||
])
|
||||
})
|
||||
|
||||
test('Can delete workflows (confirm disabled)', async ({ comfyPage }) => {
|
||||
test.skip('Can delete workflows (confirm disabled)', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.setSetting('Comfy.Workflow.ConfirmDelete', false)
|
||||
|
||||
const { topbar, workflowsTab } = comfyPage.menu
|
||||
@@ -301,7 +305,7 @@ test.describe('Workflows sidebar', () => {
|
||||
])
|
||||
})
|
||||
|
||||
test('Can delete workflows', async ({ comfyPage }) => {
|
||||
test.skip('Can delete workflows', async ({ comfyPage }) => {
|
||||
const { topbar, workflowsTab } = comfyPage.menu
|
||||
|
||||
const filename = 'workflow18.json'
|
||||
@@ -319,7 +323,9 @@ test.describe('Workflows sidebar', () => {
|
||||
])
|
||||
})
|
||||
|
||||
test('Can duplicate workflow from context menu', async ({ comfyPage }) => {
|
||||
test.skip('Can duplicate workflow from context menu', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.setupWorkflowsDirectory({
|
||||
'workflow1.json': 'default.json'
|
||||
})
|
||||
@@ -338,7 +344,9 @@ test.describe('Workflows sidebar', () => {
|
||||
])
|
||||
})
|
||||
|
||||
test('Can drop workflow from workflows sidebar', async ({ comfyPage }) => {
|
||||
test.skip('Can drop workflow from workflows sidebar', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.setupWorkflowsDirectory({
|
||||
'workflow1.json': 'default.json'
|
||||
})
|
||||
|
||||
@@ -468,7 +468,9 @@ test.describe('Subgraph Operations', () => {
|
||||
expect(finalNodeCount).toBe(initialNodeCount + 1)
|
||||
})
|
||||
|
||||
test('Can undo and redo operations in subgraph', async ({ comfyPage }) => {
|
||||
test.skip('Can undo and redo operations in subgraph', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.loadWorkflow('subgraphs/basic-subgraph')
|
||||
|
||||
const subgraphNode = await comfyPage.getNodeRefById('2')
|
||||
@@ -683,7 +685,7 @@ test.describe('Subgraph Operations', () => {
|
||||
expect(widgetCount).toBe(0)
|
||||
})
|
||||
|
||||
test('Multiple promoted widgets are handled correctly', async ({
|
||||
test.skip('Multiple promoted widgets are handled correctly', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.loadWorkflow(
|
||||
|
||||
@@ -69,7 +69,7 @@ test.describe('Templates', () => {
|
||||
}
|
||||
})
|
||||
|
||||
test('Can load template workflows', async ({ comfyPage }) => {
|
||||
test.skip('Can load template workflows', async ({ comfyPage }) => {
|
||||
// Clear the workflow
|
||||
await comfyPage.menu.workflowsTab.open()
|
||||
await comfyPage.executeCommand('Comfy.NewBlankWorkflow')
|
||||
|
||||
|
Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 68 KiB |
@@ -8,19 +8,23 @@ const CREATE_GROUP_HOTKEY = 'Control+g'
|
||||
test.describe('Vue Node Groups', () => {
|
||||
test.beforeEach(async ({ comfyPage }) => {
|
||||
await comfyPage.setSetting('Comfy.VueNodes.Enabled', true)
|
||||
await comfyPage.setSetting('Comfy.Minimap.ShowGroups', true)
|
||||
await comfyPage.vueNodes.waitForNodes()
|
||||
})
|
||||
|
||||
test('should allow creating groups with hotkey', async ({ comfyPage }) => {
|
||||
test.skip('should allow creating groups with hotkey', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.page.getByText('Load Checkpoint').click()
|
||||
await comfyPage.page.getByText('KSampler').click({ modifiers: ['Control'] })
|
||||
await comfyPage.page.keyboard.press(CREATE_GROUP_HOTKEY)
|
||||
await comfyPage.nextFrame()
|
||||
await expect(comfyPage.canvas).toHaveScreenshot(
|
||||
'vue-groups-create-group.png'
|
||||
)
|
||||
})
|
||||
|
||||
test('should allow fitting group to contents', async ({ comfyPage }) => {
|
||||
test.skip('should allow fitting group to contents', async ({ comfyPage }) => {
|
||||
await comfyPage.setup()
|
||||
await comfyPage.loadWorkflow('groups/oversized_group')
|
||||
await comfyPage.ctrlA()
|
||||
|
||||
|
Before Width: | Height: | Size: 95 KiB After Width: | Height: | Size: 99 KiB |
|
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 56 KiB |
@@ -9,7 +9,7 @@ test.describe('Vue Nodes Canvas Pan', () => {
|
||||
await comfyPage.vueNodes.waitForNodes()
|
||||
})
|
||||
|
||||
test('@mobile Can pan with touch', async ({ comfyPage }) => {
|
||||
test.skip('@mobile Can pan with touch', async ({ comfyPage }) => {
|
||||
await comfyPage.panWithTouch({ x: 64, y: 64 }, { x: 256, y: 256 })
|
||||
await expect(comfyPage.canvas).toHaveScreenshot(
|
||||
'vue-nodes-paned-with-touch.png'
|
||||
|
||||
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 23 KiB |
@@ -9,7 +9,7 @@ test.describe('Vue Nodes Zoom', () => {
|
||||
await comfyPage.vueNodes.waitForNodes()
|
||||
})
|
||||
|
||||
test('should not capture drag while zooming with ctrl+shift+drag', async ({
|
||||
test.skip('should not capture drag while zooming with ctrl+shift+drag', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const checkpointNode = comfyPage.vueNodes.getNodeByTitle('Load Checkpoint')
|
||||
|
||||
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 27 KiB |
@@ -109,7 +109,7 @@ test.describe('Vue Node Link Interaction', () => {
|
||||
await fitToViewInstant(comfyPage)
|
||||
})
|
||||
|
||||
test('should show a link dragging out from a slot when dragging on a slot', async ({
|
||||
test.skip('should show a link dragging out from a slot when dragging on a slot', async ({
|
||||
comfyPage,
|
||||
comfyMouse
|
||||
}) => {
|
||||
@@ -218,7 +218,7 @@ test.describe('Vue Node Link Interaction', () => {
|
||||
expect(await samplerInput.getLinkCount()).toBe(0)
|
||||
})
|
||||
|
||||
test('should reuse the existing origin when dragging an input link', async ({
|
||||
test.skip('should reuse the existing origin when dragging an input link', async ({
|
||||
comfyPage,
|
||||
comfyMouse
|
||||
}) => {
|
||||
@@ -255,7 +255,7 @@ test.describe('Vue Node Link Interaction', () => {
|
||||
await comfyMouse.drop()
|
||||
})
|
||||
|
||||
test('ctrl+alt drag from an input starts a fresh link', async ({
|
||||
test.skip('ctrl+alt drag from an input starts a fresh link', async ({
|
||||
comfyPage,
|
||||
comfyMouse
|
||||
}) => {
|
||||
@@ -395,7 +395,7 @@ test.describe('Vue Node Link Interaction', () => {
|
||||
expect(await vaeInput.getLinkCount()).toBe(1)
|
||||
})
|
||||
|
||||
test('rerouted input drag preview remains anchored to reroute', async ({
|
||||
test.skip('rerouted input drag preview remains anchored to reroute', async ({
|
||||
comfyPage,
|
||||
comfyMouse
|
||||
}) => {
|
||||
@@ -480,7 +480,7 @@ test.describe('Vue Node Link Interaction', () => {
|
||||
expect(linkDetails?.parentId).not.toBeNull()
|
||||
})
|
||||
|
||||
test('rerouted output shift-drag preview remains anchored to reroute', async ({
|
||||
test.skip('rerouted output shift-drag preview remains anchored to reroute', async ({
|
||||
comfyPage,
|
||||
comfyMouse
|
||||
}) => {
|
||||
@@ -639,7 +639,7 @@ test.describe('Vue Node Link Interaction', () => {
|
||||
})
|
||||
})
|
||||
|
||||
test('shift-dragging an output with multiple links should drag all links', async ({
|
||||
test.skip('shift-dragging an output with multiple links should drag all links', async ({
|
||||
comfyPage,
|
||||
comfyMouse
|
||||
}) => {
|
||||
@@ -694,7 +694,7 @@ test.describe('Vue Node Link Interaction', () => {
|
||||
}
|
||||
})
|
||||
|
||||
test('should snap to node center while dragging and link on drop', async ({
|
||||
test.skip('should snap to node center while dragging and link on drop', async ({
|
||||
comfyPage,
|
||||
comfyMouse
|
||||
}) => {
|
||||
@@ -743,7 +743,7 @@ test.describe('Vue Node Link Interaction', () => {
|
||||
expect(linked?.targetId).toBe(samplerNode.id)
|
||||
})
|
||||
|
||||
test('should snap to a specific compatible slot when targeting it', async ({
|
||||
test.skip('should snap to a specific compatible slot when targeting it', async ({
|
||||
comfyPage,
|
||||
comfyMouse
|
||||
}) => {
|
||||
@@ -788,4 +788,171 @@ test.describe('Vue Node Link Interaction', () => {
|
||||
targetSlot: 2
|
||||
})
|
||||
})
|
||||
|
||||
test.describe('Release actions (Shift-drop)', () => {
|
||||
test('Context menu opens and endpoint is pinned on Shift-drop', async ({
|
||||
comfyPage,
|
||||
comfyMouse
|
||||
}) => {
|
||||
await comfyPage.setSetting(
|
||||
'Comfy.LinkRelease.ActionShift',
|
||||
'context menu'
|
||||
)
|
||||
|
||||
const samplerNode = (await comfyPage.getNodeRefsByType('KSampler'))[0]
|
||||
expect(samplerNode).toBeTruthy()
|
||||
|
||||
const outputCenter = await getSlotCenter(
|
||||
comfyPage.page,
|
||||
samplerNode.id,
|
||||
0,
|
||||
false
|
||||
)
|
||||
|
||||
const dropPos = { x: outputCenter.x + 180, y: outputCenter.y - 140 }
|
||||
|
||||
await comfyMouse.move(outputCenter)
|
||||
await comfyPage.page.keyboard.down('Shift')
|
||||
try {
|
||||
await comfyMouse.drag(dropPos)
|
||||
await comfyMouse.drop()
|
||||
} finally {
|
||||
await comfyPage.page.keyboard.up('Shift').catch(() => {})
|
||||
}
|
||||
|
||||
// Context menu should be visible
|
||||
const contextMenu = comfyPage.page.locator('.litecontextmenu')
|
||||
await expect(contextMenu).toBeVisible()
|
||||
|
||||
// Pinned endpoint should not change with mouse movement while menu is open
|
||||
const before = await comfyPage.page.evaluate(() => {
|
||||
const snap = window['app']?.canvas?.linkConnector?.state?.snapLinksPos
|
||||
return Array.isArray(snap) ? [snap[0], snap[1]] : null
|
||||
})
|
||||
expect(before).not.toBeNull()
|
||||
|
||||
// Move mouse elsewhere and verify snap position is unchanged
|
||||
await comfyMouse.move({ x: dropPos.x + 160, y: dropPos.y + 100 })
|
||||
const after = await comfyPage.page.evaluate(() => {
|
||||
const snap = window['app']?.canvas?.linkConnector?.state?.snapLinksPos
|
||||
return Array.isArray(snap) ? [snap[0], snap[1]] : null
|
||||
})
|
||||
expect(after).toEqual(before)
|
||||
})
|
||||
|
||||
test('Context menu -> Search pre-filters by link type and connects after selection', async ({
|
||||
comfyPage,
|
||||
comfyMouse
|
||||
}) => {
|
||||
await comfyPage.setSetting(
|
||||
'Comfy.LinkRelease.ActionShift',
|
||||
'context menu'
|
||||
)
|
||||
await comfyPage.setSetting('Comfy.NodeSearchBoxImpl', 'default')
|
||||
|
||||
const samplerNode = (await comfyPage.getNodeRefsByType('KSampler'))[0]
|
||||
expect(samplerNode).toBeTruthy()
|
||||
|
||||
const outputCenter = await getSlotCenter(
|
||||
comfyPage.page,
|
||||
samplerNode.id,
|
||||
0,
|
||||
false
|
||||
)
|
||||
const dropPos = { x: outputCenter.x + 200, y: outputCenter.y - 120 }
|
||||
|
||||
await comfyMouse.move(outputCenter)
|
||||
await comfyPage.page.keyboard.down('Shift')
|
||||
try {
|
||||
await comfyMouse.drag(dropPos)
|
||||
await comfyMouse.drop()
|
||||
} finally {
|
||||
await comfyPage.page.keyboard.up('Shift').catch(() => {})
|
||||
}
|
||||
|
||||
// Open Search from the context menu
|
||||
await comfyPage.clickContextMenuItem('Search')
|
||||
|
||||
// Search box opens with prefilled type filter based on link type (LATENT)
|
||||
await expect(comfyPage.searchBox.input).toBeVisible()
|
||||
const chips = comfyPage.searchBox.filterChips
|
||||
// Ensure at least one filter chip exists and it matches the link type
|
||||
const chipCount = await chips.count()
|
||||
expect(chipCount).toBeGreaterThan(0)
|
||||
await expect(chips.first()).toContainText('LATENT')
|
||||
|
||||
// Choose a compatible node and verify it auto-connects
|
||||
await comfyPage.searchBox.fillAndSelectFirstNode('VAEDecode')
|
||||
await comfyPage.nextFrame()
|
||||
|
||||
// KSampler output should now have an outgoing link
|
||||
const samplerOutput = await samplerNode.getOutput(0)
|
||||
expect(await samplerOutput.getLinkCount()).toBe(1)
|
||||
|
||||
// One of the VAEDecode nodes should have an incoming link on input[0]
|
||||
const vaeNodes = await comfyPage.getNodeRefsByType('VAEDecode')
|
||||
let linked = false
|
||||
for (const vae of vaeNodes) {
|
||||
const details = await getInputLinkDetails(comfyPage.page, vae.id, 0)
|
||||
if (details) {
|
||||
expect(details.originId).toBe(samplerNode.id)
|
||||
linked = true
|
||||
break
|
||||
}
|
||||
}
|
||||
expect(linked).toBe(true)
|
||||
})
|
||||
|
||||
test('Search box opens on Shift-drop and connects after selection', async ({
|
||||
comfyPage,
|
||||
comfyMouse
|
||||
}) => {
|
||||
await comfyPage.setSetting('Comfy.LinkRelease.ActionShift', 'search box')
|
||||
|
||||
const samplerNode = (await comfyPage.getNodeRefsByType('KSampler'))[0]
|
||||
expect(samplerNode).toBeTruthy()
|
||||
|
||||
const outputCenter = await getSlotCenter(
|
||||
comfyPage.page,
|
||||
samplerNode.id,
|
||||
0,
|
||||
false
|
||||
)
|
||||
const dropPos = { x: outputCenter.x + 140, y: outputCenter.y - 100 }
|
||||
|
||||
await comfyMouse.move(outputCenter)
|
||||
await comfyPage.page.keyboard.down('Shift')
|
||||
try {
|
||||
await comfyMouse.drag(dropPos)
|
||||
await comfyMouse.drop()
|
||||
} finally {
|
||||
await comfyPage.page.keyboard.up('Shift').catch(() => {})
|
||||
}
|
||||
|
||||
// Search box should open directly
|
||||
await expect(comfyPage.searchBox.input).toBeVisible()
|
||||
await expect(comfyPage.searchBox.filterChips.first()).toContainText(
|
||||
'LATENT'
|
||||
)
|
||||
|
||||
// Select a compatible node and verify connection
|
||||
await comfyPage.searchBox.fillAndSelectFirstNode('VAEDecode')
|
||||
await comfyPage.nextFrame()
|
||||
|
||||
const samplerOutput = await samplerNode.getOutput(0)
|
||||
expect(await samplerOutput.getLinkCount()).toBe(1)
|
||||
|
||||
const vaeNodes = await comfyPage.getNodeRefsByType('VAEDecode')
|
||||
let linked = false
|
||||
for (const vae of vaeNodes) {
|
||||
const details = await getInputLinkDetails(comfyPage.page, vae.id, 0)
|
||||
if (details) {
|
||||
expect(details.originId).toBe(samplerNode.id)
|
||||
linked = true
|
||||
break
|
||||
}
|
||||
}
|
||||
expect(linked).toBe(true)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 63 KiB |
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 60 KiB |
|
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 61 KiB |
|
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 64 KiB |
|
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 61 KiB |
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 60 KiB |
@@ -1,8 +1,8 @@
|
||||
import {
|
||||
type ComfyPage,
|
||||
comfyExpect as expect,
|
||||
comfyPageFixture as test
|
||||
} from '../../../../fixtures/ComfyPage'
|
||||
import type { ComfyPage } from '../../../../fixtures/ComfyPage'
|
||||
import type { Position } from '../../../../fixtures/types'
|
||||
|
||||
test.describe('Vue Node Moving', () => {
|
||||
@@ -29,7 +29,7 @@ test.describe('Vue Node Moving', () => {
|
||||
expect(diffY).toBeGreaterThan(0)
|
||||
}
|
||||
|
||||
test('should allow moving nodes by dragging', async ({ comfyPage }) => {
|
||||
test.skip('should allow moving nodes by dragging', async ({ comfyPage }) => {
|
||||
const loadCheckpointHeaderPos = await getLoadCheckpointHeaderPos(comfyPage)
|
||||
await comfyPage.dragAndDrop(loadCheckpointHeaderPos, {
|
||||
x: 256,
|
||||
@@ -42,7 +42,7 @@ test.describe('Vue Node Moving', () => {
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('vue-node-moved-node.png')
|
||||
})
|
||||
|
||||
test('@mobile should allow moving nodes by dragging on touch devices', async ({
|
||||
test.skip('@mobile should allow moving nodes by dragging on touch devices', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
// Disable minimap (gets in way of the node on small screens)
|
||||
|
||||
|
Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 84 KiB |
|
Before Width: | Height: | Size: 9.2 KiB After Width: | Height: | Size: 22 KiB |
@@ -49,4 +49,36 @@ test.describe('Vue Node Selection', () => {
|
||||
expect(await comfyPage.vueNodes.getSelectedNodeCount()).toBe(0)
|
||||
})
|
||||
}
|
||||
|
||||
test('should select pinned node without dragging', async ({ comfyPage }) => {
|
||||
const PIN_HOTKEY = 'p'
|
||||
const PIN_INDICATOR = '[data-testid="node-pin-indicator"]'
|
||||
|
||||
// Select a node by clicking its title
|
||||
const checkpointNodeHeader = comfyPage.page.getByText('Load Checkpoint')
|
||||
await checkpointNodeHeader.click()
|
||||
|
||||
// Pin it using the hotkey (as a user would)
|
||||
await comfyPage.page.keyboard.press(PIN_HOTKEY)
|
||||
|
||||
const checkpointNode = comfyPage.vueNodes.getNodeByTitle('Load Checkpoint')
|
||||
const pinIndicator = checkpointNode.locator(PIN_INDICATOR)
|
||||
await expect(pinIndicator).toBeVisible()
|
||||
|
||||
expect(await comfyPage.vueNodes.getSelectedNodeCount()).toBe(1)
|
||||
|
||||
const initialPos = await checkpointNodeHeader.boundingBox()
|
||||
if (!initialPos) throw new Error('Failed to get header position')
|
||||
|
||||
await comfyPage.dragAndDrop(
|
||||
{ x: initialPos.x + 10, y: initialPos.y + 10 },
|
||||
{ x: initialPos.x + 100, y: initialPos.y + 100 }
|
||||
)
|
||||
|
||||
const finalPos = await checkpointNodeHeader.boundingBox()
|
||||
if (!finalPos) throw new Error('Failed to get header position after drag')
|
||||
expect(finalPos).toEqual(initialPos)
|
||||
|
||||
expect(await comfyPage.vueNodes.getSelectedNodeCount()).toBe(1)
|
||||
})
|
||||
})
|
||||
|
||||
|
After Width: | Height: | Size: 96 KiB |
@@ -11,7 +11,7 @@ test.describe('Vue Node Custom Colors', () => {
|
||||
await comfyPage.vueNodes.waitForNodes()
|
||||
})
|
||||
|
||||
test('displays color picker button and allows color selection', async ({
|
||||
test.skip('displays color picker button and allows color selection', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const loadCheckpointNode = comfyPage.page.locator('[data-node-id]').filter({
|
||||
@@ -30,14 +30,14 @@ test.describe('Vue Node Custom Colors', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test('should load node colors from workflow', async ({ comfyPage }) => {
|
||||
test.skip('should load node colors from workflow', async ({ comfyPage }) => {
|
||||
await comfyPage.loadWorkflow('nodes/every_node_color')
|
||||
await expect(comfyPage.canvas).toHaveScreenshot(
|
||||
'vue-node-custom-colors-dark-all-colors.png'
|
||||
)
|
||||
})
|
||||
|
||||
test('should show brightened node colors on light theme', async ({
|
||||
test.skip('should show brightened node colors on light theme', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.setSetting('Comfy.ColorPalette', 'light')
|
||||
|
||||
|
Before Width: | Height: | Size: 91 KiB After Width: | Height: | Size: 97 KiB |
|
Before Width: | Height: | Size: 90 KiB After Width: | Height: | Size: 97 KiB |
|
Before Width: | Height: | Size: 92 KiB After Width: | Height: | Size: 98 KiB |
@@ -3,7 +3,7 @@ import {
|
||||
comfyPageFixture as test
|
||||
} from '../../../fixtures/ComfyPage'
|
||||
|
||||
const ERROR_CLASS = /border-error/
|
||||
const ERROR_CLASS = /border-node-stroke-error/
|
||||
|
||||
test.describe('Vue Node Error', () => {
|
||||
test.beforeEach(async ({ comfyPage }) => {
|
||||
@@ -17,16 +17,21 @@ test.describe('Vue Node Error', () => {
|
||||
await comfyPage.setup()
|
||||
await comfyPage.loadWorkflow('missing/missing_nodes')
|
||||
|
||||
// Close missing nodes warning dialog
|
||||
await comfyPage.page.getByRole('button', { name: 'Close' }).click()
|
||||
await comfyPage.page.waitForSelector('.comfy-missing-nodes', {
|
||||
state: 'hidden'
|
||||
})
|
||||
|
||||
// Expect error state on missing unknown node
|
||||
const unknownNode = comfyPage.page.locator('[data-node-id]').filter({
|
||||
hasText: 'UNKNOWN NODE'
|
||||
})
|
||||
await expect(unknownNode).toHaveClass(ERROR_CLASS)
|
||||
})
|
||||
|
||||
test('should display error state when node causes execution error', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.setup()
|
||||
await comfyPage.loadWorkflow('nodes/execution_error')
|
||||
await comfyPage.runButton.click()
|
||||
|
||||
const raiseErrorNode = comfyPage.vueNodes.getNodeByTitle('Raise Error')
|
||||
await expect(raiseErrorNode).toHaveClass(ERROR_CLASS)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -13,7 +13,9 @@ test.describe('Vue Nodes - LOD', () => {
|
||||
await comfyPage.loadWorkflow('default')
|
||||
})
|
||||
|
||||
test('should toggle LOD based on zoom threshold', async ({ comfyPage }) => {
|
||||
test.skip('should toggle LOD based on zoom threshold', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.vueNodes.waitForNodes()
|
||||
|
||||
const initialNodeCount = await comfyPage.vueNodes.getNodeCount()
|
||||
|
||||
|
Before Width: | Height: | Size: 107 KiB After Width: | Height: | Size: 95 KiB |
|
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 34 KiB |