Compare commits

..

87 Commits

Author SHA1 Message Date
Chenlei Hu
65a8dbb7e0 1.2.52 (#814) 2024-09-13 16:25:01 +09:00
Chenlei Hu
efd8b5c19d Add playwright test for custom color palette (#812)
* Add playwright test for custom color palette

* nit

* Fix leaked side effect

* nit

* Update test expectations [skip ci]

---------

Co-authored-by: github-actions <github-actions@github.com>
2024-09-13 13:52:16 +09:00
Chenlei Hu
0a188aaf72 Disable zoom when editing titles (#813) 2024-09-13 11:42:24 +09:00
Chenlei Hu
eb45cca031 Pin searchbox at top when node library scrolls (#811)
* minor style fix

* nit

* Pin searchbox at top when node library scrolls
2024-09-13 10:50:06 +09:00
Chenlei Hu
d8d6fa86e4 Add button to clear pending tasks (#810) 2024-09-13 10:23:28 +09:00
Chenlei Hu
880ac4fa5a Add node lifecycle badge text (#809) 2024-09-13 10:04:36 +09:00
Chenlei Hu
7d3b8dc44c Make \n correctly displayed on error message (#805) 2024-09-13 09:04:21 +09:00
Chenlei Hu
1230d92b37 1.2.51 (#804) 2024-09-12 20:23:04 +09:00
Chenlei Hu
8889c4de4a Fix node def registeration (#803) 2024-09-12 20:17:21 +09:00
Chenlei Hu
637f5b501e Add about panel in settings dialog (#799)
* basic about page

* Remove frontend version from setting dialog header

* Style about page

* basic system stats

* Basic styling

* Reword

* Format memory amount
2024-09-12 17:31:19 +09:00
Chenlei Hu
d2b3e325a4 1.2.50 (#798) 2024-09-12 17:16:54 +09:00
Chenlei Hu
c99ca004b4 Fix badge position on collapsed nodes (#797)
* Update litegraph (Proper collapsed node handling)

* Add playwright test

* Update test expectations [skip ci]

---------

Co-authored-by: github-actions <github-actions@github.com>
2024-09-12 17:15:20 +09:00
Chenlei Hu
fa9a415c62 Fix litegraph crash on using custom colorPalette (#795) 2024-09-12 16:56:17 +09:00
Chenlei Hu
da3271fe57 Move InputSlider to common/ (#794) 2024-09-12 15:19:36 +09:00
Chenlei Hu
358c0ce83c Update litegraph (Pin/Unpin selected nodes) (#791)
* Update litegraph (Pin/Unpin selected nodes)

* Checkout head_ref first

* nit

* Update test expectations [skip ci]

---------

Co-authored-by: github-actions <github-actions@github.com>
2024-09-12 15:04:43 +09:00
Chenlei Hu
110c007912 Update litegraph (Proper ContextMenu export) (#790) 2024-09-12 14:09:00 +09:00
Chenlei Hu
fdb01c06f2 Split playwright tests to multiple runners by project (#789) 2024-09-12 11:35:36 +09:00
Chenlei Hu
ca6bf7d054 Split jest unit test and playwright test into different actions (#787)
* Split jest unit test and playwright test into different actions

* Use composite action

* Add tag v1
2024-09-12 10:54:50 +09:00
Chenlei Hu
14f5019556 1.2.49 (#788) 2024-09-12 10:16:59 +09:00
Chenlei Hu
80ca1808f0 Node source/id badge (#781)
* Add basic node badge

* Node source badge

* Prevent manager badge rendering

* Update litegraph (Badge support)

* Add playwright tests

* Separate nodes

* nit

* Checkout devtools repo for browser test expectation CI

* Fix failing unittests

* Rename setting

* Hide all badges in playwright tests

* Handle group node

* Update test expectations [skip ci]

* Fix unittest

---------

Co-authored-by: github-actions <github-actions@github.com>
2024-09-12 09:36:06 +09:00
Chenlei Hu
f2a30ec197 Fix missing model dialog test (#782) 2024-09-11 21:00:32 +09:00
Chenlei Hu
b8bdba0bcc Fix tailwindcss in NoResultsPlaceholder.vue (#780) 2024-09-11 17:38:42 +09:00
Chenlei Hu
baf0bc8de4 Run all playwright tests under dpr=2 (#779)
* Run all playwright tests under dpr=2

* Update test expectations [skip ci]

---------

Co-authored-by: github-actions <github-actions@github.com>
2024-09-11 11:33:40 +09:00
Chenlei Hu
8ce7b515a3 Fix ComfyExtension types (#778) 2024-09-11 10:43:01 +09:00
Chenlei Hu
06a05cb283 Fix loading large workflow embedded in webp (#777)
* Fix loading large workflow embedded in webp

* Update test expectations [skip ci]

* nit

---------

Co-authored-by: github-actions <github-actions@github.com>
2024-09-11 10:12:01 +09:00
bymyself
15758101aa Fix searchbox popover on touch devices (#773)
* Add delay on touch pointer event when opening searchbox

* Add playwright mobile test
2024-09-11 08:55:04 +09:00
ArtificialLab
05b3ad2f59 Front stack primary updates and improvements (#757)
* (fix) index.html formating for prettier

* (add) proper icon management
- on-demand icons auto importing
- handle all available icon sets (https://icones.js.org)

* (fix) proper css management

* (add) front stack improvement:
- implement vue router
- prepare for App.vue simplification
- proper management of views and layouts
- fix Tailwind CSS and prepare for overall css cleaning

* (fix) move back user.css to public dir

* (fix) remove user.css import from main.ts
2024-09-11 08:53:54 +09:00
bymyself
90abf9744c Document registerCustomNodes (#772) 2024-09-10 12:43:39 +09:00
filtered
0e01bb3c07 Fix reroute to wildcard & multi-typed slots (#769)
Use the same type check the rest of the connection process uses.
2024-09-09 17:53:47 +09:00
Chenlei Hu
8b77dde55a [skip ci] Update litegraph dev guide (#770) 2024-09-09 17:51:44 +09:00
Chenlei Hu
a41de30dc5 Fix tailwind css setup (#768)
* Fix tailwind css setup

* Install as dev dep

* Uninstall primeui tailwind
2024-09-09 16:56:32 +09:00
Chenlei Hu
534ea17816 1.2.48 (#767) 2024-09-09 16:20:01 +09:00
Chenlei Hu
913582c7cd [Chore] Update primevue (#766) 2024-09-09 16:14:20 +09:00
Chenlei Hu
3779878b57 Update litegraph (Bug fixes) (#765) 2024-09-09 16:11:28 +09:00
Chenlei Hu
023299cf1a Fix loading workflow from of edited webp (#764)
* Fix loading workflow from of edited webp

* Update test expectations [skip ci]

---------

Co-authored-by: github-actions <github-actions@github.com>
2024-09-09 16:07:15 +09:00
Chenlei Hu
23796d9040 Any keyboard layout for Ctrl + V, Z, Y... (#763)
* Any keyboard layout for Ctrl + V, Z, Y...

`if` conditioning changed for better support any keyboard layout for shortcuts

* Format

---------

Co-authored-by: Khachatur Avanesian <jailbreakvideo@gmail.com>
2024-09-09 10:17:14 +09:00
bymyself
21c3883cc7 Improve beta menu nav accessibility (#762)
* Add ARIA labels to beta menu btns without text

* Adjust test locator
2024-09-09 09:49:43 +09:00
bymyself
616e295262 Improve searchbox accessibility (#760)
* Set field key for search result options label

* Add playwright test

* Add role attr to search dialog
2024-09-09 09:49:02 +09:00
bymyself
c201e86b97 Improve sidebar accessibility (#759)
* Add ARIA label to sidebar buttons

* Add component test

* Add generalized component tests
2024-09-09 09:48:29 +09:00
bymyself
61ee43aa6f [skip ci] Fix search box props type (#753) 2024-09-07 02:34:47 -04:00
bymyself
08a1fd0056 Dismiss gallery lightbox on background click (#752)
* Dismiss gallery on background click

* Add vitest tests
2024-09-07 02:32:56 -04:00
Chenlei Hu
56f3842045 1.2.47 (#747) 2024-09-05 11:41:00 -04:00
Chenlei Hu
81bc0fd9cb Release script (#746)
* Update main repo release script

* update readme [skip ci]
2024-09-05 11:40:05 -04:00
Chenlei Hu
38c957d3a9 Fix load of string node id workflow (#744)
* Update litegraph

* Fix string node id

* Add playwright test

* Update test expectations [skip ci]

---------

Co-authored-by: github-actions <github-actions@github.com>
2024-09-05 11:16:41 -04:00
Chenlei Hu
9d855d637e Ignore missing dialog (#743)
* Do not report missing nodes/models warning repeatedly

* Add playwright tests

* cast finalOptions, add comments to interface

* Use old menu in tests to not break top left click methods

* Assert no dialog on undo and on redo separately

* nit

* nit

---------

Co-authored-by: christian-byrne <abolkonsky.rem@gmail.com>
2024-09-05 11:00:41 -04:00
bymyself
743683c01d Add logging setting display name (#742) 2024-09-05 10:39:22 -04:00
Chenlei Hu
720e7e112d 1.2.46 (#738) 2024-09-04 20:29:14 -04:00
Chenlei Hu
ce157afeac Disable minify on release dist (#737) 2024-09-04 20:28:38 -04:00
Chenlei Hu
95701ab761 Add keyboard shortcut for pin/unpin node (#736)
* Add keyboard shortcut for pin/unpin node

* Add playwright test

* Add nextFrame calls

* Keyboard event on canvas

* disable test
2024-09-04 20:26:10 -04:00
Alex "mcmonkey" Goodwin
060e61f0db initial model store (#674)
* initial model store

* refactor the 'modelstoreserviceimpl' to pinia

* pepper in some reactive

the inner ModelStore (per-folder) can't be pinia because its made of temporary instances, but it can be reactive

* use refs in metadata class

* remove 'reactive'

* remove ref too

* add simple unit tests for modelStore

* make things worse via autoformatting

* move mock impls to a function
2024-09-04 19:59:40 -04:00
Chenlei Hu
6c7fb5041d Replace locking with pining in core (#734)
* Replace locking with pin in core

* Add extra expectation

* Update litegraph

* Update test expectations [skip ci]

---------

Co-authored-by: github-actions <github-actions@github.com>
2024-09-04 19:59:06 -04:00
Chenlei Hu
25a3c30fef [skip ci] Update README (#733) 2024-09-04 10:31:12 -04:00
Chenlei Hu
287bd7ddd0 Add test on text widget popover dismiss (#732)
* Add test on text widget popover dismiss (#719)

* nit

* Update test expectations [skip ci]

---------

Co-authored-by: bymyself <abolkonsky.rem@gmail.com>
Co-authored-by: github-actions <github-actions@github.com>
2024-09-04 10:31:01 -04:00
Chenlei Hu
b396d1a9fe Trigger searchbox on group body db click (#731) 2024-09-04 10:16:12 -04:00
Chenlei Hu
ada8500d21 1.2.45 (#727) 2024-09-03 20:17:34 -04:00
Chenlei Hu
0f32ab334a Update litegraph (Group highlight option) (#725) 2024-09-03 15:34:01 -04:00
Chenlei Hu
36cdebcad1 Restore context menu for new searchbox (#724)
* Searchbox revamp

* nit

* nit

* Add playwright test

* Update litegraph

* Rename setting

* Update test expectations [skip ci]

---------

Co-authored-by: github-actions <github-actions@github.com>
2024-09-03 13:28:26 -04:00
Chenlei Hu
974a7ef63f Store shallowRef of litegraph canvas (#722)
* Store shallowRef of litegraph canvas

* nit
2024-09-03 10:11:31 -04:00
bymyself
b49b19c9b0 [skip ci] Add ToastMessageOptions docs to README (#721) 2024-09-03 08:10:23 -04:00
bymyself
a5d93f6910 Update Settings type with new fields (#718) 2024-09-02 22:50:56 -04:00
Chenlei Hu
8a99124470 1.2.44 (#716) 2024-09-02 20:31:01 -04:00
Chenlei Hu
4a230f720e Edit group name on group creation (With Ctrl + g) (#715)
* Editor store

* Merge editors

* nit

* Edit on group creation

* nit
2024-09-02 20:20:40 -04:00
Chenlei Hu
4ad1e67ebf Double click group title to edit (#714)
* Double click group title to edit

* Add playwright test

* Update test expectations [skip ci]

---------

Co-authored-by: github-actions <github-actions@github.com>
2024-09-02 18:01:02 -04:00
Chenlei Hu
80e4384644 Eager init nodeSearchService (#713) 2024-09-02 15:40:10 -04:00
Chenlei Hu
51b7467012 Auto transforms bugged node pos (#712) 2024-09-02 14:41:45 -04:00
Chenlei Hu
9d69db6db7 Update litegraph (Fix node.pos serialization) (#711) 2024-09-02 14:34:58 -04:00
Chenlei Hu
e733b87f22 Add beforeRegisterVueAppNodeDefs hook (#709)
* Add beforeRegisterVueAppNodeDefs hook

* Remove min-char constraint on node library search
2024-09-02 11:06:16 -04:00
Chenlei Hu
adcef7d2f4 Add locale setting (#708) 2024-09-02 10:01:00 -04:00
mijuku233
8ba5da14bc Custom background image (#698) 2024-09-01 19:46:55 -04:00
Chenlei Hu
c181bf1f55 1.2.43 (#705) 2024-09-01 19:01:16 -04:00
Chenlei Hu
d9a7537169 Fix top level bookmark migration (#706) 2024-09-01 18:55:56 -04:00
Chenlei Hu
75e91137f0 Display node display_name instead of name (#704) 2024-09-01 18:33:19 -04:00
Chenlei Hu
a4a298924e Show node id name in node searchbox (#703)
* Show node unique name in node searchbox

* nit
2024-09-01 18:12:27 -04:00
Chenlei Hu
14da8433f7 Migrate node library sidebar to use unique name instead of display name (#702)
* Migrate node library sidebar to use unique name instead of display name

* Break word
2024-09-01 17:52:53 -04:00
Chenlei Hu
ff2d160230 Reduce divider margin in node library (#701) 2024-09-01 14:49:31 -04:00
Chenlei Hu
b0b5f92940 Add setting to control tree explorer item padding (#700) 2024-09-01 14:36:15 -04:00
Chenlei Hu
d04dbcd2c1 [Major Refactor] Use TreeExplorer on nodeLibrarySidebarTab (#699)
* Basic move

* Add back node bookmark

* Move node preview

* Fix drag node to canvas

* Restore click node to add to canvas

* Split bookmark tree and library tree

* Migrate rename and delete context menu

* Fix expanded keys

* Split components

* Support extra menu items

* Context menu only for folder

* Migrate add folder

* Handle drop

* Store color customization

* remove extra padding

* Do not show context menu if no item

* Hide divider if no bookmark

* Sort bookmarks alphabetically default

* nit

* proper edit

* Update test selectors

* Auto expand on item drop

* nit

* Fix tests

* Search also searches bookmarks tree

* Add serach playwright test
2024-09-01 14:03:15 -04:00
huchenlei
5383f97eba Add tree explorer tree node test 2024-08-31 21:10:32 -04:00
huchenlei
bc7da487e8 Add drop handler 2024-08-31 21:10:32 -04:00
huchenlei
86e7c12e27 Add draggable/droppable flags 2024-08-31 21:10:32 -04:00
huchenlei
50f1ca8eaf Add extra interfaces 2024-08-31 21:10:32 -04:00
huchenlei
280b43fd58 Merge folder and node impl 2024-08-31 21:10:32 -04:00
huchenlei
488f0d82b4 More refactor 2024-08-31 21:10:32 -04:00
huchenlei
bc3ec65967 Move 2024-08-31 21:10:32 -04:00
huchenlei
61342edba0 1.2.42 2024-08-31 09:56:05 -04:00
huchenlei
9247aec03a nit 2024-08-31 09:54:36 -04:00
huchenlei
0e88308571 Remove github button in error dialog 2024-08-31 09:54:36 -04:00
209 changed files with 4525 additions and 1263 deletions

View File

@@ -12,43 +12,7 @@ jobs:
runs-on: ubuntu-latest
if: github.event.label.name == 'New Browser Test Expectations'
steps:
- name: Checkout ComfyUI
uses: actions/checkout@v4
with:
repository: "comfyanonymous/ComfyUI"
path: "ComfyUI"
ref: master
- name: Checkout ComfyUI_frontend
uses: actions/checkout@v4
with:
repository: "Comfy-Org/ComfyUI_frontend"
path: "ComfyUI_frontend"
ref: ${{ github.head_ref }}
- uses: actions/setup-node@v3
with:
node-version: lts/*
- uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: Install requirements
run: |
python -m pip install --upgrade pip
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu
pip install -r requirements.txt
pip install wait-for-it
working-directory: ComfyUI
- name: Build & Install ComfyUI_frontend
run: |
npm ci
npm run build
rm -rf ../ComfyUI/web/*
mv dist/* ../ComfyUI/web/
working-directory: ComfyUI_frontend
- name: Start ComfyUI server
run: |
python main.py --cpu &
wait-for-it --service 127.0.0.1:8188 -t 600
working-directory: ComfyUI
- uses: Comfy-Org/ComfyUI_frontend_setup_action@v1
- name: Install Playwright Browsers
run: npx playwright install chromium --with-deps
working-directory: ComfyUI_frontend
@@ -72,6 +36,8 @@ jobs:
run: |
git config --global user.name 'github-actions'
git config --global user.email 'github-actions@github.com'
git fetch origin ${{ github.head_ref }}
git checkout -B ${{ github.head_ref }} origin/${{ github.head_ref }}
git add browser_tests
git commit -m "Update test expectations [skip ci]"
git push origin HEAD:${{ github.head_ref }}

View File

@@ -12,80 +12,64 @@ on:
- 'dev*'
jobs:
test:
jest-tests:
runs-on: ubuntu-latest
steps:
- name: Checkout ComfyUI
uses: actions/checkout@v4
with:
repository: "comfyanonymous/ComfyUI"
path: "ComfyUI"
ref: master
- name: Checkout ComfyUI_frontend
uses: actions/checkout@v4
with:
repository: "Comfy-Org/ComfyUI_frontend"
path: "ComfyUI_frontend"
- name: Checkout ComfyUI_devtools
uses: actions/checkout@v4
with:
repository: "Comfy-Org/ComfyUI_devtools"
path: "ComfyUI/custom_nodes/ComfyUI_devtools"
- name: Get commit message
id: commit-message
run: echo "::set-output name=message::$(git log -1 --pretty=%B)"
working-directory: ComfyUI_frontend
- name: Checkout ComfyUI_examples
uses: actions/checkout@v4
with:
repository: "comfyanonymous/ComfyUI_examples"
path: "ComfyUI_frontend/tests-ui/ComfyUI_examples"
ref: master
- name: Skip CI
if: contains(steps.commit-message.outputs.message, '[skip ci]')
run: echo "Skipping CI as commit contains '[skip ci]'"
continue-on-error: true
working-directory: ComfyUI_frontend
- uses: actions/setup-node@v3
with:
node-version: lts/*
- uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: Install requirements
run: |
python -m pip install --upgrade pip
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu
pip install -r requirements.txt
pip install wait-for-it
working-directory: ComfyUI
- name: Build & Install ComfyUI_frontend
run: |
npm ci
npm run build
rm -rf ../ComfyUI/web/*
mv dist/* ../ComfyUI/web/
working-directory: ComfyUI_frontend
- name: Start ComfyUI server
run: |
python main.py --cpu &
wait-for-it --service 127.0.0.1:8188 -t 600
working-directory: ComfyUI
- name: Run UI tests
- uses: Comfy-Org/ComfyUI_frontend_setup_action@v1
- name: Run Jest tests
run: |
npm run test:generate
npm run test:generate:examples
npm test -- --verbose
working-directory: ComfyUI_frontend
playwright-tests-chromium:
runs-on: ubuntu-latest
steps:
- uses: Comfy-Org/ComfyUI_frontend_setup_action@v1
- name: Install Playwright Browsers
run: npx playwright install chromium --with-deps
working-directory: ComfyUI_frontend
- name: Run Playwright tests
run: npx playwright test
- name: Run Playwright tests (Chromium)
run: npx playwright test --project=chromium
working-directory: ComfyUI_frontend
- uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report
name: playwright-report-chromium
path: ComfyUI_frontend/playwright-report/
retention-days: 30
playwright-tests-chromium-2x:
runs-on: ubuntu-latest
steps:
- uses: Comfy-Org/ComfyUI_frontend_setup_action@v1
- name: Install Playwright Browsers
run: npx playwright install chromium --with-deps
working-directory: ComfyUI_frontend
- name: Run Playwright tests (Chromium 2x)
run: npx playwright test --project=chromium-2x
working-directory: ComfyUI_frontend
- uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report-chromium-2x
path: ComfyUI_frontend/playwright-report/
retention-days: 30
playwright-tests-mobile-chrome:
runs-on: ubuntu-latest
steps:
- uses: Comfy-Org/ComfyUI_frontend_setup_action@v1
- name: Install Playwright Browsers
run: npx playwright install chromium --with-deps
working-directory: ComfyUI_frontend
- name: Run Playwright tests (Mobile Chrome)
run: npx playwright test --project=mobile-chrome
working-directory: ComfyUI_frontend
- uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report-mobile-chrome
path: ComfyUI_frontend/playwright-report/
retention-days: 30

1
.gitignore vendored
View File

@@ -23,6 +23,7 @@ dist-ssr
*.njsproj
*.sln
*.sw?
components.d.ts
# Ignore test data.
tests-ui/data/*

View File

@@ -90,6 +90,13 @@ https://github.com/user-attachments/assets/4bbca3ee-318f-4cf0-be32-a5a5541066cf
### QoL changes
<details>
<summary>v1.2.44: **Litegraph** Double click group title to edit</summary>
https://github.com/user-attachments/assets/5bf0e2b6-8b3a-40a7-b44f-f0879e9ad26f
</details>
<details>
<summary>v1.2.39: **Litegraph** Group selected nodes with Ctrl + G</summary>
@@ -164,9 +171,11 @@ We will support custom icons later.
app.extensionManager.toast.add({
severity: 'info',
summary: 'Loaded!',
detail: 'Extension loaded!'
detail: 'Extension loaded!',
life: 3000
})
```
Documentation of all supported options can be found here: <https://primevue.org/toast/#api.toast.interfaces.ToastMessageOptions>
![image](https://github.com/user-attachments/assets/de02cd7e-cd81-43d1-a0b0-bccef92ff487)
</details>
@@ -184,18 +193,17 @@ We will support custom icons later.
- Introduce Vue to start managing part of the UI.
- Easy install and version management (<https://github.com/comfyanonymous/ComfyUI/pull/3897>).
- Better node management. Sherlock <https://github.com/Nuked88/ComfyUI-N-Sidebar>.
- Replace the existing ComfyUI front-end implementation. <https://github.com/comfyanonymous/ComfyUI/pull/4379>
### What to be done
- Replace the existing ComfyUI front-end impl
- Remove `@ts-ignore`s.
- Turn on `strict` on `tsconfig.json`.
- Add more widget types for node developers.
- LLM streaming node.
- Linear mode (Similar to InvokeAI's linear mode).
- Keybinding settings management. Register keybindings API for custom nodes.
- New extensions API for adding UI-related features.
## Development
@@ -223,9 +231,24 @@ core extensions will be loaded.
### LiteGraph
This repo is using litegraph package hosted on https://github.com/Comfy-Org/litegraph.js. Any changes to litegraph should be submitted in that repo instead.
This repo is using litegraph package hosted on <https://github.com/Comfy-Org/litegraph.js>. Any changes to litegraph should be submitted in that repo instead.
### Test litegraph changes
- Run `npm link` in the local litegraph repo.
- Run `npm uninstall @comfyorg/litegraph` in this repo.
- Run `npm link @comfyorg/litegraph` in this repo.
This will replace the litegraph package in this repo with the local litegraph repo.
## Deploy
- Option 1: Set `DEPLOY_COMFYUI_DIR` in `.env` and run `npm run deploy`.
- Option 2: Copy everything under `dist/` to `ComfyUI/web/` in your ComfyUI checkout manually.
## Publish release to ComfyUI main repo
Run following command to publish a release to ComfyUI main repo. The script will create a new branch and do a commit to `web/` folder by checkout `dist.zip`
from GitHub release.
- `python scripts/main_repo_release.py <path_to_comfyui_main_repo> <version>`

View File

@@ -2,6 +2,8 @@ import type { Page, Locator } from '@playwright/test'
import { test as base } from '@playwright/test'
import dotenv from 'dotenv'
dotenv.config()
import * as fs from 'fs'
import { NodeBadgeMode } from '../src/types/nodeSource'
interface Position {
x: number
@@ -57,8 +59,12 @@ class NodeLibrarySidebarTab {
)
}
get nodeLibrarySearchBoxInput() {
return this.page.locator('.node-lib-search-box input[type="text"]')
}
get nodeLibraryTree() {
return this.page.locator('.node-lib-tree')
return this.page.locator('.node-lib-tree-explorer')
}
get nodePreview() {
@@ -83,7 +89,7 @@ class NodeLibrarySidebarTab {
}
folderSelector(folderName: string) {
return `.p-tree-node-content:has(> .node-lib-tree-node-label:has(.folder-label:has-text("${folderName}")))`
return `.p-tree-node-content:has(> .tree-explorer-node-label:has(.tree-folder .node-label:has-text("${folderName}")))`
}
getFolder(folderName: string) {
@@ -91,7 +97,7 @@ class NodeLibrarySidebarTab {
}
nodeSelector(nodeName: string) {
return `.p-tree-node-content:has(> .node-lib-tree-node-label:has(.node-label:has-text("${nodeName}")))`
return `.p-tree-node-content:has(> .tree-explorer-node-label:has(.tree-leaf .node-label:has-text("${nodeName}")))`
}
getNode(nodeName: string) {
@@ -196,6 +202,17 @@ export class ComfyPage {
// Reset view to force re-rendering of canvas. So that info fields like fps
// become hidden.
await this.resetView()
// Hide all badges by default.
await this.setSetting('Comfy.NodeBadge.NodeIdBadgeMode', NodeBadgeMode.None)
await this.setSetting(
'Comfy.NodeBadge.NodeSourceBadgeMode',
NodeBadgeMode.None
)
}
public assetPath(fileName: string) {
return `./browser_tests/assets/${fileName}`
}
async setSetting(settingId: string, settingValue: any) {
@@ -234,7 +251,7 @@ export class ComfyPage {
async loadWorkflow(workflowName: string) {
await this.workflowUploadInput.setInputFiles(
`./browser_tests/assets/${workflowName}.json`
this.assetPath(`${workflowName}.json`)
)
await this.nextFrame()
}
@@ -296,6 +313,54 @@ export class ComfyPage {
await this.nextFrame()
}
async dragAndDropFile(fileName: string) {
const filePath = this.assetPath(fileName)
// Read the file content
const buffer = fs.readFileSync(filePath)
// Get file type
const getFileType = (fileName: string) => {
if (fileName.endsWith('.png')) return 'image/png'
if (fileName.endsWith('.webp')) return 'image/webp'
if (fileName.endsWith('.json')) return 'application/json'
return 'application/octet-stream'
}
const fileType = getFileType(fileName)
await this.page.evaluate(
async ({ buffer, fileName, fileType }) => {
const file = new File([new Uint8Array(buffer)], fileName, {
type: fileType
})
const dataTransfer = new DataTransfer()
dataTransfer.items.add(file)
const dropEvent = new DragEvent('drop', {
bubbles: true,
cancelable: true,
dataTransfer
})
Object.defineProperty(dropEvent, 'preventDefault', {
value: () => {},
writable: false
})
Object.defineProperty(dropEvent, 'stopPropagation', {
value: () => {},
writable: false
})
document.dispatchEvent(dropEvent)
},
{ buffer: [...new Uint8Array(buffer)], fileName, fileType }
)
await this.nextFrame()
}
async dragNode2() {
await this.dragAndDrop({ x: 622, y: 400 }, { x: 622, y: 300 })
await this.nextFrame()
@@ -420,6 +485,13 @@ export class ComfyPage {
await this.nextFrame()
}
async ctrlY() {
await this.page.keyboard.down('Control')
await this.page.keyboard.press('KeyY')
await this.page.keyboard.up('Control')
await this.nextFrame()
}
async ctrlArrowUp() {
await this.page.keyboard.down('Control')
await this.page.keyboard.press('ArrowUp')

Binary file not shown.

After

Width:  |  Height:  |  Size: 200 KiB

View File

@@ -5,10 +5,10 @@
{
"id": 14,
"type": "PreviewImage",
"pos": [
858,
-41
],
"pos": {
"0": 300,
"1": 60
},
"size": {
"0": 213.8594970703125,
"1": 50.65289306640625
@@ -23,6 +23,7 @@
"link": 15
}
],
"outputs": [],
"properties": {
"Node name for S&R": "PreviewImage"
}
@@ -30,10 +31,10 @@
{
"id": 17,
"type": "DevToolsErrorRaiseNode",
"pos": [
477,
-40
],
"pos": {
"0": 20,
"1": 60
},
"size": {
"0": 210,
"1": 26
@@ -41,6 +42,7 @@
"flags": {},
"order": 0,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "IMAGE",
@@ -71,10 +73,10 @@
"config": {},
"extra": {
"ds": {
"scale": 1.2100000000000006,
"scale": 1,
"offset": [
-266.1038310281165,
337.94335447664554
117.20766722169206,
472.69035116826046
]
}
},

Binary file not shown.

After

Width:  |  Height:  |  Size: 316 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 KiB

View File

@@ -0,0 +1,88 @@
{
"last_node_id": 9,
"last_link_id": 13,
"nodes": [
{
"id": 3,
"type": "KSampler",
"pos": {
"0": 10.321063995361328,
"1": 73.14462280273438
},
"size": {
"0": 315,
"1": 262
},
"flags": {},
"order": 0,
"mode": 0,
"inputs": [
{
"name": "model",
"type": "MODEL",
"link": null
},
{
"name": "positive",
"type": "CONDITIONING",
"link": null
},
{
"name": "negative",
"type": "CONDITIONING",
"link": null
},
{
"name": "latent_image",
"type": "LATENT",
"link": null
}
],
"outputs": [
{
"name": "LATENT",
"type": "LATENT",
"links": [],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "KSampler"
},
"widgets_values": [
156680208700286,
"randomize",
20,
8,
"euler",
"normal",
1
]
}
],
"links": [],
"groups": [
{
"title": "Group",
"bounding": [
0,
0,
335,
346
],
"color": "#3f789e",
"font_size": 24
}
],
"config": {},
"extra": {
"ds": {
"scale": 1,
"offset": [
0,
0
]
}
},
"version": 0.4
}

View File

@@ -0,0 +1,30 @@
{
"last_node_id": 9,
"last_link_id": 13,
"nodes": [],
"links": [],
"groups": [
{
"title": "Group",
"bounding": [
0,
0,
335,
346
],
"color": "#3f789e",
"font_size": 24
}
],
"config": {},
"extra": {
"ds": {
"scale": 1.2100000000000006,
"offset": [
104.34159172650945,
241.35965953210126
]
}
},
"version": 0.4
}

View File

@@ -0,0 +1,46 @@
{
"last_node_id": 9,
"last_link_id": 9,
"nodes": [
{
"id": 9,
"type": "SaveImage",
"pos": {
"0": 64,
"1": 104
},
"size": {
"0": 210,
"1": 58
},
"flags": {},
"order": 0,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": null
}
],
"outputs": [],
"properties": {},
"widgets_values": [
"ComfyUI"
]
}
],
"links": [],
"groups": [],
"config": {},
"extra": {
"ds": {
"scale": 1,
"offset": [
0,
0
]
}
},
"version": 0.4
}

View File

@@ -0,0 +1,377 @@
{
"last_node_id": 0,
"last_link_id": 18,
"nodes": [
{
"id": "CheckpointLoaderSimple.0",
"type": "CheckpointLoaderSimple",
"pos": {
"0": 100,
"1": 130
},
"size": {
"0": 315,
"1": 98
},
"flags": {},
"order": 0,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "MODEL",
"type": "MODEL",
"links": [
12
],
"shape": 3
},
{
"name": "CLIP",
"type": "CLIP",
"links": [
10,
11
],
"shape": 3
},
{
"name": "VAE",
"type": "VAE",
"links": [
17
],
"shape": 3
}
],
"properties": {
"Node name for S&R": "CheckpointLoaderSimple"
},
"widgets_values": [
"v1-5-pruned-emaonly.ckpt"
]
},
{
"id": "CLIPTextEncode.0",
"type": "CLIPTextEncode",
"pos": {
"0": 515,
"1": 130
},
"size": {
"0": 400,
"1": 200
},
"flags": {},
"order": 2,
"mode": 0,
"inputs": [
{
"name": "clip",
"type": "CLIP",
"link": 10
}
],
"outputs": [
{
"name": "CONDITIONING",
"type": "CONDITIONING",
"links": [
13
],
"shape": 3
}
],
"properties": {
"Node name for S&R": "CLIPTextEncode"
},
"widgets_values": [
"beautiful scenery nature glass bottle landscape, , purple galaxy bottle,"
]
},
{
"id": "CLIPTextEncode.1",
"type": "CLIPTextEncode",
"pos": {
"0": 515,
"1": 460
},
"size": {
"0": 400,
"1": 200
},
"flags": {},
"order": 3,
"mode": 0,
"inputs": [
{
"name": "clip",
"type": "CLIP",
"link": 11
}
],
"outputs": [
{
"name": "CONDITIONING",
"type": "CONDITIONING",
"links": [
14
],
"shape": 3
}
],
"properties": {
"Node name for S&R": "CLIPTextEncode"
},
"widgets_values": [
"text, watermark"
]
},
{
"id": "EmptyLatentImage.0",
"type": "EmptyLatentImage",
"pos": {
"0": 100,
"1": 358
},
"size": {
"0": 315,
"1": 106
},
"flags": {},
"order": 1,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "LATENT",
"type": "LATENT",
"links": [
15
],
"shape": 3
}
],
"properties": {
"Node name for S&R": "EmptyLatentImage"
},
"widgets_values": [
512,
512,
1
]
},
{
"id": "KSampler.0",
"type": "KSampler",
"pos": {
"0": 1015,
"1": 130
},
"size": {
"0": 315,
"1": 262
},
"flags": {},
"order": 4,
"mode": 0,
"inputs": [
{
"name": "model",
"type": "MODEL",
"link": 12
},
{
"name": "positive",
"type": "CONDITIONING",
"link": 13
},
{
"name": "negative",
"type": "CONDITIONING",
"link": 14
},
{
"name": "latent_image",
"type": "LATENT",
"link": 15
}
],
"outputs": [
{
"name": "LATENT",
"type": "LATENT",
"links": [
16
],
"shape": 3
}
],
"properties": {
"Node name for S&R": "KSampler"
},
"widgets_values": [
3,
"randomize",
20,
8,
"euler",
"normal",
1
]
},
{
"id": "VAEDecode.0",
"type": "VAEDecode",
"pos": {
"0": 1430,
"1": 130
},
"size": {
"0": 210,
"1": 46
},
"flags": {},
"order": 5,
"mode": 0,
"inputs": [
{
"name": "samples",
"type": "LATENT",
"link": 16
},
{
"name": "vae",
"type": "VAE",
"link": 17
}
],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
18
],
"shape": 3
}
],
"properties": {
"Node name for S&R": "VAEDecode"
}
},
{
"id": "SaveImage.0",
"type": "SaveImage",
"pos": {
"0": 1740,
"1": 130
},
"size": {
"0": 315,
"1": 58
},
"flags": {},
"order": 6,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 18
}
],
"outputs": [],
"properties": {},
"widgets_values": [
"ComfyUI"
]
}
],
"links": [
[
10,
"CheckpointLoaderSimple.0",
1,
"CLIPTextEncode.0",
0,
"CLIP"
],
[
11,
"CheckpointLoaderSimple.0",
1,
"CLIPTextEncode.1",
0,
"CLIP"
],
[
12,
"CheckpointLoaderSimple.0",
0,
"KSampler.0",
0,
"MODEL"
],
[
13,
"CLIPTextEncode.0",
0,
"KSampler.0",
1,
"CONDITIONING"
],
[
14,
"CLIPTextEncode.1",
0,
"KSampler.0",
2,
"CONDITIONING"
],
[
15,
"EmptyLatentImage.0",
0,
"KSampler.0",
3,
"LATENT"
],
[
16,
"KSampler.0",
0,
"VAEDecode.0",
0,
"LATENT"
],
[
17,
"CheckpointLoaderSimple.0",
2,
"VAEDecode.0",
1,
"VAE"
],
[
18,
"VAEDecode.0",
0,
"SaveImage.0",
0,
"IMAGE"
]
],
"groups": [],
"config": {},
"extra": {
"ds": {
"scale": 1,
"offset": [
149.9747408641311,
383.8593224280729
]
}
},
"version": 0.4
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 200 KiB

View File

@@ -0,0 +1,149 @@
import { expect } from '@playwright/test'
import { comfyPageFixture as test } from './ComfyPage'
const customColorPalettes = {
obsidian: {
version: 102,
id: 'obsidian',
name: 'Obsidian',
colors: {
node_slot: {
CLIP: '#FFD500',
CLIP_VISION: '#A8DADC',
CLIP_VISION_OUTPUT: '#ad7452',
CONDITIONING: '#FFA931',
CONTROL_NET: '#6EE7B7',
IMAGE: '#64B5F6',
LATENT: '#FF9CF9',
MASK: '#81C784',
MODEL: '#B39DDB',
STYLE_MODEL: '#C2FFAE',
VAE: '#FF6E6E',
TAESD: '#DCC274',
PIPE_LINE: '#7737AA',
PIPE_LINE_SDXL: '#7737AA',
INT: '#29699C',
XYPLOT: '#74DA5D',
X_Y: '#38291f'
},
litegraph_base: {
BACKGROUND_IMAGE:
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQBJREFUeNrs1rEKwjAUhlETUkj3vP9rdmr1Ysammk2w5wdxuLgcMHyptfawuZX4pJSWZTnfnu/lnIe/jNNxHHGNn//HNbbv+4dr6V+11uF527arU7+u63qfa/bnmh8sWLBgwYJlqRf8MEptXPBXJXa37BSl3ixYsGDBMliwFLyCV/DeLIMFCxYsWLBMwSt4Be/NggXLYMGCBUvBK3iNruC9WbBgwYJlsGApeAWv4L1ZBgsWLFiwYJmCV/AK3psFC5bBggULloJX8BpdwXuzYMGCBctgwVLwCl7Be7MMFixYsGDBsu8FH1FaSmExVfAxBa/gvVmwYMGCZbBg/W4vAQYA5tRF9QYlv/QAAAAASUVORK5CYII=',
CLEAR_BACKGROUND_COLOR: '#222222',
NODE_TITLE_COLOR: 'rgba(255,255,255,.75)',
NODE_SELECTED_TITLE_COLOR: '#FFF',
NODE_TEXT_SIZE: 14,
NODE_TEXT_COLOR: '#b8b8b8',
NODE_SUBTEXT_SIZE: 12,
NODE_DEFAULT_COLOR: 'rgba(0,0,0,.8)',
NODE_DEFAULT_BGCOLOR: 'rgba(22,22,22,.8)',
NODE_DEFAULT_BOXCOLOR: 'rgba(255,255,255,.75)',
NODE_DEFAULT_SHAPE: 'box',
NODE_BOX_OUTLINE_COLOR: '#236692',
DEFAULT_SHADOW_COLOR: 'rgba(0,0,0,0)',
DEFAULT_GROUP_FONT: 24,
WIDGET_BGCOLOR: '#242424',
WIDGET_OUTLINE_COLOR: '#333',
WIDGET_TEXT_COLOR: '#a3a3a8',
WIDGET_SECONDARY_TEXT_COLOR: '#97979c',
LINK_COLOR: '#9A9',
EVENT_LINK_COLOR: '#A86',
CONNECTING_LINK_COLOR: '#AFA'
},
comfy_base: {
'fg-color': '#fff',
'bg-color': '#242424',
'comfy-menu-bg': 'rgba(24,24,24,.9)',
'comfy-input-bg': '#262626',
'input-text': '#ddd',
'descrip-text': '#999',
'drag-text': '#ccc',
'error-text': '#ff4444',
'border-color': '#29292c',
'tr-even-bg-color': 'rgba(28,28,28,.9)',
'tr-odd-bg-color': 'rgba(19,19,19,.9)'
}
}
},
obsidian_dark: {
version: 102,
id: 'obsidian_dark',
name: 'Obsidian Dark',
colors: {
node_slot: {
CLIP: '#FFD500',
CLIP_VISION: '#A8DADC',
CLIP_VISION_OUTPUT: '#ad7452',
CONDITIONING: '#FFA931',
CONTROL_NET: '#6EE7B7',
IMAGE: '#64B5F6',
LATENT: '#FF9CF9',
MASK: '#81C784',
MODEL: '#B39DDB',
STYLE_MODEL: '#C2FFAE',
VAE: '#FF6E6E',
TAESD: '#DCC274',
PIPE_LINE: '#7737AA',
PIPE_LINE_SDXL: '#7737AA',
INT: '#29699C',
XYPLOT: '#74DA5D',
X_Y: '#38291f'
},
litegraph_base: {
BACKGROUND_IMAGE:
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAACXBIWXMAAAsTAAALEwEAmpwYAAAGlmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgOS4xLWMwMDEgNzkuMTQ2Mjg5OSwgMjAyMy8wNi8yNS0yMDowMTo1NSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0RXZ0PSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VFdmVudCMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIDI1LjEgKFdpbmRvd3MpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAyMy0xMS0xM1QwMDoxODowMiswMTowMCIgeG1wOk1vZGlmeURhdGU9IjIwMjMtMTEtMTVUMDI6MDQ6NTkrMDE6MDAiIHhtcDpNZXRhZGF0YURhdGU9IjIwMjMtMTEtMTVUMDI6MDQ6NTkrMDE6MDAiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOmIyYzRhNjA5LWJmYTctYTg0MC1iOGFlLTk3MzE2ZjM1ZGIyNyIgeG1wTU06RG9jdW1lbnRJRD0iYWRvYmU6ZG9jaWQ6cGhvdG9zaG9wOjk0ZmNlZGU4LTE1MTctZmQ0MC04ZGU3LWYzOTgxM2E3ODk5ZiIgeG1wTU06T3JpZ2luYWxEb2N1bWVudElEPSJ4bXAuZGlkOjIzMWIxMGIwLWI0ZmItMDI0ZS1iMTJlLTMwNTMwM2NkMDdjOCI+IDx4bXBNTTpIaXN0b3J5PiA8cmRmOlNlcT4gPHJkZjpsaSBzdEV2dDphY3Rpb249ImNyZWF0ZWQiIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6MjMxYjEwYjAtYjRmYi0wMjRlLWIxMmUtMzA1MzAzY2QwN2M4IiBzdEV2dDp3aGVuPSIyMDIzLTExLTEzVDAwOjE4OjAyKzAxOjAwIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgMjUuMSAoV2luZG93cykiLz4gPHJkZjpsaSBzdEV2dDphY3Rpb249InNhdmVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOjQ4OWY1NzlmLTJkNjUtZWQ0Zi04OTg0LTA4NGE2MGE1ZTMzNSIgc3RFdnQ6d2hlbj0iMjAyMy0xMS0xNVQwMjowNDo1OSswMTowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIDI1LjEgKFdpbmRvd3MpIiBzdEV2dDpjaGFuZ2VkPSIvIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJzYXZlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDpiMmM0YTYwOS1iZmE3LWE4NDAtYjhhZS05NzMxNmYzNWRiMjciIHN0RXZ0OndoZW49IjIwMjMtMTEtMTVUMDI6MDQ6NTkrMDE6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCAyNS4xIChXaW5kb3dzKSIgc3RFdnQ6Y2hhbmdlZD0iLyIvPiA8L3JkZjpTZXE+IDwveG1wTU06SGlzdG9yeT4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4OTe6GAAAAx0lEQVR42u3WMQoAIQxFwRzJys77X8vSLiRgITif7bYbgrwYc/mKXyBoY4VVBgsWLFiwYFmOlTv+9jfDOjHmr8u6eVkGCxYsWLBgmc5S8ApewXvgYRksWLBgKXidpeBdloL3wMOCBctgwVLwCl7BuyyDBQsWLFiwTGcpeAWv4D3wsAwWLFiwFLzOUvAuS8F74GHBgmWwYCl4Ba/gXZbBggULFixYprMUvIJX8B54WAYLFixYCl5nKXiXpeA98LBgwTJYsGC9tg1o8f4TTtqzNQAAAABJRU5ErkJggg==',
CLEAR_BACKGROUND_COLOR: '#000',
NODE_TITLE_COLOR: 'rgba(255,255,255,.75)',
NODE_SELECTED_TITLE_COLOR: '#FFF',
NODE_TEXT_SIZE: 14,
NODE_TEXT_COLOR: '#b8b8b8',
NODE_SUBTEXT_SIZE: 12,
NODE_DEFAULT_COLOR: 'rgba(0,0,0,.8)',
NODE_DEFAULT_BGCOLOR: 'rgba(22,22,22,.8)',
NODE_DEFAULT_BOXCOLOR: 'rgba(255,255,255,.75)',
NODE_DEFAULT_SHAPE: 'box',
NODE_BOX_OUTLINE_COLOR: '#236692',
DEFAULT_SHADOW_COLOR: 'rgba(0,0,0,0)',
DEFAULT_GROUP_FONT: 24,
WIDGET_BGCOLOR: '#242424',
WIDGET_OUTLINE_COLOR: '#333',
WIDGET_TEXT_COLOR: '#a3a3a8',
WIDGET_SECONDARY_TEXT_COLOR: '#97979c',
LINK_COLOR: '#9A9',
EVENT_LINK_COLOR: '#A86',
CONNECTING_LINK_COLOR: '#AFA'
},
comfy_base: {
'fg-color': '#fff',
'bg-color': '#242424',
'comfy-menu-bg': 'rgba(24,24,24,.9)',
'comfy-input-bg': '#262626',
'input-text': '#ddd',
'descrip-text': '#999',
'drag-text': '#ccc',
'error-text': '#ff4444',
'border-color': '#29292c',
'tr-even-bg-color': 'rgba(28,28,28,.9)',
'tr-odd-bg-color': 'rgba(19,19,19,.9)'
}
}
}
}
test.describe('Color Palette', () => {
test.beforeEach(async ({ comfyPage }) => {
await comfyPage.setSetting('Comfy.CustomColorPalettes', customColorPalettes)
})
test('Can show custom color palette', async ({ comfyPage }) => {
await comfyPage.setSetting('Comfy.ColorPalette', 'custom_obsidian_dark')
await comfyPage.nextFrame()
await expect(comfyPage.canvas).toHaveScreenshot(
'custom-color-palette-obsidian-dark.png'
)
// Reset to default color palette for other tests
await comfyPage.setSetting('Comfy.ColorPalette', 'dark')
await comfyPage.nextFrame()
await expect(comfyPage.canvas).toHaveScreenshot('default-color-palette.png')
})
})

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

View File

@@ -13,6 +13,48 @@ test.describe('Load workflow warning', () => {
})
})
test('Does not report warning when switching between opened workflows', async ({
comfyPage
}) => {
await comfyPage.setSetting('Comfy.UseNewMenu', 'Top')
await comfyPage.loadWorkflow('missing_nodes')
await comfyPage.page.locator('.p-dialog-close-button').click()
// Load default workflow
const workflowSelector = comfyPage.page.locator(
'button.comfyui-workflows-button'
)
await workflowSelector.hover()
await workflowSelector.click()
await comfyPage.page.locator('button[title="Load default workflow"]').click()
// Switch back to the missing_nodes workflow
await workflowSelector.click()
await comfyPage.page.locator('span:has-text("missing_nodes")').first().click()
await comfyPage.nextFrame()
await expect(comfyPage.page.locator('.comfy-missing-nodes')).not.toBeVisible()
await comfyPage.setSetting('Comfy.UseNewMenu', 'Disabled')
})
test('Does not report warning on undo/redo', async ({ comfyPage }) => {
await comfyPage.loadWorkflow('missing_nodes')
await comfyPage.page.locator('.p-dialog-close-button').click()
await comfyPage.nextFrame()
// Make a change to the graph
await comfyPage.setSetting('Comfy.NodeSearchBoxImpl', 'default')
await comfyPage.page.waitForTimeout(256)
await comfyPage.doubleClickCanvas()
await comfyPage.searchBox.fillAndSelectFirstNode('KSampler')
// Undo and redo the change
await comfyPage.ctrlZ()
await expect(comfyPage.page.locator('.comfy-missing-nodes')).not.toBeVisible()
await comfyPage.ctrlY()
await expect(comfyPage.page.locator('.comfy-missing-nodes')).not.toBeVisible()
})
test.describe('Execution error', () => {
test('Should display an error message when an execution error occurs', async ({
comfyPage
@@ -28,6 +70,9 @@ test.describe('Execution error', () => {
test.describe('Missing models warning', () => {
test.beforeEach(async ({ comfyPage }) => {
await comfyPage.page.evaluate((url: string) => {
return fetch(`${url}/api/devtools/cleanup_fake_model`)
}, comfyPage.url)
await comfyPage.setSetting('Comfy.Workflow.ModelDownload.AllowedSources', [
'http://localhost:8188'
])

View File

@@ -25,10 +25,8 @@ test.describe('Node Interaction', () => {
})
test('Can disconnect/connect edge', async ({ comfyPage }) => {
await comfyPage.setSetting('Comfy.LinkRelease.Action', 'no action')
await comfyPage.disconnectEdge()
// Close search menu popped up.
await comfyPage.page.keyboard.press('Escape')
await comfyPage.nextFrame()
await expect(comfyPage.canvas).toHaveScreenshot(
'disconnected-edge-with-menu.png'
)
@@ -113,12 +111,15 @@ test.describe('Node Interaction', () => {
)
})
test('Can close prompt dialog with canvas click', async ({ comfyPage }) => {
test('Can close prompt dialog with canvas click (number widget)', async ({
comfyPage
}) => {
const numberWidgetPos = {
x: 724,
y: 645
}
await comfyPage.canvas.click({
position: {
x: 724,
y: 645
}
position: numberWidgetPos
})
await expect(comfyPage.canvas).toHaveScreenshot('prompt-dialog-opened.png')
// Wait for 1s so that it does not trigger the search box by double click.
@@ -132,6 +133,32 @@ test.describe('Node Interaction', () => {
await expect(comfyPage.canvas).toHaveScreenshot('prompt-dialog-closed.png')
})
test('Can close prompt dialog with canvas click (text widget)', async ({
comfyPage
}) => {
const textWidgetPos = {
x: 167,
y: 143
}
await comfyPage.loadWorkflow('single_save_image_node')
await comfyPage.canvas.click({
position: textWidgetPos
})
await expect(comfyPage.canvas).toHaveScreenshot(
'prompt-dialog-opened-text.png'
)
await comfyPage.page.waitForTimeout(1000)
await comfyPage.canvas.click({
position: {
x: 10,
y: 10
}
})
await expect(comfyPage.canvas).toHaveScreenshot(
'prompt-dialog-closed-text.png'
)
})
test('Can double click node title to edit', async ({ comfyPage }) => {
await comfyPage.loadWorkflow('single_ksampler')
await comfyPage.canvas.dblclick({
@@ -165,8 +192,40 @@ test.describe('Node Interaction', () => {
await comfyPage.page.keyboard.press('KeyG')
await comfyPage.page.keyboard.up('Control')
await comfyPage.nextFrame()
// Confirm group title
await comfyPage.page.keyboard.press('Enter')
await comfyPage.nextFrame()
await expect(comfyPage.canvas).toHaveScreenshot('group-selected-nodes.png')
})
// Somehow this test fails on GitHub Actions. It works locally.
// https://github.com/Comfy-Org/ComfyUI_frontend/pull/736
test.skip('Can pin/unpin nodes with keyboard shortcut', async ({
comfyPage
}) => {
await comfyPage.select2Nodes()
await comfyPage.canvas.press('KeyP')
await comfyPage.nextFrame()
await expect(comfyPage.canvas).toHaveScreenshot('nodes-pinned.png')
await comfyPage.canvas.press('KeyP')
await comfyPage.nextFrame()
await expect(comfyPage.canvas).toHaveScreenshot('nodes-unpinned.png')
})
})
test.describe('Group Interaction', () => {
test('Can double click group title to edit', async ({ comfyPage }) => {
await comfyPage.loadWorkflow('single_group')
await comfyPage.canvas.dblclick({
position: {
x: 50,
y: 10
}
})
await comfyPage.page.keyboard.type('Hello World')
await comfyPage.page.keyboard.press('Enter')
await expect(comfyPage.canvas).toHaveScreenshot('group-title-edited.png')
})
})
test.describe('Canvas Interaction', () => {
@@ -278,3 +337,10 @@ test.describe('Widget Interaction', () => {
await expect(textBox).toHaveValue('1girl')
})
})
test.describe('Load workflow', () => {
test('Can load workflow with string node id', async ({ comfyPage }) => {
await comfyPage.loadWorkflow('string_node_id')
await expect(comfyPage.canvas).toHaveScreenshot('string_node_id.png')
})
})

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

View File

@@ -0,0 +1,16 @@
import { expect } from '@playwright/test'
import { comfyPageFixture as test } from './ComfyPage'
test.describe('Load Workflow in Media', () => {
;[
'workflow.webp',
'edited_workflow.webp',
'no_workflow.webp',
'large_workflow.webp'
].forEach(async (fileName) => {
test(`Load workflow in ${fileName}`, async ({ comfyPage }) => {
await comfyPage.dragAndDropFile(fileName)
await expect(comfyPage.canvas).toHaveScreenshot(`${fileName}.png`)
})
})
})

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

View File

@@ -61,7 +61,7 @@ test.describe('Menu', () => {
test.describe('Node library sidebar', () => {
test.beforeEach(async ({ comfyPage }) => {
await comfyPage.setSetting('Comfy.NodeLibrary.Bookmarks', [])
await comfyPage.setSetting('Comfy.NodeLibrary.Bookmarks.V2', [])
await comfyPage.setSetting('Comfy.NodeLibrary.BookmarksCustomization', {})
// Open the sidebar
const tab = comfyPage.menu.nodeLibraryTab
@@ -102,21 +102,21 @@ test.describe('Menu', () => {
.click()
// Verify the bookmark is added to the bookmarks tab
expect(await comfyPage.getSetting('Comfy.NodeLibrary.Bookmarks')).toEqual(
['KSampler (Advanced)']
)
expect(
await comfyPage.getSetting('Comfy.NodeLibrary.Bookmarks.V2')
).toEqual(['KSamplerAdvanced'])
// Verify the bookmark node with the same name is added to the tree.
expect(await tab.getNode('KSampler (Advanced)').count()).toBe(2)
// Hover on the bookmark node to display the preview
await comfyPage.page.hover('.node-tree-leaf.bookmark')
await comfyPage.page.hover('.node-lib-bookmark-tree-explorer .tree-leaf')
expect(await comfyPage.page.isVisible('.node-lib-node-preview')).toBe(
true
)
})
test('Ignores unrecognized node', async ({ comfyPage }) => {
await comfyPage.setSetting('Comfy.NodeLibrary.Bookmarks', ['foo'])
await comfyPage.setSetting('Comfy.NodeLibrary.Bookmarks.V2', ['foo'])
const tab = comfyPage.menu.nodeLibraryTab
expect(await tab.getFolder('sampling').count()).toBe(1)
@@ -124,7 +124,7 @@ test.describe('Menu', () => {
})
test('Displays empty bookmarks folder', async ({ comfyPage }) => {
await comfyPage.setSetting('Comfy.NodeLibrary.Bookmarks', ['foo/'])
await comfyPage.setSetting('Comfy.NodeLibrary.Bookmarks.V2', ['foo/'])
const tab = comfyPage.menu.nodeLibraryTab
expect(await tab.getFolder('foo').count()).toBe(1)
})
@@ -134,63 +134,67 @@ test.describe('Menu', () => {
await tab.newFolderButton.click()
await comfyPage.page.keyboard.press('Enter')
expect(await tab.getFolder('New Folder').count()).toBe(1)
expect(await comfyPage.getSetting('Comfy.NodeLibrary.Bookmarks')).toEqual(
['New Folder/']
)
expect(
await comfyPage.getSetting('Comfy.NodeLibrary.Bookmarks.V2')
).toEqual(['New Folder/'])
})
test('Can add nested bookmark folder', async ({ comfyPage }) => {
await comfyPage.setSetting('Comfy.NodeLibrary.Bookmarks', ['foo/'])
await comfyPage.setSetting('Comfy.NodeLibrary.Bookmarks.V2', ['foo/'])
const tab = comfyPage.menu.nodeLibraryTab
await tab.getFolder('foo').click({ button: 'right' })
await comfyPage.page.getByLabel('New Folder').click()
await comfyPage.page.keyboard.type('bar')
await comfyPage.page.keyboard.press('Enter')
expect(await comfyPage.getSetting('Comfy.NodeLibrary.Bookmarks')).toEqual(
['foo/', 'foo/New Folder/']
)
expect(await tab.getFolder('bar').count()).toBe(1)
expect(
await comfyPage.getSetting('Comfy.NodeLibrary.Bookmarks.V2')
).toEqual(['foo/', 'foo/bar/'])
})
test('Can delete bookmark folder', async ({ comfyPage }) => {
await comfyPage.setSetting('Comfy.NodeLibrary.Bookmarks', ['foo/'])
await comfyPage.setSetting('Comfy.NodeLibrary.Bookmarks.V2', ['foo/'])
const tab = comfyPage.menu.nodeLibraryTab
await tab.getFolder('foo').click({ button: 'right' })
await comfyPage.page.getByLabel('Delete').click()
expect(await comfyPage.getSetting('Comfy.NodeLibrary.Bookmarks')).toEqual(
[]
)
expect(
await comfyPage.getSetting('Comfy.NodeLibrary.Bookmarks.V2')
).toEqual([])
})
test('Can rename bookmark folder', async ({ comfyPage }) => {
await comfyPage.setSetting('Comfy.NodeLibrary.Bookmarks', ['foo/'])
await comfyPage.setSetting('Comfy.NodeLibrary.Bookmarks.V2', ['foo/'])
const tab = comfyPage.menu.nodeLibraryTab
await tab.getFolder('foo').click({ button: 'right' })
await comfyPage.page.getByLabel('Rename').click()
await comfyPage.page
.locator('.p-contextmenu-item-label:has-text("Rename")')
.click()
await comfyPage.page.keyboard.insertText('bar')
await comfyPage.page.keyboard.press('Enter')
expect(await comfyPage.getSetting('Comfy.NodeLibrary.Bookmarks')).toEqual(
['bar/']
)
expect(
await comfyPage.getSetting('Comfy.NodeLibrary.Bookmarks.V2')
).toEqual(['bar/'])
})
test('Can add bookmark by dragging node to bookmark folder', async ({
comfyPage
}) => {
await comfyPage.setSetting('Comfy.NodeLibrary.Bookmarks', ['foo/'])
await comfyPage.setSetting('Comfy.NodeLibrary.Bookmarks.V2', ['foo/'])
const tab = comfyPage.menu.nodeLibraryTab
await tab.getFolder('sampling').click()
await comfyPage.page.dragAndDrop(
tab.nodeSelector('KSampler (Advanced)'),
tab.folderSelector('foo')
)
expect(await comfyPage.getSetting('Comfy.NodeLibrary.Bookmarks')).toEqual(
['foo/', 'foo/KSampler (Advanced)']
)
expect(
await comfyPage.getSetting('Comfy.NodeLibrary.Bookmarks.V2')
).toEqual(['foo/', 'foo/KSamplerAdvanced'])
})
test('Can add bookmark by clicking bookmark button', async ({
@@ -202,30 +206,30 @@ test.describe('Menu', () => {
.getNode('KSampler (Advanced)')
.locator('.bookmark-button')
.click()
expect(await comfyPage.getSetting('Comfy.NodeLibrary.Bookmarks')).toEqual(
['KSampler (Advanced)']
)
expect(
await comfyPage.getSetting('Comfy.NodeLibrary.Bookmarks.V2')
).toEqual(['KSamplerAdvanced'])
})
test('Can unbookmark node (Top level bookmark)', async ({ comfyPage }) => {
await comfyPage.setSetting('Comfy.NodeLibrary.Bookmarks', [
'KSampler (Advanced)'
await comfyPage.setSetting('Comfy.NodeLibrary.Bookmarks.V2', [
'KSamplerAdvanced'
])
const tab = comfyPage.menu.nodeLibraryTab
await tab
.getNode('KSampler (Advanced)')
.locator('.bookmark-button')
.click()
expect(await comfyPage.getSetting('Comfy.NodeLibrary.Bookmarks')).toEqual(
[]
)
expect(
await comfyPage.getSetting('Comfy.NodeLibrary.Bookmarks.V2')
).toEqual([])
})
test('Can unbookmark node (Library node bookmark)', async ({
comfyPage
}) => {
await comfyPage.setSetting('Comfy.NodeLibrary.Bookmarks', [
'KSampler (Advanced)'
await comfyPage.setSetting('Comfy.NodeLibrary.Bookmarks.V2', [
'KSamplerAdvanced'
])
const tab = comfyPage.menu.nodeLibraryTab
await tab.getFolder('sampling').click()
@@ -234,12 +238,12 @@ test.describe('Menu', () => {
.nth(1)
.locator('.bookmark-button')
.click()
expect(await comfyPage.getSetting('Comfy.NodeLibrary.Bookmarks')).toEqual(
[]
)
expect(
await comfyPage.getSetting('Comfy.NodeLibrary.Bookmarks.V2')
).toEqual([])
})
test('Can customize icon', async ({ comfyPage }) => {
await comfyPage.setSetting('Comfy.NodeLibrary.Bookmarks', ['foo/'])
await comfyPage.setSetting('Comfy.NodeLibrary.Bookmarks.V2', ['foo/'])
const tab = comfyPage.menu.nodeLibraryTab
await tab.getFolder('foo').click({ button: 'right' })
await comfyPage.page.getByLabel('Customize').click()
@@ -262,7 +266,7 @@ test.describe('Menu', () => {
})
// If color is left as default, it should not be saved
test('Can customize icon (default field)', async ({ comfyPage }) => {
await comfyPage.setSetting('Comfy.NodeLibrary.Bookmarks', ['foo/'])
await comfyPage.setSetting('Comfy.NodeLibrary.Bookmarks.V2', ['foo/'])
const tab = comfyPage.menu.nodeLibraryTab
await tab.getFolder('foo').click({ button: 'right' })
await comfyPage.page.getByLabel('Customize').click()
@@ -280,7 +284,7 @@ test.describe('Menu', () => {
})
})
test('Can rename customized bookmark folder', async ({ comfyPage }) => {
await comfyPage.setSetting('Comfy.NodeLibrary.Bookmarks', ['foo/'])
await comfyPage.setSetting('Comfy.NodeLibrary.Bookmarks.V2', ['foo/'])
await comfyPage.setSetting('Comfy.NodeLibrary.BookmarksCustomization', {
'foo/': {
icon: 'pi-folder',
@@ -289,13 +293,15 @@ test.describe('Menu', () => {
})
const tab = comfyPage.menu.nodeLibraryTab
await tab.getFolder('foo').click({ button: 'right' })
await comfyPage.page.getByLabel('Rename').click()
await comfyPage.page
.locator('.p-contextmenu-item-label:has-text("Rename")')
.click()
await comfyPage.page.keyboard.insertText('bar')
await comfyPage.page.keyboard.press('Enter')
await comfyPage.nextFrame()
expect(await comfyPage.getSetting('Comfy.NodeLibrary.Bookmarks')).toEqual(
['bar/']
)
expect(
await comfyPage.getSetting('Comfy.NodeLibrary.Bookmarks.V2')
).toEqual(['bar/'])
expect(
await comfyPage.getSetting('Comfy.NodeLibrary.BookmarksCustomization')
).toEqual({
@@ -307,7 +313,7 @@ test.describe('Menu', () => {
})
test('Can delete customized bookmark folder', async ({ comfyPage }) => {
await comfyPage.setSetting('Comfy.NodeLibrary.Bookmarks', ['foo/'])
await comfyPage.setSetting('Comfy.NodeLibrary.Bookmarks.V2', ['foo/'])
await comfyPage.setSetting('Comfy.NodeLibrary.BookmarksCustomization', {
'foo/': {
icon: 'pi-folder',
@@ -318,12 +324,44 @@ test.describe('Menu', () => {
await tab.getFolder('foo').click({ button: 'right' })
await comfyPage.page.getByLabel('Delete').click()
await comfyPage.nextFrame()
expect(
await comfyPage.getSetting('Comfy.NodeLibrary.Bookmarks.V2')
).toEqual([])
expect(
await comfyPage.getSetting('Comfy.NodeLibrary.BookmarksCustomization')
).toEqual({})
})
test('Can filter nodes in both trees', async ({ comfyPage }) => {
await comfyPage.setSetting('Comfy.NodeLibrary.Bookmarks.V2', [
'foo/',
'foo/KSamplerAdvanced',
'KSampler'
])
const tab = comfyPage.menu.nodeLibraryTab
await tab.nodeLibrarySearchBoxInput.fill('KSampler')
// Node search box is debounced and may take some time to update.
await comfyPage.page.waitForTimeout(1000)
expect(await tab.getNode('KSampler (Advanced)').count()).toBe(2)
})
test('Can migrate legacy bookmarks', async ({ comfyPage }) => {
await comfyPage.setSetting('Comfy.NodeLibrary.Bookmarks', [
'foo/',
'foo/KSampler (Advanced)',
'UNKNOWN',
'KSampler'
])
await comfyPage.setSetting('Comfy.NodeLibrary.Bookmarks.V2', [])
await comfyPage.page.reload()
await comfyPage.setup()
expect(await comfyPage.getSetting('Comfy.NodeLibrary.Bookmarks')).toEqual(
[]
)
expect(
await comfyPage.getSetting('Comfy.NodeLibrary.BookmarksCustomization')
).toEqual({})
await comfyPage.getSetting('Comfy.NodeLibrary.Bookmarks.V2')
).toEqual(['foo/', 'foo/KSamplerAdvanced', 'KSampler'])
})
})

View File

@@ -0,0 +1,79 @@
import { expect } from '@playwright/test'
import { comfyPageFixture as test } from './ComfyPage'
import type { ComfyApp } from '../src/scripts/app'
import { NodeBadgeMode } from '../src/types/nodeSource'
test.describe('Node Badge', () => {
test('Can add badge', async ({ comfyPage }) => {
await comfyPage.page.evaluate(() => {
const LGraphBadge = window['LGraphBadge']
const app = window['app'] as ComfyApp
const graph = app.graph
// @ts-expect-error - accessing private property
const nodes = graph._nodes
for (const node of nodes) {
node.badges = [new LGraphBadge({ text: 'Test Badge' })]
}
graph.setDirtyCanvas(true, true)
})
await expect(comfyPage.canvas).toHaveScreenshot('node-badge.png')
})
test('Can add multiple badges', async ({ comfyPage }) => {
await comfyPage.page.evaluate(() => {
const LGraphBadge = window['LGraphBadge']
const app = window['app'] as ComfyApp
const graph = app.graph
// @ts-expect-error - accessing private property
const nodes = graph._nodes
for (const node of nodes) {
node.badges = [
new LGraphBadge({ text: 'Test Badge 1' }),
new LGraphBadge({ text: 'Test Badge 2' })
]
}
graph.setDirtyCanvas(true, true)
})
await expect(comfyPage.canvas).toHaveScreenshot('node-badge-multiple.png')
})
test('Can add badge left-side', async ({ comfyPage }) => {
await comfyPage.page.evaluate(() => {
const LGraphBadge = window['LGraphBadge']
const app = window['app'] as ComfyApp
const graph = app.graph
// @ts-expect-error - accessing private property
const nodes = graph._nodes
for (const node of nodes) {
node.badges = [new LGraphBadge({ text: 'Test Badge' })]
// @ts-expect-error - Enum value
node.badgePosition = 'top-left'
}
graph.setDirtyCanvas(true, true)
})
await expect(comfyPage.canvas).toHaveScreenshot('node-badge-left.png')
})
})
test.describe('Node source badge', () => {
Object.values(NodeBadgeMode).forEach(async (mode) => {
test(`Shows node badges (${mode})`, async ({ comfyPage }) => {
// Execution error workflow has both custom node and core node.
await comfyPage.loadWorkflow('execution_error')
await comfyPage.setSetting('Comfy.NodeBadge.NodeSourceBadgeMode', mode)
await comfyPage.setSetting('Comfy.NodeBadge.NodeIdBadgeMode', mode)
await comfyPage.nextFrame()
await comfyPage.resetView()
await expect(comfyPage.canvas).toHaveScreenshot(`node-badge-${mode}.png`)
})
})
})

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

View File

@@ -3,22 +3,21 @@ import { comfyPageFixture as test } from './ComfyPage'
test.describe('Node search box', () => {
test.beforeEach(async ({ comfyPage }) => {
await comfyPage.setSetting(
'Comfy.NodeSearchBoxImpl.LinkReleaseTrigger',
'always'
)
await comfyPage.setSetting('Comfy.LinkRelease.Action', 'search box')
await comfyPage.setSetting('Comfy.LinkRelease.ActionShift', 'search box')
await comfyPage.setSetting('Comfy.NodeSearchBoxImpl', 'default')
})
;['always', 'hold shift', 'NOT hold shift'].forEach((triggerMode) => {
test(`Can trigger on empty canvas double click (${triggerMode})`, async ({
comfyPage
}) => {
await comfyPage.setSetting(
'Comfy.NodeSearchBoxImpl.LinkReleaseTrigger',
triggerMode
)
await comfyPage.doubleClickCanvas()
await expect(comfyPage.searchBox.input).toHaveCount(1)
})
test(`Can trigger on empty canvas double click`, async ({ comfyPage }) => {
await comfyPage.doubleClickCanvas()
await expect(comfyPage.searchBox.input).toHaveCount(1)
})
test(`Can trigger on group body double click`, async ({ comfyPage }) => {
await comfyPage.loadWorkflow('single_group_only')
await comfyPage.page.mouse.dblclick(50, 50)
await comfyPage.nextFrame()
await expect(comfyPage.searchBox.input).toHaveCount(1)
})
test('Can trigger on link release', async ({ comfyPage }) => {
@@ -77,4 +76,46 @@ test.describe('Node search box', () => {
'added-node-no-connection.png'
)
})
test('Has correct aria-labels on search results', async ({ comfyPage }) => {
const node = 'Load Checkpoint'
await comfyPage.doubleClickCanvas()
await comfyPage.searchBox.fillAndSelectFirstNode(node)
const firstResult = comfyPage.page
.locator('li.p-autocomplete-option')
.first()
await expect(firstResult).toHaveAttribute('aria-label', node)
})
test('@mobile Can trigger on empty canvas tap', async ({ comfyPage }) => {
await comfyPage.closeMenu()
await comfyPage.loadWorkflow('single_ksampler')
const screenCenter = {
x: 200,
y: 400
}
await comfyPage.canvas.tap({
position: screenCenter
})
await comfyPage.canvas.tap({
position: screenCenter
})
await comfyPage.page.waitForTimeout(256)
await expect(comfyPage.searchBox.input).not.toHaveCount(0)
})
})
test.describe('Release context menu', () => {
test.beforeEach(async ({ comfyPage }) => {
await comfyPage.setSetting('Comfy.LinkRelease.Action', 'context menu')
await comfyPage.setSetting('Comfy.LinkRelease.ActionShift', 'search box')
await comfyPage.setSetting('Comfy.NodeSearchBoxImpl', 'default')
})
test('Can trigger on link release', async ({ comfyPage }) => {
await comfyPage.disconnectEdge()
await expect(comfyPage.canvas).toHaveScreenshot(
'link-release-context-menu.png'
)
})
})

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Some files were not shown because too many files have changed in this diff Show More