mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-30 11:11:53 +00:00
Merge remote-tracking branch 'origin/main' into bl/amd-gpu-picker
This commit is contained in:
23
.github/actions/start-comfyui-server/action.yml
vendored
Normal file
23
.github/actions/start-comfyui-server/action.yml
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
name: Start ComfyUI Server
|
||||||
|
description: 'Start ComfyUI server in a container environment (assumes ComfyUI is pre-installed)'
|
||||||
|
|
||||||
|
inputs:
|
||||||
|
front_end_root:
|
||||||
|
description: 'Path to frontend dist directory'
|
||||||
|
required: false
|
||||||
|
default: '$GITHUB_WORKSPACE/dist'
|
||||||
|
timeout:
|
||||||
|
description: 'Timeout in seconds for server startup'
|
||||||
|
required: false
|
||||||
|
default: '600'
|
||||||
|
|
||||||
|
runs:
|
||||||
|
using: 'composite'
|
||||||
|
steps:
|
||||||
|
- name: Copy devtools and start server
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
cp -r ./tools/devtools/* /ComfyUI/custom_nodes/ComfyUI_devtools/
|
||||||
|
cd /ComfyUI && python3 main.py --cpu --multi-user --front-end-root "${{ inputs.front_end_root }}" &
|
||||||
|
wait-for-it --service 127.0.0.1:8188 -t ${{ inputs.timeout }}
|
||||||
104
.github/workflows/ci-tests-e2e.yaml
vendored
104
.github/workflows/ci-tests-e2e.yaml
vendored
@@ -15,66 +15,56 @@ concurrency:
|
|||||||
jobs:
|
jobs:
|
||||||
setup:
|
setup:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
outputs:
|
|
||||||
cache-key: ${{ steps.cache-key.outputs.key }}
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
# Setup Test Environment, build frontend but do not start server yet
|
|
||||||
- name: Setup ComfyUI server
|
|
||||||
uses: ./.github/actions/setup-comfyui-server
|
|
||||||
- name: Setup frontend
|
- name: Setup frontend
|
||||||
uses: ./.github/actions/setup-frontend
|
uses: ./.github/actions/setup-frontend
|
||||||
with:
|
with:
|
||||||
include_build_step: true
|
include_build_step: true
|
||||||
- name: Setup Playwright
|
|
||||||
uses: ./.github/actions/setup-playwright # Setup Playwright and cache browsers
|
|
||||||
|
|
||||||
# Save the entire workspace as cache for later test jobs to restore
|
# Upload only built dist/ (containerized test jobs will pnpm install without cache)
|
||||||
- name: Generate cache key
|
- name: Upload built frontend
|
||||||
id: cache-key
|
uses: actions/upload-artifact@v4
|
||||||
run: echo "key=$(date +%s)" >> $GITHUB_OUTPUT
|
|
||||||
- name: Save cache
|
|
||||||
uses: actions/cache/save@5a3ec84eff668545956fd18022155c47e93e2684
|
|
||||||
with:
|
with:
|
||||||
path: .
|
name: frontend-dist
|
||||||
key: comfyui-setup-${{ steps.cache-key.outputs.key }}
|
path: dist/
|
||||||
|
retention-days: 1
|
||||||
|
|
||||||
# Sharded chromium tests
|
# Sharded chromium tests
|
||||||
playwright-tests-chromium-sharded:
|
playwright-tests-chromium-sharded:
|
||||||
needs: setup
|
needs: setup
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 60
|
timeout-minutes: 60
|
||||||
|
container:
|
||||||
|
image: ghcr.io/comfy-org/comfyui-ci-container:0.0.8
|
||||||
|
credentials:
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
packages: read
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
shardIndex: [1, 2, 3, 4, 5, 6, 7, 8]
|
shardIndex: [1, 2, 3, 4, 5, 6, 7, 8]
|
||||||
shardTotal: [8]
|
shardTotal: [8]
|
||||||
steps:
|
steps:
|
||||||
# download built frontend repo from setup job
|
- name: Checkout repository
|
||||||
- name: Wait for cache propagation
|
uses: actions/checkout@v5
|
||||||
run: sleep 10
|
- name: Download built frontend
|
||||||
- name: Restore cached setup
|
uses: actions/download-artifact@v4
|
||||||
uses: actions/cache/restore@5a3ec84eff668545956fd18022155c47e93e2684
|
|
||||||
with:
|
with:
|
||||||
fail-on-cache-miss: true
|
name: frontend-dist
|
||||||
path: .
|
path: dist/
|
||||||
key: comfyui-setup-${{ needs.setup.outputs.cache-key }}
|
|
||||||
|
|
||||||
# Setup Test Environment for this runner, start server, use cached built frontend ./dist from 'setup' job
|
- name: Start ComfyUI server
|
||||||
- name: Setup ComfyUI server
|
uses: ./.github/actions/start-comfyui-server
|
||||||
uses: ./.github/actions/setup-comfyui-server
|
|
||||||
with:
|
|
||||||
launch_server: true
|
|
||||||
- name: Setup nodejs, pnpm, reuse built frontend
|
|
||||||
uses: ./.github/actions/setup-frontend
|
|
||||||
- name: Setup Playwright
|
|
||||||
uses: ./.github/actions/setup-playwright
|
|
||||||
|
|
||||||
# Run sharded tests and upload sharded reports
|
- name: Install frontend deps
|
||||||
|
run: pnpm install --frozen-lockfile
|
||||||
|
|
||||||
|
# Run sharded tests (browsers pre-installed in container)
|
||||||
- name: Run Playwright tests (Shard ${{ matrix.shardIndex }}/${{ matrix.shardTotal }})
|
- name: Run Playwright tests (Shard ${{ matrix.shardIndex }}/${{ matrix.shardTotal }})
|
||||||
id: playwright
|
id: playwright
|
||||||
run: pnpm exec playwright test --project=chromium --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }} --reporter=blob
|
run: pnpm exec playwright test --project=chromium --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }} --reporter=blob
|
||||||
@@ -94,39 +84,37 @@ jobs:
|
|||||||
timeout-minutes: 15
|
timeout-minutes: 15
|
||||||
needs: setup
|
needs: setup
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: ghcr.io/comfy-org/comfyui-ci-container:0.0.8
|
||||||
|
credentials:
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
packages: read
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
browser: [chromium-2x, chromium-0.5x, mobile-chrome]
|
browser: [chromium-2x, chromium-0.5x, mobile-chrome]
|
||||||
steps:
|
steps:
|
||||||
# download built frontend repo from setup job
|
- name: Checkout repository
|
||||||
- name: Wait for cache propagation
|
uses: actions/checkout@v5
|
||||||
run: sleep 10
|
- name: Download built frontend
|
||||||
- name: Restore cached setup
|
uses: actions/download-artifact@v4
|
||||||
uses: actions/cache/restore@5a3ec84eff668545956fd18022155c47e93e2684
|
|
||||||
with:
|
with:
|
||||||
fail-on-cache-miss: true
|
name: frontend-dist
|
||||||
path: .
|
path: dist/
|
||||||
key: comfyui-setup-${{ needs.setup.outputs.cache-key }}
|
|
||||||
|
|
||||||
# Setup Test Environment for this runner, start server, use cached built frontend ./dist from 'setup' job
|
- name: Start ComfyUI server
|
||||||
- name: Setup ComfyUI server
|
uses: ./.github/actions/start-comfyui-server
|
||||||
uses: ./.github/actions/setup-comfyui-server
|
|
||||||
with:
|
|
||||||
launch_server: true
|
|
||||||
- name: Setup nodejs, pnpm, reuse built frontend
|
|
||||||
uses: ./.github/actions/setup-frontend
|
|
||||||
- name: Setup Playwright
|
|
||||||
uses: ./.github/actions/setup-playwright
|
|
||||||
|
|
||||||
# Run tests and upload reports
|
- name: Install frontend deps
|
||||||
|
run: pnpm install --frozen-lockfile
|
||||||
|
|
||||||
|
# Run tests (browsers pre-installed in container)
|
||||||
- name: Run Playwright tests (${{ matrix.browser }})
|
- name: Run Playwright tests (${{ matrix.browser }})
|
||||||
id: playwright
|
id: playwright
|
||||||
run: |
|
run: pnpm exec playwright test --project=${{ matrix.browser }} --reporter=blob
|
||||||
# Run tests with blob reporter first
|
|
||||||
pnpm exec playwright test --project=${{ matrix.browser }} --reporter=blob
|
|
||||||
env:
|
env:
|
||||||
PLAYWRIGHT_BLOB_OUTPUT_DIR: ./blob-report
|
PLAYWRIGHT_BLOB_OUTPUT_DIR: ./blob-report
|
||||||
|
|
||||||
@@ -147,7 +135,7 @@ jobs:
|
|||||||
path: ./playwright-report/
|
path: ./playwright-report/
|
||||||
retention-days: 30
|
retention-days: 30
|
||||||
|
|
||||||
# Merge sharded test reports
|
# Merge sharded test reports (no container needed - only runs CLI)
|
||||||
merge-reports:
|
merge-reports:
|
||||||
needs: [playwright-tests-chromium-sharded]
|
needs: [playwright-tests-chromium-sharded]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -156,11 +144,9 @@ jobs:
|
|||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
# Setup Test Environment, we only need playwright to merge reports
|
# Setup pnpm/node to run playwright merge-reports (no browsers needed)
|
||||||
- name: Setup frontend
|
- name: Setup frontend
|
||||||
uses: ./.github/actions/setup-frontend
|
uses: ./.github/actions/setup-frontend
|
||||||
- name: Setup Playwright
|
|
||||||
uses: ./.github/actions/setup-playwright
|
|
||||||
|
|
||||||
- name: Download blob reports
|
- name: Download blob reports
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ jobs:
|
|||||||
) &&
|
) &&
|
||||||
startsWith(github.event.comment.body, '/update-playwright') )
|
startsWith(github.event.comment.body, '/update-playwright') )
|
||||||
outputs:
|
outputs:
|
||||||
cache-key: ${{ steps.cache-key.outputs.key }}
|
|
||||||
pr-number: ${{ steps.pr-info.outputs.pr-number }}
|
pr-number: ${{ steps.pr-info.outputs.pr-number }}
|
||||||
branch: ${{ steps.pr-info.outputs.branch }}
|
branch: ${{ steps.pr-info.outputs.branch }}
|
||||||
comment-id: ${{ steps.find-update-comment.outputs.comment-id }}
|
comment-id: ${{ steps.find-update-comment.outputs.comment-id }}
|
||||||
@@ -64,70 +63,63 @@ jobs:
|
|||||||
uses: ./.github/actions/setup-frontend
|
uses: ./.github/actions/setup-frontend
|
||||||
with:
|
with:
|
||||||
include_build_step: true
|
include_build_step: true
|
||||||
# Save expensive build artifacts (Python env, built frontend, node_modules)
|
|
||||||
# Source code will be checked out fresh in sharded jobs
|
# Upload built dist/ (containerized test jobs will pnpm install without cache)
|
||||||
- name: Generate cache key
|
- name: Upload built frontend
|
||||||
id: cache-key
|
uses: actions/upload-artifact@v4
|
||||||
run: echo "key=$(date +%s)" >> $GITHUB_OUTPUT
|
|
||||||
- name: Save cache
|
|
||||||
uses: actions/cache/save@5a3ec84eff668545956fd18022155c47e93e2684
|
|
||||||
with:
|
with:
|
||||||
path: |
|
name: frontend-dist
|
||||||
ComfyUI
|
path: dist/
|
||||||
dist
|
retention-days: 1
|
||||||
key: comfyui-setup-${{ steps.cache-key.outputs.key }}
|
|
||||||
|
|
||||||
# Sharded snapshot updates
|
# Sharded snapshot updates
|
||||||
update-snapshots-sharded:
|
update-snapshots-sharded:
|
||||||
needs: setup
|
needs: setup
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: ghcr.io/comfy-org/comfyui-ci-container:0.0.8
|
||||||
|
credentials:
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: read
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
shardIndex: [1, 2, 3, 4]
|
shardIndex: [1, 2, 3, 4]
|
||||||
shardTotal: [4]
|
shardTotal: [4]
|
||||||
steps:
|
steps:
|
||||||
# Checkout source code fresh (not cached)
|
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v5
|
||||||
with:
|
with:
|
||||||
ref: ${{ needs.setup.outputs.branch }}
|
ref: ${{ needs.setup.outputs.branch }}
|
||||||
|
- name: Download built frontend
|
||||||
# Restore expensive build artifacts from setup job
|
uses: actions/download-artifact@v4
|
||||||
- name: Restore cached artifacts
|
|
||||||
uses: actions/cache/restore@5a3ec84eff668545956fd18022155c47e93e2684
|
|
||||||
with:
|
with:
|
||||||
fail-on-cache-miss: true
|
name: frontend-dist
|
||||||
path: |
|
path: dist/
|
||||||
ComfyUI
|
|
||||||
dist
|
|
||||||
key: comfyui-setup-${{ needs.setup.outputs.cache-key }}
|
|
||||||
|
|
||||||
- name: Setup ComfyUI server (from cache)
|
- name: Start ComfyUI server
|
||||||
uses: ./.github/actions/setup-comfyui-server
|
uses: ./.github/actions/start-comfyui-server
|
||||||
with:
|
|
||||||
launch_server: true
|
|
||||||
|
|
||||||
- name: Setup nodejs, pnpm, reuse built frontend
|
- name: Install frontend deps
|
||||||
uses: ./.github/actions/setup-frontend
|
run: pnpm install --frozen-lockfile
|
||||||
|
|
||||||
- name: Setup Playwright
|
# Run sharded tests with snapshot updates (browsers pre-installed in container)
|
||||||
uses: ./.github/actions/setup-playwright
|
|
||||||
|
|
||||||
# Run sharded tests with snapshot updates
|
|
||||||
- name: Update snapshots (Shard ${{ matrix.shardIndex }}/${{ matrix.shardTotal }})
|
- name: Update snapshots (Shard ${{ matrix.shardIndex }}/${{ matrix.shardTotal }})
|
||||||
id: playwright-tests
|
id: playwright-tests
|
||||||
run: pnpm exec playwright test --update-snapshots --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
|
run: pnpm exec playwright test --update-snapshots --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
|
|
||||||
# Identify and stage only changed snapshot files
|
|
||||||
- name: Stage changed snapshot files
|
- name: Stage changed snapshot files
|
||||||
id: changed-snapshots
|
id: changed-snapshots
|
||||||
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
echo "=========================================="
|
|
||||||
echo "STAGING CHANGED SNAPSHOTS (Shard ${{ matrix.shardIndex }})"
|
# Configure git safe.directory for container environment
|
||||||
echo "=========================================="
|
git config --global --add safe.directory "$(pwd)"
|
||||||
|
|
||||||
# Get list of changed snapshot files (including untracked/new files)
|
# Get list of changed snapshot files (including untracked/new files)
|
||||||
changed_files=$( (
|
changed_files=$( (
|
||||||
@@ -136,43 +128,25 @@ jobs:
|
|||||||
) | sort -u | grep -E '\-snapshots/' || true )
|
) | sort -u | grep -E '\-snapshots/' || true )
|
||||||
|
|
||||||
if [ -z "$changed_files" ]; then
|
if [ -z "$changed_files" ]; then
|
||||||
echo "No snapshot changes in this shard"
|
echo "No snapshot changes in shard ${{ matrix.shardIndex }}"
|
||||||
echo "has-changes=false" >> $GITHUB_OUTPUT
|
echo "has-changes=false" >> $GITHUB_OUTPUT
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "✓ Found changed files:"
|
|
||||||
echo "$changed_files"
|
|
||||||
file_count=$(echo "$changed_files" | wc -l)
|
file_count=$(echo "$changed_files" | wc -l)
|
||||||
echo "Count: $file_count"
|
echo "Shard ${{ matrix.shardIndex }}: $file_count changed snapshot(s):"
|
||||||
|
echo "$changed_files"
|
||||||
echo "has-changes=true" >> $GITHUB_OUTPUT
|
echo "has-changes=true" >> $GITHUB_OUTPUT
|
||||||
echo ""
|
|
||||||
|
|
||||||
# Create staging directory
|
# Copy changed files to staging directory
|
||||||
mkdir -p /tmp/changed_snapshots_shard
|
mkdir -p /tmp/changed_snapshots_shard
|
||||||
|
|
||||||
# Copy only changed files, preserving directory structure
|
|
||||||
# Strip 'browser_tests/' prefix to avoid double nesting
|
|
||||||
echo "Copying changed files to staging directory..."
|
|
||||||
while IFS= read -r file; do
|
while IFS= read -r file; do
|
||||||
# Skip paths that no longer exist (e.g. deletions)
|
[ -f "$file" ] || continue
|
||||||
if [ ! -f "$file" ]; then
|
|
||||||
echo " → (skipped; not a file) $file"
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
# Remove 'browser_tests/' prefix
|
|
||||||
file_without_prefix="${file#browser_tests/}"
|
file_without_prefix="${file#browser_tests/}"
|
||||||
# Create parent directories
|
|
||||||
mkdir -p "/tmp/changed_snapshots_shard/$(dirname "$file_without_prefix")"
|
mkdir -p "/tmp/changed_snapshots_shard/$(dirname "$file_without_prefix")"
|
||||||
# Copy file
|
|
||||||
cp "$file" "/tmp/changed_snapshots_shard/$file_without_prefix"
|
cp "$file" "/tmp/changed_snapshots_shard/$file_without_prefix"
|
||||||
echo " → $file_without_prefix"
|
|
||||||
done <<< "$changed_files"
|
done <<< "$changed_files"
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo "Staged files for upload:"
|
|
||||||
find /tmp/changed_snapshots_shard -type f
|
|
||||||
|
|
||||||
# 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@v4
|
||||||
@@ -213,9 +187,15 @@ jobs:
|
|||||||
echo "=========================================="
|
echo "=========================================="
|
||||||
echo "DOWNLOADED SNAPSHOT FILES"
|
echo "DOWNLOADED SNAPSHOT FILES"
|
||||||
echo "=========================================="
|
echo "=========================================="
|
||||||
find ./downloaded-snapshots -type f
|
if [ -d "./downloaded-snapshots" ]; then
|
||||||
echo ""
|
find ./downloaded-snapshots -type f
|
||||||
echo "Total files: $(find ./downloaded-snapshots -type f | wc -l)"
|
echo ""
|
||||||
|
echo "Total files: $(find ./downloaded-snapshots -type f | wc -l)"
|
||||||
|
else
|
||||||
|
echo "No snapshot artifacts downloaded (no changes in any shard)"
|
||||||
|
echo ""
|
||||||
|
echo "Total files: 0"
|
||||||
|
fi
|
||||||
|
|
||||||
# Merge only the changed files into browser_tests
|
# Merge only the changed files into browser_tests
|
||||||
- name: Merge changed snapshots
|
- name: Merge changed snapshots
|
||||||
@@ -226,6 +206,16 @@ jobs:
|
|||||||
echo "MERGING CHANGED SNAPSHOTS"
|
echo "MERGING CHANGED SNAPSHOTS"
|
||||||
echo "=========================================="
|
echo "=========================================="
|
||||||
|
|
||||||
|
# Check if any artifacts were downloaded
|
||||||
|
if [ ! -d "./downloaded-snapshots" ]; then
|
||||||
|
echo "No snapshot artifacts to merge"
|
||||||
|
echo "=========================================="
|
||||||
|
echo "MERGE COMPLETE"
|
||||||
|
echo "=========================================="
|
||||||
|
echo "Shards merged: 0"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
# Verify target directory exists
|
# Verify target directory exists
|
||||||
if [ ! -d "browser_tests" ]; then
|
if [ ! -d "browser_tests" ]; then
|
||||||
echo "::error::Target directory 'browser_tests' does not exist"
|
echo "::error::Target directory 'browser_tests' does not exist"
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 21 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 96 KiB After Width: | Height: | Size: 91 KiB |
849
pnpm-lock.yaml
generated
849
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -15,7 +15,7 @@ catalog:
|
|||||||
'@nx/playwright': 22.2.6
|
'@nx/playwright': 22.2.6
|
||||||
'@nx/storybook': 22.2.4
|
'@nx/storybook': 22.2.4
|
||||||
'@nx/vite': 22.2.6
|
'@nx/vite': 22.2.6
|
||||||
'@pinia/testing': ^0.1.5
|
'@pinia/testing': ^1.0.3
|
||||||
'@playwright/test': ^1.57.0
|
'@playwright/test': ^1.57.0
|
||||||
'@prettier/plugin-oxc': ^0.1.3
|
'@prettier/plugin-oxc': ^0.1.3
|
||||||
'@primeuix/forms': 0.0.2
|
'@primeuix/forms': 0.0.2
|
||||||
@@ -26,7 +26,7 @@ catalog:
|
|||||||
'@primevue/icons': 4.2.5
|
'@primevue/icons': 4.2.5
|
||||||
'@primevue/themes': ^4.2.5
|
'@primevue/themes': ^4.2.5
|
||||||
'@sentry/vite-plugin': ^4.6.0
|
'@sentry/vite-plugin': ^4.6.0
|
||||||
'@sentry/vue': ^8.48.0
|
'@sentry/vue': ^10.32.1
|
||||||
'@sparkjsdev/spark': ^0.1.10
|
'@sparkjsdev/spark': ^0.1.10
|
||||||
'@storybook/addon-docs': ^10.1.9
|
'@storybook/addon-docs': ^10.1.9
|
||||||
'@storybook/vue3': ^10.1.9
|
'@storybook/vue3': ^10.1.9
|
||||||
@@ -39,8 +39,8 @@ catalog:
|
|||||||
'@types/semver': ^7.7.0
|
'@types/semver': ^7.7.0
|
||||||
'@types/three': ^0.169.0
|
'@types/three': ^0.169.0
|
||||||
'@vitejs/plugin-vue': ^6.0.0
|
'@vitejs/plugin-vue': ^6.0.0
|
||||||
'@vitest/coverage-v8': ^3.2.4
|
'@vitest/coverage-v8': ^4.0.16
|
||||||
'@vitest/ui': ^3.2.0
|
'@vitest/ui': ^4.0.16
|
||||||
'@vue/test-utils': ^2.4.6
|
'@vue/test-utils': ^2.4.6
|
||||||
'@vueuse/core': ^11.0.0
|
'@vueuse/core': ^11.0.0
|
||||||
'@vueuse/integrations': ^13.9.0
|
'@vueuse/integrations': ^13.9.0
|
||||||
@@ -59,11 +59,11 @@ catalog:
|
|||||||
eslint-plugin-unused-imports: ^4.3.0
|
eslint-plugin-unused-imports: ^4.3.0
|
||||||
eslint-plugin-vue: ^10.6.2
|
eslint-plugin-vue: ^10.6.2
|
||||||
firebase: ^11.6.0
|
firebase: ^11.6.0
|
||||||
globals: ^15.9.0
|
globals: ^16.5.0
|
||||||
happy-dom: ^15.11.0
|
happy-dom: ^20.0.11
|
||||||
husky: ^9.1.7
|
husky: ^9.1.7
|
||||||
jiti: 2.6.1
|
jiti: 2.6.1
|
||||||
jsdom: ^26.1.0
|
jsdom: ^27.4.0
|
||||||
knip: ^5.75.1
|
knip: ^5.75.1
|
||||||
lint-staged: ^16.2.7
|
lint-staged: ^16.2.7
|
||||||
markdown-table: ^3.0.4
|
markdown-table: ^3.0.4
|
||||||
@@ -72,7 +72,7 @@ catalog:
|
|||||||
oxlint: ^1.33.0
|
oxlint: ^1.33.0
|
||||||
oxlint-tsgolint: ^0.9.1
|
oxlint-tsgolint: ^0.9.1
|
||||||
picocolors: ^1.1.1
|
picocolors: ^1.1.1
|
||||||
pinia: ^2.1.7
|
pinia: ^3.0.4
|
||||||
postcss-html: ^1.8.0
|
postcss-html: ^1.8.0
|
||||||
prettier: ^3.7.4
|
prettier: ^3.7.4
|
||||||
pretty-bytes: ^7.1.0
|
pretty-bytes: ^7.1.0
|
||||||
@@ -96,13 +96,13 @@ catalog:
|
|||||||
vite-plugin-dts: ^4.5.4
|
vite-plugin-dts: ^4.5.4
|
||||||
vite-plugin-html: ^3.2.2
|
vite-plugin-html: ^3.2.2
|
||||||
vite-plugin-vue-devtools: ^8.0.0
|
vite-plugin-vue-devtools: ^8.0.0
|
||||||
vitest: ^3.2.4
|
vitest: ^4.0.16
|
||||||
vue: ^3.5.13
|
vue: ^3.5.13
|
||||||
vue-component-type-helpers: ^3.0.7
|
vue-component-type-helpers: ^3.2.1
|
||||||
vue-eslint-parser: ^10.2.0
|
vue-eslint-parser: ^10.2.0
|
||||||
vue-i18n: ^9.14.3
|
vue-i18n: ^9.14.3
|
||||||
vue-router: ^4.4.3
|
vue-router: ^4.4.3
|
||||||
vue-tsc: ^3.1.8
|
vue-tsc: ^3.2.1
|
||||||
vuefire: ^3.2.1
|
vuefire: ^3.2.1
|
||||||
yjs: ^13.6.27
|
yjs: ^13.6.27
|
||||||
zod: ^3.23.8
|
zod: ^3.23.8
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ export const extractExecutionError = (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type UseJobErrorReportingOptions = {
|
export type UseJobErrorReportingOptions = {
|
||||||
taskForJob: ComputedRef<TaskItemImpl | null>
|
taskForJob: ComputedRef<TaskItemImpl | null>
|
||||||
copyToClipboard: CopyHandler
|
copyToClipboard: CopyHandler
|
||||||
dialog: JobErrorDialogService
|
dialog: JobErrorDialogService
|
||||||
|
|||||||
@@ -104,7 +104,8 @@ export function useMaskEditorLoader() {
|
|||||||
// If we have a widget filename, we should prioritize it over the node image
|
// If we have a widget filename, we should prioritize it over the node image
|
||||||
// because the node image might be stale (e.g. from a previous save)
|
// because the node image might be stale (e.g. from a previous save)
|
||||||
// while the widget value reflects the current selection.
|
// while the widget value reflects the current selection.
|
||||||
if (widgetFilename) {
|
// Skip internal reference formats (e.g. "$35-0" used by some plugins like Impact-Pack)
|
||||||
|
if (widgetFilename && !widgetFilename.startsWith('$')) {
|
||||||
try {
|
try {
|
||||||
// Parse the widget value which might be in format "subfolder/filename [type]" or just "filename"
|
// Parse the widget value which might be in format "subfolder/filename [type]" or just "filename"
|
||||||
let filename = widgetFilename
|
let filename = widgetFilename
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import _ from 'es-toolkit/compat'
|
import _ from 'es-toolkit/compat'
|
||||||
import type { LGraphNode } from '@/lib/litegraph/src/litegraph'
|
import type { LGraphNode } from '@/lib/litegraph/src/litegraph'
|
||||||
|
|
||||||
import { app } from '@/scripts/app'
|
import { app, ComfyApp } from '@/scripts/app'
|
||||||
import { useMaskEditorStore } from '@/stores/maskEditorStore'
|
import { useMaskEditorStore } from '@/stores/maskEditorStore'
|
||||||
import { useDialogStore } from '@/stores/dialogStore'
|
import { useDialogStore } from '@/stores/dialogStore'
|
||||||
import { useMaskEditor } from '@/composables/maskeditor/useMaskEditor'
|
import { useMaskEditor } from '@/composables/maskeditor/useMaskEditor'
|
||||||
@@ -20,6 +20,18 @@ function openMaskEditor(node: LGraphNode): void {
|
|||||||
useMaskEditor().openMaskEditor(node)
|
useMaskEditor().openMaskEditor(node)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Open mask editor from clipspace (for plugin compatibility)
|
||||||
|
// This is called when ComfyApp.open_maskeditor() is invoked without arguments
|
||||||
|
function openMaskEditorFromClipspace(): void {
|
||||||
|
const node = ComfyApp.clipspace_return_node as LGraphNode | null
|
||||||
|
if (!node) {
|
||||||
|
console.error('[MaskEditor] No clipspace_return_node found')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
openMaskEditor(node)
|
||||||
|
}
|
||||||
|
|
||||||
// Check if the dialog is already opened
|
// Check if the dialog is already opened
|
||||||
function isOpened(): boolean {
|
function isOpened(): boolean {
|
||||||
return useDialogStore().isDialogOpen('global-mask-editor')
|
return useDialogStore().isDialogOpen('global-mask-editor')
|
||||||
@@ -78,7 +90,16 @@ app.registerExtension({
|
|||||||
label: 'Decrease Brush Size in MaskEditor',
|
label: 'Decrease Brush Size in MaskEditor',
|
||||||
function: () => changeBrushSize((old) => _.clamp(old - 4, 1, 100))
|
function: () => changeBrushSize((old) => _.clamp(old - 4, 1, 100))
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
init() {
|
||||||
|
// Set up ComfyApp static methods for plugin compatibility (deprecated)
|
||||||
|
ComfyApp.open_maskeditor = openMaskEditorFromClipspace
|
||||||
|
|
||||||
|
console.warn(
|
||||||
|
'[MaskEditor] ComfyApp.open_maskeditor is deprecated. ' +
|
||||||
|
'Plugins should migrate to using the command system or direct node context menu integration.'
|
||||||
|
)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const changeBrushSize = async (sizeChanger: (oldSize: number) => number) => {
|
const changeBrushSize = async (sizeChanger: (oldSize: number) => number) => {
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { useToast } from 'primevue/usetoast'
|
import { useToast } from 'primevue/usetoast'
|
||||||
import { inject } from 'vue'
|
import { inject } from 'vue'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
import ConfirmationDialogContent from '@/components/dialog/content/ConfirmationDialogContent.vue'
|
import ConfirmationDialogContent from '@/components/dialog/content/ConfirmationDialogContent.vue'
|
||||||
import { downloadFile } from '@/base/common/downloadUtil'
|
import { downloadFile } from '@/base/common/downloadUtil'
|
||||||
import { useCopyToClipboard } from '@/composables/useCopyToClipboard'
|
import { useCopyToClipboard } from '@/composables/useCopyToClipboard'
|
||||||
import { t } from '@/i18n'
|
|
||||||
import { isCloud } from '@/platform/distribution/types'
|
import { isCloud } from '@/platform/distribution/types'
|
||||||
import { useWorkflowActionsService } from '@/platform/workflow/core/services/workflowActionsService'
|
import { useWorkflowActionsService } from '@/platform/workflow/core/services/workflowActionsService'
|
||||||
import { extractWorkflowFromAsset } from '@/platform/workflow/utils/workflowExtractionUtil'
|
import { extractWorkflowFromAsset } from '@/platform/workflow/utils/workflowExtractionUtil'
|
||||||
@@ -25,6 +25,7 @@ import { MediaAssetKey } from '../schemas/mediaAssetSchema'
|
|||||||
import { assetService } from '../services/assetService'
|
import { assetService } from '../services/assetService'
|
||||||
|
|
||||||
export function useMediaAssetActions() {
|
export function useMediaAssetActions() {
|
||||||
|
const { t } = useI18n()
|
||||||
const toast = useToast()
|
const toast = useToast()
|
||||||
const dialogStore = useDialogStore()
|
const dialogStore = useDialogStore()
|
||||||
const mediaContext = inject(MediaAssetKey, null)
|
const mediaContext = inject(MediaAssetKey, null)
|
||||||
@@ -79,7 +80,7 @@ export function useMediaAssetActions() {
|
|||||||
toast.add({
|
toast.add({
|
||||||
severity: 'success',
|
severity: 'success',
|
||||||
summary: t('g.success'),
|
summary: t('g.success'),
|
||||||
detail: t('g.downloadStarted'),
|
detail: t('mediaAsset.selection.downloadsStarted', { count: 1 }),
|
||||||
life: 2000
|
life: 2000
|
||||||
})
|
})
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -7,10 +7,14 @@ import type { AssetMeta } from '../schemas/mediaAssetSchema'
|
|||||||
import { useMediaAssetGalleryStore } from './useMediaAssetGalleryStore'
|
import { useMediaAssetGalleryStore } from './useMediaAssetGalleryStore'
|
||||||
|
|
||||||
vi.mock('@/stores/queueStore', () => ({
|
vi.mock('@/stores/queueStore', () => ({
|
||||||
ResultItemImpl: vi.fn().mockImplementation((data) => ({
|
ResultItemImpl: vi
|
||||||
...data,
|
.fn<typeof ResultItemImpl>()
|
||||||
url: ''
|
.mockImplementation(function (data) {
|
||||||
}))
|
Object.assign(this, {
|
||||||
|
...data,
|
||||||
|
url: ''
|
||||||
|
})
|
||||||
|
})
|
||||||
}))
|
}))
|
||||||
|
|
||||||
describe('useMediaAssetGalleryStore', () => {
|
describe('useMediaAssetGalleryStore', () => {
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export const CORE_SETTINGS: SettingParams[] = [
|
|||||||
id: 'Comfy.Validation.Workflows',
|
id: 'Comfy.Validation.Workflows',
|
||||||
name: 'Validate workflows',
|
name: 'Validate workflows',
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
defaultValue: isCloud ? false : true
|
defaultValue: false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'Comfy.NodeSearchBoxImpl',
|
id: 'Comfy.NodeSearchBoxImpl',
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ const createMouseEvent = (
|
|||||||
|
|
||||||
describe('useNodePointerInteractions', () => {
|
describe('useNodePointerInteractions', () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
vi.restoreAllMocks()
|
vi.resetAllMocks()
|
||||||
selectedItemsState.items = []
|
selectedItemsState.items = []
|
||||||
setActivePinia(createTestingPinia())
|
setActivePinia(createTestingPinia())
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
/>
|
/>
|
||||||
<WidgetWithControl
|
<WidgetWithControl
|
||||||
v-else-if="widget.controlWidget"
|
v-else-if="widget.controlWidget"
|
||||||
|
v-model="modelValue"
|
||||||
:component="WidgetSelectDefault"
|
:component="WidgetSelectDefault"
|
||||||
:widget="widget as StringControlWidget"
|
:widget="widget as StringControlWidget"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import type { JobState } from '@/types/queue'
|
|||||||
import { formatDuration } from '@/utils/formatUtil'
|
import { formatDuration } from '@/utils/formatUtil'
|
||||||
import { clampPercentInt, formatPercent0 } from '@/utils/numberUtil'
|
import { clampPercentInt, formatPercent0 } from '@/utils/numberUtil'
|
||||||
|
|
||||||
type BuildJobDisplayCtx = {
|
export type BuildJobDisplayCtx = {
|
||||||
t: (k: string, v?: Record<string, any>) => string
|
t: (k: string, v?: Record<string, any>) => string
|
||||||
locale: string
|
locale: string
|
||||||
formatClockTimeFn: (ts: number, locale: string) => string
|
formatClockTimeFn: (ts: number, locale: string) => string
|
||||||
|
|||||||
@@ -24,7 +24,9 @@ describe('API Feature Flags', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Mock WebSocket constructor
|
// Mock WebSocket constructor
|
||||||
global.WebSocket = vi.fn().mockImplementation(() => mockWebSocket) as any
|
vi.stubGlobal('WebSocket', function (this: WebSocket) {
|
||||||
|
Object.assign(this, mockWebSocket)
|
||||||
|
})
|
||||||
|
|
||||||
// Reset API state
|
// Reset API state
|
||||||
api.serverFeatureFlags = {}
|
api.serverFeatureFlags = {}
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ const createWrapper = (props = {}) => {
|
|||||||
|
|
||||||
describe('ZoomControlsModal', () => {
|
describe('ZoomControlsModal', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
vi.restoreAllMocks()
|
vi.resetAllMocks()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should execute zoom in command when zoom in button is clicked', async () => {
|
it('should execute zoom in command when zoom in button is clicked', async () => {
|
||||||
|
|||||||
@@ -4,7 +4,10 @@ import type { ComputedRef } from 'vue'
|
|||||||
|
|
||||||
import type { ExecutionErrorWsMessage } from '@/schemas/apiSchema'
|
import type { ExecutionErrorWsMessage } from '@/schemas/apiSchema'
|
||||||
import type { TaskItemImpl } from '@/stores/queueStore'
|
import type { TaskItemImpl } from '@/stores/queueStore'
|
||||||
import type { JobErrorDialogService } from '@/components/queue/job/useJobErrorReporting'
|
import type {
|
||||||
|
JobErrorDialogService,
|
||||||
|
UseJobErrorReportingOptions
|
||||||
|
} from '@/components/queue/job/useJobErrorReporting'
|
||||||
import * as jobErrorReporting from '@/components/queue/job/useJobErrorReporting'
|
import * as jobErrorReporting from '@/components/queue/job/useJobErrorReporting'
|
||||||
|
|
||||||
const createExecutionErrorMessage = (
|
const createExecutionErrorMessage = (
|
||||||
@@ -90,9 +93,9 @@ describe('extractExecutionError', () => {
|
|||||||
describe('useJobErrorReporting', () => {
|
describe('useJobErrorReporting', () => {
|
||||||
let taskState = ref<TaskItemImpl | null>(null)
|
let taskState = ref<TaskItemImpl | null>(null)
|
||||||
let taskForJob: ComputedRef<TaskItemImpl | null>
|
let taskForJob: ComputedRef<TaskItemImpl | null>
|
||||||
let copyToClipboard: ReturnType<typeof vi.fn>
|
let copyToClipboard: UseJobErrorReportingOptions['copyToClipboard']
|
||||||
let showExecutionErrorDialog: ReturnType<typeof vi.fn>
|
let showExecutionErrorDialog: JobErrorDialogService['showExecutionErrorDialog']
|
||||||
let showErrorDialog: ReturnType<typeof vi.fn>
|
let showErrorDialog: JobErrorDialogService['showErrorDialog']
|
||||||
let dialog: JobErrorDialogService
|
let dialog: JobErrorDialogService
|
||||||
let composable: ReturnType<typeof jobErrorReporting.useJobErrorReporting>
|
let composable: ReturnType<typeof jobErrorReporting.useJobErrorReporting>
|
||||||
|
|
||||||
@@ -146,7 +149,7 @@ describe('useJobErrorReporting', () => {
|
|||||||
expect(copyToClipboard).toHaveBeenCalledTimes(1)
|
expect(copyToClipboard).toHaveBeenCalledTimes(1)
|
||||||
expect(copyToClipboard).toHaveBeenCalledWith('Clipboard failure')
|
expect(copyToClipboard).toHaveBeenCalledWith('Clipboard failure')
|
||||||
|
|
||||||
copyToClipboard.mockClear()
|
vi.mocked(copyToClipboard).mockClear()
|
||||||
taskState.value = createTaskWithMessages([])
|
taskState.value = createTaskWithMessages([])
|
||||||
composable.copyErrorMessage()
|
composable.copyErrorMessage()
|
||||||
expect(copyToClipboard).not.toHaveBeenCalled()
|
expect(copyToClipboard).not.toHaveBeenCalled()
|
||||||
@@ -174,7 +177,7 @@ describe('useJobErrorReporting', () => {
|
|||||||
composable.reportJobError()
|
composable.reportJobError()
|
||||||
expect(showExecutionErrorDialog).not.toHaveBeenCalled()
|
expect(showExecutionErrorDialog).not.toHaveBeenCalled()
|
||||||
expect(showErrorDialog).toHaveBeenCalledTimes(1)
|
expect(showErrorDialog).toHaveBeenCalledTimes(1)
|
||||||
const [errorArg, optionsArg] = showErrorDialog.mock.calls[0]
|
const [errorArg, optionsArg] = vi.mocked(showErrorDialog).mock.calls[0]
|
||||||
expect(errorArg).toBeInstanceOf(Error)
|
expect(errorArg).toBeInstanceOf(Error)
|
||||||
expect(errorArg.message).toBe(message)
|
expect(errorArg.message).toBe(message)
|
||||||
expect(optionsArg).toEqual({ reportType: 'queueJobError' })
|
expect(optionsArg).toEqual({ reportType: 'queueJobError' })
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ describe('useCanvasHistory', () => {
|
|||||||
return rafCallCount
|
return rafCallCount
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
vi.spyOn(window, 'alert').mockImplementation(() => {})
|
vi.stubGlobal('alert', () => {})
|
||||||
|
|
||||||
const createMockImageData = () => {
|
const createMockImageData = () => {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ type NodePack = components['schemas']['Node']
|
|||||||
|
|
||||||
describe('usePacksSelection', () => {
|
describe('usePacksSelection', () => {
|
||||||
let managerStore: ReturnType<typeof useComfyManagerStore>
|
let managerStore: ReturnType<typeof useComfyManagerStore>
|
||||||
let mockIsPackInstalled: ReturnType<typeof vi.fn>
|
let mockIsPackInstalled: (packName: string | undefined) => boolean
|
||||||
|
|
||||||
const createMockPack = (id: string): NodePack => ({
|
const createMockPack = (id: string): NodePack => ({
|
||||||
id,
|
id,
|
||||||
@@ -58,7 +58,7 @@ describe('usePacksSelection', () => {
|
|||||||
createMockPack('pack3')
|
createMockPack('pack3')
|
||||||
])
|
])
|
||||||
|
|
||||||
mockIsPackInstalled.mockImplementation((id: string) => {
|
vi.mocked(mockIsPackInstalled).mockImplementation((id) => {
|
||||||
return id === 'pack1' || id === 'pack3'
|
return id === 'pack1' || id === 'pack3'
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -76,7 +76,7 @@ describe('usePacksSelection', () => {
|
|||||||
createMockPack('pack2')
|
createMockPack('pack2')
|
||||||
])
|
])
|
||||||
|
|
||||||
mockIsPackInstalled.mockReturnValue(false)
|
vi.mocked(mockIsPackInstalled).mockReturnValue(false)
|
||||||
|
|
||||||
const { installedPacks } = usePacksSelection(nodePacks)
|
const { installedPacks } = usePacksSelection(nodePacks)
|
||||||
|
|
||||||
@@ -85,7 +85,7 @@ describe('usePacksSelection', () => {
|
|||||||
|
|
||||||
it('should update when nodePacks ref changes', () => {
|
it('should update when nodePacks ref changes', () => {
|
||||||
const nodePacks = ref<NodePack[]>([createMockPack('pack1')])
|
const nodePacks = ref<NodePack[]>([createMockPack('pack1')])
|
||||||
mockIsPackInstalled.mockReturnValue(true)
|
vi.mocked(mockIsPackInstalled).mockReturnValue(true)
|
||||||
|
|
||||||
const { installedPacks } = usePacksSelection(nodePacks)
|
const { installedPacks } = usePacksSelection(nodePacks)
|
||||||
expect(installedPacks.value).toHaveLength(1)
|
expect(installedPacks.value).toHaveLength(1)
|
||||||
@@ -109,7 +109,7 @@ describe('usePacksSelection', () => {
|
|||||||
createMockPack('pack3')
|
createMockPack('pack3')
|
||||||
])
|
])
|
||||||
|
|
||||||
mockIsPackInstalled.mockImplementation((id: string) => {
|
vi.mocked(mockIsPackInstalled).mockImplementation((id) => {
|
||||||
return id === 'pack1'
|
return id === 'pack1'
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -126,7 +126,7 @@ describe('usePacksSelection', () => {
|
|||||||
createMockPack('pack2')
|
createMockPack('pack2')
|
||||||
])
|
])
|
||||||
|
|
||||||
mockIsPackInstalled.mockReturnValue(false)
|
vi.mocked(mockIsPackInstalled).mockReturnValue(false)
|
||||||
|
|
||||||
const { notInstalledPacks } = usePacksSelection(nodePacks)
|
const { notInstalledPacks } = usePacksSelection(nodePacks)
|
||||||
|
|
||||||
@@ -141,7 +141,7 @@ describe('usePacksSelection', () => {
|
|||||||
createMockPack('pack2')
|
createMockPack('pack2')
|
||||||
])
|
])
|
||||||
|
|
||||||
mockIsPackInstalled.mockReturnValue(true)
|
vi.mocked(mockIsPackInstalled).mockReturnValue(true)
|
||||||
|
|
||||||
const { isAllInstalled } = usePacksSelection(nodePacks)
|
const { isAllInstalled } = usePacksSelection(nodePacks)
|
||||||
|
|
||||||
@@ -154,7 +154,7 @@ describe('usePacksSelection', () => {
|
|||||||
createMockPack('pack2')
|
createMockPack('pack2')
|
||||||
])
|
])
|
||||||
|
|
||||||
mockIsPackInstalled.mockImplementation((id: string) => id === 'pack1')
|
vi.mocked(mockIsPackInstalled).mockImplementation((id) => id === 'pack1')
|
||||||
|
|
||||||
const { isAllInstalled } = usePacksSelection(nodePacks)
|
const { isAllInstalled } = usePacksSelection(nodePacks)
|
||||||
|
|
||||||
@@ -177,7 +177,7 @@ describe('usePacksSelection', () => {
|
|||||||
createMockPack('pack2')
|
createMockPack('pack2')
|
||||||
])
|
])
|
||||||
|
|
||||||
mockIsPackInstalled.mockReturnValue(false)
|
vi.mocked(mockIsPackInstalled).mockReturnValue(false)
|
||||||
|
|
||||||
const { isNoneInstalled } = usePacksSelection(nodePacks)
|
const { isNoneInstalled } = usePacksSelection(nodePacks)
|
||||||
|
|
||||||
@@ -190,7 +190,7 @@ describe('usePacksSelection', () => {
|
|||||||
createMockPack('pack2')
|
createMockPack('pack2')
|
||||||
])
|
])
|
||||||
|
|
||||||
mockIsPackInstalled.mockImplementation((id: string) => id === 'pack1')
|
vi.mocked(mockIsPackInstalled).mockImplementation((id) => id === 'pack1')
|
||||||
|
|
||||||
const { isNoneInstalled } = usePacksSelection(nodePacks)
|
const { isNoneInstalled } = usePacksSelection(nodePacks)
|
||||||
|
|
||||||
@@ -214,7 +214,7 @@ describe('usePacksSelection', () => {
|
|||||||
createMockPack('pack3')
|
createMockPack('pack3')
|
||||||
])
|
])
|
||||||
|
|
||||||
mockIsPackInstalled.mockImplementation((id: string) => {
|
vi.mocked(mockIsPackInstalled).mockImplementation((id) => {
|
||||||
return id === 'pack1' || id === 'pack2'
|
return id === 'pack1' || id === 'pack2'
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -229,7 +229,7 @@ describe('usePacksSelection', () => {
|
|||||||
createMockPack('pack2')
|
createMockPack('pack2')
|
||||||
])
|
])
|
||||||
|
|
||||||
mockIsPackInstalled.mockReturnValue(true)
|
vi.mocked(mockIsPackInstalled).mockReturnValue(true)
|
||||||
|
|
||||||
const { isMixed } = usePacksSelection(nodePacks)
|
const { isMixed } = usePacksSelection(nodePacks)
|
||||||
|
|
||||||
@@ -242,7 +242,7 @@ describe('usePacksSelection', () => {
|
|||||||
createMockPack('pack2')
|
createMockPack('pack2')
|
||||||
])
|
])
|
||||||
|
|
||||||
mockIsPackInstalled.mockReturnValue(false)
|
vi.mocked(mockIsPackInstalled).mockReturnValue(false)
|
||||||
|
|
||||||
const { isMixed } = usePacksSelection(nodePacks)
|
const { isMixed } = usePacksSelection(nodePacks)
|
||||||
|
|
||||||
@@ -265,7 +265,7 @@ describe('usePacksSelection', () => {
|
|||||||
createMockPack('pack2')
|
createMockPack('pack2')
|
||||||
])
|
])
|
||||||
|
|
||||||
mockIsPackInstalled.mockReturnValue(true)
|
vi.mocked(mockIsPackInstalled).mockReturnValue(true)
|
||||||
|
|
||||||
const { selectionState } = usePacksSelection(nodePacks)
|
const { selectionState } = usePacksSelection(nodePacks)
|
||||||
|
|
||||||
@@ -278,7 +278,7 @@ describe('usePacksSelection', () => {
|
|||||||
createMockPack('pack2')
|
createMockPack('pack2')
|
||||||
])
|
])
|
||||||
|
|
||||||
mockIsPackInstalled.mockReturnValue(false)
|
vi.mocked(mockIsPackInstalled).mockReturnValue(false)
|
||||||
|
|
||||||
const { selectionState } = usePacksSelection(nodePacks)
|
const { selectionState } = usePacksSelection(nodePacks)
|
||||||
|
|
||||||
@@ -292,7 +292,7 @@ describe('usePacksSelection', () => {
|
|||||||
createMockPack('pack3')
|
createMockPack('pack3')
|
||||||
])
|
])
|
||||||
|
|
||||||
mockIsPackInstalled.mockImplementation((id: string) => id === 'pack1')
|
vi.mocked(mockIsPackInstalled).mockImplementation((id) => id === 'pack1')
|
||||||
|
|
||||||
const { selectionState } = usePacksSelection(nodePacks)
|
const { selectionState } = usePacksSelection(nodePacks)
|
||||||
|
|
||||||
@@ -305,13 +305,13 @@ describe('usePacksSelection', () => {
|
|||||||
createMockPack('pack2')
|
createMockPack('pack2')
|
||||||
])
|
])
|
||||||
|
|
||||||
mockIsPackInstalled.mockReturnValue(false)
|
vi.mocked(mockIsPackInstalled).mockReturnValue(false)
|
||||||
|
|
||||||
const { selectionState } = usePacksSelection(nodePacks)
|
const { selectionState } = usePacksSelection(nodePacks)
|
||||||
expect(selectionState.value).toBe('none-installed')
|
expect(selectionState.value).toBe('none-installed')
|
||||||
|
|
||||||
// Change mock to simulate installation
|
// Change mock to simulate installation
|
||||||
mockIsPackInstalled.mockReturnValue(true)
|
vi.mocked(mockIsPackInstalled).mockReturnValue(true)
|
||||||
|
|
||||||
// Force reactivity update
|
// Force reactivity update
|
||||||
nodePacks.value = [...nodePacks.value]
|
nodePacks.value = [...nodePacks.value]
|
||||||
@@ -327,7 +327,7 @@ describe('usePacksSelection', () => {
|
|||||||
createMockPack('pack2')
|
createMockPack('pack2')
|
||||||
])
|
])
|
||||||
|
|
||||||
mockIsPackInstalled.mockImplementation((id: string) => id === 'pack2')
|
vi.mocked(mockIsPackInstalled).mockImplementation((id) => id === 'pack2')
|
||||||
|
|
||||||
const { installedPacks, notInstalledPacks } = usePacksSelection(nodePacks)
|
const { installedPacks, notInstalledPacks } = usePacksSelection(nodePacks)
|
||||||
|
|
||||||
@@ -347,8 +347,8 @@ describe('usePacksSelection', () => {
|
|||||||
pack2: false
|
pack2: false
|
||||||
}
|
}
|
||||||
|
|
||||||
mockIsPackInstalled.mockImplementation(
|
vi.mocked(mockIsPackInstalled).mockImplementation(
|
||||||
(id: string) => installationStatus[id] || false
|
(id) => (id && installationStatus[id]) || false
|
||||||
)
|
)
|
||||||
|
|
||||||
const { installedPacks, notInstalledPacks, selectionState } =
|
const { installedPacks, notInstalledPacks, selectionState } =
|
||||||
|
|||||||
@@ -3,8 +3,11 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|||||||
import { useCachedRequest } from '@/composables/useCachedRequest'
|
import { useCachedRequest } from '@/composables/useCachedRequest'
|
||||||
|
|
||||||
describe('useCachedRequest', () => {
|
describe('useCachedRequest', () => {
|
||||||
let mockRequestFn: ReturnType<typeof vi.fn>
|
let mockRequestFn: (
|
||||||
let abortSpy: ReturnType<typeof vi.fn>
|
params: any,
|
||||||
|
signal?: AbortSignal
|
||||||
|
) => Promise<unknown | null>
|
||||||
|
let abortSpy: () => void
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
vi.clearAllMocks()
|
vi.clearAllMocks()
|
||||||
|
|||||||
@@ -5,6 +5,9 @@ import type { Ref } from 'vue'
|
|||||||
|
|
||||||
import { useJobList } from '@/composables/queue/useJobList'
|
import { useJobList } from '@/composables/queue/useJobList'
|
||||||
import type { JobState } from '@/types/queue'
|
import type { JobState } from '@/types/queue'
|
||||||
|
import { buildJobDisplay } from '@/utils/queueDisplay'
|
||||||
|
import type { BuildJobDisplayCtx } from '@/utils/queueDisplay'
|
||||||
|
import type { TaskItemImpl } from '@/stores/queueStore'
|
||||||
|
|
||||||
type TestTask = {
|
type TestTask = {
|
||||||
promptId: string
|
promptId: string
|
||||||
@@ -43,19 +46,8 @@ vi.mock('vue-i18n', () => ({
|
|||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
|
||||||
let stMock: ReturnType<typeof vi.fn>
|
|
||||||
const ensureStMock = () => {
|
|
||||||
if (!stMock) {
|
|
||||||
stMock = vi.fn(
|
|
||||||
(key: string, fallback?: string) => `i18n(${key})-${fallback}`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return stMock
|
|
||||||
}
|
|
||||||
vi.mock('@/i18n', () => ({
|
vi.mock('@/i18n', () => ({
|
||||||
st: (...args: any[]) => {
|
st: vi.fn((key: string, fallback?: string) => `i18n(${key})-${fallback}`)
|
||||||
return ensureStMock()(...args)
|
|
||||||
}
|
|
||||||
}))
|
}))
|
||||||
|
|
||||||
let totalPercent: Ref<number>
|
let totalPercent: Ref<number>
|
||||||
@@ -75,40 +67,24 @@ vi.mock('@/composables/queue/useQueueProgress', () => ({
|
|||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
|
||||||
let buildJobDisplayMock: ReturnType<typeof vi.fn>
|
vi.mock('@/utils/queueDisplay', () => ({
|
||||||
const ensureBuildDisplayMock = () => {
|
buildJobDisplay: vi.fn(
|
||||||
if (!buildJobDisplayMock) {
|
(task: TaskItemImpl, state: JobState, options: BuildJobDisplayCtx) => ({
|
||||||
buildJobDisplayMock = vi.fn((task: any, state: JobState, options: any) => ({
|
|
||||||
primary: `Job ${task.promptId}`,
|
primary: `Job ${task.promptId}`,
|
||||||
secondary: `${state} meta`,
|
secondary: `${state} meta`,
|
||||||
iconName: `${state}-icon`,
|
iconName: `${state}-icon`,
|
||||||
iconImageUrl: undefined,
|
iconImageUrl: undefined,
|
||||||
showClear: state === 'failed',
|
showClear: state === 'failed',
|
||||||
options
|
options
|
||||||
}))
|
})
|
||||||
}
|
)
|
||||||
return buildJobDisplayMock
|
|
||||||
}
|
|
||||||
vi.mock('@/utils/queueDisplay', () => ({
|
|
||||||
buildJobDisplay: (...args: any[]) => {
|
|
||||||
return ensureBuildDisplayMock()(...args)
|
|
||||||
}
|
|
||||||
}))
|
}))
|
||||||
|
|
||||||
let jobStateFromTaskMock: ReturnType<typeof vi.fn>
|
|
||||||
const ensureJobStateMock = () => {
|
|
||||||
if (!jobStateFromTaskMock) {
|
|
||||||
jobStateFromTaskMock = vi.fn(
|
|
||||||
(task: TestTask, isInitializing?: boolean): JobState =>
|
|
||||||
task.mockState ?? (isInitializing ? 'running' : 'completed')
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return jobStateFromTaskMock
|
|
||||||
}
|
|
||||||
vi.mock('@/utils/queueUtil', () => ({
|
vi.mock('@/utils/queueUtil', () => ({
|
||||||
jobStateFromTask: (...args: any[]) => {
|
jobStateFromTask: vi.fn(
|
||||||
return ensureJobStateMock()(...args)
|
(task: TestTask, isInitializing?: boolean): JobState =>
|
||||||
}
|
task.mockState ?? (isInitializing ? 'running' : 'completed')
|
||||||
|
)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
let queueStoreMock: {
|
let queueStoreMock: {
|
||||||
@@ -137,7 +113,7 @@ let executionStoreMock: {
|
|||||||
executingNode: null | { title?: string; type?: string }
|
executingNode: null | { title?: string; type?: string }
|
||||||
isPromptInitializing: (promptId?: string | number) => boolean
|
isPromptInitializing: (promptId?: string | number) => boolean
|
||||||
}
|
}
|
||||||
let isPromptInitializingMock: ReturnType<typeof vi.fn>
|
let isPromptInitializingMock: (promptId?: string | number) => boolean
|
||||||
const ensureExecutionStore = () => {
|
const ensureExecutionStore = () => {
|
||||||
if (!isPromptInitializingMock) {
|
if (!isPromptInitializingMock) {
|
||||||
isPromptInitializingMock = vi.fn(() => false)
|
isPromptInitializingMock = vi.fn(() => false)
|
||||||
@@ -221,13 +197,9 @@ const resetStores = () => {
|
|||||||
localeRef.value = 'en-US'
|
localeRef.value = 'en-US'
|
||||||
tMock.mockClear()
|
tMock.mockClear()
|
||||||
|
|
||||||
if (stMock) stMock.mockClear()
|
|
||||||
if (buildJobDisplayMock) buildJobDisplayMock.mockClear()
|
|
||||||
if (jobStateFromTaskMock) jobStateFromTaskMock.mockClear()
|
|
||||||
|
|
||||||
if (isPromptInitializingMock) {
|
if (isPromptInitializingMock) {
|
||||||
isPromptInitializingMock.mockReset()
|
vi.mocked(isPromptInitializingMock).mockReset()
|
||||||
isPromptInitializingMock.mockReturnValue(false)
|
vi.mocked(isPromptInitializingMock).mockReturnValue(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -240,6 +212,7 @@ describe('useJobList', () => {
|
|||||||
let api: ReturnType<typeof useJobList> | null = null
|
let api: ReturnType<typeof useJobList> | null = null
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
vi.resetAllMocks()
|
||||||
resetStores()
|
resetStores()
|
||||||
wrapper?.unmount()
|
wrapper?.unmount()
|
||||||
wrapper = null
|
wrapper = null
|
||||||
@@ -270,18 +243,18 @@ describe('useJobList', () => {
|
|||||||
await flush()
|
await flush()
|
||||||
|
|
||||||
jobItems.value
|
jobItems.value
|
||||||
expect(buildJobDisplayMock).toHaveBeenCalledWith(
|
expect(buildJobDisplay).toHaveBeenCalledWith(
|
||||||
expect.anything(),
|
expect.anything(),
|
||||||
'pending',
|
'pending',
|
||||||
expect.objectContaining({ showAddedHint: true })
|
expect.objectContaining({ showAddedHint: true })
|
||||||
)
|
)
|
||||||
|
|
||||||
buildJobDisplayMock.mockClear()
|
vi.mocked(buildJobDisplay).mockClear()
|
||||||
await vi.advanceTimersByTimeAsync(3000)
|
await vi.advanceTimersByTimeAsync(3000)
|
||||||
await flush()
|
await flush()
|
||||||
|
|
||||||
jobItems.value
|
jobItems.value
|
||||||
expect(buildJobDisplayMock).toHaveBeenCalledWith(
|
expect(buildJobDisplay).toHaveBeenCalledWith(
|
||||||
expect.anything(),
|
expect.anything(),
|
||||||
'pending',
|
'pending',
|
||||||
expect.objectContaining({ showAddedHint: false })
|
expect.objectContaining({ showAddedHint: false })
|
||||||
@@ -303,13 +276,13 @@ describe('useJobList', () => {
|
|||||||
await flush()
|
await flush()
|
||||||
expect(vi.getTimerCount()).toBe(0)
|
expect(vi.getTimerCount()).toBe(0)
|
||||||
|
|
||||||
buildJobDisplayMock.mockClear()
|
vi.mocked(buildJobDisplay).mockClear()
|
||||||
queueStoreMock.pendingTasks = [
|
queueStoreMock.pendingTasks = [
|
||||||
createTask({ promptId: taskId, queueIndex: 2, mockState: 'pending' })
|
createTask({ promptId: taskId, queueIndex: 2, mockState: 'pending' })
|
||||||
]
|
]
|
||||||
await flush()
|
await flush()
|
||||||
jobItems.value
|
jobItems.value
|
||||||
expect(buildJobDisplayMock).toHaveBeenCalledWith(
|
expect(buildJobDisplay).toHaveBeenCalledWith(
|
||||||
expect.anything(),
|
expect.anything(),
|
||||||
'pending',
|
'pending',
|
||||||
expect.objectContaining({ showAddedHint: true })
|
expect.objectContaining({ showAddedHint: true })
|
||||||
|
|||||||
@@ -117,7 +117,9 @@ describe('useLoad3d', () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
vi.mocked(Load3d).mockImplementation(() => mockLoad3d)
|
vi.mocked(Load3d).mockImplementation(function () {
|
||||||
|
Object.assign(this, mockLoad3d)
|
||||||
|
})
|
||||||
|
|
||||||
mockToastStore = {
|
mockToastStore = {
|
||||||
addAlert: vi.fn()
|
addAlert: vi.fn()
|
||||||
@@ -289,7 +291,7 @@ describe('useLoad3d', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should handle initialization errors', async () => {
|
it('should handle initialization errors', async () => {
|
||||||
vi.mocked(Load3d).mockImplementationOnce(() => {
|
vi.mocked(Load3d).mockImplementationOnce(function () {
|
||||||
throw new Error('Load3d creation failed')
|
throw new Error('Load3d creation failed')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ function createMockDragEvent(
|
|||||||
|
|
||||||
describe('useLoad3dDrag', () => {
|
describe('useLoad3dDrag', () => {
|
||||||
let mockToastStore: any
|
let mockToastStore: any
|
||||||
let mockOnModelDrop: ReturnType<typeof vi.fn>
|
let mockOnModelDrop: (file: File) => void | Promise<void>
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
vi.clearAllMocks()
|
vi.clearAllMocks()
|
||||||
@@ -199,7 +199,7 @@ describe('useLoad3dDrag', () => {
|
|||||||
const extensions = ['.gltf', '.glb', '.obj', '.fbx', '.stl']
|
const extensions = ['.gltf', '.glb', '.obj', '.fbx', '.stl']
|
||||||
|
|
||||||
for (const ext of extensions) {
|
for (const ext of extensions) {
|
||||||
mockOnModelDrop.mockClear()
|
vi.mocked(mockOnModelDrop).mockClear()
|
||||||
|
|
||||||
const modelFile = new File([], `model${ext}`)
|
const modelFile = new File([], `model${ext}`)
|
||||||
const event = createMockDragEvent('drop', {
|
const event = createMockDragEvent('drop', {
|
||||||
|
|||||||
@@ -120,7 +120,9 @@ describe('useLoad3dViewer', () => {
|
|||||||
forceRender: vi.fn()
|
forceRender: vi.fn()
|
||||||
}
|
}
|
||||||
|
|
||||||
vi.mocked(Load3d).mockImplementation(() => mockLoad3d)
|
vi.mocked(Load3d).mockImplementation(function () {
|
||||||
|
Object.assign(this, mockLoad3d)
|
||||||
|
})
|
||||||
|
|
||||||
mockLoad3dService = {
|
mockLoad3dService = {
|
||||||
copyLoad3dState: vi.fn().mockResolvedValue(undefined),
|
copyLoad3dState: vi.fn().mockResolvedValue(undefined),
|
||||||
@@ -198,7 +200,7 @@ describe('useLoad3dViewer', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should handle initialization errors', async () => {
|
it('should handle initialization errors', async () => {
|
||||||
vi.mocked(Load3d).mockImplementationOnce(() => {
|
vi.mocked(Load3d).mockImplementationOnce(function () {
|
||||||
throw new Error('Load3d creation failed')
|
throw new Error('Load3d creation failed')
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -312,7 +314,7 @@ describe('useLoad3dViewer', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should handle watcher errors gracefully', async () => {
|
it('should handle watcher errors gracefully', async () => {
|
||||||
mockLoad3d.setBackgroundColor.mockImplementationOnce(() => {
|
mockLoad3d.setBackgroundColor.mockImplementationOnce(function () {
|
||||||
throw new Error('Color update failed')
|
throw new Error('Color update failed')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -10,14 +10,6 @@ const { LGraphCanvas } = await vi.importActual<
|
|||||||
>('@/lib/litegraph/src/LGraphCanvas')
|
>('@/lib/litegraph/src/LGraphCanvas')
|
||||||
type LGraphCanvasType = InstanceType<typeof LGraphCanvas>
|
type LGraphCanvasType = InstanceType<typeof LGraphCanvas>
|
||||||
|
|
||||||
type ContextMenuInstance = {
|
|
||||||
addItem?: (
|
|
||||||
name: string,
|
|
||||||
value: string,
|
|
||||||
options: { callback?: (value: string) => void; className?: string }
|
|
||||||
) => void
|
|
||||||
}
|
|
||||||
|
|
||||||
interface MockWidgetConfig extends Omit<IComboWidget, 'options'> {
|
interface MockWidgetConfig extends Omit<IComboWidget, 'options'> {
|
||||||
options: IComboWidget['options']
|
options: IComboWidget['options']
|
||||||
}
|
}
|
||||||
@@ -465,12 +457,12 @@ describe('ComboWidget', () => {
|
|||||||
node.size = [200, 30]
|
node.size = [200, 30]
|
||||||
|
|
||||||
let capturedCallback: ((value: string) => void) | undefined
|
let capturedCallback: ((value: string) => void) | undefined
|
||||||
const mockContextMenu = vi.fn((_values, options) => {
|
const mockContextMenu = vi
|
||||||
capturedCallback = options.callback
|
.fn<typeof LiteGraph.ContextMenu>()
|
||||||
return {} as ContextMenuInstance
|
.mockImplementation(function (_values, options) {
|
||||||
})
|
capturedCallback = options.callback
|
||||||
LiteGraph.ContextMenu =
|
})
|
||||||
mockContextMenu as unknown as typeof LiteGraph.ContextMenu
|
LiteGraph.ContextMenu = mockContextMenu
|
||||||
|
|
||||||
const setValueSpy = vi.spyOn(widget, 'setValue')
|
const setValueSpy = vi.spyOn(widget, 'setValue')
|
||||||
widget.onClick({ e: mockEvent, node, canvas: mockCanvas })
|
widget.onClick({ e: mockEvent, node, canvas: mockCanvas })
|
||||||
@@ -507,12 +499,12 @@ describe('ComboWidget', () => {
|
|||||||
node.size = [200, 30]
|
node.size = [200, 30]
|
||||||
|
|
||||||
let capturedCallback: ((value: string) => void) | undefined
|
let capturedCallback: ((value: string) => void) | undefined
|
||||||
const mockContextMenu = vi.fn((_values, options) => {
|
const mockContextMenu = vi
|
||||||
capturedCallback = options.callback
|
.fn<typeof LiteGraph.ContextMenu>()
|
||||||
return {} as ContextMenuInstance
|
.mockImplementation(function (_values, options) {
|
||||||
})
|
capturedCallback = options.callback
|
||||||
LiteGraph.ContextMenu =
|
})
|
||||||
mockContextMenu as unknown as typeof LiteGraph.ContextMenu
|
LiteGraph.ContextMenu = mockContextMenu
|
||||||
|
|
||||||
const setValueSpy = vi.spyOn(widget, 'setValue')
|
const setValueSpy = vi.spyOn(widget, 'setValue')
|
||||||
widget.onClick({ e: mockEvent, node, canvas: mockCanvas })
|
widget.onClick({ e: mockEvent, node, canvas: mockCanvas })
|
||||||
@@ -653,7 +645,7 @@ describe('ComboWidget', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should handle getOptionLabel error gracefully', () => {
|
it('should handle getOptionLabel error gracefully', () => {
|
||||||
const mockGetOptionLabel = vi.fn().mockImplementation(() => {
|
const mockGetOptionLabel = vi.fn().mockImplementation(function () {
|
||||||
throw new Error('Formatting failed')
|
throw new Error('Formatting failed')
|
||||||
})
|
})
|
||||||
const consoleErrorSpy = vi
|
const consoleErrorSpy = vi
|
||||||
@@ -768,9 +760,12 @@ describe('ComboWidget', () => {
|
|||||||
node.size = [200, 30]
|
node.size = [200, 30]
|
||||||
|
|
||||||
const mockAddItem = vi.fn()
|
const mockAddItem = vi.fn()
|
||||||
const mockContextMenu = vi.fn(() => ({ addItem: mockAddItem }))
|
const mockContextMenu = vi
|
||||||
LiteGraph.ContextMenu =
|
.fn<typeof LiteGraph.ContextMenu>()
|
||||||
mockContextMenu as unknown as typeof LiteGraph.ContextMenu
|
.mockImplementation(function () {
|
||||||
|
this.addItem = mockAddItem
|
||||||
|
})
|
||||||
|
LiteGraph.ContextMenu = mockContextMenu
|
||||||
|
|
||||||
widget.onClick({ e: mockEvent, node, canvas: mockCanvas })
|
widget.onClick({ e: mockEvent, node, canvas: mockCanvas })
|
||||||
|
|
||||||
@@ -827,12 +822,13 @@ describe('ComboWidget', () => {
|
|||||||
|
|
||||||
const mockAddItem = vi.fn()
|
const mockAddItem = vi.fn()
|
||||||
let capturedCallback: ((value: string) => void) | undefined
|
let capturedCallback: ((value: string) => void) | undefined
|
||||||
const mockContextMenu = vi.fn((_values, options) => {
|
const mockContextMenu = vi
|
||||||
capturedCallback = options.callback
|
.fn<typeof LiteGraph.ContextMenu>()
|
||||||
return { addItem: mockAddItem }
|
.mockImplementation(function (_values, options) {
|
||||||
})
|
capturedCallback = options.callback
|
||||||
LiteGraph.ContextMenu =
|
this.addItem = mockAddItem
|
||||||
mockContextMenu as unknown as typeof LiteGraph.ContextMenu
|
})
|
||||||
|
LiteGraph.ContextMenu = mockContextMenu
|
||||||
|
|
||||||
const setValueSpy = vi.spyOn(widget, 'setValue')
|
const setValueSpy = vi.spyOn(widget, 'setValue')
|
||||||
widget.onClick({ e: mockEvent, node, canvas: mockCanvas })
|
widget.onClick({ e: mockEvent, node, canvas: mockCanvas })
|
||||||
@@ -879,12 +875,13 @@ describe('ComboWidget', () => {
|
|||||||
const mockAddItem = vi.fn()
|
const mockAddItem = vi.fn()
|
||||||
let capturedCallback: ((value: string) => void) | undefined
|
let capturedCallback: ((value: string) => void) | undefined
|
||||||
|
|
||||||
const mockContextMenu = vi.fn((_values, options) => {
|
const mockContextMenu = vi
|
||||||
capturedCallback = options.callback
|
.fn<typeof LiteGraph.ContextMenu>()
|
||||||
return { addItem: mockAddItem } as ContextMenuInstance
|
.mockImplementation(function (_values, options) {
|
||||||
})
|
capturedCallback = options.callback
|
||||||
LiteGraph.ContextMenu =
|
this.addItem = mockAddItem
|
||||||
mockContextMenu as unknown as typeof LiteGraph.ContextMenu
|
})
|
||||||
|
LiteGraph.ContextMenu = mockContextMenu
|
||||||
|
|
||||||
widget.onClick({ e: mockEvent, node, canvas: mockCanvas })
|
widget.onClick({ e: mockEvent, node, canvas: mockCanvas })
|
||||||
|
|
||||||
@@ -931,7 +928,7 @@ describe('ComboWidget', () => {
|
|||||||
const mockGetOptionLabel = vi
|
const mockGetOptionLabel = vi
|
||||||
.fn()
|
.fn()
|
||||||
.mockReturnValueOnce('Beautiful Sunset.png')
|
.mockReturnValueOnce('Beautiful Sunset.png')
|
||||||
.mockImplementationOnce(() => {
|
.mockImplementationOnce(function () {
|
||||||
throw new Error('Formatting failed')
|
throw new Error('Formatting failed')
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -957,11 +954,12 @@ describe('ComboWidget', () => {
|
|||||||
.mockImplementation(() => {})
|
.mockImplementation(() => {})
|
||||||
|
|
||||||
const mockAddItem = vi.fn()
|
const mockAddItem = vi.fn()
|
||||||
const mockContextMenu = vi.fn(() => {
|
const mockContextMenu = vi
|
||||||
return { addItem: mockAddItem } as ContextMenuInstance
|
.fn<typeof LiteGraph.ContextMenu>()
|
||||||
})
|
.mockImplementation(function () {
|
||||||
LiteGraph.ContextMenu =
|
this.addItem = mockAddItem
|
||||||
mockContextMenu as unknown as typeof LiteGraph.ContextMenu
|
})
|
||||||
|
LiteGraph.ContextMenu = mockContextMenu
|
||||||
|
|
||||||
widget.onClick({ e: mockEvent, node, canvas: mockCanvas })
|
widget.onClick({ e: mockEvent, node, canvas: mockCanvas })
|
||||||
|
|
||||||
@@ -1007,9 +1005,8 @@ describe('ComboWidget', () => {
|
|||||||
node.pos = [50, 50]
|
node.pos = [50, 50]
|
||||||
node.size = [200, 30]
|
node.size = [200, 30]
|
||||||
|
|
||||||
const mockContextMenu = vi.fn()
|
const mockContextMenu = vi.fn<typeof LiteGraph.ContextMenu>()
|
||||||
LiteGraph.ContextMenu =
|
LiteGraph.ContextMenu = mockContextMenu
|
||||||
mockContextMenu as unknown as typeof LiteGraph.ContextMenu
|
|
||||||
|
|
||||||
widget.onClick({ e: mockEvent, node, canvas: mockCanvas })
|
widget.onClick({ e: mockEvent, node, canvas: mockCanvas })
|
||||||
|
|
||||||
|
|||||||
@@ -1,21 +1,21 @@
|
|||||||
// TODO: Fix these tests after migration
|
// TODO: Fix these tests after migration
|
||||||
import { afterEach, describe, expect, vi } from 'vitest'
|
import { afterEach, describe, expect, vi } from 'vitest'
|
||||||
|
|
||||||
import type { LGraph, Reroute } from '@/lib/litegraph/src/litegraph'
|
import type {
|
||||||
import {
|
LGraph,
|
||||||
type CanvasPointerEvent,
|
Reroute,
|
||||||
LGraphNode,
|
CanvasPointerEvent,
|
||||||
LLink,
|
RerouteId
|
||||||
LinkConnector,
|
|
||||||
type RerouteId
|
|
||||||
} from '@/lib/litegraph/src/litegraph'
|
} from '@/lib/litegraph/src/litegraph'
|
||||||
|
import { LGraphNode, LLink, LinkConnector } from '@/lib/litegraph/src/litegraph'
|
||||||
|
|
||||||
import { test as baseTest } from './fixtures/testExtensions'
|
import { test as baseTest } from './fixtures/testExtensions'
|
||||||
|
import type { ConnectingLink } from '@/lib/litegraph/src/interfaces'
|
||||||
|
|
||||||
interface TestContext {
|
interface TestContext {
|
||||||
graph: LGraph
|
graph: LGraph
|
||||||
connector: LinkConnector
|
connector: LinkConnector
|
||||||
setConnectingLinks: ReturnType<typeof vi.fn>
|
setConnectingLinks: (value: ConnectingLink[]) => void
|
||||||
createTestNode: (id: number) => LGraphNode
|
createTestNode: (id: number) => LGraphNode
|
||||||
reroutesBeforeTest: [rerouteId: RerouteId, reroute: Reroute][]
|
reroutesBeforeTest: [rerouteId: RerouteId, reroute: Reroute][]
|
||||||
validateIntegrityNoChanges: () => void
|
validateIntegrityNoChanges: () => void
|
||||||
@@ -41,9 +41,8 @@ const test = baseTest.extend<TestContext>({
|
|||||||
await use(reroutesComplexGraph)
|
await use(reroutesComplexGraph)
|
||||||
},
|
},
|
||||||
setConnectingLinks: async (
|
setConnectingLinks: async (
|
||||||
// eslint-disable-next-line no-empty-pattern
|
|
||||||
{},
|
{},
|
||||||
use: (mock: ReturnType<typeof vi.fn>) => Promise<void>
|
use: (mock: (value: ConnectingLink[]) => void) => Promise<void>
|
||||||
) => {
|
) => {
|
||||||
const mock = vi.fn()
|
const mock = vi.fn()
|
||||||
await use(mock)
|
await use(mock)
|
||||||
|
|||||||
@@ -1,23 +1,26 @@
|
|||||||
import { test as baseTest, describe, expect, vi } from 'vitest'
|
import { test as baseTest, describe, expect, vi } from 'vitest'
|
||||||
|
|
||||||
import { LinkConnector } from '@/lib/litegraph/src/litegraph'
|
import type {
|
||||||
import type { MovingInputLink } from '@/lib/litegraph/src/litegraph'
|
MovingInputLink,
|
||||||
import { ToInputRenderLink } from '@/lib/litegraph/src/litegraph'
|
RerouteId,
|
||||||
import type { LinkNetwork } from '@/lib/litegraph/src/litegraph'
|
LinkNetwork,
|
||||||
import type { ISlotType } from '@/lib/litegraph/src/litegraph'
|
ISlotType
|
||||||
|
} from '@/lib/litegraph/src/litegraph'
|
||||||
import {
|
import {
|
||||||
LGraph,
|
LGraph,
|
||||||
LGraphNode,
|
LGraphNode,
|
||||||
LLink,
|
LLink,
|
||||||
Reroute,
|
Reroute,
|
||||||
type RerouteId
|
LinkConnector,
|
||||||
|
ToInputRenderLink,
|
||||||
|
LinkDirection
|
||||||
} from '@/lib/litegraph/src/litegraph'
|
} from '@/lib/litegraph/src/litegraph'
|
||||||
import { LinkDirection } from '@/lib/litegraph/src/litegraph'
|
import type { ConnectingLink } from '@/lib/litegraph/src/interfaces'
|
||||||
|
|
||||||
interface TestContext {
|
interface TestContext {
|
||||||
network: LinkNetwork & { add(node: LGraphNode): void }
|
network: LinkNetwork & { add(node: LGraphNode): void }
|
||||||
connector: LinkConnector
|
connector: LinkConnector
|
||||||
setConnectingLinks: ReturnType<typeof vi.fn>
|
setConnectingLinks: (value: ConnectingLink[]) => void
|
||||||
createTestNode: (id: number, slotType?: ISlotType) => LGraphNode
|
createTestNode: (id: number, slotType?: ISlotType) => LGraphNode
|
||||||
createTestLink: (
|
createTestLink: (
|
||||||
id: number,
|
id: number,
|
||||||
@@ -28,7 +31,6 @@ interface TestContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const test = baseTest.extend<TestContext>({
|
const test = baseTest.extend<TestContext>({
|
||||||
// eslint-disable-next-line no-empty-pattern
|
|
||||||
network: async ({}, use) => {
|
network: async ({}, use) => {
|
||||||
const graph = new LGraph()
|
const graph = new LGraph()
|
||||||
const floatingLinks = new Map<number, LLink>()
|
const floatingLinks = new Map<number, LLink>()
|
||||||
@@ -53,9 +55,8 @@ const test = baseTest.extend<TestContext>({
|
|||||||
},
|
},
|
||||||
|
|
||||||
setConnectingLinks: async (
|
setConnectingLinks: async (
|
||||||
// eslint-disable-next-line no-empty-pattern
|
|
||||||
{},
|
{},
|
||||||
use: (mock: ReturnType<typeof vi.fn>) => Promise<void>
|
use: (mock: (value: ConnectingLink[]) => void) => Promise<void>
|
||||||
) => {
|
) => {
|
||||||
const mock = vi.fn()
|
const mock = vi.fn()
|
||||||
await use(mock)
|
await use(mock)
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ vi.mock('@/scripts/api', () => ({
|
|||||||
|
|
||||||
describe('useMinimapGraph', () => {
|
describe('useMinimapGraph', () => {
|
||||||
let mockGraph: LGraph
|
let mockGraph: LGraph
|
||||||
let onGraphChangedMock: ReturnType<typeof vi.fn>
|
let onGraphChangedMock: () => void
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
vi.clearAllMocks()
|
vi.clearAllMocks()
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import type { MinimapCanvas } from '@/renderer/extensions/minimap/types'
|
|||||||
describe('useMinimapInteraction', () => {
|
describe('useMinimapInteraction', () => {
|
||||||
let mockContainer: HTMLDivElement
|
let mockContainer: HTMLDivElement
|
||||||
let mockCanvas: MinimapCanvas
|
let mockCanvas: MinimapCanvas
|
||||||
let centerViewOnMock: ReturnType<typeof vi.fn>
|
let centerViewOnMock: (worldX: number, worldY: number) => void
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
vi.clearAllMocks()
|
vi.clearAllMocks()
|
||||||
@@ -29,7 +29,7 @@ describe('useMinimapInteraction', () => {
|
|||||||
setDirty: vi.fn()
|
setDirty: vi.fn()
|
||||||
} as any
|
} as any
|
||||||
|
|
||||||
centerViewOnMock = vi.fn()
|
centerViewOnMock = vi.fn<(worldX: number, worldY: number) => void>()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should initialize with default values', () => {
|
it('should initialize with default values', () => {
|
||||||
|
|||||||
@@ -26,15 +26,15 @@ vi.mock('@/stores/dialogStore', () => ({
|
|||||||
|
|
||||||
describe('keybindingService - Escape key handling', () => {
|
describe('keybindingService - Escape key handling', () => {
|
||||||
let keybindingService: ReturnType<typeof useKeybindingService>
|
let keybindingService: ReturnType<typeof useKeybindingService>
|
||||||
let mockCommandExecute: ReturnType<typeof vi.fn>
|
let mockCommandExecute: ReturnType<typeof useCommandStore>['execute']
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
vi.clearAllMocks()
|
vi.clearAllMocks()
|
||||||
setActivePinia(createPinia())
|
setActivePinia(createPinia())
|
||||||
|
|
||||||
// Mock command store execute
|
// Mock command store execute
|
||||||
mockCommandExecute = vi.fn()
|
|
||||||
const commandStore = useCommandStore()
|
const commandStore = useCommandStore()
|
||||||
|
mockCommandExecute = vi.fn()
|
||||||
commandStore.execute = mockCommandExecute
|
commandStore.execute = mockCommandExecute
|
||||||
|
|
||||||
// Reset dialog store mock to empty
|
// Reset dialog store mock to empty
|
||||||
|
|||||||
Reference in New Issue
Block a user