fix: merge main, fix Segformer node class name per CodeRabbit review
- Resolve merge conflicts with main - Fix incorrect Segformer node class: LS_LoadSegformerModel -> LoadSegformerModel (LS_LoadSegformerModel is only the internal node ID, not the Python class name) - Update tests to use correct class name Amp-Thread-ID: https://ampcode.com/threads/T-019c0cdc-0150-7698-b71f-1f18d053521c
1
.gitattributes
vendored
@@ -11,6 +11,7 @@
|
|||||||
*.ts text eol=lf
|
*.ts text eol=lf
|
||||||
*.vue text eol=lf
|
*.vue text eol=lf
|
||||||
*.yaml text eol=lf
|
*.yaml text eol=lf
|
||||||
|
*.yml text eol=lf
|
||||||
|
|
||||||
# Generated files
|
# Generated files
|
||||||
packages/registry-types/src/comfyRegistryTypes.ts linguist-generated=true
|
packages/registry-types/src/comfyRegistryTypes.ts linguist-generated=true
|
||||||
|
|||||||
@@ -104,14 +104,14 @@ runs:
|
|||||||
|
|
||||||
- name: Find existing comment
|
- name: Find existing comment
|
||||||
id: find
|
id: find
|
||||||
uses: peter-evans/find-comment@b30e6a3c0ed37e7c023ccd3f1db5c6c0b0c23aad
|
uses: peter-evans/find-comment@b30e6a3c0ed37e7c023ccd3f1db5c6c0b0c23aad # v4.0.0
|
||||||
with:
|
with:
|
||||||
issue-number: ${{ inputs.issue-number || github.event.pull_request.number }}
|
issue-number: ${{ inputs.issue-number || github.event.pull_request.number }}
|
||||||
comment-author: github-actions[bot]
|
comment-author: github-actions[bot]
|
||||||
body-includes: ${{ steps.build.outputs.marker_search }}
|
body-includes: ${{ steps.build.outputs.marker_search }}
|
||||||
|
|
||||||
- name: Post or update comment
|
- name: Post or update comment
|
||||||
uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9
|
uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 # v5.0.0
|
||||||
with:
|
with:
|
||||||
issue-number: ${{ inputs.issue-number || github.event.pull_request.number }}
|
issue-number: ${{ inputs.issue-number || github.event.pull_request.number }}
|
||||||
comment-id: ${{ steps.find.outputs.comment-id }}
|
comment-id: ${{ steps.find.outputs.comment-id }}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ runs:
|
|||||||
|
|
||||||
# Checkout ComfyUI repo, install the dev_tools node and start server
|
# Checkout ComfyUI repo, install the dev_tools node and start server
|
||||||
- name: Checkout ComfyUI
|
- name: Checkout ComfyUI
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
repository: 'comfyanonymous/ComfyUI'
|
repository: 'comfyanonymous/ComfyUI'
|
||||||
path: 'ComfyUI'
|
path: 'ComfyUI'
|
||||||
@@ -33,7 +33,7 @@ runs:
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Setup Python
|
- name: Setup Python
|
||||||
uses: actions/setup-python@v4
|
uses: actions/setup-python@v6
|
||||||
with:
|
with:
|
||||||
python-version: '3.10'
|
python-version: '3.10'
|
||||||
|
|
||||||
@@ -12,29 +12,17 @@ runs:
|
|||||||
|
|
||||||
# Install pnpm, Node.js, build frontend
|
# Install pnpm, Node.js, build frontend
|
||||||
- name: Install pnpm
|
- name: Install pnpm
|
||||||
uses: pnpm/action-setup@v4
|
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
||||||
with:
|
with:
|
||||||
version: 10
|
version: 10
|
||||||
|
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v6
|
||||||
with:
|
with:
|
||||||
node-version: 'lts/*'
|
node-version: 'lts/*'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
cache-dependency-path: './pnpm-lock.yaml'
|
cache-dependency-path: './pnpm-lock.yaml'
|
||||||
|
|
||||||
# Restore tool caches before running any build/lint operations
|
|
||||||
- name: Restore tool output cache
|
|
||||||
uses: actions/cache/restore@v4
|
|
||||||
with:
|
|
||||||
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 dependencies
|
- name: Install dependencies
|
||||||
shell: bash
|
shell: bash
|
||||||
run: pnpm install --frozen-lockfile
|
run: pnpm install --frozen-lockfile
|
||||||
@@ -11,7 +11,7 @@ runs:
|
|||||||
echo "playwright-version=$PLAYWRIGHT_VERSION" >> $GITHUB_OUTPUT
|
echo "playwright-version=$PLAYWRIGHT_VERSION" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Cache Playwright Browsers
|
- name: Cache Playwright Browsers
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v5 # v5.0.2
|
||||||
id: cache-playwright-browsers
|
id: cache-playwright-browsers
|
||||||
with:
|
with:
|
||||||
path: '~/.cache/ms-playwright'
|
path: '~/.cache/ms-playwright'
|
||||||
@@ -13,15 +13,15 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Install pnpm
|
- name: Install pnpm
|
||||||
uses: pnpm/action-setup@v4
|
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
||||||
with:
|
with:
|
||||||
version: 10
|
version: 10
|
||||||
|
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v6
|
||||||
with:
|
with:
|
||||||
node-version: lts/*
|
node-version: lts/*
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
@@ -36,7 +36,7 @@ jobs:
|
|||||||
echo "NEW_VERSION=$NEW_VERSION" >> $GITHUB_OUTPUT
|
echo "NEW_VERSION=$NEW_VERSION" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Create Pull Request
|
- name: Create Pull Request
|
||||||
uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e
|
uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8.1.0
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.PR_GH_TOKEN }}
|
token: ${{ secrets.PR_GH_TOKEN }}
|
||||||
commit-message: '[chore] Update electron-types to ${{ steps.get-version.outputs.NEW_VERSION }}'
|
commit-message: '[chore] Update electron-types to ${{ steps.get-version.outputs.NEW_VERSION }}'
|
||||||
|
|||||||
@@ -18,15 +18,15 @@ jobs:
|
|||||||
pull-requests: write
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Install pnpm
|
- name: Install pnpm
|
||||||
uses: pnpm/action-setup@v4
|
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
||||||
with:
|
with:
|
||||||
version: 10
|
version: 10
|
||||||
|
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v6
|
||||||
with:
|
with:
|
||||||
node-version: lts/*
|
node-version: lts/*
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
@@ -35,7 +35,7 @@ jobs:
|
|||||||
run: pnpm install --frozen-lockfile
|
run: pnpm install --frozen-lockfile
|
||||||
|
|
||||||
- name: Checkout ComfyUI-Manager repository
|
- name: Checkout ComfyUI-Manager repository
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
repository: Comfy-Org/ComfyUI-Manager
|
repository: Comfy-Org/ComfyUI-Manager
|
||||||
path: ComfyUI-Manager
|
path: ComfyUI-Manager
|
||||||
@@ -86,7 +86,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Create Pull Request
|
- name: Create Pull Request
|
||||||
if: steps.check-changes.outputs.changed == 'true'
|
if: steps.check-changes.outputs.changed == 'true'
|
||||||
uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e
|
uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8.1.0
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.PR_GH_TOKEN }}
|
token: ${{ secrets.PR_GH_TOKEN }}
|
||||||
commit-message: '[chore] Update ComfyUI-Manager API types from ComfyUI-Manager@${{ steps.manager-info.outputs.commit }}'
|
commit-message: '[chore] Update ComfyUI-Manager API types from ComfyUI-Manager@${{ steps.manager-info.outputs.commit }}'
|
||||||
|
|||||||
@@ -17,15 +17,15 @@ jobs:
|
|||||||
pull-requests: write
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Install pnpm
|
- name: Install pnpm
|
||||||
uses: pnpm/action-setup@v4
|
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
||||||
with:
|
with:
|
||||||
version: 10
|
version: 10
|
||||||
|
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v6
|
||||||
with:
|
with:
|
||||||
node-version: lts/*
|
node-version: lts/*
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
@@ -34,7 +34,7 @@ jobs:
|
|||||||
run: pnpm install --frozen-lockfile
|
run: pnpm install --frozen-lockfile
|
||||||
|
|
||||||
- name: Checkout comfy-api repository
|
- name: Checkout comfy-api repository
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
repository: Comfy-Org/comfy-api
|
repository: Comfy-Org/comfy-api
|
||||||
path: comfy-api
|
path: comfy-api
|
||||||
@@ -87,7 +87,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Create Pull Request
|
- name: Create Pull Request
|
||||||
if: steps.check-changes.outputs.changed == 'true'
|
if: steps.check-changes.outputs.changed == 'true'
|
||||||
uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e
|
uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8.1.0
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.PR_GH_TOKEN }}
|
token: ${{ secrets.PR_GH_TOKEN }}
|
||||||
commit-message: '[chore] Update Comfy Registry API types from comfy-api@${{ steps.api-info.outputs.commit }}'
|
commit-message: '[chore] Update Comfy Registry API types from comfy-api@${{ steps.api-info.outputs.commit }}'
|
||||||
|
|||||||
2
.github/workflows/ci-json-validation.yaml
vendored
@@ -13,6 +13,6 @@ jobs:
|
|||||||
json-lint:
|
json-lint:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v6
|
||||||
- name: Validate JSON syntax
|
- name: Validate JSON syntax
|
||||||
run: ./scripts/cicd/check-json.sh
|
run: ./scripts/cicd/check-json.sh
|
||||||
|
|||||||
22
.github/workflows/ci-lint-format.yaml
vendored
@@ -18,23 +18,13 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout PR
|
- name: Checkout PR
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
ref: ${{ !github.event.pull_request.head.repo.fork && github.head_ref || github.ref }}
|
ref: ${{ !github.event.pull_request.head.repo.fork && github.head_ref || github.ref }}
|
||||||
|
token: ${{ secrets.PR_GH_TOKEN }}
|
||||||
|
|
||||||
- name: Install pnpm
|
- name: Setup frontend
|
||||||
uses: pnpm/action-setup@v4
|
uses: ./.github/actions/setup-frontend
|
||||||
with:
|
|
||||||
version: 10
|
|
||||||
|
|
||||||
- name: Use Node.js
|
|
||||||
uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version: 'lts/*'
|
|
||||||
cache: 'pnpm'
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: pnpm install --frozen-lockfile
|
|
||||||
|
|
||||||
- name: Run ESLint with auto-fix
|
- name: Run ESLint with auto-fix
|
||||||
run: pnpm lint:fix
|
run: pnpm lint:fix
|
||||||
@@ -73,7 +63,7 @@ jobs:
|
|||||||
- name: Comment on PR about auto-fix
|
- name: Comment on PR about auto-fix
|
||||||
if: steps.verify-changed-files.outputs.changed == 'true' && github.event.pull_request.head.repo.full_name == github.repository
|
if: steps.verify-changed-files.outputs.changed == 'true' && github.event.pull_request.head.repo.full_name == github.repository
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
uses: actions/github-script@v7
|
uses: actions/github-script@v8
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
github.rest.issues.createComment({
|
github.rest.issues.createComment({
|
||||||
@@ -86,7 +76,7 @@ jobs:
|
|||||||
- name: Comment on PR about manual fix needed
|
- name: Comment on PR about manual fix needed
|
||||||
if: steps.verify-changed-files.outputs.changed == 'true' && github.event.pull_request.head.repo.full_name != github.repository
|
if: steps.verify-changed-files.outputs.changed == 'true' && github.event.pull_request.head.repo.full_name != github.repository
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
uses: actions/github-script@v7
|
uses: actions/github-script@v8
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
github.rest.issues.createComment({
|
github.rest.issues.createComment({
|
||||||
|
|||||||
4
.github/workflows/ci-python-validation.yaml
vendored
@@ -16,10 +16,10 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@v6
|
||||||
with:
|
with:
|
||||||
python-version: '3.11'
|
python-version: '3.11'
|
||||||
|
|
||||||
|
|||||||
19
.github/workflows/ci-size-data.yaml
vendored
@@ -17,21 +17,10 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Install pnpm
|
- name: Setup frontend
|
||||||
uses: pnpm/action-setup@v4.1.0
|
uses: ./.github/actions/setup-frontend
|
||||||
with:
|
|
||||||
version: 10
|
|
||||||
|
|
||||||
- name: Install Node.js
|
|
||||||
uses: actions/setup-node@v5
|
|
||||||
with:
|
|
||||||
node-version: '24.x'
|
|
||||||
cache: pnpm
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: pnpm install
|
|
||||||
|
|
||||||
- name: Build project
|
- name: Build project
|
||||||
run: pnpm build
|
run: pnpm build
|
||||||
@@ -46,7 +35,7 @@ jobs:
|
|||||||
echo ${{ github.base_ref }} > ./temp/size/base.txt
|
echo ${{ github.base_ref }} > ./temp/size/base.txt
|
||||||
|
|
||||||
- name: Upload size data
|
- name: Upload size data
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v6
|
||||||
with:
|
with:
|
||||||
name: size-data
|
name: size-data
|
||||||
path: temp/size
|
path: temp/size
|
||||||
|
|||||||
6
.github/workflows/ci-tests-e2e-forks.yaml
vendored
@@ -31,11 +31,11 @@ jobs:
|
|||||||
echo "Is forked: ${{ github.event.workflow_run.head_repository.full_name != github.event.workflow_run.repository.full_name }}"
|
echo "Is forked: ${{ github.event.workflow_run.head_repository.full_name != github.event.workflow_run.repository.full_name }}"
|
||||||
|
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Get PR Number
|
- name: Get PR Number
|
||||||
id: pr
|
id: pr
|
||||||
uses: actions/github-script@v7
|
uses: actions/github-script@v8
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
const { data: prs } = await github.rest.pulls.list({
|
const { data: prs } = await github.rest.pulls.list({
|
||||||
@@ -68,7 +68,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Download and Deploy Reports
|
- name: Download and Deploy Reports
|
||||||
if: steps.pr.outputs.result != 'null' && github.event.action == 'completed'
|
if: steps.pr.outputs.result != 'null' && github.event.action == 'completed'
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v7
|
||||||
with:
|
with:
|
||||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
run-id: ${{ github.event.workflow_run.id }}
|
run-id: ${{ github.event.workflow_run.id }}
|
||||||
|
|||||||
35
.github/workflows/ci-tests-e2e.yaml
vendored
@@ -5,8 +5,8 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches: [main, master, core/*, desktop/*]
|
branches: [main, master, core/*, desktop/*]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches-ignore:
|
branches-ignore: [wip/*, draft/*, temp/*]
|
||||||
[wip/*, draft/*, temp/*, vue-nodes-migration, sno-playwright-*]
|
workflow_dispatch:
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: ${{ github.workflow }}-${{ github.ref }}
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
@@ -17,7 +17,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
- name: Setup frontend
|
- name: Setup frontend
|
||||||
uses: ./.github/actions/setup-frontend
|
uses: ./.github/actions/setup-frontend
|
||||||
with:
|
with:
|
||||||
@@ -25,7 +25,7 @@ jobs:
|
|||||||
|
|
||||||
# Upload only built dist/ (containerized test jobs will pnpm install without cache)
|
# Upload only built dist/ (containerized test jobs will pnpm install without cache)
|
||||||
- name: Upload built frontend
|
- name: Upload built frontend
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v6
|
||||||
with:
|
with:
|
||||||
name: frontend-dist
|
name: frontend-dist
|
||||||
path: dist/
|
path: dist/
|
||||||
@@ -51,9 +51,9 @@ jobs:
|
|||||||
shardTotal: [8]
|
shardTotal: [8]
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
- name: Download built frontend
|
- name: Download built frontend
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: frontend-dist
|
name: frontend-dist
|
||||||
path: dist/
|
path: dist/
|
||||||
@@ -72,7 +72,7 @@ jobs:
|
|||||||
PLAYWRIGHT_BLOB_OUTPUT_DIR: ./blob-report
|
PLAYWRIGHT_BLOB_OUTPUT_DIR: ./blob-report
|
||||||
|
|
||||||
- name: Upload blob report
|
- name: Upload blob report
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v6
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
with:
|
with:
|
||||||
name: blob-report-chromium-${{ matrix.shardIndex }}
|
name: blob-report-chromium-${{ matrix.shardIndex }}
|
||||||
@@ -98,9 +98,9 @@ jobs:
|
|||||||
browser: [chromium-2x, chromium-0.5x, mobile-chrome]
|
browser: [chromium-2x, chromium-0.5x, mobile-chrome]
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
- name: Download built frontend
|
- name: Download built frontend
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: frontend-dist
|
name: frontend-dist
|
||||||
path: dist/
|
path: dist/
|
||||||
@@ -128,7 +128,7 @@ jobs:
|
|||||||
pnpm exec playwright merge-reports --reporter=json ./blob-report
|
pnpm exec playwright merge-reports --reporter=json ./blob-report
|
||||||
|
|
||||||
- name: Upload Playwright report
|
- name: Upload Playwright report
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v6
|
||||||
if: always()
|
if: always()
|
||||||
with:
|
with:
|
||||||
name: playwright-report-${{ matrix.browser }}
|
name: playwright-report-${{ matrix.browser }}
|
||||||
@@ -141,16 +141,13 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v5
|
|
||||||
|
|
||||||
- name: Install pnpm
|
- name: Install pnpm
|
||||||
uses: pnpm/action-setup@v4
|
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
||||||
with:
|
with:
|
||||||
version: 10
|
version: 10
|
||||||
|
|
||||||
- name: Download blob reports
|
- name: Download blob reports
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v7
|
||||||
with:
|
with:
|
||||||
path: ./all-blob-reports
|
path: ./all-blob-reports
|
||||||
pattern: blob-report-chromium-*
|
pattern: blob-report-chromium-*
|
||||||
@@ -165,7 +162,7 @@ jobs:
|
|||||||
pnpm dlx @playwright/test merge-reports --reporter=json ./all-blob-reports
|
pnpm dlx @playwright/test merge-reports --reporter=json ./all-blob-reports
|
||||||
|
|
||||||
- name: Upload HTML report
|
- name: Upload HTML report
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v6
|
||||||
with:
|
with:
|
||||||
name: playwright-report-chromium
|
name: playwright-report-chromium
|
||||||
path: ./playwright-report/
|
path: ./playwright-report/
|
||||||
@@ -183,7 +180,7 @@ jobs:
|
|||||||
pull-requests: write
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Get start time
|
- name: Get start time
|
||||||
id: start-time
|
id: start-time
|
||||||
@@ -210,10 +207,10 @@ jobs:
|
|||||||
contents: read
|
contents: read
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Download all playwright reports
|
- name: Download all playwright reports
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v7
|
||||||
with:
|
with:
|
||||||
pattern: playwright-report-*
|
pattern: playwright-report-*
|
||||||
path: reports
|
path: reports
|
||||||
|
|||||||
@@ -31,11 +31,11 @@ jobs:
|
|||||||
echo "Is forked: ${{ github.event.workflow_run.head_repository.full_name != github.event.workflow_run.repository.full_name }}"
|
echo "Is forked: ${{ github.event.workflow_run.head_repository.full_name != github.event.workflow_run.repository.full_name }}"
|
||||||
|
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Get PR Number
|
- name: Get PR Number
|
||||||
id: pr
|
id: pr
|
||||||
uses: actions/github-script@v7
|
uses: actions/github-script@v8
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
const { data: prs } = await github.rest.pulls.list({
|
const { data: prs } = await github.rest.pulls.list({
|
||||||
@@ -68,7 +68,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Download and Deploy Storybook
|
- name: Download and Deploy Storybook
|
||||||
if: steps.pr.outputs.result != 'null' && github.event.action == 'completed' && github.event.workflow_run.conclusion == 'success'
|
if: steps.pr.outputs.result != 'null' && github.event.action == 'completed' && github.event.workflow_run.conclusion == 'success'
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v7
|
||||||
with:
|
with:
|
||||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
run-id: ${{ github.event.workflow_run.id }}
|
run-id: ${{ github.event.workflow_run.id }}
|
||||||
|
|||||||
46
.github/workflows/ci-tests-storybook.yaml
vendored
@@ -14,7 +14,7 @@ jobs:
|
|||||||
pull-requests: write
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Post starting comment
|
- name: Post starting comment
|
||||||
env:
|
env:
|
||||||
@@ -36,21 +36,10 @@ jobs:
|
|||||||
workflow-url: ${{ steps.workflow-url.outputs.url }}
|
workflow-url: ${{ steps.workflow-url.outputs.url }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Install pnpm
|
- name: Setup frontend
|
||||||
uses: pnpm/action-setup@v4
|
uses: ./.github/actions/setup-frontend
|
||||||
with:
|
|
||||||
version: 10
|
|
||||||
|
|
||||||
- name: Setup Node.js
|
|
||||||
uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version: '20'
|
|
||||||
cache: 'pnpm'
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: pnpm install --frozen-lockfile
|
|
||||||
|
|
||||||
- name: Build Storybook
|
- name: Build Storybook
|
||||||
run: pnpm build-storybook
|
run: pnpm build-storybook
|
||||||
@@ -69,7 +58,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Upload Storybook build
|
- name: Upload Storybook build
|
||||||
if: success() && github.event.pull_request.head.repo.fork == false
|
if: success() && github.event.pull_request.head.repo.fork == false
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v6
|
||||||
with:
|
with:
|
||||||
name: storybook-static
|
name: storybook-static
|
||||||
path: storybook-static/
|
path: storybook-static/
|
||||||
@@ -86,27 +75,16 @@ jobs:
|
|||||||
chromatic-storybook-url: ${{ steps.chromatic.outputs.storybookUrl }}
|
chromatic-storybook-url: ${{ steps.chromatic.outputs.storybookUrl }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0 # Required for Chromatic baseline
|
fetch-depth: 0 # Required for Chromatic baseline
|
||||||
|
|
||||||
- name: Install pnpm
|
- name: Setup frontend
|
||||||
uses: pnpm/action-setup@v4
|
uses: ./.github/actions/setup-frontend
|
||||||
with:
|
|
||||||
version: 10
|
|
||||||
|
|
||||||
- name: Setup Node.js
|
|
||||||
uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version: '20'
|
|
||||||
cache: 'pnpm'
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: pnpm install --frozen-lockfile
|
|
||||||
|
|
||||||
- name: Build Storybook and run Chromatic
|
- name: Build Storybook and run Chromatic
|
||||||
id: chromatic
|
id: chromatic
|
||||||
uses: chromaui/action@latest
|
uses: chromaui/action@07791f8243f4cb2698bf4d00426baf4b2d1cb7e0 # v13.3.5
|
||||||
with:
|
with:
|
||||||
projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
|
projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
|
||||||
buildScriptName: build-storybook
|
buildScriptName: build-storybook
|
||||||
@@ -136,11 +114,11 @@ jobs:
|
|||||||
contents: read
|
contents: read
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Download Storybook build
|
- name: Download Storybook build
|
||||||
if: needs.storybook-build.outputs.conclusion == 'success'
|
if: needs.storybook-build.outputs.conclusion == 'success'
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: storybook-static
|
name: storybook-static
|
||||||
path: storybook-static
|
path: storybook-static
|
||||||
@@ -170,7 +148,7 @@ jobs:
|
|||||||
pull-requests: write
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
- name: Update comment with Chromatic URLs
|
- name: Update comment with Chromatic URLs
|
||||||
uses: actions/github-script@v7
|
uses: actions/github-script@v8
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
const buildUrl = '${{ needs.chromatic-deployment.outputs.chromatic-build-url }}';
|
const buildUrl = '${{ needs.chromatic-deployment.outputs.chromatic-build-url }}';
|
||||||
|
|||||||
17
.github/workflows/ci-tests-unit.yaml
vendored
@@ -16,21 +16,10 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Install pnpm
|
- name: Setup frontend
|
||||||
uses: pnpm/action-setup@v4
|
uses: ./.github/actions/setup-frontend
|
||||||
with:
|
|
||||||
version: 10
|
|
||||||
|
|
||||||
- name: Use Node.js
|
|
||||||
uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version: 'lts/*'
|
|
||||||
cache: 'pnpm'
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: pnpm install --frozen-lockfile
|
|
||||||
|
|
||||||
- name: Run Vitest tests
|
- name: Run Vitest tests
|
||||||
run: pnpm test:unit
|
run: pnpm test:unit
|
||||||
|
|||||||
21
.github/workflows/ci-validate-action-pins.yaml
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
name: Validate Action SHA Pins
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- '.github/workflows/**'
|
||||||
|
- '.github/actions/**'
|
||||||
|
- '.pinact.yaml'
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
validate-pins:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
|
- uses: suzuki-shunsuke/pinact-action@3d49c6412901042473ffa78becddab1aea46bbea # v1.3.1
|
||||||
|
with:
|
||||||
|
skip_push: 'true'
|
||||||
4
.github/workflows/ci-yaml-validation.yaml
vendored
@@ -17,10 +17,10 @@ jobs:
|
|||||||
yaml-lint:
|
yaml-lint:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@v6
|
||||||
with:
|
with:
|
||||||
python-version: '3.x'
|
python-version: '3.x'
|
||||||
|
|
||||||
|
|||||||
4
.github/workflows/cloud-backport-tag.yaml
vendored
@@ -18,12 +18,12 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout merge commit
|
- name: Checkout merge commit
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
ref: ${{ github.event.pull_request.merge_commit_sha }}
|
ref: ${{ github.event.pull_request.merge_commit_sha }}
|
||||||
|
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v5
|
uses: actions/setup-node@v6
|
||||||
with:
|
with:
|
||||||
node-version-file: '.nvmrc'
|
node-version-file: '.nvmrc'
|
||||||
|
|
||||||
|
|||||||
4
.github/workflows/i18n-update-core.yaml
vendored
@@ -16,7 +16,9 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.PR_GH_TOKEN }}
|
||||||
|
|
||||||
# Setup playwright environment
|
# Setup playwright environment
|
||||||
- name: Setup ComfyUI Frontend
|
- name: Setup ComfyUI Frontend
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
# Setup playwright environment with custom node repository
|
# Setup playwright environment with custom node repository
|
||||||
- name: Setup ComfyUI Server (without launching)
|
- name: Setup ComfyUI Server (without launching)
|
||||||
@@ -36,7 +36,7 @@ jobs:
|
|||||||
|
|
||||||
# Install the custom node repository
|
# Install the custom node repository
|
||||||
- name: Checkout custom node repository
|
- name: Checkout custom node repository
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
repository: ${{ inputs.owner }}/${{ inputs.repository }}
|
repository: ${{ inputs.owner }}/${{ inputs.repository }}
|
||||||
path: 'ComfyUI/custom_nodes/${{ inputs.repository }}'
|
path: 'ComfyUI/custom_nodes/${{ inputs.repository }}'
|
||||||
@@ -113,7 +113,7 @@ jobs:
|
|||||||
git commit -m "Update locales"
|
git commit -m "Update locales"
|
||||||
|
|
||||||
- name: Install SSH key For PUSH
|
- name: Install SSH key For PUSH
|
||||||
uses: shimataro/ssh-key-action@d4fffb50872869abe2d9a9098a6d9c5aa7d16be4
|
uses: shimataro/ssh-key-action@d4fffb50872869abe2d9a9098a6d9c5aa7d16be4 # v2.7.0
|
||||||
with:
|
with:
|
||||||
# PR private key from action server
|
# PR private key from action server
|
||||||
key: ${{ secrets.PR_SSH_PRIVATE_KEY }}
|
key: ${{ secrets.PR_SSH_PRIVATE_KEY }}
|
||||||
|
|||||||
4
.github/workflows/i18n-update-nodes.yaml
vendored
@@ -14,7 +14,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
# Setup playwright environment
|
# Setup playwright environment
|
||||||
- name: Setup ComfyUI Server (and start)
|
- name: Setup ComfyUI Server (and start)
|
||||||
uses: ./.github/actions/setup-comfyui-server
|
uses: ./.github/actions/setup-comfyui-server
|
||||||
@@ -40,7 +40,7 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
||||||
- name: Create Pull Request
|
- name: Create Pull Request
|
||||||
uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e
|
uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8.1.0
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.PR_GH_TOKEN }}
|
token: ${{ secrets.PR_GH_TOKEN }}
|
||||||
commit-message: 'Update locales for node definitions'
|
commit-message: 'Update locales for node definitions'
|
||||||
|
|||||||
2
.github/workflows/pr-backport.yaml
vendored
@@ -64,7 +64,7 @@ jobs:
|
|||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
|
|||||||
8
.github/workflows/pr-claude-review.yaml
vendored
@@ -23,18 +23,18 @@ jobs:
|
|||||||
timeout-minutes: 30
|
timeout-minutes: 30
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
ref: refs/pull/${{ github.event.pull_request.number }}/head
|
ref: refs/pull/${{ github.event.pull_request.number }}/head
|
||||||
|
|
||||||
- name: Install pnpm
|
- name: Install pnpm
|
||||||
uses: pnpm/action-setup@v4
|
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
||||||
with:
|
with:
|
||||||
version: 10
|
version: 10
|
||||||
|
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v6
|
||||||
with:
|
with:
|
||||||
node-version: '20'
|
node-version: '20'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
@@ -44,7 +44,7 @@ jobs:
|
|||||||
pnpm install -g typescript @vue/compiler-sfc
|
pnpm install -g typescript @vue/compiler-sfc
|
||||||
|
|
||||||
- name: Run Claude PR Review
|
- name: Run Claude PR Review
|
||||||
uses: anthropics/claude-code-action@v1.0.6
|
uses: anthropics/claude-code-action@ff34ce0ff04a470bd3fa56c1ef391c8f1c19f8e9 # v1.0.38
|
||||||
with:
|
with:
|
||||||
label_trigger: 'claude-review'
|
label_trigger: 'claude-review'
|
||||||
prompt: |
|
prompt: |
|
||||||
|
|||||||
25
.github/workflows/pr-size-report.yaml
vendored
@@ -33,24 +33,13 @@ jobs:
|
|||||||
github.event_name == 'workflow_dispatch'
|
github.event_name == 'workflow_dispatch'
|
||||||
)
|
)
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Install pnpm
|
- name: Setup frontend
|
||||||
uses: pnpm/action-setup@v4.1.0
|
uses: ./.github/actions/setup-frontend
|
||||||
with:
|
|
||||||
version: 10
|
|
||||||
|
|
||||||
- name: Install Node.js
|
|
||||||
uses: actions/setup-node@v5
|
|
||||||
with:
|
|
||||||
node-version: '24.x'
|
|
||||||
cache: pnpm
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: pnpm install
|
|
||||||
|
|
||||||
- name: Download size data
|
- name: Download size data
|
||||||
uses: dawidd6/action-download-artifact@v11
|
uses: dawidd6/action-download-artifact@0bd50d53a6d7fb5cb921e607957e9cc12b4ce392 # v12
|
||||||
with:
|
with:
|
||||||
name: size-data
|
name: size-data
|
||||||
run_id: ${{ github.event_name == 'workflow_dispatch' && inputs.run_id || github.event.workflow_run.id }}
|
run_id: ${{ github.event_name == 'workflow_dispatch' && inputs.run_id || github.event.workflow_run.id }}
|
||||||
@@ -75,7 +64,7 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Download previous size data
|
- name: Download previous size data
|
||||||
uses: dawidd6/action-download-artifact@v11
|
uses: dawidd6/action-download-artifact@0bd50d53a6d7fb5cb921e607957e9cc12b4ce392 # v12
|
||||||
with:
|
with:
|
||||||
branch: ${{ steps.pr-base.outputs.content }}
|
branch: ${{ steps.pr-base.outputs.content }}
|
||||||
workflow: ci-size-data.yaml
|
workflow: ci-size-data.yaml
|
||||||
@@ -89,12 +78,12 @@ jobs:
|
|||||||
|
|
||||||
- name: Read size report
|
- name: Read size report
|
||||||
id: size-report
|
id: size-report
|
||||||
uses: juliangruber/read-file-action@v1
|
uses: juliangruber/read-file-action@b549046febe0fe86f8cb4f93c24e284433f9ab58 # v1.1.7
|
||||||
with:
|
with:
|
||||||
path: ./size-report.md
|
path: ./size-report.md
|
||||||
|
|
||||||
- name: Create or update PR comment
|
- name: Create or update PR comment
|
||||||
uses: actions-cool/maintain-one-comment@v3
|
uses: actions-cool/maintain-one-comment@4b2dbf086015f892dcb5e8c1106f5fccd6c1476b # v3.2.0
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
number: ${{ steps.pr-number.outputs.content }}
|
number: ${{ steps.pr-number.outputs.content }}
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ jobs:
|
|||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Find Update Comment
|
- name: Find Update Comment
|
||||||
uses: peter-evans/find-comment@b30e6a3c0ed37e7c023ccd3f1db5c6c0b0c23aad
|
uses: peter-evans/find-comment@b30e6a3c0ed37e7c023ccd3f1db5c6c0b0c23aad # v4.0.0
|
||||||
id: 'find-update-comment'
|
id: 'find-update-comment'
|
||||||
with:
|
with:
|
||||||
issue-number: ${{ steps.pr-info.outputs.pr-number }}
|
issue-number: ${{ steps.pr-info.outputs.pr-number }}
|
||||||
@@ -46,7 +46,7 @@ jobs:
|
|||||||
body-includes: 'Updating Playwright Expectations'
|
body-includes: 'Updating Playwright Expectations'
|
||||||
|
|
||||||
- name: Add Starting Reaction
|
- name: Add Starting Reaction
|
||||||
uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9
|
uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 # v5.0.0
|
||||||
with:
|
with:
|
||||||
comment-id: ${{ steps.find-update-comment.outputs.comment-id }}
|
comment-id: ${{ steps.find-update-comment.outputs.comment-id }}
|
||||||
issue-number: ${{ steps.pr-info.outputs.pr-number }}
|
issue-number: ${{ steps.pr-info.outputs.pr-number }}
|
||||||
@@ -56,7 +56,7 @@ jobs:
|
|||||||
reactions: eyes
|
reactions: eyes
|
||||||
|
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
ref: ${{ steps.pr-info.outputs.branch }}
|
ref: ${{ steps.pr-info.outputs.branch }}
|
||||||
- name: Setup frontend
|
- name: Setup frontend
|
||||||
@@ -66,7 +66,7 @@ jobs:
|
|||||||
|
|
||||||
# Upload built dist/ (containerized test jobs will pnpm install without cache)
|
# Upload built dist/ (containerized test jobs will pnpm install without cache)
|
||||||
- name: Upload built frontend
|
- name: Upload built frontend
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v6
|
||||||
with:
|
with:
|
||||||
name: frontend-dist
|
name: frontend-dist
|
||||||
path: dist/
|
path: dist/
|
||||||
@@ -91,11 +91,11 @@ jobs:
|
|||||||
shardTotal: [4]
|
shardTotal: [4]
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
ref: ${{ needs.setup.outputs.branch }}
|
ref: ${{ needs.setup.outputs.branch }}
|
||||||
- name: Download built frontend
|
- name: Download built frontend
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: frontend-dist
|
name: frontend-dist
|
||||||
path: dist/
|
path: dist/
|
||||||
@@ -149,7 +149,7 @@ jobs:
|
|||||||
|
|
||||||
# Upload ONLY the changed files from this shard
|
# Upload ONLY the changed files from this shard
|
||||||
- name: Upload changed snapshots
|
- name: Upload changed snapshots
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v6
|
||||||
if: steps.changed-snapshots.outputs.has-changes == 'true'
|
if: steps.changed-snapshots.outputs.has-changes == 'true'
|
||||||
with:
|
with:
|
||||||
name: snapshots-shard-${{ matrix.shardIndex }}
|
name: snapshots-shard-${{ matrix.shardIndex }}
|
||||||
@@ -157,7 +157,7 @@ jobs:
|
|||||||
retention-days: 1
|
retention-days: 1
|
||||||
|
|
||||||
- name: Upload test report
|
- name: Upload test report
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v6
|
||||||
if: always()
|
if: always()
|
||||||
with:
|
with:
|
||||||
name: playwright-report-shard-${{ matrix.shardIndex }}
|
name: playwright-report-shard-${{ matrix.shardIndex }}
|
||||||
@@ -170,17 +170,17 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
ref: ${{ needs.setup.outputs.branch }}
|
ref: ${{ needs.setup.outputs.branch }}
|
||||||
|
|
||||||
# Download all changed snapshot files from shards
|
# Download all changed snapshot files from shards
|
||||||
- name: Download snapshot artifacts
|
- name: Download snapshot artifacts
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v7
|
||||||
with:
|
with:
|
||||||
pattern: snapshots-shard-*
|
pattern: snapshots-shard-*
|
||||||
path: ./downloaded-snapshots
|
path: ./downloaded-snapshots
|
||||||
merge-multiple: false
|
merge-multiple: true
|
||||||
|
|
||||||
- name: List downloaded files
|
- name: List downloaded files
|
||||||
run: |
|
run: |
|
||||||
@@ -206,13 +206,13 @@ jobs:
|
|||||||
echo "MERGING CHANGED SNAPSHOTS"
|
echo "MERGING CHANGED SNAPSHOTS"
|
||||||
echo "=========================================="
|
echo "=========================================="
|
||||||
|
|
||||||
# Check if any artifacts were downloaded
|
# Check if any artifacts were downloaded (merge-multiple puts files directly in path)
|
||||||
if [ ! -d "./downloaded-snapshots" ]; then
|
if [ ! -d "./downloaded-snapshots" ]; then
|
||||||
echo "No snapshot artifacts to merge"
|
echo "No snapshot artifacts to merge"
|
||||||
echo "=========================================="
|
echo "=========================================="
|
||||||
echo "MERGE COMPLETE"
|
echo "MERGE COMPLETE"
|
||||||
echo "=========================================="
|
echo "=========================================="
|
||||||
echo "Shards merged: 0"
|
echo "Files merged: 0"
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -222,37 +222,29 @@ jobs:
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
merged_count=0
|
# Count files to merge
|
||||||
|
file_count=$(find ./downloaded-snapshots -type f | wc -l)
|
||||||
|
|
||||||
# For each shard's changed files, copy them directly
|
if [ "$file_count" -eq 0 ]; then
|
||||||
for shard_dir in ./downloaded-snapshots/snapshots-shard-*/; do
|
echo "No snapshot files found in downloaded artifacts"
|
||||||
if [ ! -d "$shard_dir" ]; then
|
echo "=========================================="
|
||||||
continue
|
echo "MERGE COMPLETE"
|
||||||
fi
|
echo "=========================================="
|
||||||
|
echo "Files merged: 0"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
shard_name=$(basename "$shard_dir")
|
echo "Merging $file_count snapshot file(s)..."
|
||||||
file_count=$(find "$shard_dir" -type f | wc -l)
|
|
||||||
|
|
||||||
if [ "$file_count" -eq 0 ]; then
|
# Copy all files directly, preserving directory structure
|
||||||
echo " $shard_name: no files"
|
# With merge-multiple: true, files are directly in ./downloaded-snapshots/ without shard subdirs
|
||||||
continue
|
cp -v -r ./downloaded-snapshots/* browser_tests/ 2>&1 | sed 's/^/ /'
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Processing $shard_name ($file_count file(s))..."
|
|
||||||
|
|
||||||
# Copy files directly, preserving directory structure
|
|
||||||
# Since files are already in correct structure (no browser_tests/ prefix), just copy them all
|
|
||||||
cp -v -r "$shard_dir"* browser_tests/ 2>&1 | sed 's/^/ /'
|
|
||||||
|
|
||||||
merged_count=$((merged_count + 1))
|
|
||||||
echo " ✓ Merged"
|
|
||||||
echo ""
|
|
||||||
done
|
|
||||||
|
|
||||||
|
echo ""
|
||||||
echo "=========================================="
|
echo "=========================================="
|
||||||
echo "MERGE COMPLETE"
|
echo "MERGE COMPLETE"
|
||||||
echo "=========================================="
|
echo "=========================================="
|
||||||
echo "Shards merged: $merged_count"
|
echo "Files merged: $file_count"
|
||||||
|
|
||||||
- name: Show changes
|
- name: Show changes
|
||||||
run: |
|
run: |
|
||||||
@@ -301,7 +293,7 @@ jobs:
|
|||||||
echo "✓ Commit and push successful"
|
echo "✓ Commit and push successful"
|
||||||
|
|
||||||
- name: Add Done Reaction
|
- name: Add Done Reaction
|
||||||
uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9
|
uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 # v5.0.0
|
||||||
if: github.event_name == 'issue_comment' && steps.commit.outputs.has-changes == 'true'
|
if: github.event_name == 'issue_comment' && steps.commit.outputs.has-changes == 'true'
|
||||||
with:
|
with:
|
||||||
comment-id: ${{ needs.setup.outputs.comment-id }}
|
comment-id: ${{ needs.setup.outputs.comment-id }}
|
||||||
|
|||||||
@@ -20,13 +20,13 @@ jobs:
|
|||||||
dist_tag: ${{ steps.dist.outputs.dist_tag }}
|
dist_tag: ${{ steps.dist.outputs.dist_tag }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
ref: ${{ github.event.pull_request.merge_commit_sha }}
|
ref: ${{ github.event.pull_request.merge_commit_sha }}
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v5
|
uses: actions/setup-node@v6
|
||||||
with:
|
with:
|
||||||
node-version: '24.x'
|
node-version: '24.x'
|
||||||
|
|
||||||
@@ -71,7 +71,7 @@ jobs:
|
|||||||
pull-requests: write
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout merge commit
|
- name: Checkout merge commit
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
ref: ${{ github.event.pull_request.merge_commit_sha }}
|
ref: ${{ github.event.pull_request.merge_commit_sha }}
|
||||||
fetch-depth: 2
|
fetch-depth: 2
|
||||||
|
|||||||
6
.github/workflows/publish-desktop-ui.yaml
vendored
@@ -77,19 +77,19 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
ref: ${{ steps.resolve_ref.outputs.ref }}
|
ref: ${{ steps.resolve_ref.outputs.ref }}
|
||||||
fetch-depth: 1
|
fetch-depth: 1
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Install pnpm
|
- name: Install pnpm
|
||||||
uses: pnpm/action-setup@v4
|
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
||||||
with:
|
with:
|
||||||
version: 10
|
version: 10
|
||||||
|
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v5
|
uses: actions/setup-node@v6
|
||||||
with:
|
with:
|
||||||
node-version: '24.x'
|
node-version: '24.x'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
|
|||||||
10
.github/workflows/release-biweekly-comfyui.yaml
vendored
@@ -61,13 +61,13 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout ComfyUI_frontend
|
- name: Checkout ComfyUI_frontend
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
path: frontend
|
path: frontend
|
||||||
|
|
||||||
- name: Checkout ComfyUI (sparse)
|
- name: Checkout ComfyUI (sparse)
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
repository: Comfy-Org/ComfyUI
|
repository: Comfy-Org/ComfyUI
|
||||||
sparse-checkout: |
|
sparse-checkout: |
|
||||||
@@ -75,12 +75,12 @@ jobs:
|
|||||||
path: comfyui
|
path: comfyui
|
||||||
|
|
||||||
- name: Install pnpm
|
- name: Install pnpm
|
||||||
uses: pnpm/action-setup@v4
|
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
||||||
with:
|
with:
|
||||||
version: 10
|
version: 10
|
||||||
|
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v6
|
||||||
with:
|
with:
|
||||||
node-version: lts/*
|
node-version: lts/*
|
||||||
|
|
||||||
@@ -169,7 +169,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout ComfyUI fork
|
- name: Checkout ComfyUI fork
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
repository: ${{ inputs.comfyui_fork || 'Comfy-Org/ComfyUI' }}
|
repository: ${{ inputs.comfyui_fork || 'Comfy-Org/ComfyUI' }}
|
||||||
token: ${{ secrets.PR_GH_TOKEN }}
|
token: ${{ secrets.PR_GH_TOKEN }}
|
||||||
|
|||||||
4
.github/workflows/release-branch-create.yaml
vendored
@@ -18,13 +18,13 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
token: ${{ secrets.PR_GH_TOKEN || secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.PR_GH_TOKEN || secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v6
|
||||||
with:
|
with:
|
||||||
node-version: 'lts/*'
|
node-version: 'lts/*'
|
||||||
|
|
||||||
|
|||||||
27
.github/workflows/release-draft-create.yaml
vendored
@@ -19,12 +19,12 @@ jobs:
|
|||||||
is_prerelease: ${{ steps.check_prerelease.outputs.is_prerelease }}
|
is_prerelease: ${{ steps.check_prerelease.outputs.is_prerelease }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
- name: Install pnpm
|
- name: Install pnpm
|
||||||
uses: pnpm/action-setup@v4
|
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
||||||
with:
|
with:
|
||||||
version: 10
|
version: 10
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v6
|
||||||
with:
|
with:
|
||||||
node-version: 'lts/*'
|
node-version: 'lts/*'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
@@ -50,12 +50,13 @@ jobs:
|
|||||||
ALGOLIA_API_KEY: ${{ secrets.ALGOLIA_API_KEY }}
|
ALGOLIA_API_KEY: ${{ secrets.ALGOLIA_API_KEY }}
|
||||||
ENABLE_MINIFY: 'true'
|
ENABLE_MINIFY: 'true'
|
||||||
USE_PROD_CONFIG: 'true'
|
USE_PROD_CONFIG: 'true'
|
||||||
|
IS_NIGHTLY: ${{ case(github.ref == 'refs/heads/main', 'true', 'false') }}
|
||||||
run: |
|
run: |
|
||||||
pnpm install --frozen-lockfile
|
pnpm install --frozen-lockfile
|
||||||
pnpm build
|
pnpm build
|
||||||
pnpm zipdist
|
pnpm zipdist
|
||||||
- name: Upload dist artifact
|
- name: Upload dist artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v6
|
||||||
with:
|
with:
|
||||||
name: dist-files
|
name: dist-files
|
||||||
path: |
|
path: |
|
||||||
@@ -66,16 +67,13 @@ jobs:
|
|||||||
needs: build
|
needs: build
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v5
|
|
||||||
- name: Download dist artifact
|
- name: Download dist artifact
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: dist-files
|
name: dist-files
|
||||||
- name: Create release
|
- name: Create release
|
||||||
id: create_release
|
id: create_release
|
||||||
uses: >-
|
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
|
||||||
softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631
|
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
with:
|
with:
|
||||||
@@ -98,13 +96,13 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
- name: Download dist artifact
|
- name: Download dist artifact
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: dist-files
|
name: dist-files
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v4
|
uses: actions/setup-python@v6
|
||||||
with:
|
with:
|
||||||
python-version: '3.x'
|
python-version: '3.x'
|
||||||
- name: Install build dependencies
|
- name: Install build dependencies
|
||||||
@@ -119,8 +117,7 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
COMFYUI_FRONTEND_VERSION: ${{ needs.build.outputs.version }}
|
COMFYUI_FRONTEND_VERSION: ${{ needs.build.outputs.version }}
|
||||||
- name: Publish pypi package
|
- name: Publish pypi package
|
||||||
uses: >-
|
uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0
|
||||||
pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc
|
|
||||||
with:
|
with:
|
||||||
password: ${{ secrets.PYPI_TOKEN }}
|
password: ${{ secrets.PYPI_TOKEN }}
|
||||||
packages-dir: comfyui_frontend_package/dist
|
packages-dir: comfyui_frontend_package/dist
|
||||||
@@ -147,7 +144,7 @@ jobs:
|
|||||||
pull-requests: write
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout merge commit
|
- name: Checkout merge commit
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
ref: ${{ github.event.pull_request.merge_commit_sha }}
|
ref: ${{ github.event.pull_request.merge_commit_sha }}
|
||||||
fetch-depth: 2
|
fetch-depth: 2
|
||||||
|
|||||||
6
.github/workflows/release-npm-types.yaml
vendored
@@ -69,18 +69,18 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
ref: ${{ steps.resolve_ref.outputs.ref }}
|
ref: ${{ steps.resolve_ref.outputs.ref }}
|
||||||
fetch-depth: 1
|
fetch-depth: 1
|
||||||
|
|
||||||
- name: Install pnpm
|
- name: Install pnpm
|
||||||
uses: pnpm/action-setup@v4
|
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
||||||
with:
|
with:
|
||||||
version: 10
|
version: 10
|
||||||
|
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v5
|
uses: actions/setup-node@v6
|
||||||
with:
|
with:
|
||||||
node-version: 'lts/*'
|
node-version: 'lts/*'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
|
|||||||
16
.github/workflows/release-pypi-dev.yaml
vendored
@@ -15,12 +15,12 @@ jobs:
|
|||||||
version: ${{ steps.current_version.outputs.version }}
|
version: ${{ steps.current_version.outputs.version }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
- name: Install pnpm
|
- name: Install pnpm
|
||||||
uses: pnpm/action-setup@v4
|
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
||||||
with:
|
with:
|
||||||
version: 10
|
version: 10
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v6
|
||||||
with:
|
with:
|
||||||
node-version: 'lts/*'
|
node-version: 'lts/*'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
@@ -40,7 +40,7 @@ jobs:
|
|||||||
pnpm build
|
pnpm build
|
||||||
pnpm zipdist
|
pnpm zipdist
|
||||||
- name: Upload dist artifact
|
- name: Upload dist artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v6
|
||||||
with:
|
with:
|
||||||
name: dist-files
|
name: dist-files
|
||||||
path: |
|
path: |
|
||||||
@@ -52,13 +52,13 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
- name: Download dist artifact
|
- name: Download dist artifact
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: dist-files
|
name: dist-files
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v4
|
uses: actions/setup-python@v6
|
||||||
with:
|
with:
|
||||||
python-version: '3.x'
|
python-version: '3.x'
|
||||||
- name: Install build dependencies
|
- name: Install build dependencies
|
||||||
@@ -73,7 +73,7 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
COMFYUI_FRONTEND_VERSION: ${{ format('{0}.dev{1}', needs.build.outputs.version, inputs.devVersion) }}
|
COMFYUI_FRONTEND_VERSION: ${{ format('{0}.dev{1}', needs.build.outputs.version, inputs.devVersion) }}
|
||||||
- name: Publish pypi package
|
- name: Publish pypi package
|
||||||
uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc
|
uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0
|
||||||
with:
|
with:
|
||||||
password: ${{ secrets.PYPI_TOKEN }}
|
password: ${{ secrets.PYPI_TOKEN }}
|
||||||
packages-dir: comfyui_frontend_package/dist
|
packages-dir: comfyui_frontend_package/dist
|
||||||
|
|||||||
10
.github/workflows/release-version-bump.yaml
vendored
@@ -65,7 +65,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Close stale nightly version bump PRs
|
- name: Close stale nightly version bump PRs
|
||||||
if: github.event_name == 'schedule'
|
if: github.event_name == 'schedule'
|
||||||
uses: actions/github-script@v7
|
uses: actions/github-script@v8
|
||||||
with:
|
with:
|
||||||
github-token: ${{ github.token }}
|
github-token: ${{ github.token }}
|
||||||
script: |
|
script: |
|
||||||
@@ -118,7 +118,7 @@ jobs:
|
|||||||
core.info(`Closed ${closed.length} stale PR(s).`)
|
core.info(`Closed ${closed.length} stale PR(s).`)
|
||||||
|
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
ref: ${{ steps.prepared-inputs.outputs.branch }}
|
ref: ${{ steps.prepared-inputs.outputs.branch }}
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
@@ -142,12 +142,12 @@ jobs:
|
|||||||
echo "✅ Branch '$BRANCH' exists"
|
echo "✅ Branch '$BRANCH' exists"
|
||||||
|
|
||||||
- name: Install pnpm
|
- name: Install pnpm
|
||||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061
|
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
||||||
with:
|
with:
|
||||||
version: 10
|
version: 10
|
||||||
|
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v6
|
||||||
with:
|
with:
|
||||||
node-version: lts/*
|
node-version: lts/*
|
||||||
|
|
||||||
@@ -180,7 +180,7 @@ jobs:
|
|||||||
echo "capitalised=${CAPITALISED_TYPE@u}" >> "$GITHUB_OUTPUT"
|
echo "capitalised=${CAPITALISED_TYPE@u}" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
- name: Create Pull Request
|
- name: Create Pull Request
|
||||||
uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e
|
uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8.1.0
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.PR_GH_TOKEN }}
|
token: ${{ secrets.PR_GH_TOKEN }}
|
||||||
commit-message: '[release] Increment version to ${{ steps.bump-version.outputs.NEW_VERSION }}'
|
commit-message: '[release] Increment version to ${{ steps.bump-version.outputs.NEW_VERSION }}'
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
ref: ${{ github.event.inputs.branch }}
|
ref: ${{ github.event.inputs.branch }}
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
@@ -51,12 +51,12 @@ jobs:
|
|||||||
echo "✅ Branch '$BRANCH' exists"
|
echo "✅ Branch '$BRANCH' exists"
|
||||||
|
|
||||||
- name: Install pnpm
|
- name: Install pnpm
|
||||||
uses: pnpm/action-setup@v4
|
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
||||||
with:
|
with:
|
||||||
version: 10
|
version: 10
|
||||||
|
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v5
|
uses: actions/setup-node@v6
|
||||||
with:
|
with:
|
||||||
node-version: '24.x'
|
node-version: '24.x'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
@@ -79,7 +79,7 @@ jobs:
|
|||||||
echo "capitalised=${VERSION_TYPE@u}" >> $GITHUB_OUTPUT
|
echo "capitalised=${VERSION_TYPE@u}" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Create Pull Request
|
- name: Create Pull Request
|
||||||
uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e
|
uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8.1.0
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.PR_GH_TOKEN }}
|
token: ${{ secrets.PR_GH_TOKEN }}
|
||||||
commit-message: '[release] Increment desktop-ui to ${{ steps.bump-version.outputs.NEW_VERSION }}'
|
commit-message: '[release] Increment desktop-ui to ${{ steps.bump-version.outputs.NEW_VERSION }}'
|
||||||
|
|||||||
12
.github/workflows/weekly-docs-check.yaml
vendored
@@ -22,18 +22,18 @@ jobs:
|
|||||||
timeout-minutes: 45
|
timeout-minutes: 45
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 50
|
||||||
ref: main
|
ref: main
|
||||||
|
|
||||||
- name: Install pnpm
|
- name: Install pnpm
|
||||||
uses: pnpm/action-setup@v4
|
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
||||||
with:
|
with:
|
||||||
version: 10
|
version: 10
|
||||||
|
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v6
|
||||||
with:
|
with:
|
||||||
node-version: '20'
|
node-version: '20'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
@@ -49,7 +49,7 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Run Claude Documentation Review
|
- name: Run Claude Documentation Review
|
||||||
uses: anthropics/claude-code-action@v1.0.6
|
uses: anthropics/claude-code-action@ff34ce0ff04a470bd3fa56c1ef391c8f1c19f8e9 # v1.0.38
|
||||||
with:
|
with:
|
||||||
prompt: |
|
prompt: |
|
||||||
Is all documentation still 100% accurate?
|
Is all documentation still 100% accurate?
|
||||||
@@ -130,7 +130,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Create or Update Pull Request
|
- name: Create or Update Pull Request
|
||||||
if: steps.check_changes.outputs.has_changes == 'true'
|
if: steps.check_changes.outputs.has_changes == 'true'
|
||||||
uses: peter-evans/create-pull-request@v7
|
uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8.1.0
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.PR_GH_TOKEN }}
|
token: ${{ secrets.PR_GH_TOKEN }}
|
||||||
commit-message: 'docs: weekly documentation accuracy update'
|
commit-message: 'docs: weekly documentation accuracy update'
|
||||||
|
|||||||
24
.pinact.yaml
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
# pinact configuration
|
||||||
|
# https://github.com/suzuki-shunsuke/pinact
|
||||||
|
version: 3
|
||||||
|
|
||||||
|
files:
|
||||||
|
- pattern: .github/workflows/*.yaml
|
||||||
|
- pattern: .github/actions/**/*.yaml
|
||||||
|
|
||||||
|
# Actions that don't need SHA pinning (official GitHub actions are trusted)
|
||||||
|
ignore_actions:
|
||||||
|
- name: actions/cache
|
||||||
|
ref: v5
|
||||||
|
- name: actions/checkout
|
||||||
|
ref: v6
|
||||||
|
- name: actions/setup-node
|
||||||
|
ref: v6
|
||||||
|
- name: actions/setup-python
|
||||||
|
ref: v6
|
||||||
|
- name: actions/upload-artifact
|
||||||
|
ref: v6
|
||||||
|
- name: actions/download-artifact
|
||||||
|
ref: v7
|
||||||
|
- name: actions/github-script
|
||||||
|
ref: v8
|
||||||
@@ -8,3 +8,6 @@ rules:
|
|||||||
line-length: disable
|
line-length: disable
|
||||||
document-start: disable
|
document-start: disable
|
||||||
truthy: disable
|
truthy: disable
|
||||||
|
comments:
|
||||||
|
min-spaces-from-content: 1
|
||||||
|
|
||||||
@@ -5,7 +5,7 @@ import * as fs from 'fs'
|
|||||||
|
|
||||||
import type { LGraphNode, LGraph } from '../../src/lib/litegraph/src/litegraph'
|
import type { LGraphNode, LGraph } from '../../src/lib/litegraph/src/litegraph'
|
||||||
import type { NodeId } from '../../src/platform/workflow/validation/schemas/workflowSchema'
|
import type { NodeId } from '../../src/platform/workflow/validation/schemas/workflowSchema'
|
||||||
import type { KeyCombo } from '../../src/schemas/keyBindingSchema'
|
import type { KeyCombo } from '../../src/platform/keybindings'
|
||||||
import type { useWorkspaceStore } from '../../src/stores/workspaceStore'
|
import type { useWorkspaceStore } from '../../src/stores/workspaceStore'
|
||||||
import { NodeBadgeMode } from '../../src/types/nodeSource'
|
import { NodeBadgeMode } from '../../src/types/nodeSource'
|
||||||
import { ComfyActionbar } from '../helpers/actionbar'
|
import { ComfyActionbar } from '../helpers/actionbar'
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { webSocketFixture } from '../fixtures/ws.ts'
|
|||||||
|
|
||||||
const test = mergeTests(comfyPageFixture, webSocketFixture)
|
const test = mergeTests(comfyPageFixture, webSocketFixture)
|
||||||
|
|
||||||
test.describe('Actionbar', () => {
|
test.describe('Actionbar', { tag: '@ui' }, () => {
|
||||||
test.beforeEach(async ({ comfyPage }) => {
|
test.beforeEach(async ({ comfyPage }) => {
|
||||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Top')
|
await comfyPage.setSetting('Comfy.UseNewMenu', 'Top')
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { expect } from '@playwright/test'
|
|||||||
|
|
||||||
import { comfyPageFixture as test } from '../fixtures/ComfyPage'
|
import { comfyPageFixture as test } from '../fixtures/ComfyPage'
|
||||||
|
|
||||||
test.describe('Bottom Panel Shortcuts', () => {
|
test.describe('Bottom Panel Shortcuts', { tag: '@ui' }, () => {
|
||||||
test.beforeEach(async ({ comfyPage }) => {
|
test.beforeEach(async ({ comfyPage }) => {
|
||||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Top')
|
await comfyPage.setSetting('Comfy.UseNewMenu', 'Top')
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { expect } from '@playwright/test'
|
|||||||
|
|
||||||
import { comfyPageFixture as test } from '../fixtures/ComfyPage'
|
import { comfyPageFixture as test } from '../fixtures/ComfyPage'
|
||||||
|
|
||||||
test.describe('Browser tab title', () => {
|
test.describe('Browser tab title', { tag: '@smoke' }, () => {
|
||||||
test.describe('Beta Menu', () => {
|
test.describe('Beta Menu', () => {
|
||||||
test.beforeEach(async ({ comfyPage }) => {
|
test.beforeEach(async ({ comfyPage }) => {
|
||||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Top')
|
await comfyPage.setSetting('Comfy.UseNewMenu', 'Top')
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ test.beforeEach(async ({ comfyPage }) => {
|
|||||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
|
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
|
||||||
})
|
})
|
||||||
|
|
||||||
test.describe('Change Tracker', () => {
|
test.describe('Change Tracker', { tag: '@workflow' }, () => {
|
||||||
test.describe('Undo/Redo', () => {
|
test.describe('Undo/Redo', () => {
|
||||||
test.beforeEach(async ({ comfyPage }) => {
|
test.beforeEach(async ({ comfyPage }) => {
|
||||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Top')
|
await comfyPage.setSetting('Comfy.UseNewMenu', 'Top')
|
||||||
|
|||||||
@@ -151,7 +151,7 @@ const customColorPalettes: Record<string, Palette> = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
test.describe('Color Palette', () => {
|
test.describe('Color Palette', { tag: ['@screenshot', '@settings'] }, () => {
|
||||||
test('Can show custom color palette', async ({ comfyPage }) => {
|
test('Can show custom color palette', async ({ comfyPage }) => {
|
||||||
await comfyPage.setSetting('Comfy.CustomColorPalettes', customColorPalettes)
|
await comfyPage.setSetting('Comfy.CustomColorPalettes', customColorPalettes)
|
||||||
// Reload to apply the new setting. Setting Comfy.CustomColorPalettes directly
|
// Reload to apply the new setting. Setting Comfy.CustomColorPalettes directly
|
||||||
@@ -194,104 +194,110 @@ test.describe('Color Palette', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test.describe('Node Color Adjustments', () => {
|
test.describe(
|
||||||
test.beforeEach(async ({ comfyPage }) => {
|
'Node Color Adjustments',
|
||||||
await comfyPage.loadWorkflow('nodes/every_node_color')
|
{ tag: ['@screenshot', '@settings'] },
|
||||||
})
|
() => {
|
||||||
|
|
||||||
test('should adjust opacity via node opacity setting', async ({
|
|
||||||
comfyPage
|
|
||||||
}) => {
|
|
||||||
await comfyPage.setSetting('Comfy.Node.Opacity', 0.5)
|
|
||||||
|
|
||||||
// Drag mouse to force canvas to redraw
|
|
||||||
await comfyPage.page.mouse.move(0, 0)
|
|
||||||
|
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot('node-opacity-0.5.png')
|
|
||||||
|
|
||||||
await comfyPage.setSetting('Comfy.Node.Opacity', 1.0)
|
|
||||||
|
|
||||||
await comfyPage.page.mouse.move(8, 8)
|
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot('node-opacity-1.png')
|
|
||||||
})
|
|
||||||
|
|
||||||
test('should persist color adjustments when changing themes', async ({
|
|
||||||
comfyPage
|
|
||||||
}) => {
|
|
||||||
await comfyPage.setSetting('Comfy.Node.Opacity', 0.2)
|
|
||||||
await comfyPage.setSetting('Comfy.ColorPalette', 'arc')
|
|
||||||
await comfyPage.nextFrame()
|
|
||||||
await comfyPage.page.mouse.move(0, 0)
|
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot(
|
|
||||||
'node-opacity-0.2-arc-theme.png'
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('should not serialize color adjustments in workflow', async ({
|
|
||||||
comfyPage
|
|
||||||
}) => {
|
|
||||||
await comfyPage.setSetting('Comfy.Node.Opacity', 0.5)
|
|
||||||
await comfyPage.setSetting('Comfy.ColorPalette', 'light')
|
|
||||||
await comfyPage.nextFrame()
|
|
||||||
const parsed = await (
|
|
||||||
await comfyPage.page.waitForFunction(
|
|
||||||
() => {
|
|
||||||
const workflow = localStorage.getItem('workflow')
|
|
||||||
if (!workflow) return null
|
|
||||||
try {
|
|
||||||
const data = JSON.parse(workflow)
|
|
||||||
return Array.isArray(data?.nodes) ? data : null
|
|
||||||
} catch {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ timeout: 3000 }
|
|
||||||
)
|
|
||||||
).jsonValue()
|
|
||||||
expect(parsed.nodes).toBeDefined()
|
|
||||||
expect(Array.isArray(parsed.nodes)).toBe(true)
|
|
||||||
for (const node of parsed.nodes) {
|
|
||||||
if (node.bgcolor) expect(node.bgcolor).not.toMatch(/hsla/)
|
|
||||||
if (node.color) expect(node.color).not.toMatch(/hsla/)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
test('should lighten node colors when switching to light theme', async ({
|
|
||||||
comfyPage
|
|
||||||
}) => {
|
|
||||||
await comfyPage.setSetting('Comfy.ColorPalette', 'light')
|
|
||||||
await comfyPage.nextFrame()
|
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot('node-lightened-colors.png')
|
|
||||||
})
|
|
||||||
|
|
||||||
test.describe('Context menu color adjustments', () => {
|
|
||||||
test.beforeEach(async ({ comfyPage }) => {
|
test.beforeEach(async ({ comfyPage }) => {
|
||||||
|
await comfyPage.loadWorkflow('nodes/every_node_color')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should adjust opacity via node opacity setting', async ({
|
||||||
|
comfyPage
|
||||||
|
}) => {
|
||||||
|
await comfyPage.setSetting('Comfy.Node.Opacity', 0.5)
|
||||||
|
|
||||||
|
// Drag mouse to force canvas to redraw
|
||||||
|
await comfyPage.page.mouse.move(0, 0)
|
||||||
|
|
||||||
|
await expect(comfyPage.canvas).toHaveScreenshot('node-opacity-0.5.png')
|
||||||
|
|
||||||
|
await comfyPage.setSetting('Comfy.Node.Opacity', 1.0)
|
||||||
|
|
||||||
|
await comfyPage.page.mouse.move(8, 8)
|
||||||
|
await expect(comfyPage.canvas).toHaveScreenshot('node-opacity-1.png')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should persist color adjustments when changing themes', async ({
|
||||||
|
comfyPage
|
||||||
|
}) => {
|
||||||
|
await comfyPage.setSetting('Comfy.Node.Opacity', 0.2)
|
||||||
|
await comfyPage.setSetting('Comfy.ColorPalette', 'arc')
|
||||||
|
await comfyPage.nextFrame()
|
||||||
|
await comfyPage.page.mouse.move(0, 0)
|
||||||
|
await expect(comfyPage.canvas).toHaveScreenshot(
|
||||||
|
'node-opacity-0.2-arc-theme.png'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should not serialize color adjustments in workflow', async ({
|
||||||
|
comfyPage
|
||||||
|
}) => {
|
||||||
|
await comfyPage.setSetting('Comfy.Node.Opacity', 0.5)
|
||||||
await comfyPage.setSetting('Comfy.ColorPalette', 'light')
|
await comfyPage.setSetting('Comfy.ColorPalette', 'light')
|
||||||
await comfyPage.setSetting('Comfy.Node.Opacity', 0.3)
|
await comfyPage.nextFrame()
|
||||||
const node = await comfyPage.getFirstNodeRef()
|
const parsed = await (
|
||||||
await node?.clickContextMenuOption('Colors')
|
await comfyPage.page.waitForFunction(
|
||||||
|
() => {
|
||||||
|
const workflow = localStorage.getItem('workflow')
|
||||||
|
if (!workflow) return null
|
||||||
|
try {
|
||||||
|
const data = JSON.parse(workflow)
|
||||||
|
return Array.isArray(data?.nodes) ? data : null
|
||||||
|
} catch {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ timeout: 3000 }
|
||||||
|
)
|
||||||
|
).jsonValue()
|
||||||
|
expect(parsed.nodes).toBeDefined()
|
||||||
|
expect(Array.isArray(parsed.nodes)).toBe(true)
|
||||||
|
for (const node of parsed.nodes) {
|
||||||
|
if (node.bgcolor) expect(node.bgcolor).not.toMatch(/hsla/)
|
||||||
|
if (node.color) expect(node.color).not.toMatch(/hsla/)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
test('should persist color adjustments when changing custom node colors', async ({
|
test('should lighten node colors when switching to light theme', async ({
|
||||||
comfyPage
|
comfyPage
|
||||||
}) => {
|
}) => {
|
||||||
await comfyPage.page
|
await comfyPage.setSetting('Comfy.ColorPalette', 'light')
|
||||||
.locator('.litemenu-entry.submenu span:has-text("red")')
|
await comfyPage.nextFrame()
|
||||||
.click()
|
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot(
|
await expect(comfyPage.canvas).toHaveScreenshot(
|
||||||
'node-opacity-0.3-color-changed.png'
|
'node-lightened-colors.png'
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('should persist color adjustments when removing custom node color', async ({
|
test.describe('Context menu color adjustments', () => {
|
||||||
comfyPage
|
test.beforeEach(async ({ comfyPage }) => {
|
||||||
}) => {
|
await comfyPage.setSetting('Comfy.ColorPalette', 'light')
|
||||||
await comfyPage.page
|
await comfyPage.setSetting('Comfy.Node.Opacity', 0.3)
|
||||||
.locator('.litemenu-entry.submenu span:has-text("No color")')
|
const node = await comfyPage.getFirstNodeRef()
|
||||||
.click()
|
await node?.clickContextMenuOption('Colors')
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot(
|
})
|
||||||
'node-opacity-0.3-color-removed.png'
|
|
||||||
)
|
test('should persist color adjustments when changing custom node colors', async ({
|
||||||
|
comfyPage
|
||||||
|
}) => {
|
||||||
|
await comfyPage.page
|
||||||
|
.locator('.litemenu-entry.submenu span:has-text("red")')
|
||||||
|
.click()
|
||||||
|
await expect(comfyPage.canvas).toHaveScreenshot(
|
||||||
|
'node-opacity-0.3-color-changed.png'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should persist color adjustments when removing custom node color', async ({
|
||||||
|
comfyPage
|
||||||
|
}) => {
|
||||||
|
await comfyPage.page
|
||||||
|
.locator('.litemenu-entry.submenu span:has-text("No color")')
|
||||||
|
.click()
|
||||||
|
await expect(comfyPage.canvas).toHaveScreenshot(
|
||||||
|
'node-opacity-0.3-color-removed.png'
|
||||||
|
)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
}
|
||||||
})
|
)
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ test.beforeEach(async ({ comfyPage }) => {
|
|||||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
|
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
|
||||||
})
|
})
|
||||||
|
|
||||||
test.describe('Keybindings', () => {
|
test.describe('Keybindings', { tag: '@keyboard' }, () => {
|
||||||
test('Should execute command', async ({ comfyPage }) => {
|
test('Should execute command', async ({ comfyPage }) => {
|
||||||
await comfyPage.registerCommand('TestCommand', () => {
|
await comfyPage.registerCommand('TestCommand', () => {
|
||||||
window['foo'] = true
|
window['foo'] = true
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ test.beforeEach(async ({ comfyPage }) => {
|
|||||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
|
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
|
||||||
})
|
})
|
||||||
|
|
||||||
test.describe('Copy Paste', () => {
|
test.describe('Copy Paste', { tag: ['@screenshot', '@workflow'] }, () => {
|
||||||
test('Can copy and paste node', async ({ comfyPage }) => {
|
test('Can copy and paste node', async ({ comfyPage }) => {
|
||||||
await comfyPage.clickEmptyLatentNode()
|
await comfyPage.clickEmptyLatentNode()
|
||||||
await comfyPage.page.mouse.move(10, 10)
|
await comfyPage.page.mouse.move(10, 10)
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ async function verifyCustomIconSvg(iconElement: Locator) {
|
|||||||
expect(decodedSvg).toContain("<svg xmlns='http://www.w3.org/2000/svg'")
|
expect(decodedSvg).toContain("<svg xmlns='http://www.w3.org/2000/svg'")
|
||||||
}
|
}
|
||||||
|
|
||||||
test.describe('Custom Icons', () => {
|
test.describe('Custom Icons', { tag: '@settings' }, () => {
|
||||||
test.beforeEach(async ({ comfyPage }) => {
|
test.beforeEach(async ({ comfyPage }) => {
|
||||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Top')
|
await comfyPage.setSetting('Comfy.UseNewMenu', 'Top')
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import type { Locator } from '@playwright/test'
|
import type { Locator } from '@playwright/test'
|
||||||
import { expect } from '@playwright/test'
|
import { expect } from '@playwright/test'
|
||||||
|
|
||||||
import type { Keybinding } from '../../src/schemas/keyBindingSchema'
|
import type { Keybinding } from '../../src/platform/keybindings'
|
||||||
import { comfyPageFixture as test } from '../fixtures/ComfyPage'
|
import { comfyPageFixture as test } from '../fixtures/ComfyPage'
|
||||||
|
|
||||||
test.beforeEach(async ({ comfyPage }) => {
|
test.beforeEach(async ({ comfyPage }) => {
|
||||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
|
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
|
||||||
})
|
})
|
||||||
|
|
||||||
test.describe('Load workflow warning', () => {
|
test.describe('Load workflow warning', { tag: '@ui' }, () => {
|
||||||
test('Should display a warning when loading a workflow with missing nodes', async ({
|
test('Should display a warning when loading a workflow with missing nodes', async ({
|
||||||
comfyPage
|
comfyPage
|
||||||
}) => {
|
}) => {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ test.beforeEach(async ({ comfyPage }) => {
|
|||||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
|
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
|
||||||
})
|
})
|
||||||
|
|
||||||
test.describe('DOM Widget', () => {
|
test.describe('DOM Widget', { tag: '@widget' }, () => {
|
||||||
test('Collapsed multiline textarea is not visible', async ({ comfyPage }) => {
|
test('Collapsed multiline textarea is not visible', async ({ comfyPage }) => {
|
||||||
await comfyPage.loadWorkflow('widgets/collapsed_multiline')
|
await comfyPage.loadWorkflow('widgets/collapsed_multiline')
|
||||||
const textareaWidget = comfyPage.page.locator('.comfy-multiline-input')
|
const textareaWidget = comfyPage.page.locator('.comfy-multiline-input')
|
||||||
@@ -29,12 +29,16 @@ test.describe('DOM Widget', () => {
|
|||||||
await expect(lastMultiline).not.toBeVisible()
|
await expect(lastMultiline).not.toBeVisible()
|
||||||
})
|
})
|
||||||
|
|
||||||
test('Position update when entering focus mode', async ({ comfyPage }) => {
|
test(
|
||||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Top')
|
'Position update when entering focus mode',
|
||||||
await comfyPage.executeCommand('Workspace.ToggleFocusMode')
|
{ tag: '@screenshot' },
|
||||||
await comfyPage.nextFrame()
|
async ({ comfyPage }) => {
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot('focus-mode-on.png')
|
await comfyPage.setSetting('Comfy.UseNewMenu', 'Top')
|
||||||
})
|
await comfyPage.executeCommand('Workspace.ToggleFocusMode')
|
||||||
|
await comfyPage.nextFrame()
|
||||||
|
await expect(comfyPage.canvas).toHaveScreenshot('focus-mode-on.png')
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
// No DOM widget should be created by creation of interim LGraphNode objects.
|
// No DOM widget should be created by creation of interim LGraphNode objects.
|
||||||
test('Copy node with DOM widget by dragging + alt', async ({ comfyPage }) => {
|
test('Copy node with DOM widget by dragging + alt', async ({ comfyPage }) => {
|
||||||
|
|||||||
@@ -6,40 +6,48 @@ test.beforeEach(async ({ comfyPage }) => {
|
|||||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
|
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
|
||||||
})
|
})
|
||||||
|
|
||||||
test.describe('Execution', () => {
|
test.describe('Execution', { tag: ['@smoke', '@workflow'] }, () => {
|
||||||
test('Report error on unconnected slot', async ({ comfyPage }) => {
|
test(
|
||||||
await comfyPage.disconnectEdge()
|
'Report error on unconnected slot',
|
||||||
await comfyPage.clickEmptySpace()
|
{ tag: '@screenshot' },
|
||||||
|
async ({ comfyPage }) => {
|
||||||
|
await comfyPage.disconnectEdge()
|
||||||
|
await comfyPage.clickEmptySpace()
|
||||||
|
|
||||||
await comfyPage.executeCommand('Comfy.QueuePrompt')
|
await comfyPage.executeCommand('Comfy.QueuePrompt')
|
||||||
await expect(comfyPage.page.locator('.comfy-error-report')).toBeVisible()
|
await expect(comfyPage.page.locator('.comfy-error-report')).toBeVisible()
|
||||||
await comfyPage.page.locator('.p-dialog-close-button').click()
|
await comfyPage.page.locator('.p-dialog-close-button').click()
|
||||||
await comfyPage.page.locator('.comfy-error-report').waitFor({
|
await comfyPage.page.locator('.comfy-error-report').waitFor({
|
||||||
state: 'hidden'
|
state: 'hidden'
|
||||||
})
|
})
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot(
|
await expect(comfyPage.canvas).toHaveScreenshot(
|
||||||
'execution-error-unconnected-slot.png'
|
'execution-error-unconnected-slot.png'
|
||||||
)
|
)
|
||||||
})
|
}
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
test.describe('Execute to selected output nodes', () => {
|
test.describe(
|
||||||
test('Execute to selected output nodes', async ({ comfyPage }) => {
|
'Execute to selected output nodes',
|
||||||
await comfyPage.loadWorkflow('execution/partial_execution')
|
{ tag: ['@smoke', '@workflow'] },
|
||||||
const input = await comfyPage.getNodeRefById(3)
|
() => {
|
||||||
const output1 = await comfyPage.getNodeRefById(1)
|
test('Execute to selected output nodes', async ({ comfyPage }) => {
|
||||||
const output2 = await comfyPage.getNodeRefById(4)
|
await comfyPage.loadWorkflow('execution/partial_execution')
|
||||||
expect(await (await input.getWidget(0)).getValue()).toBe('foo')
|
const input = await comfyPage.getNodeRefById(3)
|
||||||
expect(await (await output1.getWidget(0)).getValue()).toBe('')
|
const output1 = await comfyPage.getNodeRefById(1)
|
||||||
expect(await (await output2.getWidget(0)).getValue()).toBe('')
|
const output2 = await comfyPage.getNodeRefById(4)
|
||||||
|
|
||||||
await output1.click('title')
|
|
||||||
|
|
||||||
await comfyPage.executeCommand('Comfy.QueueSelectedOutputNodes')
|
|
||||||
await expect(async () => {
|
|
||||||
expect(await (await input.getWidget(0)).getValue()).toBe('foo')
|
expect(await (await input.getWidget(0)).getValue()).toBe('foo')
|
||||||
expect(await (await output1.getWidget(0)).getValue()).toBe('foo')
|
expect(await (await output1.getWidget(0)).getValue()).toBe('')
|
||||||
expect(await (await output2.getWidget(0)).getValue()).toBe('')
|
expect(await (await output2.getWidget(0)).getValue()).toBe('')
|
||||||
}).toPass({ timeout: 2_000 })
|
|
||||||
})
|
await output1.click('title')
|
||||||
})
|
|
||||||
|
await comfyPage.executeCommand('Comfy.QueueSelectedOutputNodes')
|
||||||
|
await expect(async () => {
|
||||||
|
expect(await (await input.getWidget(0)).getValue()).toBe('foo')
|
||||||
|
expect(await (await output1.getWidget(0)).getValue()).toBe('foo')
|
||||||
|
expect(await (await output2.getWidget(0)).getValue()).toBe('')
|
||||||
|
}).toPass({ timeout: 2_000 })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ test.beforeEach(async ({ comfyPage }) => {
|
|||||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
|
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
|
||||||
})
|
})
|
||||||
|
|
||||||
test.describe('Feature Flags', () => {
|
test.describe('Feature Flags', { tag: ['@slow', '@settings'] }, () => {
|
||||||
test('Client and server exchange feature flags on connection', async ({
|
test('Client and server exchange feature flags on connection', async ({
|
||||||
comfyPage
|
comfyPage
|
||||||
}) => {
|
}) => {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ test.beforeEach(async ({ comfyPage }) => {
|
|||||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
|
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
|
||||||
})
|
})
|
||||||
|
|
||||||
test.describe('Graph', () => {
|
test.describe('Graph', { tag: ['@smoke', '@canvas'] }, () => {
|
||||||
// Should be able to fix link input slot index after swap the input order
|
// Should be able to fix link input slot index after swap the input order
|
||||||
// Ref: https://github.com/Comfy-Org/ComfyUI_frontend/issues/3348
|
// Ref: https://github.com/Comfy-Org/ComfyUI_frontend/issues/3348
|
||||||
test('Fix link input slots', async ({ comfyPage }) => {
|
test('Fix link input slots', async ({ comfyPage }) => {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ test.beforeEach(async ({ comfyPage }) => {
|
|||||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
|
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
|
||||||
})
|
})
|
||||||
|
|
||||||
test.describe('Graph Canvas Menu', () => {
|
test.describe('Graph Canvas Menu', { tag: ['@screenshot', '@canvas'] }, () => {
|
||||||
test.beforeEach(async ({ comfyPage }) => {
|
test.beforeEach(async ({ comfyPage }) => {
|
||||||
// Set link render mode to spline to make sure it's not affected by other tests'
|
// Set link render mode to spline to make sure it's not affected by other tests'
|
||||||
// side effects.
|
// side effects.
|
||||||
@@ -15,29 +15,33 @@ test.describe('Graph Canvas Menu', () => {
|
|||||||
await comfyPage.setSetting('Comfy.Graph.CanvasMenu', true)
|
await comfyPage.setSetting('Comfy.Graph.CanvasMenu', true)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('Can toggle link visibility', async ({ comfyPage }) => {
|
test(
|
||||||
const button = comfyPage.page.getByTestId('toggle-link-visibility-button')
|
'Can toggle link visibility',
|
||||||
await button.click()
|
{ tag: '@screenshot' },
|
||||||
await comfyPage.nextFrame()
|
async ({ comfyPage }) => {
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot(
|
const button = comfyPage.page.getByTestId('toggle-link-visibility-button')
|
||||||
'canvas-with-hidden-links.png'
|
await button.click()
|
||||||
)
|
await comfyPage.nextFrame()
|
||||||
const hiddenLinkRenderMode = await comfyPage.page.evaluate(() => {
|
await expect(comfyPage.canvas).toHaveScreenshot(
|
||||||
return window['LiteGraph'].HIDDEN_LINK
|
'canvas-with-hidden-links.png'
|
||||||
})
|
)
|
||||||
expect(await comfyPage.getSetting('Comfy.LinkRenderMode')).toBe(
|
const hiddenLinkRenderMode = await comfyPage.page.evaluate(() => {
|
||||||
hiddenLinkRenderMode
|
return window['LiteGraph'].HIDDEN_LINK
|
||||||
)
|
})
|
||||||
|
expect(await comfyPage.getSetting('Comfy.LinkRenderMode')).toBe(
|
||||||
|
hiddenLinkRenderMode
|
||||||
|
)
|
||||||
|
|
||||||
await button.click()
|
await button.click()
|
||||||
await comfyPage.nextFrame()
|
await comfyPage.nextFrame()
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot(
|
await expect(comfyPage.canvas).toHaveScreenshot(
|
||||||
'canvas-with-visible-links.png'
|
'canvas-with-visible-links.png'
|
||||||
)
|
)
|
||||||
expect(await comfyPage.getSetting('Comfy.LinkRenderMode')).not.toBe(
|
expect(await comfyPage.getSetting('Comfy.LinkRenderMode')).not.toBe(
|
||||||
hiddenLinkRenderMode
|
hiddenLinkRenderMode
|
||||||
)
|
)
|
||||||
})
|
}
|
||||||
|
)
|
||||||
|
|
||||||
test('Toggle minimap button is clickable and has correct test id', async ({
|
test('Toggle minimap button is clickable and has correct test id', async ({
|
||||||
comfyPage
|
comfyPage
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ test.beforeEach(async ({ comfyPage }) => {
|
|||||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
|
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
|
||||||
})
|
})
|
||||||
|
|
||||||
test.describe('Group Node', () => {
|
test.describe('Group Node', { tag: '@node' }, () => {
|
||||||
test.describe('Node library sidebar', () => {
|
test.describe('Node library sidebar', () => {
|
||||||
const groupNodeName = 'DefautWorkflowGroupNode'
|
const groupNodeName = 'DefautWorkflowGroupNode'
|
||||||
const groupNodeCategory = 'group nodes>workflow'
|
const groupNodeCategory = 'group nodes>workflow'
|
||||||
@@ -89,16 +89,20 @@ test.describe('Group Node', () => {
|
|||||||
// does not have a v-model on the query, so we cannot observe the raw
|
// does not have a v-model on the query, so we cannot observe the raw
|
||||||
// query update, and thus cannot set the spinning state between the raw query
|
// query update, and thus cannot set the spinning state between the raw query
|
||||||
// update and the debounced search update.
|
// update and the debounced search update.
|
||||||
test.skip('Can be added to canvas using search', async ({ comfyPage }) => {
|
test.skip(
|
||||||
const groupNodeName = 'DefautWorkflowGroupNode'
|
'Can be added to canvas using search',
|
||||||
await comfyPage.convertAllNodesToGroupNode(groupNodeName)
|
{ tag: '@screenshot' },
|
||||||
await comfyPage.doubleClickCanvas()
|
async ({ comfyPage }) => {
|
||||||
await comfyPage.nextFrame()
|
const groupNodeName = 'DefautWorkflowGroupNode'
|
||||||
await comfyPage.searchBox.fillAndSelectFirstNode(groupNodeName)
|
await comfyPage.convertAllNodesToGroupNode(groupNodeName)
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot(
|
await comfyPage.doubleClickCanvas()
|
||||||
'group-node-copy-added-from-search.png'
|
await comfyPage.nextFrame()
|
||||||
)
|
await comfyPage.searchBox.fillAndSelectFirstNode(groupNodeName)
|
||||||
})
|
await expect(comfyPage.canvas).toHaveScreenshot(
|
||||||
|
'group-node-copy-added-from-search.png'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
test('Displays tooltip on title hover', async ({ comfyPage }) => {
|
test('Displays tooltip on title hover', async ({ comfyPage }) => {
|
||||||
await comfyPage.setSetting('Comfy.EnableTooltips', true)
|
await comfyPage.setSetting('Comfy.EnableTooltips', true)
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ test.beforeEach(async ({ comfyPage }) => {
|
|||||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
|
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
|
||||||
})
|
})
|
||||||
|
|
||||||
test.describe('Item Interaction', () => {
|
test.describe('Item Interaction', { tag: ['@screenshot', '@node'] }, () => {
|
||||||
test('Can select/delete all items', async ({ comfyPage }) => {
|
test('Can select/delete all items', async ({ comfyPage }) => {
|
||||||
await comfyPage.loadWorkflow('groups/mixed_graph_items')
|
await comfyPage.loadWorkflow('groups/mixed_graph_items')
|
||||||
await comfyPage.canvas.press('Control+a')
|
await comfyPage.canvas.press('Control+a')
|
||||||
@@ -60,13 +60,17 @@ test.describe('Node Interaction', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test('@2x Can highlight selected', async ({ comfyPage }) => {
|
test(
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot('default.png')
|
'@2x Can highlight selected',
|
||||||
await comfyPage.clickTextEncodeNode1()
|
{ tag: '@screenshot' },
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot('selected-node1.png')
|
async ({ comfyPage }) => {
|
||||||
await comfyPage.clickTextEncodeNode2()
|
await expect(comfyPage.canvas).toHaveScreenshot('default.png')
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot('selected-node2.png')
|
await comfyPage.clickTextEncodeNode1()
|
||||||
})
|
await expect(comfyPage.canvas).toHaveScreenshot('selected-node1.png')
|
||||||
|
await comfyPage.clickTextEncodeNode2()
|
||||||
|
await expect(comfyPage.canvas).toHaveScreenshot('selected-node2.png')
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
const dragSelectNodes = async (
|
const dragSelectNodes = async (
|
||||||
comfyPage: ComfyPage,
|
comfyPage: ComfyPage,
|
||||||
@@ -150,12 +154,12 @@ test.describe('Node Interaction', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test('Can drag node', async ({ comfyPage }) => {
|
test('Can drag node', { tag: '@screenshot' }, async ({ comfyPage }) => {
|
||||||
await comfyPage.dragNode2()
|
await comfyPage.dragNode2()
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot('dragged-node1.png')
|
await expect(comfyPage.canvas).toHaveScreenshot('dragged-node1.png')
|
||||||
})
|
})
|
||||||
|
|
||||||
test.describe('Edge Interaction', () => {
|
test.describe('Edge Interaction', { tag: '@screenshot' }, () => {
|
||||||
test.beforeEach(async ({ comfyPage }) => {
|
test.beforeEach(async ({ comfyPage }) => {
|
||||||
await comfyPage.setSetting('Comfy.LinkRelease.Action', 'no action')
|
await comfyPage.setSetting('Comfy.LinkRelease.Action', 'no action')
|
||||||
await comfyPage.setSetting('Comfy.LinkRelease.ActionShift', 'no action')
|
await comfyPage.setSetting('Comfy.LinkRelease.ActionShift', 'no action')
|
||||||
@@ -222,12 +226,18 @@ test.describe('Node Interaction', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test('Can adjust widget value', async ({ comfyPage }) => {
|
test(
|
||||||
await comfyPage.adjustWidgetValue()
|
'Can adjust widget value',
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot('adjusted-widget-value.png')
|
{ tag: '@screenshot' },
|
||||||
})
|
async ({ comfyPage }) => {
|
||||||
|
await comfyPage.adjustWidgetValue()
|
||||||
|
await expect(comfyPage.canvas).toHaveScreenshot(
|
||||||
|
'adjusted-widget-value.png'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
test('Link snap to slot', async ({ comfyPage }) => {
|
test('Link snap to slot', { tag: '@screenshot' }, async ({ comfyPage }) => {
|
||||||
await comfyPage.loadWorkflow('links/snap_to_slot')
|
await comfyPage.loadWorkflow('links/snap_to_slot')
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot('snap_to_slot.png')
|
await expect(comfyPage.canvas).toHaveScreenshot('snap_to_slot.png')
|
||||||
|
|
||||||
@@ -244,57 +254,67 @@ test.describe('Node Interaction', () => {
|
|||||||
await expect(comfyPage.canvas).toHaveScreenshot('snap_to_slot_linked.png')
|
await expect(comfyPage.canvas).toHaveScreenshot('snap_to_slot_linked.png')
|
||||||
})
|
})
|
||||||
|
|
||||||
test('Can batch move links by drag with shift', async ({ comfyPage }) => {
|
test(
|
||||||
await comfyPage.loadWorkflow('links/batch_move_links')
|
'Can batch move links by drag with shift',
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot('batch_move_links.png')
|
{ tag: '@screenshot' },
|
||||||
|
async ({ comfyPage }) => {
|
||||||
|
await comfyPage.loadWorkflow('links/batch_move_links')
|
||||||
|
await expect(comfyPage.canvas).toHaveScreenshot('batch_move_links.png')
|
||||||
|
|
||||||
const outputSlot1Pos = {
|
const outputSlot1Pos = {
|
||||||
x: 304,
|
x: 304,
|
||||||
y: 127
|
y: 127
|
||||||
|
}
|
||||||
|
const outputSlot2Pos = {
|
||||||
|
x: 307,
|
||||||
|
y: 310
|
||||||
|
}
|
||||||
|
|
||||||
|
await comfyPage.page.keyboard.down('Shift')
|
||||||
|
await comfyPage.dragAndDrop(outputSlot1Pos, outputSlot2Pos)
|
||||||
|
await comfyPage.page.keyboard.up('Shift')
|
||||||
|
|
||||||
|
await expect(comfyPage.canvas).toHaveScreenshot(
|
||||||
|
'batch_move_links_moved.png'
|
||||||
|
)
|
||||||
}
|
}
|
||||||
const outputSlot2Pos = {
|
)
|
||||||
x: 307,
|
|
||||||
y: 310
|
test(
|
||||||
|
'Can batch disconnect links with ctrl+alt+click',
|
||||||
|
{ tag: '@screenshot' },
|
||||||
|
async ({ comfyPage }) => {
|
||||||
|
const loadCheckpointClipSlotPos = {
|
||||||
|
x: 332,
|
||||||
|
y: 508
|
||||||
|
}
|
||||||
|
await comfyPage.canvas.click({
|
||||||
|
modifiers: ['Control', 'Alt'],
|
||||||
|
position: loadCheckpointClipSlotPos
|
||||||
|
})
|
||||||
|
await comfyPage.nextFrame()
|
||||||
|
await expect(comfyPage.canvas).toHaveScreenshot(
|
||||||
|
'batch-disconnect-links-disconnected.png'
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
)
|
||||||
|
|
||||||
await comfyPage.page.keyboard.down('Shift')
|
test(
|
||||||
await comfyPage.dragAndDrop(outputSlot1Pos, outputSlot2Pos)
|
'Can toggle dom widget node open/closed',
|
||||||
await comfyPage.page.keyboard.up('Shift')
|
{ tag: '@screenshot' },
|
||||||
|
async ({ comfyPage }) => {
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot(
|
await expect(comfyPage.canvas).toHaveScreenshot('default.png')
|
||||||
'batch_move_links_moved.png'
|
await comfyPage.clickTextEncodeNodeToggler()
|
||||||
)
|
await expect(comfyPage.canvas).toHaveScreenshot(
|
||||||
})
|
'text-encode-toggled-off.png'
|
||||||
|
)
|
||||||
test('Can batch disconnect links with ctrl+alt+click', async ({
|
await comfyPage.delay(1000)
|
||||||
comfyPage
|
await comfyPage.clickTextEncodeNodeToggler()
|
||||||
}) => {
|
await expect(comfyPage.canvas).toHaveScreenshot(
|
||||||
const loadCheckpointClipSlotPos = {
|
'text-encode-toggled-back-open.png'
|
||||||
x: 332,
|
)
|
||||||
y: 508
|
|
||||||
}
|
}
|
||||||
await comfyPage.canvas.click({
|
)
|
||||||
modifiers: ['Control', 'Alt'],
|
|
||||||
position: loadCheckpointClipSlotPos
|
|
||||||
})
|
|
||||||
await comfyPage.nextFrame()
|
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot(
|
|
||||||
'batch-disconnect-links-disconnected.png'
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('Can toggle dom widget node open/closed', async ({ comfyPage }) => {
|
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot('default.png')
|
|
||||||
await comfyPage.clickTextEncodeNodeToggler()
|
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot(
|
|
||||||
'text-encode-toggled-off.png'
|
|
||||||
)
|
|
||||||
await comfyPage.delay(1000)
|
|
||||||
await comfyPage.clickTextEncodeNodeToggler()
|
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot(
|
|
||||||
'text-encode-toggled-back-open.png'
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('Can close prompt dialog with canvas click (number widget)', async ({
|
test('Can close prompt dialog with canvas click (number widget)', async ({
|
||||||
comfyPage
|
comfyPage
|
||||||
@@ -341,19 +361,23 @@ test.describe('Node Interaction', () => {
|
|||||||
await expect(legacyPrompt).toBeHidden()
|
await expect(legacyPrompt).toBeHidden()
|
||||||
})
|
})
|
||||||
|
|
||||||
test('Can double click node title to edit', async ({ comfyPage }) => {
|
test(
|
||||||
await comfyPage.loadWorkflow('nodes/single_ksampler')
|
'Can double click node title to edit',
|
||||||
await comfyPage.canvas.dblclick({
|
{ tag: '@screenshot' },
|
||||||
position: {
|
async ({ comfyPage }) => {
|
||||||
x: 50,
|
await comfyPage.loadWorkflow('nodes/single_ksampler')
|
||||||
y: 10
|
await comfyPage.canvas.dblclick({
|
||||||
},
|
position: {
|
||||||
delay: 5
|
x: 50,
|
||||||
})
|
y: 10
|
||||||
await comfyPage.page.keyboard.type('Hello World')
|
},
|
||||||
await comfyPage.page.keyboard.press('Enter')
|
delay: 5
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot('node-title-edited.png')
|
})
|
||||||
})
|
await comfyPage.page.keyboard.type('Hello World')
|
||||||
|
await comfyPage.page.keyboard.press('Enter')
|
||||||
|
await expect(comfyPage.canvas).toHaveScreenshot('node-title-edited.png')
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
test('Double click node body does not trigger edit', async ({
|
test('Double click node body does not trigger edit', async ({
|
||||||
comfyPage
|
comfyPage
|
||||||
@@ -369,29 +393,41 @@ test.describe('Node Interaction', () => {
|
|||||||
expect(await comfyPage.page.locator('.node-title-editor').count()).toBe(0)
|
expect(await comfyPage.page.locator('.node-title-editor').count()).toBe(0)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('Can group selected nodes', async ({ comfyPage }) => {
|
test(
|
||||||
await comfyPage.setSetting('Comfy.GroupSelectedNodes.Padding', 10)
|
'Can group selected nodes',
|
||||||
await comfyPage.select2Nodes()
|
{ tag: '@screenshot' },
|
||||||
await comfyPage.page.keyboard.down('Control')
|
async ({ comfyPage }) => {
|
||||||
await comfyPage.page.keyboard.press('KeyG')
|
await comfyPage.setSetting('Comfy.GroupSelectedNodes.Padding', 10)
|
||||||
await comfyPage.page.keyboard.up('Control')
|
await comfyPage.select2Nodes()
|
||||||
await comfyPage.nextFrame()
|
await comfyPage.page.keyboard.down('Control')
|
||||||
// Confirm group title
|
await comfyPage.page.keyboard.press('KeyG')
|
||||||
await comfyPage.page.keyboard.press('Enter')
|
await comfyPage.page.keyboard.up('Control')
|
||||||
await comfyPage.nextFrame()
|
await comfyPage.nextFrame()
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot('group-selected-nodes.png')
|
// Confirm group title
|
||||||
})
|
await comfyPage.page.keyboard.press('Enter')
|
||||||
|
await comfyPage.nextFrame()
|
||||||
|
await expect(comfyPage.canvas).toHaveScreenshot(
|
||||||
|
'group-selected-nodes.png'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
test('Can fit group to contents', async ({ comfyPage }) => {
|
test(
|
||||||
await comfyPage.loadWorkflow('groups/oversized_group')
|
'Can fit group to contents',
|
||||||
await comfyPage.ctrlA()
|
{ tag: '@screenshot' },
|
||||||
await comfyPage.nextFrame()
|
async ({ comfyPage }) => {
|
||||||
await comfyPage.executeCommand('Comfy.Graph.FitGroupToContents')
|
await comfyPage.loadWorkflow('groups/oversized_group')
|
||||||
await comfyPage.nextFrame()
|
await comfyPage.ctrlA()
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot('group-fit-to-contents.png')
|
await comfyPage.nextFrame()
|
||||||
})
|
await comfyPage.executeCommand('Comfy.Graph.FitGroupToContents')
|
||||||
|
await comfyPage.nextFrame()
|
||||||
|
await expect(comfyPage.canvas).toHaveScreenshot(
|
||||||
|
'group-fit-to-contents.png'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
test('Can pin/unpin nodes', async ({ comfyPage }) => {
|
test('Can pin/unpin nodes', { tag: '@screenshot' }, async ({ comfyPage }) => {
|
||||||
await comfyPage.select2Nodes()
|
await comfyPage.select2Nodes()
|
||||||
await comfyPage.executeCommand('Comfy.Canvas.ToggleSelectedNodes.Pin')
|
await comfyPage.executeCommand('Comfy.Canvas.ToggleSelectedNodes.Pin')
|
||||||
await comfyPage.nextFrame()
|
await comfyPage.nextFrame()
|
||||||
@@ -401,20 +437,22 @@ test.describe('Node Interaction', () => {
|
|||||||
await expect(comfyPage.canvas).toHaveScreenshot('nodes-unpinned.png')
|
await expect(comfyPage.canvas).toHaveScreenshot('nodes-unpinned.png')
|
||||||
})
|
})
|
||||||
|
|
||||||
test('Can bypass/unbypass nodes with keyboard shortcut', async ({
|
test(
|
||||||
comfyPage
|
'Can bypass/unbypass nodes with keyboard shortcut',
|
||||||
}) => {
|
{ tag: '@screenshot' },
|
||||||
await comfyPage.select2Nodes()
|
async ({ comfyPage }) => {
|
||||||
await comfyPage.canvas.press('Control+b')
|
await comfyPage.select2Nodes()
|
||||||
await comfyPage.nextFrame()
|
await comfyPage.canvas.press('Control+b')
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot('nodes-bypassed.png')
|
await comfyPage.nextFrame()
|
||||||
await comfyPage.canvas.press('Control+b')
|
await expect(comfyPage.canvas).toHaveScreenshot('nodes-bypassed.png')
|
||||||
await comfyPage.nextFrame()
|
await comfyPage.canvas.press('Control+b')
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot('nodes-unbypassed.png')
|
await comfyPage.nextFrame()
|
||||||
})
|
await expect(comfyPage.canvas).toHaveScreenshot('nodes-unbypassed.png')
|
||||||
|
}
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
test.describe('Group Interaction', () => {
|
test.describe('Group Interaction', { tag: '@screenshot' }, () => {
|
||||||
test('Can double click group title to edit', async ({ comfyPage }) => {
|
test('Can double click group title to edit', async ({ comfyPage }) => {
|
||||||
await comfyPage.loadWorkflow('groups/single_group')
|
await comfyPage.loadWorkflow('groups/single_group')
|
||||||
await comfyPage.canvas.dblclick({
|
await comfyPage.canvas.dblclick({
|
||||||
@@ -430,7 +468,7 @@ test.describe('Group Interaction', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test.describe('Canvas Interaction', () => {
|
test.describe('Canvas Interaction', { tag: '@screenshot' }, () => {
|
||||||
test('Can zoom in/out', async ({ comfyPage }) => {
|
test('Can zoom in/out', async ({ comfyPage }) => {
|
||||||
await comfyPage.zoom(-100)
|
await comfyPage.zoom(-100)
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot('zoomed-in.png')
|
await expect(comfyPage.canvas).toHaveScreenshot('zoomed-in.png')
|
||||||
@@ -632,7 +670,7 @@ test.describe('Widget Interaction', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test.describe('Load workflow', () => {
|
test.describe('Load workflow', { tag: '@screenshot' }, () => {
|
||||||
test('Can load workflow with string node id', async ({ comfyPage }) => {
|
test('Can load workflow with string node id', async ({ comfyPage }) => {
|
||||||
await comfyPage.loadWorkflow('nodes/string_node_id')
|
await comfyPage.loadWorkflow('nodes/string_node_id')
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot('string_node_id.png')
|
await expect(comfyPage.canvas).toHaveScreenshot('string_node_id.png')
|
||||||
@@ -824,7 +862,7 @@ test.describe('Viewport settings', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test.describe('Canvas Navigation', () => {
|
test.describe('Canvas Navigation', { tag: '@screenshot' }, () => {
|
||||||
test.describe('Legacy Mode', () => {
|
test.describe('Legacy Mode', () => {
|
||||||
test.beforeEach(async ({ comfyPage }) => {
|
test.beforeEach(async ({ comfyPage }) => {
|
||||||
await comfyPage.setSetting('Comfy.Canvas.NavigationMode', 'legacy')
|
await comfyPage.setSetting('Comfy.Canvas.NavigationMode', 'legacy')
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ test.beforeEach(async ({ comfyPage }) => {
|
|||||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
|
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
|
||||||
})
|
})
|
||||||
|
|
||||||
test.describe('Keybindings', () => {
|
test.describe('Keybindings', { tag: '@keyboard' }, () => {
|
||||||
test('Should not trigger non-modifier keybinding when typing in input fields', async ({
|
test('Should not trigger non-modifier keybinding when typing in input fields', async ({
|
||||||
comfyPage
|
comfyPage
|
||||||
}) => {
|
}) => {
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ function listenForEvent(): Promise<Event> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
test.describe('Canvas Event', () => {
|
test.describe('Canvas Event', { tag: '@canvas' }, () => {
|
||||||
test('Emit litegraph:canvas empty-release', async ({ comfyPage }) => {
|
test('Emit litegraph:canvas empty-release', async ({ comfyPage }) => {
|
||||||
const eventPromise = comfyPage.page.evaluate(listenForEvent)
|
const eventPromise = comfyPage.page.evaluate(listenForEvent)
|
||||||
const disconnectPromise = comfyPage.disconnectEdge()
|
const disconnectPromise = comfyPage.disconnectEdge()
|
||||||
|
|||||||
@@ -6,46 +6,50 @@ test.beforeEach(async ({ comfyPage }) => {
|
|||||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
|
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
|
||||||
})
|
})
|
||||||
|
|
||||||
test.describe('Load Workflow in Media', () => {
|
test.describe(
|
||||||
const fileNames = [
|
'Load Workflow in Media',
|
||||||
'workflow.webp',
|
{ tag: ['@screenshot', '@workflow'] },
|
||||||
'edited_workflow.webp',
|
() => {
|
||||||
'no_workflow.webp',
|
const fileNames = [
|
||||||
'large_workflow.webp',
|
'workflow.webp',
|
||||||
'workflow_prompt_parameters.png',
|
'edited_workflow.webp',
|
||||||
'workflow.webm',
|
'no_workflow.webp',
|
||||||
// Skipped due to 3d widget unstable visual result.
|
'large_workflow.webp',
|
||||||
// 3d widget shows grid after fully loaded.
|
'workflow_prompt_parameters.png',
|
||||||
// 'workflow.glb',
|
'workflow.webm',
|
||||||
'workflow.mp4',
|
// Skipped due to 3d widget unstable visual result.
|
||||||
'workflow.mov',
|
// 3d widget shows grid after fully loaded.
|
||||||
'workflow.m4v',
|
// 'workflow.glb',
|
||||||
'workflow.svg'
|
'workflow.mp4',
|
||||||
// TODO: Re-enable after fixing test asset to use core nodes only
|
'workflow.mov',
|
||||||
// Currently opens missing nodes dialog which is outside scope of AVIF loading functionality
|
'workflow.m4v',
|
||||||
// 'workflow.avif'
|
'workflow.svg'
|
||||||
]
|
// TODO: Re-enable after fixing test asset to use core nodes only
|
||||||
fileNames.forEach(async (fileName) => {
|
// Currently opens missing nodes dialog which is outside scope of AVIF loading functionality
|
||||||
test(`Load workflow in ${fileName} (drop from filesystem)`, async ({
|
// 'workflow.avif'
|
||||||
comfyPage
|
]
|
||||||
}) => {
|
fileNames.forEach(async (fileName) => {
|
||||||
await comfyPage.dragAndDropFile(`workflowInMedia/${fileName}`)
|
test(`Load workflow in ${fileName} (drop from filesystem)`, async ({
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot(`${fileName}.png`)
|
comfyPage
|
||||||
|
}) => {
|
||||||
|
await comfyPage.dragAndDropFile(`workflowInMedia/${fileName}`)
|
||||||
|
await expect(comfyPage.canvas).toHaveScreenshot(`${fileName}.png`)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
|
||||||
|
|
||||||
const urls = [
|
const urls = [
|
||||||
'https://comfyanonymous.github.io/ComfyUI_examples/hidream/hidream_dev_example.png'
|
'https://comfyanonymous.github.io/ComfyUI_examples/hidream/hidream_dev_example.png'
|
||||||
]
|
]
|
||||||
urls.forEach(async (url) => {
|
urls.forEach(async (url) => {
|
||||||
test(`Load workflow from URL ${url} (drop from different browser tabs)`, async ({
|
test(`Load workflow from URL ${url} (drop from different browser tabs)`, async ({
|
||||||
comfyPage
|
comfyPage
|
||||||
}) => {
|
}) => {
|
||||||
await comfyPage.dragAndDropURL(url)
|
await comfyPage.dragAndDropURL(url)
|
||||||
const readableName = url.split('/').pop()
|
const readableName = url.split('/').pop()
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot(
|
await expect(comfyPage.canvas).toHaveScreenshot(
|
||||||
`dropped_workflow_url_${readableName}.png`
|
`dropped_workflow_url_${readableName}.png`
|
||||||
)
|
)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
}
|
||||||
})
|
)
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 97 KiB After Width: | Height: | Size: 135 KiB |
@@ -6,7 +6,7 @@ test.beforeEach(async ({ comfyPage }) => {
|
|||||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
|
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
|
||||||
})
|
})
|
||||||
|
|
||||||
test.describe('LOD Threshold', () => {
|
test.describe('LOD Threshold', { tag: ['@screenshot', '@canvas'] }, () => {
|
||||||
test('Should switch to low quality mode at correct zoom threshold', async ({
|
test('Should switch to low quality mode at correct zoom threshold', async ({
|
||||||
comfyPage
|
comfyPage
|
||||||
}) => {
|
}) => {
|
||||||
@@ -149,53 +149,55 @@ test.describe('LOD Threshold', () => {
|
|||||||
expect(state.scale).toBeLessThan(0.2) // Very zoomed out
|
expect(state.scale).toBeLessThan(0.2) // Very zoomed out
|
||||||
})
|
})
|
||||||
|
|
||||||
test('Should show visual difference between LOD on and off', async ({
|
test(
|
||||||
comfyPage
|
'Should show visual difference between LOD on and off',
|
||||||
}) => {
|
{ tag: '@screenshot' },
|
||||||
// Load a workflow with text-heavy nodes for clear visual difference
|
async ({ comfyPage }) => {
|
||||||
await comfyPage.loadWorkflow('default')
|
// Load a workflow with text-heavy nodes for clear visual difference
|
||||||
|
await comfyPage.loadWorkflow('default')
|
||||||
|
|
||||||
// Set zoom level clearly below the threshold to ensure LOD activates
|
// Set zoom level clearly below the threshold to ensure LOD activates
|
||||||
const targetZoom = 0.4 // Well below default threshold of ~0.571
|
const targetZoom = 0.4 // Well below default threshold of ~0.571
|
||||||
|
|
||||||
// Zoom to target level
|
// Zoom to target level
|
||||||
await comfyPage.page.evaluate((zoom) => {
|
await comfyPage.page.evaluate((zoom) => {
|
||||||
window['app'].canvas.ds.scale = zoom
|
window['app'].canvas.ds.scale = zoom
|
||||||
window['app'].canvas.setDirty(true, true)
|
window['app'].canvas.setDirty(true, true)
|
||||||
}, targetZoom)
|
}, targetZoom)
|
||||||
await comfyPage.nextFrame()
|
await comfyPage.nextFrame()
|
||||||
|
|
||||||
// Take snapshot with LOD active (default 8px setting)
|
// Take snapshot with LOD active (default 8px setting)
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot(
|
await expect(comfyPage.canvas).toHaveScreenshot(
|
||||||
'lod-comparison-low-quality.png'
|
'lod-comparison-low-quality.png'
|
||||||
)
|
)
|
||||||
|
|
||||||
const lowQualityState = await comfyPage.page.evaluate(() => {
|
const lowQualityState = await comfyPage.page.evaluate(() => {
|
||||||
const canvas = window['app'].canvas
|
const canvas = window['app'].canvas
|
||||||
return {
|
return {
|
||||||
lowQuality: canvas.low_quality,
|
lowQuality: canvas.low_quality,
|
||||||
scale: canvas.ds.scale
|
scale: canvas.ds.scale
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
expect(lowQualityState.lowQuality).toBe(true)
|
expect(lowQualityState.lowQuality).toBe(true)
|
||||||
|
|
||||||
// Disable LOD to see high quality at same zoom
|
// Disable LOD to see high quality at same zoom
|
||||||
await comfyPage.setSetting('LiteGraph.Canvas.MinFontSizeForLOD', 0)
|
await comfyPage.setSetting('LiteGraph.Canvas.MinFontSizeForLOD', 0)
|
||||||
await comfyPage.nextFrame()
|
await comfyPage.nextFrame()
|
||||||
|
|
||||||
// Take snapshot with LOD disabled (full quality at same zoom)
|
// Take snapshot with LOD disabled (full quality at same zoom)
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot(
|
await expect(comfyPage.canvas).toHaveScreenshot(
|
||||||
'lod-comparison-high-quality.png'
|
'lod-comparison-high-quality.png'
|
||||||
)
|
)
|
||||||
|
|
||||||
const highQualityState = await comfyPage.page.evaluate(() => {
|
const highQualityState = await comfyPage.page.evaluate(() => {
|
||||||
const canvas = window['app'].canvas
|
const canvas = window['app'].canvas
|
||||||
return {
|
return {
|
||||||
lowQuality: canvas.low_quality,
|
lowQuality: canvas.low_quality,
|
||||||
scale: canvas.ds.scale
|
scale: canvas.ds.scale
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
expect(highQualityState.lowQuality).toBe(false)
|
expect(highQualityState.lowQuality).toBe(false)
|
||||||
expect(highQualityState.scale).toBeCloseTo(targetZoom, 2)
|
expect(highQualityState.scale).toBeCloseTo(targetZoom, 2)
|
||||||
})
|
}
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { expect } from '@playwright/test'
|
|||||||
|
|
||||||
import { comfyPageFixture as test } from '../fixtures/ComfyPage'
|
import { comfyPageFixture as test } from '../fixtures/ComfyPage'
|
||||||
|
|
||||||
test.describe('Menu', () => {
|
test.describe('Menu', { tag: '@ui' }, () => {
|
||||||
test.beforeEach(async ({ comfyPage }) => {
|
test.beforeEach(async ({ comfyPage }) => {
|
||||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Top')
|
await comfyPage.setSetting('Comfy.UseNewMenu', 'Top')
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { expect } from '@playwright/test'
|
|||||||
|
|
||||||
import { comfyPageFixture as test } from '../fixtures/ComfyPage'
|
import { comfyPageFixture as test } from '../fixtures/ComfyPage'
|
||||||
|
|
||||||
test.describe('Minimap', () => {
|
test.describe('Minimap', { tag: '@canvas' }, () => {
|
||||||
test.beforeEach(async ({ comfyPage }) => {
|
test.beforeEach(async ({ comfyPage }) => {
|
||||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Top')
|
await comfyPage.setSetting('Comfy.UseNewMenu', 'Top')
|
||||||
await comfyPage.setSetting('Comfy.Minimap.Visible', true)
|
await comfyPage.setSetting('Comfy.Minimap.Visible', true)
|
||||||
|
|||||||
@@ -1,35 +1,39 @@
|
|||||||
import { comfyPageFixture as test } from '../fixtures/ComfyPage'
|
import { comfyPageFixture as test } from '../fixtures/ComfyPage'
|
||||||
import { expect } from '@playwright/test'
|
import { expect } from '@playwright/test'
|
||||||
|
|
||||||
test.describe('Mobile Baseline Snapshots', () => {
|
test.describe(
|
||||||
test('@mobile empty canvas', async ({ comfyPage }) => {
|
'Mobile Baseline Snapshots',
|
||||||
await comfyPage.setSetting('Comfy.ConfirmClear', false)
|
{ tag: ['@mobile', '@screenshot'] },
|
||||||
await comfyPage.executeCommand('Comfy.ClearWorkflow')
|
() => {
|
||||||
await expect(async () => {
|
test('@mobile empty canvas', async ({ comfyPage }) => {
|
||||||
expect(await comfyPage.getGraphNodesCount()).toBe(0)
|
await comfyPage.setSetting('Comfy.ConfirmClear', false)
|
||||||
}).toPass({ timeout: 256 })
|
await comfyPage.executeCommand('Comfy.ClearWorkflow')
|
||||||
await comfyPage.nextFrame()
|
await expect(async () => {
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot('mobile-empty-canvas.png')
|
expect(await comfyPage.getGraphNodesCount()).toBe(0)
|
||||||
})
|
}).toPass({ timeout: 256 })
|
||||||
|
await comfyPage.nextFrame()
|
||||||
|
await expect(comfyPage.canvas).toHaveScreenshot('mobile-empty-canvas.png')
|
||||||
|
})
|
||||||
|
|
||||||
test('@mobile default workflow', async ({ comfyPage }) => {
|
test('@mobile default workflow', async ({ comfyPage }) => {
|
||||||
await comfyPage.loadWorkflow('default')
|
await comfyPage.loadWorkflow('default')
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot(
|
await expect(comfyPage.canvas).toHaveScreenshot(
|
||||||
'mobile-default-workflow.png'
|
'mobile-default-workflow.png'
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('@mobile settings dialog', async ({ comfyPage }) => {
|
test('@mobile settings dialog', async ({ comfyPage }) => {
|
||||||
await comfyPage.settingDialog.open()
|
await comfyPage.settingDialog.open()
|
||||||
await comfyPage.nextFrame()
|
await comfyPage.nextFrame()
|
||||||
|
|
||||||
await expect(comfyPage.settingDialog.root).toHaveScreenshot(
|
await expect(comfyPage.settingDialog.root).toHaveScreenshot(
|
||||||
'mobile-settings-dialog.png',
|
'mobile-settings-dialog.png',
|
||||||
{
|
{
|
||||||
mask: [
|
mask: [
|
||||||
comfyPage.settingDialog.root.getByTestId('current-user-indicator')
|
comfyPage.settingDialog.root.getByTestId('current-user-indicator')
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
})
|
}
|
||||||
|
)
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
@@ -8,7 +8,7 @@ test.beforeEach(async ({ comfyPage }) => {
|
|||||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
|
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
|
||||||
})
|
})
|
||||||
|
|
||||||
test.describe('Node Badge', () => {
|
test.describe('Node Badge', { tag: ['@screenshot', '@smoke', '@node'] }, () => {
|
||||||
test('Can add badge', async ({ comfyPage }) => {
|
test('Can add badge', async ({ comfyPage }) => {
|
||||||
await comfyPage.page.evaluate(() => {
|
await comfyPage.page.evaluate(() => {
|
||||||
const LGraphBadge = window['LGraphBadge']
|
const LGraphBadge = window['LGraphBadge']
|
||||||
@@ -66,50 +66,60 @@ test.describe('Node Badge', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test.describe('Node source badge', () => {
|
test.describe(
|
||||||
Object.values(NodeBadgeMode).forEach(async (mode) => {
|
'Node source badge',
|
||||||
test(`Shows node badges (${mode})`, async ({ comfyPage }) => {
|
{ tag: ['@screenshot', '@smoke', '@node'] },
|
||||||
// Execution error workflow has both custom node and core node.
|
() => {
|
||||||
await comfyPage.loadWorkflow('nodes/execution_error')
|
Object.values(NodeBadgeMode).forEach(async (mode) => {
|
||||||
await comfyPage.setSetting('Comfy.NodeBadge.NodeSourceBadgeMode', mode)
|
test(`Shows node badges (${mode})`, async ({ comfyPage }) => {
|
||||||
await comfyPage.setSetting('Comfy.NodeBadge.NodeIdBadgeMode', mode)
|
// Execution error workflow has both custom node and core node.
|
||||||
await comfyPage.nextFrame()
|
await comfyPage.loadWorkflow('nodes/execution_error')
|
||||||
await comfyPage.resetView()
|
await comfyPage.setSetting('Comfy.NodeBadge.NodeSourceBadgeMode', mode)
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot(`node-badge-${mode}.png`)
|
await comfyPage.setSetting('Comfy.NodeBadge.NodeIdBadgeMode', mode)
|
||||||
|
await comfyPage.nextFrame()
|
||||||
|
await comfyPage.resetView()
|
||||||
|
await expect(comfyPage.canvas).toHaveScreenshot(
|
||||||
|
`node-badge-${mode}.png`
|
||||||
|
)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
}
|
||||||
})
|
)
|
||||||
|
|
||||||
test.describe('Node badge color', () => {
|
test.describe(
|
||||||
test('Can show node badge with unknown color palette', async ({
|
'Node badge color',
|
||||||
comfyPage
|
{ tag: ['@screenshot', '@smoke', '@node'] },
|
||||||
}) => {
|
() => {
|
||||||
await comfyPage.setSetting(
|
test('Can show node badge with unknown color palette', async ({
|
||||||
'Comfy.NodeBadge.NodeIdBadgeMode',
|
comfyPage
|
||||||
NodeBadgeMode.ShowAll
|
}) => {
|
||||||
)
|
await comfyPage.setSetting(
|
||||||
await comfyPage.setSetting('Comfy.ColorPalette', 'unknown')
|
'Comfy.NodeBadge.NodeIdBadgeMode',
|
||||||
await comfyPage.nextFrame()
|
NodeBadgeMode.ShowAll
|
||||||
// Click empty space to trigger canvas re-render.
|
)
|
||||||
await comfyPage.clickEmptySpace()
|
await comfyPage.setSetting('Comfy.ColorPalette', 'unknown')
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot(
|
await comfyPage.nextFrame()
|
||||||
'node-badge-unknown-color-palette.png'
|
// Click empty space to trigger canvas re-render.
|
||||||
)
|
await comfyPage.clickEmptySpace()
|
||||||
})
|
await expect(comfyPage.canvas).toHaveScreenshot(
|
||||||
|
'node-badge-unknown-color-palette.png'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
test('Can show node badge with light color palette', async ({
|
test('Can show node badge with light color palette', async ({
|
||||||
comfyPage
|
comfyPage
|
||||||
}) => {
|
}) => {
|
||||||
await comfyPage.setSetting(
|
await comfyPage.setSetting(
|
||||||
'Comfy.NodeBadge.NodeIdBadgeMode',
|
'Comfy.NodeBadge.NodeIdBadgeMode',
|
||||||
NodeBadgeMode.ShowAll
|
NodeBadgeMode.ShowAll
|
||||||
)
|
)
|
||||||
await comfyPage.setSetting('Comfy.ColorPalette', 'light')
|
await comfyPage.setSetting('Comfy.ColorPalette', 'light')
|
||||||
await comfyPage.nextFrame()
|
await comfyPage.nextFrame()
|
||||||
// Click empty space to trigger canvas re-render.
|
// Click empty space to trigger canvas re-render.
|
||||||
await comfyPage.clickEmptySpace()
|
await comfyPage.clickEmptySpace()
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot(
|
await expect(comfyPage.canvas).toHaveScreenshot(
|
||||||
'node-badge-light-color-palette.png'
|
'node-badge-light-color-palette.png'
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
})
|
}
|
||||||
|
)
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ test.beforeEach(async ({ comfyPage }) => {
|
|||||||
|
|
||||||
// If an input is optional by node definition, it should be shown as
|
// 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.
|
// a hollow circle no matter what shape it was defined in the workflow JSON.
|
||||||
test.describe('Optional input', () => {
|
test.describe('Optional input', { tag: ['@screenshot', '@node'] }, () => {
|
||||||
test('No shape specified', async ({ comfyPage }) => {
|
test('No shape specified', async ({ comfyPage }) => {
|
||||||
await comfyPage.loadWorkflow('inputs/optional_input_no_shape')
|
await comfyPage.loadWorkflow('inputs/optional_input_no_shape')
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot('optional_input.png')
|
await expect(comfyPage.canvas).toHaveScreenshot('optional_input.png')
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ async function selectNodeWithPan(comfyPage: ComfyPage, nodeRef: NodeReference) {
|
|||||||
await nodeRef.click('title')
|
await nodeRef.click('title')
|
||||||
}
|
}
|
||||||
|
|
||||||
test.describe('Node Help', () => {
|
test.describe('Node Help', { tag: ['@slow', '@ui'] }, () => {
|
||||||
test.beforeEach(async ({ comfyPage }) => {
|
test.beforeEach(async ({ comfyPage }) => {
|
||||||
await comfyPage.setup()
|
await comfyPage.setup()
|
||||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Top')
|
await comfyPage.setSetting('Comfy.UseNewMenu', 'Top')
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ test.beforeEach(async ({ comfyPage }) => {
|
|||||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
|
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
|
||||||
})
|
})
|
||||||
|
|
||||||
test.describe('Node search box', () => {
|
test.describe('Node search box', { tag: '@node' }, () => {
|
||||||
test.beforeEach(async ({ comfyPage }) => {
|
test.beforeEach(async ({ comfyPage }) => {
|
||||||
await comfyPage.setSetting('Comfy.LinkRelease.Action', 'search box')
|
await comfyPage.setSetting('Comfy.LinkRelease.Action', 'search box')
|
||||||
await comfyPage.setSetting('Comfy.LinkRelease.ActionShift', 'search box')
|
await comfyPage.setSetting('Comfy.LinkRelease.ActionShift', 'search box')
|
||||||
@@ -46,14 +46,14 @@ test.describe('Node search box', () => {
|
|||||||
await expect(comfyPage.searchBox.input).toBeVisible()
|
await expect(comfyPage.searchBox.input).toBeVisible()
|
||||||
})
|
})
|
||||||
|
|
||||||
test('Can add node', async ({ comfyPage }) => {
|
test('Can add node', { tag: '@screenshot' }, async ({ comfyPage }) => {
|
||||||
await comfyPage.doubleClickCanvas()
|
await comfyPage.doubleClickCanvas()
|
||||||
await expect(comfyPage.searchBox.input).toHaveCount(1)
|
await expect(comfyPage.searchBox.input).toHaveCount(1)
|
||||||
await comfyPage.searchBox.fillAndSelectFirstNode('KSampler')
|
await comfyPage.searchBox.fillAndSelectFirstNode('KSampler')
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot('added-node.png')
|
await expect(comfyPage.canvas).toHaveScreenshot('added-node.png')
|
||||||
})
|
})
|
||||||
|
|
||||||
test('Can auto link node', async ({ comfyPage }) => {
|
test('Can auto link node', { tag: '@screenshot' }, async ({ comfyPage }) => {
|
||||||
await comfyPage.disconnectEdge()
|
await comfyPage.disconnectEdge()
|
||||||
// Select the second item as the first item is always reroute
|
// Select the second item as the first item is always reroute
|
||||||
await comfyPage.searchBox.fillAndSelectFirstNode('CLIPTextEncode', {
|
await comfyPage.searchBox.fillAndSelectFirstNode('CLIPTextEncode', {
|
||||||
@@ -62,41 +62,47 @@ test.describe('Node search box', () => {
|
|||||||
await expect(comfyPage.canvas).toHaveScreenshot('auto-linked-node.png')
|
await expect(comfyPage.canvas).toHaveScreenshot('auto-linked-node.png')
|
||||||
})
|
})
|
||||||
|
|
||||||
test('Can auto link batch moved node', async ({ comfyPage }) => {
|
test(
|
||||||
await comfyPage.loadWorkflow('links/batch_move_links')
|
'Can auto link batch moved node',
|
||||||
|
{ tag: '@screenshot' },
|
||||||
|
async ({ comfyPage }) => {
|
||||||
|
await comfyPage.loadWorkflow('links/batch_move_links')
|
||||||
|
|
||||||
const outputSlot1Pos = {
|
const outputSlot1Pos = {
|
||||||
x: 304,
|
x: 304,
|
||||||
y: 127
|
y: 127
|
||||||
|
}
|
||||||
|
const emptySpacePos = {
|
||||||
|
x: 5,
|
||||||
|
y: 5
|
||||||
|
}
|
||||||
|
await comfyPage.page.keyboard.down('Shift')
|
||||||
|
await comfyPage.dragAndDrop(outputSlot1Pos, emptySpacePos)
|
||||||
|
await comfyPage.page.keyboard.up('Shift')
|
||||||
|
|
||||||
|
// Select the second item as the first item is always reroute
|
||||||
|
await comfyPage.searchBox.fillAndSelectFirstNode('Load Checkpoint', {
|
||||||
|
suggestionIndex: 0
|
||||||
|
})
|
||||||
|
await expect(comfyPage.canvas).toHaveScreenshot(
|
||||||
|
'auto-linked-node-batch.png'
|
||||||
|
)
|
||||||
}
|
}
|
||||||
const emptySpacePos = {
|
)
|
||||||
x: 5,
|
|
||||||
y: 5
|
test(
|
||||||
|
'Link release connecting to node with no slots',
|
||||||
|
{ tag: '@screenshot' },
|
||||||
|
async ({ comfyPage }) => {
|
||||||
|
await comfyPage.disconnectEdge()
|
||||||
|
await expect(comfyPage.searchBox.input).toHaveCount(1)
|
||||||
|
await comfyPage.page.locator('.p-chip-remove-icon').click()
|
||||||
|
await comfyPage.searchBox.fillAndSelectFirstNode('KSampler')
|
||||||
|
await expect(comfyPage.canvas).toHaveScreenshot(
|
||||||
|
'added-node-no-connection.png'
|
||||||
|
)
|
||||||
}
|
}
|
||||||
await comfyPage.page.keyboard.down('Shift')
|
)
|
||||||
await comfyPage.dragAndDrop(outputSlot1Pos, emptySpacePos)
|
|
||||||
await comfyPage.page.keyboard.up('Shift')
|
|
||||||
|
|
||||||
// Select the second item as the first item is always reroute
|
|
||||||
await comfyPage.searchBox.fillAndSelectFirstNode('Load Checkpoint', {
|
|
||||||
suggestionIndex: 0
|
|
||||||
})
|
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot(
|
|
||||||
'auto-linked-node-batch.png'
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('Link release connecting to node with no slots', async ({
|
|
||||||
comfyPage
|
|
||||||
}) => {
|
|
||||||
await comfyPage.disconnectEdge()
|
|
||||||
await expect(comfyPage.searchBox.input).toHaveCount(1)
|
|
||||||
await comfyPage.page.locator('.p-chip-remove-icon').click()
|
|
||||||
await comfyPage.searchBox.fillAndSelectFirstNode('KSampler')
|
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot(
|
|
||||||
'added-node-no-connection.png'
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('Has correct aria-labels on search results', async ({ comfyPage }) => {
|
test('Has correct aria-labels on search results', async ({ comfyPage }) => {
|
||||||
const node = 'Load Checkpoint'
|
const node = 'Load Checkpoint'
|
||||||
@@ -251,40 +257,45 @@ test.describe('Node search box', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test.describe('Release context menu', () => {
|
test.describe('Release context menu', { tag: '@node' }, () => {
|
||||||
test.beforeEach(async ({ comfyPage }) => {
|
test.beforeEach(async ({ comfyPage }) => {
|
||||||
await comfyPage.setSetting('Comfy.LinkRelease.Action', 'context menu')
|
await comfyPage.setSetting('Comfy.LinkRelease.Action', 'context menu')
|
||||||
await comfyPage.setSetting('Comfy.LinkRelease.ActionShift', 'search box')
|
await comfyPage.setSetting('Comfy.LinkRelease.ActionShift', 'search box')
|
||||||
await comfyPage.setSetting('Comfy.NodeSearchBoxImpl', 'default')
|
await comfyPage.setSetting('Comfy.NodeSearchBoxImpl', 'default')
|
||||||
})
|
})
|
||||||
|
|
||||||
test('Can trigger on link release', async ({ comfyPage }) => {
|
test(
|
||||||
await comfyPage.disconnectEdge()
|
'Can trigger on link release',
|
||||||
const contextMenu = comfyPage.page.locator('.litecontextmenu')
|
{ tag: '@screenshot' },
|
||||||
// Wait for context menu with correct title (slot name | slot type)
|
async ({ comfyPage }) => {
|
||||||
// The title shows the output slot name and type from the disconnected link
|
await comfyPage.disconnectEdge()
|
||||||
await expect(contextMenu.locator('.litemenu-title')).toContainText(
|
const contextMenu = comfyPage.page.locator('.litecontextmenu')
|
||||||
'CLIP | CLIP'
|
// Wait for context menu with correct title (slot name | slot type)
|
||||||
)
|
// The title shows the output slot name and type from the disconnected link
|
||||||
await comfyPage.page.mouse.move(10, 10)
|
await expect(contextMenu.locator('.litemenu-title')).toContainText(
|
||||||
await comfyPage.nextFrame()
|
'CLIP | CLIP'
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot(
|
)
|
||||||
'link-release-context-menu.png'
|
await comfyPage.page.mouse.move(10, 10)
|
||||||
)
|
await comfyPage.nextFrame()
|
||||||
})
|
await expect(comfyPage.canvas).toHaveScreenshot(
|
||||||
|
'link-release-context-menu.png'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
test('Can search and add node from context menu', async ({
|
test(
|
||||||
comfyPage,
|
'Can search and add node from context menu',
|
||||||
comfyMouse
|
{ tag: '@screenshot' },
|
||||||
}) => {
|
async ({ comfyPage, comfyMouse }) => {
|
||||||
await comfyPage.disconnectEdge()
|
await comfyPage.disconnectEdge()
|
||||||
await comfyMouse.move({ x: 10, y: 10 })
|
await comfyMouse.move({ x: 10, y: 10 })
|
||||||
await comfyPage.clickContextMenuItem('Search')
|
await comfyPage.clickContextMenuItem('Search')
|
||||||
await comfyPage.searchBox.fillAndSelectFirstNode('CLIP Prompt')
|
await comfyPage.searchBox.fillAndSelectFirstNode('CLIP Prompt')
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot(
|
await expect(comfyPage.canvas).toHaveScreenshot(
|
||||||
'link-context-menu-search.png'
|
'link-context-menu-search.png'
|
||||||
)
|
)
|
||||||
})
|
}
|
||||||
|
)
|
||||||
|
|
||||||
test('Existing user (pre-1.24.1) gets context menu by default on link release', async ({
|
test('Existing user (pre-1.24.1) gets context menu by default on link release', async ({
|
||||||
comfyPage
|
comfyPage
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ test.beforeEach(async ({ comfyPage }) => {
|
|||||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
|
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
|
||||||
})
|
})
|
||||||
|
|
||||||
test.describe('Note Node', () => {
|
test.describe('Note Node', { tag: '@node' }, () => {
|
||||||
test('Can load node nodes', async ({ comfyPage }) => {
|
test('Can load node nodes', { tag: '@screenshot' }, async ({ comfyPage }) => {
|
||||||
await comfyPage.loadWorkflow('nodes/note_nodes')
|
await comfyPage.loadWorkflow('nodes/note_nodes')
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot('note_nodes.png')
|
await expect(comfyPage.canvas).toHaveScreenshot('note_nodes.png')
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ test.beforeEach(async ({ comfyPage }) => {
|
|||||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
|
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
|
||||||
})
|
})
|
||||||
|
|
||||||
test.describe('Primitive Node', () => {
|
test.describe('Primitive Node', { tag: ['@screenshot', '@node'] }, () => {
|
||||||
test('Can load with correct size', async ({ comfyPage }) => {
|
test('Can load with correct size', async ({ comfyPage }) => {
|
||||||
await comfyPage.loadWorkflow('primitive/primitive_node')
|
await comfyPage.loadWorkflow('primitive/primitive_node')
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot('primitive_node.png')
|
await expect(comfyPage.canvas).toHaveScreenshot('primitive_node.png')
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ test.beforeEach(async ({ comfyPage }) => {
|
|||||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
|
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
|
||||||
})
|
})
|
||||||
|
|
||||||
test.describe('Record Audio Node', () => {
|
test.describe('Record Audio Node', { tag: '@screenshot' }, () => {
|
||||||
test('should add a record audio node and take a screenshot', async ({
|
test('should add a record audio node and take a screenshot', async ({
|
||||||
comfyPage
|
comfyPage
|
||||||
}) => {
|
}) => {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { expect } from '@playwright/test'
|
|||||||
import type { ComfyPage } from '../fixtures/ComfyPage'
|
import type { ComfyPage } from '../fixtures/ComfyPage'
|
||||||
import { comfyPageFixture as test } from '../fixtures/ComfyPage'
|
import { comfyPageFixture as test } from '../fixtures/ComfyPage'
|
||||||
|
|
||||||
test.describe('Remote COMBO Widget', () => {
|
test.describe('Remote COMBO Widget', { tag: '@widget' }, () => {
|
||||||
const mockOptions = ['d', 'c', 'b', 'a']
|
const mockOptions = ['d', 'c', 'b', 'a']
|
||||||
|
|
||||||
const addRemoteWidgetNode = async (
|
const addRemoteWidgetNode = async (
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { expect } from '@playwright/test'
|
|||||||
import { comfyPageFixture as test } from '../fixtures/ComfyPage'
|
import { comfyPageFixture as test } from '../fixtures/ComfyPage'
|
||||||
import { getMiddlePoint } from '../fixtures/utils/litegraphUtils'
|
import { getMiddlePoint } from '../fixtures/utils/litegraphUtils'
|
||||||
|
|
||||||
test.describe('Reroute Node', () => {
|
test.describe('Reroute Node', { tag: ['@screenshot', '@node'] }, () => {
|
||||||
test.beforeEach(async ({ comfyPage }) => {
|
test.beforeEach(async ({ comfyPage }) => {
|
||||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Top')
|
await comfyPage.setSetting('Comfy.UseNewMenu', 'Top')
|
||||||
})
|
})
|
||||||
@@ -38,92 +38,96 @@ test.describe('Reroute Node', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test.describe('LiteGraph Native Reroute Node', () => {
|
test.describe(
|
||||||
test.beforeEach(async ({ comfyPage }) => {
|
'LiteGraph Native Reroute Node',
|
||||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
|
{ tag: ['@screenshot', '@node'] },
|
||||||
await comfyPage.setSetting('LiteGraph.Reroute.SplineOffset', 80)
|
() => {
|
||||||
})
|
test.beforeEach(async ({ comfyPage }) => {
|
||||||
|
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
|
||||||
|
await comfyPage.setSetting('LiteGraph.Reroute.SplineOffset', 80)
|
||||||
|
})
|
||||||
|
|
||||||
test('loads from workflow', async ({ comfyPage }) => {
|
test('loads from workflow', async ({ comfyPage }) => {
|
||||||
await comfyPage.loadWorkflow('reroute/native_reroute')
|
await comfyPage.loadWorkflow('reroute/native_reroute')
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot('native_reroute.png')
|
await expect(comfyPage.canvas).toHaveScreenshot('native_reroute.png')
|
||||||
})
|
})
|
||||||
|
|
||||||
test('@2x @0.5x Can add reroute by alt clicking on link', async ({
|
test('@2x @0.5x Can add reroute by alt clicking on link', async ({
|
||||||
comfyPage
|
comfyPage
|
||||||
}) => {
|
}) => {
|
||||||
const loadCheckpointNode = (
|
const loadCheckpointNode = (
|
||||||
await comfyPage.getNodeRefsByTitle('Load Checkpoint')
|
await comfyPage.getNodeRefsByTitle('Load Checkpoint')
|
||||||
)[0]
|
)[0]
|
||||||
const clipEncodeNode = (
|
const clipEncodeNode = (
|
||||||
await comfyPage.getNodeRefsByTitle('CLIP Text Encode (Prompt)')
|
await comfyPage.getNodeRefsByTitle('CLIP Text Encode (Prompt)')
|
||||||
)[0]
|
)[0]
|
||||||
|
|
||||||
const slot1 = await loadCheckpointNode.getOutput(1)
|
const slot1 = await loadCheckpointNode.getOutput(1)
|
||||||
const slot2 = await clipEncodeNode.getInput(0)
|
const slot2 = await clipEncodeNode.getInput(0)
|
||||||
const middlePoint = getMiddlePoint(
|
const middlePoint = getMiddlePoint(
|
||||||
await slot1.getPosition(),
|
await slot1.getPosition(),
|
||||||
await slot2.getPosition()
|
await slot2.getPosition()
|
||||||
)
|
)
|
||||||
|
|
||||||
await comfyPage.page.keyboard.down('Alt')
|
await comfyPage.page.keyboard.down('Alt')
|
||||||
await comfyPage.page.mouse.click(middlePoint.x, middlePoint.y)
|
await comfyPage.page.mouse.click(middlePoint.x, middlePoint.y)
|
||||||
await comfyPage.page.keyboard.up('Alt')
|
await comfyPage.page.keyboard.up('Alt')
|
||||||
|
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot(
|
await expect(comfyPage.canvas).toHaveScreenshot(
|
||||||
'native_reroute_alt_click.png'
|
'native_reroute_alt_click.png'
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('Can add reroute by clicking middle of link context menu', async ({
|
test('Can add reroute by clicking middle of link context menu', async ({
|
||||||
comfyPage
|
comfyPage
|
||||||
}) => {
|
}) => {
|
||||||
const loadCheckpointNode = (
|
const loadCheckpointNode = (
|
||||||
await comfyPage.getNodeRefsByTitle('Load Checkpoint')
|
await comfyPage.getNodeRefsByTitle('Load Checkpoint')
|
||||||
)[0]
|
)[0]
|
||||||
const clipEncodeNode = (
|
const clipEncodeNode = (
|
||||||
await comfyPage.getNodeRefsByTitle('CLIP Text Encode (Prompt)')
|
await comfyPage.getNodeRefsByTitle('CLIP Text Encode (Prompt)')
|
||||||
)[0]
|
)[0]
|
||||||
|
|
||||||
const slot1 = await loadCheckpointNode.getOutput(1)
|
const slot1 = await loadCheckpointNode.getOutput(1)
|
||||||
const slot2 = await clipEncodeNode.getInput(0)
|
const slot2 = await clipEncodeNode.getInput(0)
|
||||||
const middlePoint = getMiddlePoint(
|
const middlePoint = getMiddlePoint(
|
||||||
await slot1.getPosition(),
|
await slot1.getPosition(),
|
||||||
await slot2.getPosition()
|
await slot2.getPosition()
|
||||||
)
|
)
|
||||||
|
|
||||||
await comfyPage.page.mouse.click(middlePoint.x, middlePoint.y)
|
await comfyPage.page.mouse.click(middlePoint.x, middlePoint.y)
|
||||||
await comfyPage.page
|
await comfyPage.page
|
||||||
.locator('.litecontextmenu .litemenu-entry', { hasText: 'Add Reroute' })
|
.locator('.litecontextmenu .litemenu-entry', { hasText: 'Add Reroute' })
|
||||||
.click()
|
.click()
|
||||||
|
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot(
|
await expect(comfyPage.canvas).toHaveScreenshot(
|
||||||
'native_reroute_context_menu.png'
|
'native_reroute_context_menu.png'
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('Can delete link that is connected to two reroutes', async ({
|
test('Can delete link that is connected to two reroutes', async ({
|
||||||
comfyPage
|
comfyPage
|
||||||
}) => {
|
}) => {
|
||||||
// https://github.com/Comfy-Org/ComfyUI_frontend/issues/4695
|
// https://github.com/Comfy-Org/ComfyUI_frontend/issues/4695
|
||||||
await comfyPage.loadWorkflow(
|
await comfyPage.loadWorkflow(
|
||||||
'reroute/single-native-reroute-default-workflow'
|
'reroute/single-native-reroute-default-workflow'
|
||||||
)
|
)
|
||||||
|
|
||||||
// To find the clickable midpoint button, we use the hardcoded value from the browser logs
|
// To find the clickable midpoint button, we use the hardcoded value from the browser logs
|
||||||
// since the link is a bezier curve and not a straight line.
|
// since the link is a bezier curve and not a straight line.
|
||||||
const middlePoint = { x: 359.4188232421875, y: 468.7716979980469 }
|
const middlePoint = { x: 359.4188232421875, y: 468.7716979980469 }
|
||||||
|
|
||||||
// Click the middle point of the link to open the context menu.
|
// Click the middle point of the link to open the context menu.
|
||||||
await comfyPage.page.mouse.click(middlePoint.x, middlePoint.y)
|
await comfyPage.page.mouse.click(middlePoint.x, middlePoint.y)
|
||||||
|
|
||||||
// Click the "Delete" context menu option.
|
// Click the "Delete" context menu option.
|
||||||
await comfyPage.page
|
await comfyPage.page
|
||||||
.locator('.litecontextmenu .litemenu-entry', { hasText: 'Delete' })
|
.locator('.litecontextmenu .litemenu-entry', { hasText: 'Delete' })
|
||||||
.click()
|
.click()
|
||||||
|
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot(
|
await expect(comfyPage.canvas).toHaveScreenshot(
|
||||||
'native_reroute_delete_from_midpoint_context_menu.png'
|
'native_reroute_delete_from_midpoint_context_menu.png'
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
})
|
}
|
||||||
|
)
|
||||||
|
|||||||
@@ -7,43 +7,49 @@ test.beforeEach(async ({ comfyPage }) => {
|
|||||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
|
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
|
||||||
})
|
})
|
||||||
|
|
||||||
test.describe('Canvas Right Click Menu', () => {
|
test.describe(
|
||||||
test('Can add node', async ({ comfyPage }) => {
|
'Canvas Right Click Menu',
|
||||||
await comfyPage.rightClickCanvas()
|
{ tag: ['@screenshot', '@ui'] },
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot('right-click-menu.png')
|
() => {
|
||||||
await comfyPage.page.getByText('Add Node').click()
|
test('Can add node', async ({ comfyPage }) => {
|
||||||
await comfyPage.nextFrame()
|
await comfyPage.rightClickCanvas()
|
||||||
await comfyPage.page.getByText('loaders').click()
|
await expect(comfyPage.canvas).toHaveScreenshot('right-click-menu.png')
|
||||||
await comfyPage.nextFrame()
|
await comfyPage.page.getByText('Add Node').click()
|
||||||
await comfyPage.page.getByText('Load VAE').click()
|
await comfyPage.nextFrame()
|
||||||
await comfyPage.nextFrame()
|
await comfyPage.page.getByText('loaders').click()
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot('add-node-node-added.png')
|
await comfyPage.nextFrame()
|
||||||
})
|
await comfyPage.page.getByText('Load VAE').click()
|
||||||
|
await comfyPage.nextFrame()
|
||||||
|
await expect(comfyPage.canvas).toHaveScreenshot('add-node-node-added.png')
|
||||||
|
})
|
||||||
|
|
||||||
test('Can add group', async ({ comfyPage }) => {
|
test('Can add group', async ({ comfyPage }) => {
|
||||||
await comfyPage.rightClickCanvas()
|
await comfyPage.rightClickCanvas()
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot('right-click-menu.png')
|
await expect(comfyPage.canvas).toHaveScreenshot('right-click-menu.png')
|
||||||
await comfyPage.page.getByText('Add Group', { exact: true }).click()
|
await comfyPage.page.getByText('Add Group', { exact: true }).click()
|
||||||
await comfyPage.nextFrame()
|
await comfyPage.nextFrame()
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot('add-group-group-added.png')
|
await expect(comfyPage.canvas).toHaveScreenshot(
|
||||||
})
|
'add-group-group-added.png'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
test('Can convert to group node', async ({ comfyPage }) => {
|
test('Can convert to group node', async ({ comfyPage }) => {
|
||||||
await comfyPage.select2Nodes()
|
await comfyPage.select2Nodes()
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot('selected-2-nodes.png')
|
await expect(comfyPage.canvas).toHaveScreenshot('selected-2-nodes.png')
|
||||||
await comfyPage.rightClickCanvas()
|
await comfyPage.rightClickCanvas()
|
||||||
await comfyPage.clickContextMenuItem('Convert to Group Node (Deprecated)')
|
await comfyPage.clickContextMenuItem('Convert to Group Node (Deprecated)')
|
||||||
await comfyPage.promptDialogInput.fill('GroupNode2CLIP')
|
await comfyPage.promptDialogInput.fill('GroupNode2CLIP')
|
||||||
await comfyPage.page.keyboard.press('Enter')
|
await comfyPage.page.keyboard.press('Enter')
|
||||||
await comfyPage.promptDialogInput.waitFor({ state: 'hidden' })
|
await comfyPage.promptDialogInput.waitFor({ state: 'hidden' })
|
||||||
await comfyPage.nextFrame()
|
await comfyPage.nextFrame()
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot(
|
await expect(comfyPage.canvas).toHaveScreenshot(
|
||||||
'right-click-node-group-node.png'
|
'right-click-node-group-node.png'
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
})
|
}
|
||||||
|
)
|
||||||
|
|
||||||
test.describe('Node Right Click Menu', () => {
|
test.describe('Node Right Click Menu', { tag: ['@screenshot', '@ui'] }, () => {
|
||||||
test('Can open properties panel', async ({ comfyPage }) => {
|
test('Can open properties panel', async ({ comfyPage }) => {
|
||||||
await comfyPage.rightClickEmptyLatentNode()
|
await comfyPage.rightClickEmptyLatentNode()
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot('right-click-node.png')
|
await expect(comfyPage.canvas).toHaveScreenshot('right-click-node.png')
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ test.beforeEach(async ({ comfyPage }) => {
|
|||||||
const BLUE_COLOR = 'rgb(51, 51, 85)'
|
const BLUE_COLOR = 'rgb(51, 51, 85)'
|
||||||
const RED_COLOR = 'rgb(85, 51, 51)'
|
const RED_COLOR = 'rgb(85, 51, 51)'
|
||||||
|
|
||||||
test.describe('Selection Toolbox', () => {
|
test.describe('Selection Toolbox', { tag: ['@screenshot', '@ui'] }, () => {
|
||||||
test.beforeEach(async ({ comfyPage }) => {
|
test.beforeEach(async ({ comfyPage }) => {
|
||||||
await comfyPage.setSetting('Comfy.Canvas.SelectionToolbox', true)
|
await comfyPage.setSetting('Comfy.Canvas.SelectionToolbox', true)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -7,178 +7,190 @@ test.beforeEach(async ({ comfyPage }) => {
|
|||||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
|
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
|
||||||
})
|
})
|
||||||
|
|
||||||
test.describe('Selection Toolbox - More Options Submenus', () => {
|
test.describe(
|
||||||
test.beforeEach(async ({ comfyPage }) => {
|
'Selection Toolbox - More Options Submenus',
|
||||||
await comfyPage.setSetting('Comfy.Canvas.SelectionToolbox', true)
|
{ tag: '@ui' },
|
||||||
await comfyPage.loadWorkflow('nodes/single_ksampler')
|
() => {
|
||||||
await comfyPage.nextFrame()
|
test.beforeEach(async ({ comfyPage }) => {
|
||||||
await comfyPage.selectNodes(['KSampler'])
|
await comfyPage.setSetting('Comfy.Canvas.SelectionToolbox', true)
|
||||||
await comfyPage.nextFrame()
|
await comfyPage.loadWorkflow('nodes/single_ksampler')
|
||||||
})
|
|
||||||
|
|
||||||
const openMoreOptions = async (comfyPage: ComfyPage) => {
|
|
||||||
const ksamplerNodes = await comfyPage.getNodeRefsByTitle('KSampler')
|
|
||||||
if (ksamplerNodes.length === 0) {
|
|
||||||
throw new Error('No KSampler nodes found')
|
|
||||||
}
|
|
||||||
|
|
||||||
// Drag the KSampler to the center of the screen
|
|
||||||
const nodePos = await ksamplerNodes[0].getPosition()
|
|
||||||
const viewportSize = comfyPage.page.viewportSize()
|
|
||||||
const centerX = viewportSize.width / 3
|
|
||||||
const centerY = viewportSize.height / 2
|
|
||||||
await comfyPage.dragAndDrop(
|
|
||||||
{ x: nodePos.x, y: nodePos.y },
|
|
||||||
{ x: centerX, y: centerY }
|
|
||||||
)
|
|
||||||
await comfyPage.nextFrame()
|
|
||||||
|
|
||||||
await ksamplerNodes[0].click('title')
|
|
||||||
await comfyPage.nextFrame()
|
|
||||||
|
|
||||||
await expect(comfyPage.page.locator('.selection-toolbox')).toBeVisible({
|
|
||||||
timeout: 5000
|
|
||||||
})
|
|
||||||
|
|
||||||
const moreOptionsBtn = comfyPage.page.locator(
|
|
||||||
'[data-testid="more-options-button"]'
|
|
||||||
)
|
|
||||||
await expect(moreOptionsBtn).toBeVisible({ timeout: 3000 })
|
|
||||||
|
|
||||||
await comfyPage.page.click('[data-testid="more-options-button"]')
|
|
||||||
|
|
||||||
await comfyPage.nextFrame()
|
|
||||||
|
|
||||||
const menuOptionsVisible = await comfyPage.page
|
|
||||||
.getByText('Rename')
|
|
||||||
.isVisible({ timeout: 2000 })
|
|
||||||
.catch(() => false)
|
|
||||||
if (menuOptionsVisible) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
await moreOptionsBtn.click({ force: true })
|
|
||||||
await comfyPage.nextFrame()
|
|
||||||
|
|
||||||
const menuOptionsVisibleAfterClick = await comfyPage.page
|
|
||||||
.getByText('Rename')
|
|
||||||
.isVisible({ timeout: 2000 })
|
|
||||||
.catch(() => false)
|
|
||||||
if (menuOptionsVisibleAfterClick) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error('Could not open More Options menu - popover not showing')
|
|
||||||
}
|
|
||||||
|
|
||||||
test('opens Node Info from More Options menu', async ({ comfyPage }) => {
|
|
||||||
await openMoreOptions(comfyPage)
|
|
||||||
const nodeInfoButton = comfyPage.page.getByText('Node Info', {
|
|
||||||
exact: true
|
|
||||||
})
|
|
||||||
await expect(nodeInfoButton).toBeVisible()
|
|
||||||
await nodeInfoButton.click()
|
|
||||||
await comfyPage.nextFrame()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('changes node shape via Shape submenu', async ({ comfyPage }) => {
|
|
||||||
const nodeRef = (await comfyPage.getNodeRefsByTitle('KSampler'))[0]
|
|
||||||
const initialShape = await nodeRef.getProperty<number>('shape')
|
|
||||||
|
|
||||||
await openMoreOptions(comfyPage)
|
|
||||||
await comfyPage.page.getByText('Shape', { exact: true }).hover()
|
|
||||||
await expect(comfyPage.page.getByText('Box', { exact: true })).toBeVisible({
|
|
||||||
timeout: 5000
|
|
||||||
})
|
|
||||||
await comfyPage.page.getByText('Box', { exact: true }).click()
|
|
||||||
await comfyPage.nextFrame()
|
|
||||||
|
|
||||||
const newShape = await nodeRef.getProperty<number>('shape')
|
|
||||||
expect(newShape).not.toBe(initialShape)
|
|
||||||
expect(newShape).toBe(1)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('changes node color via Color submenu swatch', async ({ comfyPage }) => {
|
|
||||||
const nodeRef = (await comfyPage.getNodeRefsByTitle('KSampler'))[0]
|
|
||||||
const initialColor = await nodeRef.getProperty<string | undefined>('color')
|
|
||||||
|
|
||||||
await openMoreOptions(comfyPage)
|
|
||||||
await comfyPage.page.getByText('Color', { exact: true }).click()
|
|
||||||
const blueSwatch = comfyPage.page.locator('[title="Blue"]')
|
|
||||||
await expect(blueSwatch.first()).toBeVisible({ timeout: 5000 })
|
|
||||||
await blueSwatch.first().click()
|
|
||||||
await comfyPage.nextFrame()
|
|
||||||
|
|
||||||
const newColor = await nodeRef.getProperty<string | undefined>('color')
|
|
||||||
expect(newColor).toBe('#223')
|
|
||||||
if (initialColor) {
|
|
||||||
expect(newColor).not.toBe(initialColor)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
test('renames a node using Rename action', async ({ comfyPage }) => {
|
|
||||||
const nodeRef = (await comfyPage.getNodeRefsByTitle('KSampler'))[0]
|
|
||||||
await openMoreOptions(comfyPage)
|
|
||||||
await comfyPage.page
|
|
||||||
.getByText('Rename', { exact: true })
|
|
||||||
.click({ force: true })
|
|
||||||
const input = comfyPage.page.locator(
|
|
||||||
'.group-title-editor.node-title-editor .editable-text input'
|
|
||||||
)
|
|
||||||
await expect(input).toBeVisible()
|
|
||||||
await input.fill('RenamedNode')
|
|
||||||
await input.press('Enter')
|
|
||||||
await comfyPage.nextFrame()
|
|
||||||
const newTitle = await nodeRef.getProperty<string>('title')
|
|
||||||
expect(newTitle).toBe('RenamedNode')
|
|
||||||
})
|
|
||||||
|
|
||||||
test('closes More Options menu when clicking outside', async ({
|
|
||||||
comfyPage
|
|
||||||
}) => {
|
|
||||||
await openMoreOptions(comfyPage)
|
|
||||||
const renameItem = comfyPage.page.getByText('Rename', { exact: true })
|
|
||||||
await expect(renameItem).toBeVisible({ timeout: 5000 })
|
|
||||||
|
|
||||||
// Wait for multiple frames to allow PrimeVue's outside click handler to initialize
|
|
||||||
for (let i = 0; i < 30; i++) {
|
|
||||||
await comfyPage.nextFrame()
|
await comfyPage.nextFrame()
|
||||||
|
await comfyPage.selectNodes(['KSampler'])
|
||||||
|
await comfyPage.nextFrame()
|
||||||
|
})
|
||||||
|
|
||||||
|
const openMoreOptions = async (comfyPage: ComfyPage) => {
|
||||||
|
const ksamplerNodes = await comfyPage.getNodeRefsByTitle('KSampler')
|
||||||
|
if (ksamplerNodes.length === 0) {
|
||||||
|
throw new Error('No KSampler nodes found')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Drag the KSampler to the center of the screen
|
||||||
|
const nodePos = await ksamplerNodes[0].getPosition()
|
||||||
|
const viewportSize = comfyPage.page.viewportSize()
|
||||||
|
const centerX = viewportSize.width / 3
|
||||||
|
const centerY = viewportSize.height / 2
|
||||||
|
await comfyPage.dragAndDrop(
|
||||||
|
{ x: nodePos.x, y: nodePos.y },
|
||||||
|
{ x: centerX, y: centerY }
|
||||||
|
)
|
||||||
|
await comfyPage.nextFrame()
|
||||||
|
|
||||||
|
await ksamplerNodes[0].click('title')
|
||||||
|
await comfyPage.nextFrame()
|
||||||
|
|
||||||
|
await expect(comfyPage.page.locator('.selection-toolbox')).toBeVisible({
|
||||||
|
timeout: 5000
|
||||||
|
})
|
||||||
|
|
||||||
|
const moreOptionsBtn = comfyPage.page.locator(
|
||||||
|
'[data-testid="more-options-button"]'
|
||||||
|
)
|
||||||
|
await expect(moreOptionsBtn).toBeVisible({ timeout: 3000 })
|
||||||
|
|
||||||
|
await comfyPage.page.click('[data-testid="more-options-button"]')
|
||||||
|
|
||||||
|
await comfyPage.nextFrame()
|
||||||
|
|
||||||
|
const menuOptionsVisible = await comfyPage.page
|
||||||
|
.getByText('Rename')
|
||||||
|
.isVisible({ timeout: 2000 })
|
||||||
|
.catch(() => false)
|
||||||
|
if (menuOptionsVisible) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
await moreOptionsBtn.click({ force: true })
|
||||||
|
await comfyPage.nextFrame()
|
||||||
|
|
||||||
|
const menuOptionsVisibleAfterClick = await comfyPage.page
|
||||||
|
.getByText('Rename')
|
||||||
|
.isVisible({ timeout: 2000 })
|
||||||
|
.catch(() => false)
|
||||||
|
if (menuOptionsVisibleAfterClick) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error('Could not open More Options menu - popover not showing')
|
||||||
}
|
}
|
||||||
|
|
||||||
await comfyPage.page
|
test('opens Node Info from More Options menu', async ({ comfyPage }) => {
|
||||||
.locator('#graph-canvas')
|
await openMoreOptions(comfyPage)
|
||||||
.click({ position: { x: 0, y: 50 }, force: true })
|
const nodeInfoButton = comfyPage.page.getByText('Node Info', {
|
||||||
|
exact: true
|
||||||
|
})
|
||||||
|
await expect(nodeInfoButton).toBeVisible()
|
||||||
|
await nodeInfoButton.click()
|
||||||
|
await comfyPage.nextFrame()
|
||||||
|
})
|
||||||
|
|
||||||
await comfyPage.nextFrame()
|
test('changes node shape via Shape submenu', async ({ comfyPage }) => {
|
||||||
await expect(
|
const nodeRef = (await comfyPage.getNodeRefsByTitle('KSampler'))[0]
|
||||||
comfyPage.page.getByText('Rename', { exact: true })
|
const initialShape = await nodeRef.getProperty<number>('shape')
|
||||||
).not.toBeVisible()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('closes More Options menu when clicking the button again (toggle)', async ({
|
await openMoreOptions(comfyPage)
|
||||||
comfyPage
|
await comfyPage.page.getByText('Shape', { exact: true }).hover()
|
||||||
}) => {
|
await expect(
|
||||||
await openMoreOptions(comfyPage)
|
comfyPage.page.getByText('Box', { exact: true })
|
||||||
await expect(
|
).toBeVisible({
|
||||||
comfyPage.page.getByText('Rename', { exact: true })
|
timeout: 5000
|
||||||
).toBeVisible({ timeout: 5000 })
|
})
|
||||||
|
await comfyPage.page.getByText('Box', { exact: true }).click()
|
||||||
|
await comfyPage.nextFrame()
|
||||||
|
|
||||||
await comfyPage.page.evaluate(() => {
|
const newShape = await nodeRef.getProperty<number>('shape')
|
||||||
const btn = document.querySelector('[data-testid="more-options-button"]')
|
expect(newShape).not.toBe(initialShape)
|
||||||
if (btn) {
|
expect(newShape).toBe(1)
|
||||||
const event = new MouseEvent('click', {
|
})
|
||||||
bubbles: true,
|
|
||||||
cancelable: true,
|
test('changes node color via Color submenu swatch', async ({
|
||||||
view: window,
|
comfyPage
|
||||||
detail: 1
|
}) => {
|
||||||
})
|
const nodeRef = (await comfyPage.getNodeRefsByTitle('KSampler'))[0]
|
||||||
btn.dispatchEvent(event)
|
const initialColor = await nodeRef.getProperty<string | undefined>(
|
||||||
|
'color'
|
||||||
|
)
|
||||||
|
|
||||||
|
await openMoreOptions(comfyPage)
|
||||||
|
await comfyPage.page.getByText('Color', { exact: true }).click()
|
||||||
|
const blueSwatch = comfyPage.page.locator('[title="Blue"]')
|
||||||
|
await expect(blueSwatch.first()).toBeVisible({ timeout: 5000 })
|
||||||
|
await blueSwatch.first().click()
|
||||||
|
await comfyPage.nextFrame()
|
||||||
|
|
||||||
|
const newColor = await nodeRef.getProperty<string | undefined>('color')
|
||||||
|
expect(newColor).toBe('#223')
|
||||||
|
if (initialColor) {
|
||||||
|
expect(newColor).not.toBe(initialColor)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
await comfyPage.nextFrame()
|
|
||||||
|
|
||||||
await expect(
|
test('renames a node using Rename action', async ({ comfyPage }) => {
|
||||||
comfyPage.page.getByText('Rename', { exact: true })
|
const nodeRef = (await comfyPage.getNodeRefsByTitle('KSampler'))[0]
|
||||||
).not.toBeVisible()
|
await openMoreOptions(comfyPage)
|
||||||
})
|
await comfyPage.page
|
||||||
})
|
.getByText('Rename', { exact: true })
|
||||||
|
.click({ force: true })
|
||||||
|
const input = comfyPage.page.locator(
|
||||||
|
'.group-title-editor.node-title-editor .editable-text input'
|
||||||
|
)
|
||||||
|
await expect(input).toBeVisible()
|
||||||
|
await input.fill('RenamedNode')
|
||||||
|
await input.press('Enter')
|
||||||
|
await comfyPage.nextFrame()
|
||||||
|
const newTitle = await nodeRef.getProperty<string>('title')
|
||||||
|
expect(newTitle).toBe('RenamedNode')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('closes More Options menu when clicking outside', async ({
|
||||||
|
comfyPage
|
||||||
|
}) => {
|
||||||
|
await openMoreOptions(comfyPage)
|
||||||
|
const renameItem = comfyPage.page.getByText('Rename', { exact: true })
|
||||||
|
await expect(renameItem).toBeVisible({ timeout: 5000 })
|
||||||
|
|
||||||
|
// Wait for multiple frames to allow PrimeVue's outside click handler to initialize
|
||||||
|
for (let i = 0; i < 30; i++) {
|
||||||
|
await comfyPage.nextFrame()
|
||||||
|
}
|
||||||
|
|
||||||
|
await comfyPage.page
|
||||||
|
.locator('#graph-canvas')
|
||||||
|
.click({ position: { x: 0, y: 50 }, force: true })
|
||||||
|
|
||||||
|
await comfyPage.nextFrame()
|
||||||
|
await expect(
|
||||||
|
comfyPage.page.getByText('Rename', { exact: true })
|
||||||
|
).not.toBeVisible()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('closes More Options menu when clicking the button again (toggle)', async ({
|
||||||
|
comfyPage
|
||||||
|
}) => {
|
||||||
|
await openMoreOptions(comfyPage)
|
||||||
|
await expect(
|
||||||
|
comfyPage.page.getByText('Rename', { exact: true })
|
||||||
|
).toBeVisible({ timeout: 5000 })
|
||||||
|
|
||||||
|
await comfyPage.page.evaluate(() => {
|
||||||
|
const btn = document.querySelector(
|
||||||
|
'[data-testid="more-options-button"]'
|
||||||
|
)
|
||||||
|
if (btn) {
|
||||||
|
const event = new MouseEvent('click', {
|
||||||
|
bubbles: true,
|
||||||
|
cancelable: true,
|
||||||
|
view: window,
|
||||||
|
detail: 1
|
||||||
|
})
|
||||||
|
btn.dispatchEvent(event)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
await comfyPage.nextFrame()
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
comfyPage.page.getByText('Rename', { exact: true })
|
||||||
|
).not.toBeVisible()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ const SELECTORS = {
|
|||||||
promptDialog: '.graphdialog input'
|
promptDialog: '.graphdialog input'
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
test.describe('Subgraph Slot Rename Dialog', () => {
|
test.describe('Subgraph Slot Rename Dialog', { tag: '@subgraph' }, () => {
|
||||||
test.beforeEach(async ({ comfyPage }) => {
|
test.beforeEach(async ({ comfyPage }) => {
|
||||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
|
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ const SELECTORS = {
|
|||||||
domWidget: '.comfy-multiline-input'
|
domWidget: '.comfy-multiline-input'
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
test.describe('Subgraph Operations', () => {
|
test.describe('Subgraph Operations', { tag: ['@slow', '@subgraph'] }, () => {
|
||||||
test.beforeEach(async ({ comfyPage }) => {
|
test.beforeEach(async ({ comfyPage }) => {
|
||||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
|
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ async function checkTemplateFileExists(
|
|||||||
return response.ok()
|
return response.ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
test.describe('Templates', () => {
|
test.describe('Templates', { tag: ['@slow', '@workflow'] }, () => {
|
||||||
test.beforeEach(async ({ comfyPage }) => {
|
test.beforeEach(async ({ comfyPage }) => {
|
||||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Top')
|
await comfyPage.setSetting('Comfy.UseNewMenu', 'Top')
|
||||||
await comfyPage.setSetting('Comfy.Workflow.ShowMissingModelsWarning', false)
|
await comfyPage.setSetting('Comfy.Workflow.ShowMissingModelsWarning', false)
|
||||||
@@ -207,109 +207,114 @@ test.describe('Templates', () => {
|
|||||||
await expect(nav).toBeVisible() // Nav should be visible at tablet size
|
await expect(nav).toBeVisible() // Nav should be visible at tablet size
|
||||||
})
|
})
|
||||||
|
|
||||||
test('template cards descriptions adjust height dynamically', async ({
|
test(
|
||||||
comfyPage
|
'template cards descriptions adjust height dynamically',
|
||||||
}) => {
|
{ tag: '@screenshot' },
|
||||||
// Setup test by intercepting templates response to inject cards with varying description lengths
|
async ({ comfyPage }) => {
|
||||||
await comfyPage.page.route('**/templates/index.json', async (route, _) => {
|
// Setup test by intercepting templates response to inject cards with varying description lengths
|
||||||
const response = [
|
await comfyPage.page.route(
|
||||||
{
|
'**/templates/index.json',
|
||||||
moduleName: 'default',
|
async (route, _) => {
|
||||||
title: 'Test Templates',
|
const response = [
|
||||||
type: 'image',
|
|
||||||
templates: [
|
|
||||||
{
|
{
|
||||||
name: 'short-description',
|
moduleName: 'default',
|
||||||
title: 'Short Description',
|
title: 'Test Templates',
|
||||||
mediaType: 'image',
|
type: 'image',
|
||||||
mediaSubtype: 'webp',
|
templates: [
|
||||||
description: 'This is a short description.'
|
{
|
||||||
},
|
name: 'short-description',
|
||||||
{
|
title: 'Short Description',
|
||||||
name: 'medium-description',
|
mediaType: 'image',
|
||||||
title: 'Medium Description',
|
mediaSubtype: 'webp',
|
||||||
mediaType: 'image',
|
description: 'This is a short description.'
|
||||||
mediaSubtype: 'webp',
|
},
|
||||||
description:
|
{
|
||||||
'This is a medium length description that should take up two lines on most displays.'
|
name: 'medium-description',
|
||||||
},
|
title: 'Medium Description',
|
||||||
{
|
mediaType: 'image',
|
||||||
name: 'long-description',
|
mediaSubtype: 'webp',
|
||||||
title: 'Long Description',
|
description:
|
||||||
mediaType: 'image',
|
'This is a medium length description that should take up two lines on most displays.'
|
||||||
mediaSubtype: 'webp',
|
},
|
||||||
description:
|
{
|
||||||
'This is a much longer description that should definitely wrap to multiple lines. It contains enough text to demonstrate how the cards handle varying amounts of content while maintaining a consistent layout grid.'
|
name: 'long-description',
|
||||||
|
title: 'Long Description',
|
||||||
|
mediaType: 'image',
|
||||||
|
mediaSubtype: 'webp',
|
||||||
|
description:
|
||||||
|
'This is a much longer description that should definitely wrap to multiple lines. It contains enough text to demonstrate how the cards handle varying amounts of content while maintaining a consistent layout grid.'
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
await route.fulfill({
|
||||||
|
status: 200,
|
||||||
|
body: JSON.stringify(response),
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Cache-Control': 'no-store'
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
]
|
)
|
||||||
await route.fulfill({
|
|
||||||
status: 200,
|
// Mock the thumbnail images to avoid 404s
|
||||||
body: JSON.stringify(response),
|
await comfyPage.page.route('**/templates/**.webp', async (route) => {
|
||||||
headers: {
|
const headers = {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'image/webp',
|
||||||
'Cache-Control': 'no-store'
|
'Cache-Control': 'no-store'
|
||||||
}
|
}
|
||||||
|
await route.fulfill({
|
||||||
|
status: 200,
|
||||||
|
path: 'browser_tests/assets/example.webp',
|
||||||
|
headers
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
|
||||||
|
|
||||||
// Mock the thumbnail images to avoid 404s
|
// Open templates dialog
|
||||||
await comfyPage.page.route('**/templates/**.webp', async (route) => {
|
await comfyPage.executeCommand('Comfy.BrowseTemplates')
|
||||||
const headers = {
|
await expect(comfyPage.templates.content).toBeVisible()
|
||||||
'Content-Type': 'image/webp',
|
|
||||||
'Cache-Control': 'no-store'
|
|
||||||
}
|
|
||||||
await route.fulfill({
|
|
||||||
status: 200,
|
|
||||||
path: 'browser_tests/assets/example.webp',
|
|
||||||
headers
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
// Open templates dialog
|
// Wait for cards to load
|
||||||
await comfyPage.executeCommand('Comfy.BrowseTemplates')
|
await expect(
|
||||||
await expect(comfyPage.templates.content).toBeVisible()
|
comfyPage.page.locator(
|
||||||
|
'[data-testid="template-workflow-short-description"]'
|
||||||
|
)
|
||||||
|
).toBeVisible({ timeout: 5000 })
|
||||||
|
|
||||||
// Wait for cards to load
|
// Verify all three cards with different descriptions are visible
|
||||||
await expect(
|
const shortDescCard = comfyPage.page.locator(
|
||||||
comfyPage.page.locator(
|
|
||||||
'[data-testid="template-workflow-short-description"]'
|
'[data-testid="template-workflow-short-description"]'
|
||||||
)
|
)
|
||||||
).toBeVisible({ timeout: 5000 })
|
const mediumDescCard = comfyPage.page.locator(
|
||||||
|
'[data-testid="template-workflow-medium-description"]'
|
||||||
|
)
|
||||||
|
const longDescCard = comfyPage.page.locator(
|
||||||
|
'[data-testid="template-workflow-long-description"]'
|
||||||
|
)
|
||||||
|
|
||||||
// Verify all three cards with different descriptions are visible
|
await expect(shortDescCard).toBeVisible()
|
||||||
const shortDescCard = comfyPage.page.locator(
|
await expect(mediumDescCard).toBeVisible()
|
||||||
'[data-testid="template-workflow-short-description"]'
|
await expect(longDescCard).toBeVisible()
|
||||||
)
|
|
||||||
const mediumDescCard = comfyPage.page.locator(
|
|
||||||
'[data-testid="template-workflow-medium-description"]'
|
|
||||||
)
|
|
||||||
const longDescCard = comfyPage.page.locator(
|
|
||||||
'[data-testid="template-workflow-long-description"]'
|
|
||||||
)
|
|
||||||
|
|
||||||
await expect(shortDescCard).toBeVisible()
|
// Verify descriptions are visible and have line-clamp class
|
||||||
await expect(mediumDescCard).toBeVisible()
|
// The description is in a p tag with text-muted class
|
||||||
await expect(longDescCard).toBeVisible()
|
const shortDesc = shortDescCard.locator('p.text-muted.line-clamp-2')
|
||||||
|
const mediumDesc = mediumDescCard.locator('p.text-muted.line-clamp-2')
|
||||||
|
const longDesc = longDescCard.locator('p.text-muted.line-clamp-2')
|
||||||
|
|
||||||
// Verify descriptions are visible and have line-clamp class
|
await expect(shortDesc).toContainText('short description')
|
||||||
// The description is in a p tag with text-muted class
|
await expect(mediumDesc).toContainText('medium length description')
|
||||||
const shortDesc = shortDescCard.locator('p.text-muted.line-clamp-2')
|
await expect(longDesc).toContainText('much longer description')
|
||||||
const mediumDesc = mediumDescCard.locator('p.text-muted.line-clamp-2')
|
|
||||||
const longDesc = longDescCard.locator('p.text-muted.line-clamp-2')
|
|
||||||
|
|
||||||
await expect(shortDesc).toContainText('short description')
|
// Verify grid layout maintains consistency
|
||||||
await expect(mediumDesc).toContainText('medium length description')
|
const templateGrid = comfyPage.page.locator(
|
||||||
await expect(longDesc).toContainText('much longer description')
|
'[data-testid="template-workflows-content"]'
|
||||||
|
)
|
||||||
// Verify grid layout maintains consistency
|
await expect(templateGrid).toBeVisible()
|
||||||
const templateGrid = comfyPage.page.locator(
|
await expect(templateGrid).toHaveScreenshot(
|
||||||
'[data-testid="template-workflows-content"]'
|
'template-grid-varying-content.png'
|
||||||
)
|
)
|
||||||
await expect(templateGrid).toBeVisible()
|
}
|
||||||
await expect(templateGrid).toHaveScreenshot(
|
)
|
||||||
'template-grid-varying-content.png'
|
|
||||||
)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ test.beforeEach(async ({ comfyPage }) => {
|
|||||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
|
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
|
||||||
})
|
})
|
||||||
|
|
||||||
test.describe('Settings Search functionality', () => {
|
test.describe('Settings Search functionality', { tag: '@settings' }, () => {
|
||||||
test.beforeEach(async ({ comfyPage }) => {
|
test.beforeEach(async ({ comfyPage }) => {
|
||||||
// Register test settings to verify hidden/deprecated filtering
|
// Register test settings to verify hidden/deprecated filtering
|
||||||
await comfyPage.page.evaluate(() => {
|
await comfyPage.page.evaluate(() => {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { userSelectPageFixture as test } from '../fixtures/UserSelectPage'
|
|||||||
/**
|
/**
|
||||||
* Expects ComfyUI backend to be launched with `--multi-user` flag.
|
* Expects ComfyUI backend to be launched with `--multi-user` flag.
|
||||||
*/
|
*/
|
||||||
test.describe('User Select View', () => {
|
test.describe('User Select View', { tag: '@settings' }, () => {
|
||||||
test.beforeEach(async ({ userSelectPage, page }) => {
|
test.beforeEach(async ({ userSelectPage, page }) => {
|
||||||
await page.goto(userSelectPage.url)
|
await page.goto(userSelectPage.url)
|
||||||
await page.evaluate(() => {
|
await page.evaluate(() => {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { expect } from '@playwright/test'
|
|||||||
import type { SystemStats } from '../../src/schemas/apiSchema'
|
import type { SystemStats } from '../../src/schemas/apiSchema'
|
||||||
import { comfyPageFixture as test } from '../fixtures/ComfyPage'
|
import { comfyPageFixture as test } from '../fixtures/ComfyPage'
|
||||||
|
|
||||||
test.describe('Version Mismatch Warnings', () => {
|
test.describe('Version Mismatch Warnings', { tag: '@slow' }, () => {
|
||||||
const ALWAYS_AHEAD_OF_INSTALLED_VERSION = '100.100.100'
|
const ALWAYS_AHEAD_OF_INSTALLED_VERSION = '100.100.100'
|
||||||
const ALWAYS_BEHIND_INSTALLED_VERSION = '0.0.0'
|
const ALWAYS_BEHIND_INSTALLED_VERSION = '0.0.0'
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { expect } from '@playwright/test'
|
|||||||
|
|
||||||
import { comfyPageFixture as test } from '../fixtures/ComfyPage'
|
import { comfyPageFixture as test } from '../fixtures/ComfyPage'
|
||||||
|
|
||||||
test.describe('Viewport', () => {
|
test.describe('Viewport', { tag: ['@screenshot', '@smoke', '@canvas'] }, () => {
|
||||||
test('Fits view to nodes when saved viewport position is offscreen', async ({
|
test('Fits view to nodes when saved viewport position is offscreen', async ({
|
||||||
comfyPage
|
comfyPage
|
||||||
}) => {
|
}) => {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import {
|
|||||||
|
|
||||||
const CREATE_GROUP_HOTKEY = 'Control+g'
|
const CREATE_GROUP_HOTKEY = 'Control+g'
|
||||||
|
|
||||||
test.describe('Vue Node Groups', () => {
|
test.describe('Vue Node Groups', { tag: '@screenshot' }, () => {
|
||||||
test.beforeEach(async ({ comfyPage }) => {
|
test.beforeEach(async ({ comfyPage }) => {
|
||||||
await comfyPage.setSetting('Comfy.VueNodes.Enabled', true)
|
await comfyPage.setSetting('Comfy.VueNodes.Enabled', true)
|
||||||
await comfyPage.setSetting('Comfy.Minimap.ShowGroups', true)
|
await comfyPage.setSetting('Comfy.Minimap.ShowGroups', true)
|
||||||
|
|||||||
@@ -9,10 +9,14 @@ test.describe('Vue Nodes Canvas Pan', () => {
|
|||||||
await comfyPage.vueNodes.waitForNodes()
|
await comfyPage.vueNodes.waitForNodes()
|
||||||
})
|
})
|
||||||
|
|
||||||
test('@mobile Can pan with touch', async ({ comfyPage }) => {
|
test(
|
||||||
await comfyPage.panWithTouch({ x: 64, y: 64 }, { x: 256, y: 256 })
|
'@mobile Can pan with touch',
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot(
|
{ tag: '@screenshot' },
|
||||||
'vue-nodes-paned-with-touch.png'
|
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: 25 KiB After Width: | Height: | Size: 25 KiB |
@@ -10,25 +10,30 @@ test.describe('Vue Nodes Zoom', () => {
|
|||||||
await comfyPage.vueNodes.waitForNodes()
|
await comfyPage.vueNodes.waitForNodes()
|
||||||
})
|
})
|
||||||
|
|
||||||
test('should not capture drag while zooming with ctrl+shift+drag', async ({
|
test(
|
||||||
comfyPage
|
'should not capture drag while zooming with ctrl+shift+drag',
|
||||||
}) => {
|
{ tag: '@screenshot' },
|
||||||
const checkpointNode = comfyPage.vueNodes.getNodeByTitle('Load Checkpoint')
|
async ({ comfyPage }) => {
|
||||||
const nodeBoundingBox = await checkpointNode.boundingBox()
|
const checkpointNode =
|
||||||
if (!nodeBoundingBox) throw new Error('Node bounding box not available')
|
comfyPage.vueNodes.getNodeByTitle('Load Checkpoint')
|
||||||
|
const nodeBoundingBox = await checkpointNode.boundingBox()
|
||||||
|
if (!nodeBoundingBox) throw new Error('Node bounding box not available')
|
||||||
|
|
||||||
const nodeMidpointX = nodeBoundingBox.x + nodeBoundingBox.width / 2
|
const nodeMidpointX = nodeBoundingBox.x + nodeBoundingBox.width / 2
|
||||||
const nodeMidpointY = nodeBoundingBox.y + nodeBoundingBox.height / 2
|
const nodeMidpointY = nodeBoundingBox.y + nodeBoundingBox.height / 2
|
||||||
|
|
||||||
// Start the Ctrl+Shift drag-to-zoom on the canvas and continue dragging over
|
// Start the Ctrl+Shift drag-to-zoom on the canvas and continue dragging over
|
||||||
// the node. The node should not capture the drag while drag-zooming.
|
// the node. The node should not capture the drag while drag-zooming.
|
||||||
await comfyPage.page.keyboard.down('Control')
|
await comfyPage.page.keyboard.down('Control')
|
||||||
await comfyPage.page.keyboard.down('Shift')
|
await comfyPage.page.keyboard.down('Shift')
|
||||||
await comfyPage.dragAndDrop(
|
await comfyPage.dragAndDrop(
|
||||||
{ x: 200, y: 300 },
|
{ x: 200, y: 300 },
|
||||||
{ x: nodeMidpointX, y: nodeMidpointY }
|
{ x: nodeMidpointX, y: nodeMidpointY }
|
||||||
)
|
)
|
||||||
|
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot('zoomed-in-ctrl-shift.png')
|
await expect(comfyPage.canvas).toHaveScreenshot(
|
||||||
})
|
'zoomed-in-ctrl-shift.png'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ async function connectSlots(
|
|||||||
await nextFrame()
|
await nextFrame()
|
||||||
}
|
}
|
||||||
|
|
||||||
test.describe('Vue Node Link Interaction', () => {
|
test.describe('Vue Node Link Interaction', { tag: '@screenshot' }, () => {
|
||||||
test.beforeEach(async ({ comfyPage }) => {
|
test.beforeEach(async ({ comfyPage }) => {
|
||||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Top')
|
await comfyPage.setSetting('Comfy.UseNewMenu', 'Top')
|
||||||
await comfyPage.setSetting('Comfy.VueNodes.Enabled', true)
|
await comfyPage.setSetting('Comfy.VueNodes.Enabled', true)
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import {
|
|||||||
import type { ComfyPage } from '../../../../fixtures/ComfyPage'
|
import type { ComfyPage } from '../../../../fixtures/ComfyPage'
|
||||||
import { fitToViewInstant } from '../../../../helpers/fitToView'
|
import { fitToViewInstant } from '../../../../helpers/fitToView'
|
||||||
|
|
||||||
test.describe('Vue Node Bring to Front', () => {
|
test.describe('Vue Node Bring to Front', { tag: '@screenshot' }, () => {
|
||||||
test.beforeEach(async ({ comfyPage }) => {
|
test.beforeEach(async ({ comfyPage }) => {
|
||||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
|
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
|
||||||
await comfyPage.setSetting('Comfy.VueNodes.Enabled', true)
|
await comfyPage.setSetting('Comfy.VueNodes.Enabled', true)
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import {
|
import {
|
||||||
type ComfyPage,
|
|
||||||
comfyExpect as expect,
|
comfyExpect as expect,
|
||||||
comfyPageFixture as test
|
comfyPageFixture as test
|
||||||
} from '../../../../fixtures/ComfyPage'
|
} from '../../../../fixtures/ComfyPage'
|
||||||
|
import type { ComfyPage } from '../../../../fixtures/ComfyPage'
|
||||||
import type { Position } from '../../../../fixtures/types'
|
import type { Position } from '../../../../fixtures/types'
|
||||||
|
|
||||||
test.describe('Vue Node Moving', () => {
|
test.describe('Vue Node Moving', () => {
|
||||||
@@ -29,39 +29,47 @@ test.describe('Vue Node Moving', () => {
|
|||||||
expect(diffY).toBeGreaterThan(0)
|
expect(diffY).toBeGreaterThan(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
test('should allow moving nodes by dragging', async ({ comfyPage }) => {
|
test(
|
||||||
const loadCheckpointHeaderPos = await getLoadCheckpointHeaderPos(comfyPage)
|
'should allow moving nodes by dragging',
|
||||||
await comfyPage.dragAndDrop(loadCheckpointHeaderPos, {
|
{ tag: '@screenshot' },
|
||||||
x: 256,
|
async ({ comfyPage }) => {
|
||||||
y: 256
|
const loadCheckpointHeaderPos =
|
||||||
})
|
await getLoadCheckpointHeaderPos(comfyPage)
|
||||||
|
await comfyPage.dragAndDrop(loadCheckpointHeaderPos, {
|
||||||
|
x: 256,
|
||||||
|
y: 256
|
||||||
|
})
|
||||||
|
|
||||||
const newHeaderPos = await getLoadCheckpointHeaderPos(comfyPage)
|
const newHeaderPos = await getLoadCheckpointHeaderPos(comfyPage)
|
||||||
await expectPosChanged(loadCheckpointHeaderPos, newHeaderPos)
|
await expectPosChanged(loadCheckpointHeaderPos, newHeaderPos)
|
||||||
|
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot('vue-node-moved-node.png')
|
await expect(comfyPage.canvas).toHaveScreenshot('vue-node-moved-node.png')
|
||||||
})
|
}
|
||||||
|
)
|
||||||
|
|
||||||
test('@mobile should allow moving nodes by dragging on touch devices', async ({
|
test(
|
||||||
comfyPage
|
'@mobile should allow moving nodes by dragging on touch devices',
|
||||||
}) => {
|
{ tag: '@screenshot' },
|
||||||
// Disable minimap (gets in way of the node on small screens)
|
async ({ comfyPage }) => {
|
||||||
await comfyPage.setSetting('Comfy.Minimap.Visible', false)
|
// Disable minimap (gets in way of the node on small screens)
|
||||||
|
await comfyPage.setSetting('Comfy.Minimap.Visible', false)
|
||||||
|
|
||||||
const loadCheckpointHeaderPos = await getLoadCheckpointHeaderPos(comfyPage)
|
const loadCheckpointHeaderPos =
|
||||||
await comfyPage.panWithTouch(
|
await getLoadCheckpointHeaderPos(comfyPage)
|
||||||
{
|
await comfyPage.panWithTouch(
|
||||||
x: 64,
|
{
|
||||||
y: 64
|
x: 64,
|
||||||
},
|
y: 64
|
||||||
loadCheckpointHeaderPos
|
},
|
||||||
)
|
loadCheckpointHeaderPos
|
||||||
|
)
|
||||||
|
|
||||||
const newHeaderPos = await getLoadCheckpointHeaderPos(comfyPage)
|
const newHeaderPos = await getLoadCheckpointHeaderPos(comfyPage)
|
||||||
await expectPosChanged(loadCheckpointHeaderPos, newHeaderPos)
|
await expectPosChanged(loadCheckpointHeaderPos, newHeaderPos)
|
||||||
|
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot(
|
await expect(comfyPage.canvas).toHaveScreenshot(
|
||||||
'vue-node-moved-node-touch.png'
|
'vue-node-moved-node-touch.png'
|
||||||
)
|
)
|
||||||
})
|
}
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 26 KiB |
@@ -15,22 +15,25 @@ test.describe('Vue Node Bypass', () => {
|
|||||||
await comfyPage.vueNodes.waitForNodes()
|
await comfyPage.vueNodes.waitForNodes()
|
||||||
})
|
})
|
||||||
|
|
||||||
test('should allow toggling bypass on a selected node with hotkey', async ({
|
test(
|
||||||
comfyPage
|
'should allow toggling bypass on a selected node with hotkey',
|
||||||
}) => {
|
{ tag: '@screenshot' },
|
||||||
await comfyPage.page.getByText('Load Checkpoint').click()
|
async ({ comfyPage }) => {
|
||||||
await comfyPage.page.keyboard.press(BYPASS_HOTKEY)
|
await comfyPage.page.getByText('Load Checkpoint').click()
|
||||||
|
await comfyPage.page.keyboard.press(BYPASS_HOTKEY)
|
||||||
|
|
||||||
const checkpointNode = comfyPage.vueNodes.getNodeByTitle('Load Checkpoint')
|
const checkpointNode =
|
||||||
await expect(checkpointNode).toHaveClass(BYPASS_CLASS)
|
comfyPage.vueNodes.getNodeByTitle('Load Checkpoint')
|
||||||
await comfyPage.nextFrame()
|
await expect(checkpointNode).toHaveClass(BYPASS_CLASS)
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot(
|
await comfyPage.nextFrame()
|
||||||
'vue-node-bypassed-state.png'
|
await expect(comfyPage.canvas).toHaveScreenshot(
|
||||||
)
|
'vue-node-bypassed-state.png'
|
||||||
|
)
|
||||||
|
|
||||||
await comfyPage.page.keyboard.press(BYPASS_HOTKEY)
|
await comfyPage.page.keyboard.press(BYPASS_HOTKEY)
|
||||||
await expect(checkpointNode).not.toHaveClass(BYPASS_CLASS)
|
await expect(checkpointNode).not.toHaveClass(BYPASS_CLASS)
|
||||||
})
|
}
|
||||||
|
)
|
||||||
|
|
||||||
test('should allow toggling bypass on multiple selected nodes with hotkey', async ({
|
test('should allow toggling bypass on multiple selected nodes with hotkey', async ({
|
||||||
comfyPage
|
comfyPage
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import {
|
|||||||
comfyPageFixture as test
|
comfyPageFixture as test
|
||||||
} from '../../../fixtures/ComfyPage'
|
} from '../../../fixtures/ComfyPage'
|
||||||
|
|
||||||
test.describe('Vue Node Custom Colors', () => {
|
test.describe('Vue Node Custom Colors', { tag: '@screenshot' }, () => {
|
||||||
test.beforeEach(async ({ comfyPage }) => {
|
test.beforeEach(async ({ comfyPage }) => {
|
||||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Top')
|
await comfyPage.setSetting('Comfy.UseNewMenu', 'Top')
|
||||||
await comfyPage.setSetting('Comfy.Canvas.SelectionToolbox', true)
|
await comfyPage.setSetting('Comfy.Canvas.SelectionToolbox', true)
|
||||||
|
|||||||
@@ -12,19 +12,24 @@ test.describe('Vue Node Mute', () => {
|
|||||||
await comfyPage.vueNodes.waitForNodes()
|
await comfyPage.vueNodes.waitForNodes()
|
||||||
})
|
})
|
||||||
|
|
||||||
test('should allow toggling mute on a selected node with hotkey', async ({
|
test(
|
||||||
comfyPage
|
'should allow toggling mute on a selected node with hotkey',
|
||||||
}) => {
|
{ tag: '@screenshot' },
|
||||||
await comfyPage.page.getByText('Load Checkpoint').click()
|
async ({ comfyPage }) => {
|
||||||
await comfyPage.page.keyboard.press(MUTE_HOTKEY)
|
await comfyPage.page.getByText('Load Checkpoint').click()
|
||||||
|
await comfyPage.page.keyboard.press(MUTE_HOTKEY)
|
||||||
|
|
||||||
const checkpointNode = comfyPage.vueNodes.getNodeByTitle('Load Checkpoint')
|
const checkpointNode =
|
||||||
await expect(checkpointNode).toHaveCSS('opacity', MUTE_OPACITY)
|
comfyPage.vueNodes.getNodeByTitle('Load Checkpoint')
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot('vue-node-muted-state.png')
|
await expect(checkpointNode).toHaveCSS('opacity', MUTE_OPACITY)
|
||||||
|
await expect(comfyPage.canvas).toHaveScreenshot(
|
||||||
|
'vue-node-muted-state.png'
|
||||||
|
)
|
||||||
|
|
||||||
await comfyPage.page.keyboard.press(MUTE_HOTKEY)
|
await comfyPage.page.keyboard.press(MUTE_HOTKEY)
|
||||||
await expect(checkpointNode).not.toHaveCSS('opacity', MUTE_OPACITY)
|
await expect(checkpointNode).not.toHaveCSS('opacity', MUTE_OPACITY)
|
||||||
})
|
}
|
||||||
|
)
|
||||||
|
|
||||||
test('should allow toggling mute on multiple selected nodes with hotkey', async ({
|
test('should allow toggling mute on multiple selected nodes with hotkey', async ({
|
||||||
comfyPage
|
comfyPage
|
||||||
|
|||||||
@@ -9,13 +9,17 @@ test.describe('Vue Upload Widgets', () => {
|
|||||||
await comfyPage.vueNodes.waitForNodes()
|
await comfyPage.vueNodes.waitForNodes()
|
||||||
})
|
})
|
||||||
|
|
||||||
test('should hide canvas-only upload buttons', async ({ comfyPage }) => {
|
test(
|
||||||
await comfyPage.setup()
|
'should hide canvas-only upload buttons',
|
||||||
await comfyPage.loadWorkflow('widgets/all_load_widgets')
|
{ tag: '@screenshot' },
|
||||||
await comfyPage.vueNodes.waitForNodes()
|
async ({ comfyPage }) => {
|
||||||
|
await comfyPage.setup()
|
||||||
|
await comfyPage.loadWorkflow('widgets/all_load_widgets')
|
||||||
|
await comfyPage.vueNodes.waitForNodes()
|
||||||
|
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot(
|
await expect(comfyPage.canvas).toHaveScreenshot(
|
||||||
'vue-nodes-upload-widgets.png'
|
'vue-nodes-upload-widgets.png'
|
||||||
)
|
)
|
||||||
})
|
}
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 81 KiB After Width: | Height: | Size: 80 KiB |
@@ -6,7 +6,7 @@ test.beforeEach(async ({ comfyPage }) => {
|
|||||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
|
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
|
||||||
})
|
})
|
||||||
|
|
||||||
test.describe('Combo text widget', () => {
|
test.describe('Combo text widget', { tag: ['@screenshot', '@widget'] }, () => {
|
||||||
test('Truncates text when resized', async ({ comfyPage }) => {
|
test('Truncates text when resized', async ({ comfyPage }) => {
|
||||||
await comfyPage.resizeLoadCheckpointNode(0.2, 1)
|
await comfyPage.resizeLoadCheckpointNode(0.2, 1)
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot(
|
await expect(comfyPage.canvas).toHaveScreenshot(
|
||||||
@@ -79,7 +79,7 @@ test.describe('Combo text widget', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test.describe('Boolean widget', () => {
|
test.describe('Boolean widget', { tag: ['@screenshot', '@widget'] }, () => {
|
||||||
test('Can toggle', async ({ comfyPage }) => {
|
test('Can toggle', async ({ comfyPage }) => {
|
||||||
await comfyPage.loadWorkflow('widgets/boolean_widget')
|
await comfyPage.loadWorkflow('widgets/boolean_widget')
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot('boolean_widget.png')
|
await expect(comfyPage.canvas).toHaveScreenshot('boolean_widget.png')
|
||||||
@@ -92,7 +92,7 @@ test.describe('Boolean widget', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test.describe('Slider widget', () => {
|
test.describe('Slider widget', { tag: ['@screenshot', '@widget'] }, () => {
|
||||||
test('Can drag adjust value', async ({ comfyPage }) => {
|
test('Can drag adjust value', async ({ comfyPage }) => {
|
||||||
await comfyPage.loadWorkflow('inputs/simple_slider')
|
await comfyPage.loadWorkflow('inputs/simple_slider')
|
||||||
const node = (await comfyPage.getFirstNodeRef())!
|
const node = (await comfyPage.getFirstNodeRef())!
|
||||||
@@ -113,7 +113,7 @@ test.describe('Slider widget', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test.describe('Number widget', () => {
|
test.describe('Number widget', { tag: ['@screenshot', '@widget'] }, () => {
|
||||||
test('Can drag adjust value', async ({ comfyPage }) => {
|
test('Can drag adjust value', async ({ comfyPage }) => {
|
||||||
await comfyPage.loadWorkflow('widgets/seed_widget')
|
await comfyPage.loadWorkflow('widgets/seed_widget')
|
||||||
|
|
||||||
@@ -134,22 +134,28 @@ test.describe('Number widget', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test.describe('Dynamic widget manipulation', () => {
|
test.describe(
|
||||||
test('Auto expand node when widget is added dynamically', async ({
|
'Dynamic widget manipulation',
|
||||||
comfyPage
|
{ tag: ['@screenshot', '@widget'] },
|
||||||
}) => {
|
() => {
|
||||||
await comfyPage.loadWorkflow('nodes/single_ksampler')
|
test('Auto expand node when widget is added dynamically', async ({
|
||||||
|
comfyPage
|
||||||
|
}) => {
|
||||||
|
await comfyPage.loadWorkflow('nodes/single_ksampler')
|
||||||
|
|
||||||
await comfyPage.page.evaluate(() => {
|
await comfyPage.page.evaluate(() => {
|
||||||
window['graph'].nodes[0].addWidget('number', 'new_widget', 10)
|
window['graph'].nodes[0].addWidget('number', 'new_widget', 10)
|
||||||
window['graph'].setDirtyCanvas(true, true)
|
window['graph'].setDirtyCanvas(true, true)
|
||||||
|
})
|
||||||
|
|
||||||
|
await expect(comfyPage.canvas).toHaveScreenshot(
|
||||||
|
'ksampler_widget_added.png'
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot('ksampler_widget_added.png')
|
test.describe('Image widget', { tag: ['@screenshot', '@widget'] }, () => {
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
test.describe('Image widget', () => {
|
|
||||||
test('Can load image', async ({ comfyPage }) => {
|
test('Can load image', async ({ comfyPage }) => {
|
||||||
await comfyPage.loadWorkflow('widgets/load_image_widget')
|
await comfyPage.loadWorkflow('widgets/load_image_widget')
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot('load_image_widget.png')
|
await expect(comfyPage.canvas).toHaveScreenshot('load_image_widget.png')
|
||||||
@@ -236,99 +242,103 @@ test.describe('Image widget', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test.describe('Animated image widget', () => {
|
test.describe(
|
||||||
// https://github.com/Comfy-Org/ComfyUI_frontend/issues/3718
|
'Animated image widget',
|
||||||
test.skip('Shows preview of uploaded animated image', async ({
|
{ tag: ['@screenshot', '@widget'] },
|
||||||
comfyPage
|
() => {
|
||||||
}) => {
|
// https://github.com/Comfy-Org/ComfyUI_frontend/issues/3718
|
||||||
await comfyPage.loadWorkflow('widgets/load_animated_webp')
|
test.skip('Shows preview of uploaded animated image', async ({
|
||||||
|
comfyPage
|
||||||
|
}) => {
|
||||||
|
await comfyPage.loadWorkflow('widgets/load_animated_webp')
|
||||||
|
|
||||||
// Get position of the load animated webp node
|
// Get position of the load animated webp node
|
||||||
const nodes = await comfyPage.getNodeRefsByType(
|
const nodes = await comfyPage.getNodeRefsByType(
|
||||||
'DevToolsLoadAnimatedImageTest'
|
'DevToolsLoadAnimatedImageTest'
|
||||||
)
|
)
|
||||||
const loadAnimatedWebpNode = nodes[0]
|
const loadAnimatedWebpNode = nodes[0]
|
||||||
const { x, y } = await loadAnimatedWebpNode.getPosition()
|
const { x, y } = await loadAnimatedWebpNode.getPosition()
|
||||||
|
|
||||||
// Drag and drop image file onto the load animated webp node
|
// Drag and drop image file onto the load animated webp node
|
||||||
await comfyPage.dragAndDropFile('animated_webp.webp', {
|
await comfyPage.dragAndDropFile('animated_webp.webp', {
|
||||||
dropPosition: { x, y }
|
dropPosition: { x, y }
|
||||||
|
})
|
||||||
|
|
||||||
|
// Expect the image preview to change automatically
|
||||||
|
await expect(comfyPage.canvas).toHaveScreenshot(
|
||||||
|
'animated_image_preview_drag_and_dropped.png'
|
||||||
|
)
|
||||||
|
|
||||||
|
// Move mouse and click on canvas to trigger render
|
||||||
|
await comfyPage.page.mouse.click(64, 64)
|
||||||
|
|
||||||
|
// Expect the image preview to change to the next frame of the animation
|
||||||
|
await expect(comfyPage.canvas).toHaveScreenshot(
|
||||||
|
'animated_image_preview_drag_and_dropped_next_frame.png'
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Expect the image preview to change automatically
|
test('Can drag-and-drop animated webp image', async ({ comfyPage }) => {
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot(
|
await comfyPage.loadWorkflow('widgets/load_animated_webp')
|
||||||
'animated_image_preview_drag_and_dropped.png'
|
|
||||||
)
|
|
||||||
|
|
||||||
// Move mouse and click on canvas to trigger render
|
// Get position of the load animated webp node
|
||||||
await comfyPage.page.mouse.click(64, 64)
|
const nodes = await comfyPage.getNodeRefsByType(
|
||||||
|
'DevToolsLoadAnimatedImageTest'
|
||||||
|
)
|
||||||
|
const loadAnimatedWebpNode = nodes[0]
|
||||||
|
const { x, y } = await loadAnimatedWebpNode.getPosition()
|
||||||
|
|
||||||
// Expect the image preview to change to the next frame of the animation
|
// Drag and drop image file onto the load animated webp node
|
||||||
await expect(comfyPage.canvas).toHaveScreenshot(
|
await comfyPage.dragAndDropFile('animated_webp.webp', {
|
||||||
'animated_image_preview_drag_and_dropped_next_frame.png'
|
dropPosition: { x, y },
|
||||||
)
|
waitForUpload: true
|
||||||
})
|
})
|
||||||
|
|
||||||
test('Can drag-and-drop animated webp image', async ({ comfyPage }) => {
|
// Expect the filename combo value to be updated
|
||||||
await comfyPage.loadWorkflow('widgets/load_animated_webp')
|
const fileComboWidget = await loadAnimatedWebpNode.getWidget(0)
|
||||||
|
const filename = await fileComboWidget.getValue()
|
||||||
// Get position of the load animated webp node
|
expect(filename).toContain('animated_webp.webp')
|
||||||
const nodes = await comfyPage.getNodeRefsByType(
|
|
||||||
'DevToolsLoadAnimatedImageTest'
|
|
||||||
)
|
|
||||||
const loadAnimatedWebpNode = nodes[0]
|
|
||||||
const { x, y } = await loadAnimatedWebpNode.getPosition()
|
|
||||||
|
|
||||||
// Drag and drop image file onto the load animated webp node
|
|
||||||
await comfyPage.dragAndDropFile('animated_webp.webp', {
|
|
||||||
dropPosition: { x, y },
|
|
||||||
waitForUpload: true
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// Expect the filename combo value to be updated
|
test('Can preview saved animated webp image', async ({ comfyPage }) => {
|
||||||
const fileComboWidget = await loadAnimatedWebpNode.getWidget(0)
|
await comfyPage.loadWorkflow('widgets/save_animated_webp')
|
||||||
const filename = await fileComboWidget.getValue()
|
|
||||||
expect(filename).toContain('animated_webp.webp')
|
|
||||||
})
|
|
||||||
|
|
||||||
test('Can preview saved animated webp image', async ({ comfyPage }) => {
|
// Get position of the load animated webp node
|
||||||
await comfyPage.loadWorkflow('widgets/save_animated_webp')
|
const loadNodes = await comfyPage.getNodeRefsByType(
|
||||||
|
'DevToolsLoadAnimatedImageTest'
|
||||||
|
)
|
||||||
|
const loadAnimatedWebpNode = loadNodes[0]
|
||||||
|
const { x, y } = await loadAnimatedWebpNode.getPosition()
|
||||||
|
|
||||||
// Get position of the load animated webp node
|
// Drag and drop image file onto the load animated webp node
|
||||||
const loadNodes = await comfyPage.getNodeRefsByType(
|
await comfyPage.dragAndDropFile('animated_webp.webp', {
|
||||||
'DevToolsLoadAnimatedImageTest'
|
dropPosition: { x, y }
|
||||||
)
|
})
|
||||||
const loadAnimatedWebpNode = loadNodes[0]
|
await comfyPage.nextFrame()
|
||||||
const { x, y } = await loadAnimatedWebpNode.getPosition()
|
|
||||||
|
|
||||||
// Drag and drop image file onto the load animated webp node
|
// Get the SaveAnimatedWEBP node
|
||||||
await comfyPage.dragAndDropFile('animated_webp.webp', {
|
const saveNodes = await comfyPage.getNodeRefsByType('SaveAnimatedWEBP')
|
||||||
dropPosition: { x, y }
|
const saveAnimatedWebpNode = saveNodes[0]
|
||||||
|
if (!saveAnimatedWebpNode)
|
||||||
|
throw new Error('SaveAnimatedWEBP node not found')
|
||||||
|
|
||||||
|
// Simulate the graph executing
|
||||||
|
await comfyPage.page.evaluate(
|
||||||
|
([loadId, saveId]) => {
|
||||||
|
// Set the output of the SaveAnimatedWEBP node to equal the loader node's image
|
||||||
|
window['app'].nodeOutputs[saveId] = window['app'].nodeOutputs[loadId]
|
||||||
|
app.canvas.setDirty(true)
|
||||||
|
},
|
||||||
|
[loadAnimatedWebpNode.id, saveAnimatedWebpNode.id]
|
||||||
|
)
|
||||||
|
await expect(
|
||||||
|
comfyPage.page.locator('.dom-widget').locator('img')
|
||||||
|
).toHaveCount(2)
|
||||||
})
|
})
|
||||||
await comfyPage.nextFrame()
|
}
|
||||||
|
)
|
||||||
|
|
||||||
// Get the SaveAnimatedWEBP node
|
test.describe('Load audio widget', { tag: ['@screenshot', '@widget'] }, () => {
|
||||||
const saveNodes = await comfyPage.getNodeRefsByType('SaveAnimatedWEBP')
|
|
||||||
const saveAnimatedWebpNode = saveNodes[0]
|
|
||||||
if (!saveAnimatedWebpNode)
|
|
||||||
throw new Error('SaveAnimatedWEBP node not found')
|
|
||||||
|
|
||||||
// Simulate the graph executing
|
|
||||||
await comfyPage.page.evaluate(
|
|
||||||
([loadId, saveId]) => {
|
|
||||||
// Set the output of the SaveAnimatedWEBP node to equal the loader node's image
|
|
||||||
window['app'].nodeOutputs[saveId] = window['app'].nodeOutputs[loadId]
|
|
||||||
app.canvas.setDirty(true)
|
|
||||||
},
|
|
||||||
[loadAnimatedWebpNode.id, saveAnimatedWebpNode.id]
|
|
||||||
)
|
|
||||||
await expect(
|
|
||||||
comfyPage.page.locator('.dom-widget').locator('img')
|
|
||||||
).toHaveCount(2)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
test.describe('Load audio widget', () => {
|
|
||||||
test('Can load audio', async ({ comfyPage }) => {
|
test('Can load audio', async ({ comfyPage }) => {
|
||||||
await comfyPage.loadWorkflow('widgets/load_audio_widget')
|
await comfyPage.loadWorkflow('widgets/load_audio_widget')
|
||||||
// Wait for the audio widget to be rendered in the DOM
|
// Wait for the audio widget to be rendered in the DOM
|
||||||
@@ -338,7 +348,7 @@ test.describe('Load audio widget', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test.describe('Unserialized widgets', () => {
|
test.describe('Unserialized widgets', { tag: '@widget' }, () => {
|
||||||
test('Unserialized widgets values do not mark graph as modified', async ({
|
test('Unserialized widgets values do not mark graph as modified', async ({
|
||||||
comfyPage
|
comfyPage
|
||||||
}) => {
|
}) => {
|
||||||
|
|||||||