Compare commits
454 Commits
manual-i18
...
bl-refacto
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
799ab5e5aa | ||
|
|
d7796fcda4 | ||
|
|
4404c0461d | ||
|
|
4cb03cf052 | ||
|
|
eeb0977738 | ||
|
|
9a505100ac | ||
|
|
21873d40d5 | ||
|
|
cbbbadf438 | ||
|
|
d2972220bb | ||
|
|
4e08ed64f0 | ||
|
|
13db1e484b | ||
|
|
8b7bc5eb89 | ||
|
|
fd474fe2aa | ||
|
|
b6b6455189 | ||
|
|
1455845a30 | ||
|
|
6b3a4d214b | ||
|
|
06b0eecfe4 | ||
|
|
b222cae56e | ||
|
|
8188029c6c | ||
|
|
b943c0fa75 | ||
|
|
c9da8b200d | ||
|
|
9f0fa7202d | ||
|
|
eda781ad37 | ||
|
|
ec3a77355f | ||
|
|
c56fff0b8b | ||
|
|
87f5480462 | ||
|
|
ea01723249 | ||
|
|
b2ea7b4a62 | ||
|
|
a234dac038 | ||
|
|
1461e371ad | ||
|
|
874ef3ba0c | ||
|
|
45ebc59033 | ||
|
|
fc1d040e06 | ||
|
|
8fc54646de | ||
|
|
fe0eaaefb3 | ||
|
|
99b3a59679 | ||
|
|
0616c049e4 | ||
|
|
34d5a4523a | ||
|
|
f62175ed0c | ||
|
|
d9157925f5 | ||
|
|
89f4452488 | ||
|
|
247080f0d7 | ||
|
|
61d0a12aae | ||
|
|
6617de771f | ||
|
|
893621265c | ||
|
|
338cbd4eed | ||
|
|
9a452fc31a | ||
|
|
0a73072ff1 | ||
|
|
e7745eb2be | ||
|
|
51f0f111ea | ||
|
|
d69c54820f | ||
|
|
3eedff3876 | ||
|
|
349f351f54 | ||
|
|
72ab84e37b | ||
|
|
022cf6dd8e | ||
|
|
2cb078cd9e | ||
|
|
dc0b729efa | ||
|
|
480f37e2f5 | ||
|
|
1df6187972 | ||
|
|
c636900487 | ||
|
|
f09590f269 | ||
|
|
07a74e3cdc | ||
|
|
ac9ebe1266 | ||
|
|
cd7310cb8c | ||
|
|
4e7e6e2bf3 | ||
|
|
1c6edd146b | ||
|
|
2c555efdfe | ||
|
|
59736c6d1d | ||
|
|
84e6e99f17 | ||
|
|
abf2b3b980 | ||
|
|
661885f5e5 | ||
|
|
5cff131144 | ||
|
|
038ed27107 | ||
|
|
139ae87983 | ||
|
|
b994608506 | ||
|
|
7b1cce1d0e | ||
|
|
39eca0bc0a | ||
|
|
2970692176 | ||
|
|
4b1c165d43 | ||
|
|
720de8cc8a | ||
|
|
48335475dc | ||
|
|
0d3d258995 | ||
|
|
3818ba5d17 | ||
|
|
5090f41028 | ||
|
|
7e4c756258 | ||
|
|
37fab21daf | ||
|
|
706ff953de | ||
|
|
9c97fb359d | ||
|
|
c662c77305 | ||
|
|
d0e81cdd33 | ||
|
|
01b3aeae68 | ||
|
|
20731fe3f0 | ||
|
|
bf9659fb2c | ||
|
|
e9352f613e | ||
|
|
d76b1abc46 | ||
|
|
fd757027a9 | ||
|
|
1efc2233c5 | ||
|
|
557b2fdb0e | ||
|
|
7fd2dc304a | ||
|
|
e8de474d42 | ||
|
|
3f291672d4 | ||
|
|
ac72999e26 | ||
|
|
e27b5311d1 | ||
|
|
35ddb19a68 | ||
|
|
aed342572c | ||
|
|
f16f5a0bc6 | ||
|
|
4fc7d6352f | ||
|
|
c47cedcec2 | ||
|
|
789617415d | ||
|
|
26cf45ed36 | ||
|
|
af10082134 | ||
|
|
28a779d41a | ||
|
|
ed5d258ab6 | ||
|
|
5d0aee59a6 | ||
|
|
d852bab617 | ||
|
|
dd93ac43d9 | ||
|
|
e0b1a6d212 | ||
|
|
bbd8d67f5f | ||
|
|
5c707a1b93 | ||
|
|
3077f4c4c4 | ||
|
|
9734466a13 | ||
|
|
5d62e01c11 | ||
|
|
8135b67f34 | ||
|
|
1215cb4670 | ||
|
|
a30705161d | ||
|
|
02b3b8ef5b | ||
|
|
aa212bea90 | ||
|
|
a2c7db9dc2 | ||
|
|
840f7f04fa | ||
|
|
042c2caa88 | ||
|
|
cd8b5b5d50 | ||
|
|
a25d89881b | ||
|
|
5b866b74b8 | ||
|
|
e8b9f8f4bc | ||
|
|
b0d2b44dad | ||
|
|
976b1a6bbd | ||
|
|
856eb446a5 | ||
|
|
992ba9d585 | ||
|
|
4b9d448b6c | ||
|
|
0ac1074a25 | ||
|
|
45f5ec15c2 | ||
|
|
a519e681dd | ||
|
|
fdb15e1296 | ||
|
|
d444b2c285 | ||
|
|
46ad1318e5 | ||
|
|
da332ed75d | ||
|
|
75d31f9d57 | ||
|
|
edcbcdfa84 | ||
|
|
1f4a52ca3e | ||
|
|
f2e925de62 | ||
|
|
5f7c7ca949 | ||
|
|
62441fa0f9 | ||
|
|
c96f719f91 | ||
|
|
9f19d8fb4b | ||
|
|
d954336973 | ||
|
|
49f373c46f | ||
|
|
9678a87846 | ||
|
|
3a9365af13 | ||
|
|
3ee0d394ca | ||
|
|
6a70152220 | ||
|
|
5c1e00ff8e | ||
|
|
78d585eca0 | ||
|
|
a6600aa109 | ||
|
|
c033e9e4d7 | ||
|
|
ac93a6ba3f | ||
|
|
961af8731e | ||
|
|
703de3e669 | ||
|
|
415ebfd67b | ||
|
|
97542efc9b | ||
|
|
13ce23399c | ||
|
|
a0c06bd723 | ||
|
|
3fc17ebdac | ||
|
|
0db2a2c03e | ||
|
|
b96bf3871c | ||
|
|
bc4549244e | ||
|
|
f3e68804e8 | ||
|
|
1749cfa678 | ||
|
|
0fea54c542 | ||
|
|
cd7666e3bc | ||
|
|
8d1261133a | ||
|
|
0919856a05 | ||
|
|
b0f81b2245 | ||
|
|
6449d26cee | ||
|
|
80cabc61ee | ||
|
|
76dd935b35 | ||
|
|
f7f3240100 | ||
|
|
e4022c455a | ||
|
|
c6e50d8f1b | ||
|
|
c004a2b8bd | ||
|
|
d0aee031e9 | ||
|
|
cec1de0147 | ||
|
|
b4976c1ddc | ||
|
|
1611c7a224 | ||
|
|
d01081dab4 | ||
|
|
e5d4d07d32 | ||
|
|
f086377307 | ||
|
|
687b9e659c | ||
|
|
da0d51311b | ||
|
|
e314d9cbd9 | ||
|
|
95baf8d2f1 | ||
|
|
f951e07cea | ||
|
|
023e466dba | ||
|
|
abd6823744 | ||
|
|
c4c0e52e64 | ||
|
|
295332dc46 | ||
|
|
5c498348b8 | ||
|
|
8133bd4b7b | ||
|
|
fd12591756 | ||
|
|
b3c939ff15 | ||
|
|
0801778f60 | ||
|
|
8ffe63f54e | ||
|
|
893409dfc8 | ||
|
|
df2fda6077 | ||
|
|
4f5bbe0605 | ||
|
|
a975e50f1b | ||
|
|
a17c74fa0c | ||
|
|
5e625a5002 | ||
|
|
002fac0232 | ||
|
|
7e115543fa | ||
|
|
80d75bb164 | ||
|
|
d59885839a | ||
|
|
cbb0f765b8 | ||
|
|
726a2fbbc9 | ||
|
|
553b5aa02b | ||
|
|
2ff0d951ed | ||
|
|
1f88925144 | ||
|
|
250433a91a | ||
|
|
eb664f47af | ||
|
|
bc85d4e87b | ||
|
|
7585444ce6 | ||
|
|
a886798a10 | ||
|
|
37975e4eac | ||
|
|
a41b8a6d4f | ||
|
|
b264685052 | ||
|
|
78d0ea6fa5 | ||
|
|
ea4e57b602 | ||
|
|
4789d86fe8 | ||
|
|
09e7d1040e | ||
|
|
dfa1cbba4f | ||
|
|
08220d50d9 | ||
|
|
045232a99b | ||
|
|
fb07941700 | ||
|
|
6866e1277a | ||
|
|
ff5d0923ca | ||
|
|
6b59f839e0 | ||
|
|
6a01b08ebf | ||
|
|
ede43c5e5c | ||
|
|
0483630f82 | ||
|
|
15cffe9d9e | ||
|
|
f5b949762d | ||
|
|
6786d8e4fb | ||
|
|
71ca28a46f | ||
|
|
4ff18fd7f0 | ||
|
|
45a46be513 | ||
|
|
631746939a | ||
|
|
4f8e820c51 | ||
|
|
b8d8193a38 | ||
|
|
2b57291756 | ||
|
|
62897c669b | ||
|
|
9918914b9d | ||
|
|
e601bcb300 | ||
|
|
853038859b | ||
|
|
186e065528 | ||
|
|
237e807fc9 | ||
|
|
27ab355f9c | ||
|
|
4c8c4a1ad4 | ||
|
|
e3bb29ceb8 | ||
|
|
ca312fd1ea | ||
|
|
bbff9c8217 | ||
|
|
6349ceee6c | ||
|
|
8c6ee026c0 | ||
|
|
668500cfa5 | ||
|
|
07fbe7267e | ||
|
|
fe09f88ea3 | ||
|
|
96a663704f | ||
|
|
11cb525545 | ||
|
|
4eb13ca17c | ||
|
|
6017fc43a6 | ||
|
|
6e2d86520b | ||
|
|
6e2a3a0d07 | ||
|
|
c7325c4da9 | ||
|
|
d146a7896a | ||
|
|
6b166a9d2f | ||
|
|
c145fd9df1 | ||
|
|
c04af09956 | ||
|
|
ac107b45ea | ||
|
|
f6a115e182 | ||
|
|
aa7a99f4e3 | ||
|
|
8804755ffa | ||
|
|
f28ebcac19 | ||
|
|
bfef8d7d4b | ||
|
|
90bf8dc74a | ||
|
|
6f878abea4 | ||
|
|
b1917c6469 | ||
|
|
bf054113f8 | ||
|
|
4604bbd669 | ||
|
|
77ee20597f | ||
|
|
8ae50d140d | ||
|
|
63f194b394 | ||
|
|
2b8abca6d8 | ||
|
|
48d01745cd | ||
|
|
90b1b47dd0 | ||
|
|
0e01ca0a98 | ||
|
|
cf093d02a7 | ||
|
|
1845708ddb | ||
|
|
c588f2f457 | ||
|
|
29ad47d20f | ||
|
|
9aeeab9a9f | ||
|
|
6645b3917f | ||
|
|
4ec6223189 | ||
|
|
dfcbbec2b9 | ||
|
|
c051c3a507 | ||
|
|
54cbf42a84 | ||
|
|
ef7575b8d6 | ||
|
|
68845ce33a | ||
|
|
46f4ce3890 | ||
|
|
5df037dfc4 | ||
|
|
3bc25b7aeb | ||
|
|
08fe2829d4 | ||
|
|
e70ddea684 | ||
|
|
038f86fe84 | ||
|
|
cb0dab6cdc | ||
|
|
568be0c44c | ||
|
|
6ea021d595 | ||
|
|
7245213ed6 | ||
|
|
b72e22f6be | ||
|
|
5f045b335d | ||
|
|
44e470488d | ||
|
|
ca220440b2 | ||
|
|
2e64c64ac7 | ||
|
|
d561f315d3 | ||
|
|
080334754c | ||
|
|
169d7404fe | ||
|
|
97d00ea47d | ||
|
|
0a2260a666 | ||
|
|
5b834acc86 | ||
|
|
7d4437c724 | ||
|
|
9997053290 | ||
|
|
43ab1c9b09 | ||
|
|
35b30a3ac6 | ||
|
|
6da2cf7b4d | ||
|
|
6fbd692370 | ||
|
|
b800227783 | ||
|
|
fa9f5fbca6 | ||
|
|
aa7f8912a7 | ||
|
|
76bfc9e678 | ||
|
|
0e44a4a354 | ||
|
|
9a89869517 | ||
|
|
551af4c0e0 | ||
|
|
76f21b9975 | ||
|
|
9f42f3dfb9 | ||
|
|
d7a7ed6d93 | ||
|
|
713ad134cf | ||
|
|
1328da0fbd | ||
|
|
6c7adf954a | ||
|
|
adcd81d08c | ||
|
|
f6405e9125 | ||
|
|
5fa3b83918 | ||
|
|
e2de4b19fc | ||
|
|
0d3b15503f | ||
|
|
5c3b67bc6b | ||
|
|
d83dd4b984 | ||
|
|
55882924fc | ||
|
|
fc8d5621ac | ||
|
|
20d4fe709c | ||
|
|
2b8d354862 | ||
|
|
db37693688 | ||
|
|
c2eb4f03e9 | ||
|
|
88809c7006 | ||
|
|
2eb5c2ab91 | ||
|
|
57ebba1e72 | ||
|
|
30d337c264 | ||
|
|
b4b732ad8f | ||
|
|
8445605777 | ||
|
|
0df7a53ead | ||
|
|
0854194aa1 | ||
|
|
f719ed941f | ||
|
|
b00952e6b3 | ||
|
|
42128fa39f | ||
|
|
9122a4758d | ||
|
|
e65ebdb4c2 | ||
|
|
a9715a8a09 | ||
|
|
718ec42deb | ||
|
|
104760b2c2 | ||
|
|
bd299b8210 | ||
|
|
687580e155 | ||
|
|
0fdc584f34 | ||
|
|
0bc53eb2ad | ||
|
|
a69b241f07 | ||
|
|
1ae67dd9d1 | ||
|
|
091a3b3136 | ||
|
|
fac27723fb | ||
|
|
f0c607e940 | ||
|
|
d4916fa999 | ||
|
|
a49cbab684 | ||
|
|
3fbcf4aa7e | ||
|
|
006e6bd57c | ||
|
|
58313ea05b | ||
|
|
ae690bbfa4 | ||
|
|
9920f310be | ||
|
|
ec7de6f4c5 | ||
|
|
a82fcd8ec6 | ||
|
|
9a9f8c72f2 | ||
|
|
065d9e82b9 | ||
|
|
4748378387 | ||
|
|
ad64dbb81a | ||
|
|
3c247e57f3 | ||
|
|
589c362bc6 | ||
|
|
c4bb8a1435 | ||
|
|
c74c1c014f | ||
|
|
85017dbba0 | ||
|
|
caee3832a5 | ||
|
|
e1f29465a9 | ||
|
|
0afbf94eb7 | ||
|
|
f7889b514e | ||
|
|
a704d6e56f | ||
|
|
9a7d8cdca5 | ||
|
|
35f5773deb | ||
|
|
65b6b27831 | ||
|
|
66a76c0ee0 | ||
|
|
9f208b876c | ||
|
|
7862ae4b65 | ||
|
|
8f7ee4e9a3 | ||
|
|
b091f3aa08 | ||
|
|
f215872e2e | ||
|
|
481e3b593a | ||
|
|
b592c9015e | ||
|
|
f0afc261a4 | ||
|
|
1e6ba5c689 | ||
|
|
ddd7b4866f | ||
|
|
e731f3b833 | ||
|
|
b99e451f91 | ||
|
|
f8b8b1c6ed | ||
|
|
2358a97fe9 | ||
|
|
571043fa58 | ||
|
|
186b1888a3 | ||
|
|
68c41839ec | ||
|
|
2e72988ef8 | ||
|
|
176ff73788 | ||
|
|
843ea39954 | ||
|
|
c3c2681819 | ||
|
|
b515ef0a5b | ||
|
|
8eef1b727e | ||
|
|
23d0362267 | ||
|
|
2bf92a0e57 | ||
|
|
b5d3cfdd9a | ||
|
|
18d724c08d | ||
|
|
d0eee738b7 | ||
|
|
f3d9f4cffb | ||
|
|
d766cd6fe5 | ||
|
|
b9f232ebc6 | ||
|
|
6b1584ebce | ||
|
|
20181195e1 | ||
|
|
1bdc190154 |
90
.github/workflows/pr-storybook-deploy.yaml
vendored
@@ -1,90 +0,0 @@
|
||||
name: PR Storybook Deploy (Forks)
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: ['Storybook and Chromatic CI']
|
||||
types: [requested, completed]
|
||||
|
||||
env:
|
||||
DATE_FORMAT: '+%m/%d/%Y, %I:%M:%S %p'
|
||||
|
||||
jobs:
|
||||
deploy-and-comment-forked-pr:
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
github.repository == 'Comfy-Org/ComfyUI_frontend' &&
|
||||
github.event.workflow_run.event == 'pull_request' &&
|
||||
github.event.workflow_run.head_repository != null &&
|
||||
github.event.workflow_run.repository != null &&
|
||||
github.event.workflow_run.head_repository.full_name != github.event.workflow_run.repository.full_name
|
||||
permissions:
|
||||
pull-requests: write
|
||||
actions: read
|
||||
steps:
|
||||
- name: Log workflow trigger info
|
||||
run: |
|
||||
echo "Repository: ${{ github.repository }}"
|
||||
echo "Event: ${{ github.event.workflow_run.event }}"
|
||||
echo "Head repo: ${{ github.event.workflow_run.head_repository.full_name || 'null' }}"
|
||||
echo "Base repo: ${{ github.event.workflow_run.repository.full_name || 'null' }}"
|
||||
echo "Is forked: ${{ github.event.workflow_run.head_repository.full_name != github.event.workflow_run.repository.full_name }}"
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Get PR Number
|
||||
id: pr
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const { data: prs } = await github.rest.pulls.list({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
state: 'open',
|
||||
});
|
||||
|
||||
const pr = prs.find(p => p.head.sha === context.payload.workflow_run.head_sha);
|
||||
|
||||
if (!pr) {
|
||||
console.log('No PR found for SHA:', context.payload.workflow_run.head_sha);
|
||||
return null;
|
||||
}
|
||||
|
||||
console.log(`Found PR #${pr.number} from fork: ${context.payload.workflow_run.head_repository.full_name}`);
|
||||
return pr.number;
|
||||
|
||||
- name: Handle Storybook Start
|
||||
if: steps.pr.outputs.result != 'null' && github.event.action == 'requested'
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
chmod +x scripts/cicd/pr-storybook-deploy-and-comment.sh
|
||||
./scripts/cicd/pr-storybook-deploy-and-comment.sh \
|
||||
"${{ steps.pr.outputs.result }}" \
|
||||
"${{ github.event.workflow_run.head_branch }}" \
|
||||
"starting" \
|
||||
"$(date -u '${{ env.DATE_FORMAT }}')"
|
||||
|
||||
- name: Download and Deploy Storybook
|
||||
if: steps.pr.outputs.result != 'null' && github.event.action == 'completed' && github.event.workflow_run.conclusion == 'success'
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
run-id: ${{ github.event.workflow_run.id }}
|
||||
name: storybook-static
|
||||
path: storybook-static
|
||||
|
||||
- name: Handle Storybook Completion
|
||||
if: steps.pr.outputs.result != 'null' && github.event.action == 'completed'
|
||||
env:
|
||||
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
WORKFLOW_CONCLUSION: ${{ github.event.workflow_run.conclusion }}
|
||||
WORKFLOW_URL: ${{ github.event.workflow_run.html_url }}
|
||||
run: |
|
||||
chmod +x scripts/cicd/pr-storybook-deploy-and-comment.sh
|
||||
./scripts/cicd/pr-storybook-deploy-and-comment.sh \
|
||||
"${{ steps.pr.outputs.result }}" \
|
||||
"${{ github.event.workflow_run.head_branch }}" \
|
||||
"completed"
|
||||
8
.github/workflows/update-locales.yaml
vendored
@@ -18,14 +18,14 @@ jobs:
|
||||
uses: actions/checkout@v5
|
||||
|
||||
# Setup playwright environment
|
||||
- name: Setup ComfyUI Frontend
|
||||
uses: ./.github/actions/setup-frontend
|
||||
with:
|
||||
include_build_step: true
|
||||
- name: Setup ComfyUI Server
|
||||
uses: ./.github/actions/setup-comfyui-server
|
||||
with:
|
||||
launch_server: true
|
||||
- name: Setup ComfyUI Frontend
|
||||
uses: ./.github/actions/setup-frontend
|
||||
with:
|
||||
include_build_step: true
|
||||
- name: Setup Playwright
|
||||
uses: ./.github/actions/setup-playwright
|
||||
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
<template>
|
||||
<div
|
||||
class="task-div relative grid min-h-52 max-w-48"
|
||||
class="task-div max-w-48 min-h-52 grid relative"
|
||||
:class="{ 'opacity-75': isLoading }"
|
||||
>
|
||||
<Card
|
||||
class="relative h-full max-w-48 overflow-hidden"
|
||||
class="max-w-48 relative h-full overflow-hidden"
|
||||
:class="{ 'opacity-65': runner.state !== 'error' }"
|
||||
v-bind="(({ onClick, ...rest }) => rest)($attrs)"
|
||||
>
|
||||
<template #header>
|
||||
<i
|
||||
v-if="runner.state === 'error'"
|
||||
class="pi pi-exclamation-triangle absolute top-0 -right-14 m-2 text-red-500 opacity-15"
|
||||
class="pi pi-exclamation-triangle text-red-500 absolute m-2 top-0 -right-14 opacity-15"
|
||||
style="font-size: 10rem"
|
||||
/>
|
||||
<img
|
||||
v-if="task.headerImg"
|
||||
:src="task.headerImg"
|
||||
class="h-full w-full object-contain px-4 pt-4 opacity-25"
|
||||
class="object-contain w-full h-full opacity-25 pt-4 px-4"
|
||||
/>
|
||||
</template>
|
||||
<template #title>
|
||||
@@ -27,7 +27,7 @@
|
||||
{{ description }}
|
||||
</template>
|
||||
<template #footer>
|
||||
<div class="mt-1 flex gap-4">
|
||||
<div class="flex gap-4 mt-1">
|
||||
<Button
|
||||
:icon="task.button?.icon"
|
||||
:label="task.button?.text"
|
||||
@@ -73,7 +73,7 @@ defineEmits<{
|
||||
// Bindings
|
||||
const description = computed(() =>
|
||||
runner.value.state === 'error'
|
||||
? (props.task.errorDescription ?? props.task.shortDescription)
|
||||
? props.task.errorDescription ?? props.task.shortDescription
|
||||
: props.task.shortDescription
|
||||
)
|
||||
|
||||
|
||||
@@ -1,163 +1,67 @@
|
||||
// Import only English locale eagerly as the default/fallback
|
||||
// ESLint cannot statically resolve dynamic imports with path aliases (@frontend-locales/*),
|
||||
// but these are properly configured in tsconfig.json and resolved by Vite at build time.
|
||||
// eslint-disable-next-line import-x/no-unresolved
|
||||
import arCommands from '@frontend-locales/ar/commands.json' with { type: 'json' }
|
||||
import ar from '@frontend-locales/ar/main.json' with { type: 'json' }
|
||||
import arNodes from '@frontend-locales/ar/nodeDefs.json' with { type: 'json' }
|
||||
import arSettings from '@frontend-locales/ar/settings.json' with { type: 'json' }
|
||||
import enCommands from '@frontend-locales/en/commands.json' with { type: 'json' }
|
||||
// eslint-disable-next-line import-x/no-unresolved
|
||||
import en from '@frontend-locales/en/main.json' with { type: 'json' }
|
||||
// eslint-disable-next-line import-x/no-unresolved
|
||||
import enNodes from '@frontend-locales/en/nodeDefs.json' with { type: 'json' }
|
||||
// eslint-disable-next-line import-x/no-unresolved
|
||||
import enSettings from '@frontend-locales/en/settings.json' with { type: 'json' }
|
||||
import esCommands from '@frontend-locales/es/commands.json' with { type: 'json' }
|
||||
import es from '@frontend-locales/es/main.json' with { type: 'json' }
|
||||
import esNodes from '@frontend-locales/es/nodeDefs.json' with { type: 'json' }
|
||||
import esSettings from '@frontend-locales/es/settings.json' with { type: 'json' }
|
||||
import frCommands from '@frontend-locales/fr/commands.json' with { type: 'json' }
|
||||
import fr from '@frontend-locales/fr/main.json' with { type: 'json' }
|
||||
import frNodes from '@frontend-locales/fr/nodeDefs.json' with { type: 'json' }
|
||||
import frSettings from '@frontend-locales/fr/settings.json' with { type: 'json' }
|
||||
import jaCommands from '@frontend-locales/ja/commands.json' with { type: 'json' }
|
||||
import ja from '@frontend-locales/ja/main.json' with { type: 'json' }
|
||||
import jaNodes from '@frontend-locales/ja/nodeDefs.json' with { type: 'json' }
|
||||
import jaSettings from '@frontend-locales/ja/settings.json' with { type: 'json' }
|
||||
import koCommands from '@frontend-locales/ko/commands.json' with { type: 'json' }
|
||||
import ko from '@frontend-locales/ko/main.json' with { type: 'json' }
|
||||
import koNodes from '@frontend-locales/ko/nodeDefs.json' with { type: 'json' }
|
||||
import koSettings from '@frontend-locales/ko/settings.json' with { type: 'json' }
|
||||
import ruCommands from '@frontend-locales/ru/commands.json' with { type: 'json' }
|
||||
import ru from '@frontend-locales/ru/main.json' with { type: 'json' }
|
||||
import ruNodes from '@frontend-locales/ru/nodeDefs.json' with { type: 'json' }
|
||||
import ruSettings from '@frontend-locales/ru/settings.json' with { type: 'json' }
|
||||
import trCommands from '@frontend-locales/tr/commands.json' with { type: 'json' }
|
||||
import tr from '@frontend-locales/tr/main.json' with { type: 'json' }
|
||||
import trNodes from '@frontend-locales/tr/nodeDefs.json' with { type: 'json' }
|
||||
import trSettings from '@frontend-locales/tr/settings.json' with { type: 'json' }
|
||||
import zhTWCommands from '@frontend-locales/zh-TW/commands.json' with { type: 'json' }
|
||||
import zhTW from '@frontend-locales/zh-TW/main.json' with { type: 'json' }
|
||||
import zhTWNodes from '@frontend-locales/zh-TW/nodeDefs.json' with { type: 'json' }
|
||||
import zhTWSettings from '@frontend-locales/zh-TW/settings.json' with { type: 'json' }
|
||||
import zhCommands from '@frontend-locales/zh/commands.json' with { type: 'json' }
|
||||
import zh from '@frontend-locales/zh/main.json' with { type: 'json' }
|
||||
import zhNodes from '@frontend-locales/zh/nodeDefs.json' with { type: 'json' }
|
||||
import zhSettings from '@frontend-locales/zh/settings.json' with { type: 'json' }
|
||||
import { createI18n } from 'vue-i18n'
|
||||
|
||||
function buildLocale<
|
||||
M extends Record<string, unknown>,
|
||||
N extends Record<string, unknown>,
|
||||
C extends Record<string, unknown>,
|
||||
S extends Record<string, unknown>
|
||||
>(main: M, nodes: N, commands: C, settings: S) {
|
||||
function buildLocale<M, N, C, S>(main: M, nodes: N, commands: C, settings: S) {
|
||||
return {
|
||||
...main,
|
||||
nodeDefs: nodes,
|
||||
commands: commands,
|
||||
settings: settings
|
||||
} as M & { nodeDefs: N; commands: C; settings: S }
|
||||
}
|
||||
|
||||
// Locale loader map - dynamically import locales only when needed
|
||||
// ESLint cannot statically resolve these dynamic imports, but they are valid at build time
|
||||
/* eslint-disable import-x/no-unresolved */
|
||||
const localeLoaders: Record<
|
||||
string,
|
||||
() => Promise<{ default: Record<string, unknown> }>
|
||||
> = {
|
||||
ar: () => import('@frontend-locales/ar/main.json'),
|
||||
es: () => import('@frontend-locales/es/main.json'),
|
||||
fr: () => import('@frontend-locales/fr/main.json'),
|
||||
ja: () => import('@frontend-locales/ja/main.json'),
|
||||
ko: () => import('@frontend-locales/ko/main.json'),
|
||||
ru: () => import('@frontend-locales/ru/main.json'),
|
||||
tr: () => import('@frontend-locales/tr/main.json'),
|
||||
zh: () => import('@frontend-locales/zh/main.json'),
|
||||
'zh-TW': () => import('@frontend-locales/zh-TW/main.json')
|
||||
}
|
||||
|
||||
const nodeDefsLoaders: Record<
|
||||
string,
|
||||
() => Promise<{ default: Record<string, unknown> }>
|
||||
> = {
|
||||
ar: () => import('@frontend-locales/ar/nodeDefs.json'),
|
||||
es: () => import('@frontend-locales/es/nodeDefs.json'),
|
||||
fr: () => import('@frontend-locales/fr/nodeDefs.json'),
|
||||
ja: () => import('@frontend-locales/ja/nodeDefs.json'),
|
||||
ko: () => import('@frontend-locales/ko/nodeDefs.json'),
|
||||
ru: () => import('@frontend-locales/ru/nodeDefs.json'),
|
||||
tr: () => import('@frontend-locales/tr/nodeDefs.json'),
|
||||
zh: () => import('@frontend-locales/zh/nodeDefs.json'),
|
||||
'zh-TW': () => import('@frontend-locales/zh-TW/nodeDefs.json')
|
||||
}
|
||||
|
||||
const commandsLoaders: Record<
|
||||
string,
|
||||
() => Promise<{ default: Record<string, unknown> }>
|
||||
> = {
|
||||
ar: () => import('@frontend-locales/ar/commands.json'),
|
||||
es: () => import('@frontend-locales/es/commands.json'),
|
||||
fr: () => import('@frontend-locales/fr/commands.json'),
|
||||
ja: () => import('@frontend-locales/ja/commands.json'),
|
||||
ko: () => import('@frontend-locales/ko/commands.json'),
|
||||
ru: () => import('@frontend-locales/ru/commands.json'),
|
||||
tr: () => import('@frontend-locales/tr/commands.json'),
|
||||
zh: () => import('@frontend-locales/zh/commands.json'),
|
||||
'zh-TW': () => import('@frontend-locales/zh-TW/commands.json')
|
||||
}
|
||||
|
||||
const settingsLoaders: Record<
|
||||
string,
|
||||
() => Promise<{ default: Record<string, unknown> }>
|
||||
> = {
|
||||
ar: () => import('@frontend-locales/ar/settings.json'),
|
||||
es: () => import('@frontend-locales/es/settings.json'),
|
||||
fr: () => import('@frontend-locales/fr/settings.json'),
|
||||
ja: () => import('@frontend-locales/ja/settings.json'),
|
||||
ko: () => import('@frontend-locales/ko/settings.json'),
|
||||
ru: () => import('@frontend-locales/ru/settings.json'),
|
||||
tr: () => import('@frontend-locales/tr/settings.json'),
|
||||
zh: () => import('@frontend-locales/zh/settings.json'),
|
||||
'zh-TW': () => import('@frontend-locales/zh-TW/settings.json')
|
||||
}
|
||||
|
||||
// Track which locales have been loaded
|
||||
const loadedLocales = new Set<string>(['en'])
|
||||
|
||||
// Track locales currently being loaded to prevent race conditions
|
||||
const loadingLocales = new Map<string, Promise<void>>()
|
||||
|
||||
/**
|
||||
* Dynamically load a locale and its associated files (nodeDefs, commands, settings)
|
||||
*/
|
||||
export async function loadLocale(locale: string): Promise<void> {
|
||||
if (loadedLocales.has(locale)) {
|
||||
return
|
||||
}
|
||||
|
||||
// If already loading, return the existing promise to prevent duplicate loads
|
||||
const existingLoad = loadingLocales.get(locale)
|
||||
if (existingLoad) {
|
||||
return existingLoad
|
||||
}
|
||||
|
||||
const loader = localeLoaders[locale]
|
||||
const nodeDefsLoader = nodeDefsLoaders[locale]
|
||||
const commandsLoader = commandsLoaders[locale]
|
||||
const settingsLoader = settingsLoaders[locale]
|
||||
|
||||
if (!loader || !nodeDefsLoader || !commandsLoader || !settingsLoader) {
|
||||
console.warn(`Locale "${locale}" is not supported`)
|
||||
return
|
||||
}
|
||||
|
||||
// Create and track the loading promise
|
||||
const loadPromise = (async () => {
|
||||
try {
|
||||
const [main, nodes, commands, settings] = await Promise.all([
|
||||
loader(),
|
||||
nodeDefsLoader(),
|
||||
commandsLoader(),
|
||||
settingsLoader()
|
||||
])
|
||||
|
||||
const messages = buildLocale(
|
||||
main.default,
|
||||
nodes.default,
|
||||
commands.default,
|
||||
settings.default
|
||||
)
|
||||
|
||||
i18n.global.setLocaleMessage(locale, messages as LocaleMessages)
|
||||
loadedLocales.add(locale)
|
||||
} catch (error) {
|
||||
console.error(`Failed to load locale "${locale}":`, error)
|
||||
throw error
|
||||
} finally {
|
||||
// Clean up the loading promise once complete
|
||||
loadingLocales.delete(locale)
|
||||
}
|
||||
})()
|
||||
|
||||
loadingLocales.set(locale, loadPromise)
|
||||
return loadPromise
|
||||
}
|
||||
|
||||
// Only include English in the initial bundle
|
||||
const messages = {
|
||||
en: buildLocale(en, enNodes, enCommands, enSettings)
|
||||
en: buildLocale(en, enNodes, enCommands, enSettings),
|
||||
zh: buildLocale(zh, zhNodes, zhCommands, zhSettings),
|
||||
'zh-TW': buildLocale(zhTW, zhTWNodes, zhTWCommands, zhTWSettings),
|
||||
ru: buildLocale(ru, ruNodes, ruCommands, ruSettings),
|
||||
ja: buildLocale(ja, jaNodes, jaCommands, jaSettings),
|
||||
ko: buildLocale(ko, koNodes, koCommands, koSettings),
|
||||
fr: buildLocale(fr, frNodes, frCommands, frSettings),
|
||||
es: buildLocale(es, esNodes, esCommands, esSettings),
|
||||
ar: buildLocale(ar, arNodes, arCommands, arSettings),
|
||||
tr: buildLocale(tr, trNodes, trCommands, trSettings)
|
||||
}
|
||||
|
||||
// Type for locale messages - inferred from the English locale structure
|
||||
type LocaleMessages = typeof messages.en
|
||||
|
||||
export const i18n = createI18n({
|
||||
// Must set `false`, as Vue I18n Legacy API is for Vue 2
|
||||
legacy: false,
|
||||
|
||||
@@ -218,4 +218,4 @@
|
||||
"frontendVersion": "1.28.3"
|
||||
},
|
||||
"version": 0.4
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { APIRequestContext, Locator, Page } from '@playwright/test'
|
||||
import { test as base, expect } from '@playwright/test'
|
||||
import { expect } from '@playwright/test'
|
||||
import { test as base } from '@playwright/test'
|
||||
import dotenv from 'dotenv'
|
||||
import * as fs from 'fs'
|
||||
|
||||
@@ -129,8 +130,7 @@ export class ComfyPage {
|
||||
|
||||
// Buttons
|
||||
public readonly resetViewButton: Locator
|
||||
public readonly queueButton: Locator // Run button in Legacy UI
|
||||
public readonly runButton: Locator // Run button (renamed "Queue" -> "Run")
|
||||
public readonly queueButton: Locator
|
||||
|
||||
// Inputs
|
||||
public readonly workflowUploadInput: Locator
|
||||
@@ -165,9 +165,6 @@ export class ComfyPage {
|
||||
this.widgetTextBox = page.getByPlaceholder('text').nth(1)
|
||||
this.resetViewButton = page.getByRole('button', { name: 'Reset View' })
|
||||
this.queueButton = page.getByRole('button', { name: 'Queue Prompt' })
|
||||
this.runButton = page
|
||||
.getByTestId('queue-button')
|
||||
.getByRole('button', { name: 'Run' })
|
||||
this.workflowUploadInput = page.locator('#comfy-file-input')
|
||||
this.visibleToasts = page.locator('.p-toast-message:visible')
|
||||
|
||||
@@ -285,33 +282,6 @@ export class ComfyPage {
|
||||
} = {}) {
|
||||
await this.goto()
|
||||
|
||||
// Mock remote config endpoint for cloud builds
|
||||
// Cloud builds (rh-test) call /api/features on startup, which blocks initialization
|
||||
// if the endpoint doesn't exist or times out. Try real backend first, fallback to empty config.
|
||||
await this.page.route('**/api/features', async (route) => {
|
||||
try {
|
||||
// Try to get response from real backend
|
||||
const response = await route.fetch()
|
||||
if (response.ok()) {
|
||||
await route.fulfill({ response })
|
||||
} else {
|
||||
// Backend doesn't have endpoint, return empty config
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
body: JSON.stringify({})
|
||||
})
|
||||
}
|
||||
} catch {
|
||||
// Network error, return empty config
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
body: JSON.stringify({})
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// Mock release endpoint to prevent changelog popups
|
||||
if (mockReleases) {
|
||||
await this.page.route('**/releases**', async (route) => {
|
||||
@@ -1116,6 +1086,12 @@ export class ComfyPage {
|
||||
|
||||
const targetPosition = await targetSlot.getPosition()
|
||||
|
||||
// Debug: Log the positions we're trying to use
|
||||
console.log('Drag positions:', {
|
||||
source: sourcePosition,
|
||||
target: targetPosition
|
||||
})
|
||||
|
||||
await this.dragAndDrop(sourcePosition, targetPosition)
|
||||
await this.nextFrame()
|
||||
}
|
||||
|
||||
@@ -34,23 +34,17 @@ const getContentType = (filename: string, fileType: OutputFileType) => {
|
||||
}
|
||||
|
||||
const setQueueIndex = (task: TaskItem) => {
|
||||
task.prompt.priority = TaskHistory.queueIndex++
|
||||
task.prompt[0] = TaskHistory.queueIndex++
|
||||
}
|
||||
|
||||
const setPromptId = (task: TaskItem) => {
|
||||
if (!task.prompt.prompt_id || task.prompt.prompt_id === 'prompt-id') {
|
||||
task.prompt.prompt_id = uuidv4()
|
||||
}
|
||||
task.prompt[1] = uuidv4()
|
||||
}
|
||||
|
||||
export default class TaskHistory {
|
||||
static queueIndex = 0
|
||||
static readonly defaultTask: Readonly<HistoryTaskItem> = {
|
||||
prompt: {
|
||||
priority: 0,
|
||||
prompt_id: 'prompt-id',
|
||||
extra_data: { client_id: uuidv4() }
|
||||
},
|
||||
prompt: [0, 'prompt-id', {}, { client_id: uuidv4() }, []],
|
||||
outputs: {},
|
||||
status: {
|
||||
status_str: 'success',
|
||||
@@ -72,37 +66,10 @@ export default class TaskHistory {
|
||||
)
|
||||
|
||||
private async handleGetHistory(route: Route) {
|
||||
const url = route.request().url()
|
||||
|
||||
// Handle history_v2/:prompt_id endpoint
|
||||
const promptIdMatch = url.match(/history_v2\/([^?]+)/)
|
||||
if (promptIdMatch) {
|
||||
const promptId = promptIdMatch[1]
|
||||
const task = this.tasks.find((t) => t.prompt.prompt_id === promptId)
|
||||
const response: Record<string, any> = {}
|
||||
if (task) {
|
||||
response[promptId] = task
|
||||
}
|
||||
return route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
body: JSON.stringify(response)
|
||||
})
|
||||
}
|
||||
|
||||
// Handle history_v2 list endpoint
|
||||
// Convert HistoryTaskItem to RawHistoryItem format expected by API
|
||||
const rawHistoryItems = this.tasks.map((task) => ({
|
||||
prompt_id: task.prompt.prompt_id,
|
||||
prompt: task.prompt,
|
||||
status: task.status,
|
||||
outputs: task.outputs,
|
||||
...(task.meta && { meta: task.meta })
|
||||
}))
|
||||
return route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
body: JSON.stringify({ history: rawHistoryItems })
|
||||
body: JSON.stringify(this.tasks)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -126,7 +93,7 @@ export default class TaskHistory {
|
||||
|
||||
async setupRoutes() {
|
||||
return this.comfyPage.page.route(
|
||||
/.*\/api\/(view|history_v2)(\/[^?]*)?(\?.*)?$/,
|
||||
/.*\/api\/(view|history)(\?.*)?$/,
|
||||
async (route) => {
|
||||
const request = route.request()
|
||||
const method = request.method()
|
||||
|
||||
@@ -7,9 +7,7 @@ test.describe('Bottom Panel Shortcuts', () => {
|
||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Top')
|
||||
})
|
||||
|
||||
test.skip('should toggle shortcuts panel visibility', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
test('should toggle shortcuts panel visibility', async ({ comfyPage }) => {
|
||||
// Initially shortcuts panel should be hidden
|
||||
await expect(comfyPage.page.locator('.bottom-panel')).not.toBeVisible()
|
||||
|
||||
@@ -30,9 +28,7 @@ test.describe('Bottom Panel Shortcuts', () => {
|
||||
await expect(comfyPage.page.locator('.bottom-panel')).not.toBeVisible()
|
||||
})
|
||||
|
||||
test.skip('should display essentials shortcuts tab', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
test('should display essentials shortcuts tab', async ({ comfyPage }) => {
|
||||
// Open shortcuts panel
|
||||
await comfyPage.page
|
||||
.locator('button[aria-label*="Keyboard Shortcuts"]')
|
||||
@@ -66,9 +62,7 @@ test.describe('Bottom Panel Shortcuts', () => {
|
||||
).toBeVisible()
|
||||
})
|
||||
|
||||
test.skip('should display view controls shortcuts tab', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
test('should display view controls shortcuts tab', async ({ comfyPage }) => {
|
||||
// Open shortcuts panel
|
||||
await comfyPage.page
|
||||
.locator('button[aria-label*="Keyboard Shortcuts"]')
|
||||
@@ -94,7 +88,7 @@ test.describe('Bottom Panel Shortcuts', () => {
|
||||
).toBeVisible()
|
||||
})
|
||||
|
||||
test.skip('should switch between shortcuts tabs', async ({ comfyPage }) => {
|
||||
test('should switch between shortcuts tabs', async ({ comfyPage }) => {
|
||||
// Open shortcuts panel
|
||||
await comfyPage.page
|
||||
.locator('button[aria-label*="Keyboard Shortcuts"]')
|
||||
@@ -128,9 +122,7 @@ test.describe('Bottom Panel Shortcuts', () => {
|
||||
).not.toHaveAttribute('aria-selected', 'true')
|
||||
})
|
||||
|
||||
test.skip('should display formatted keyboard shortcuts', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
test('should display formatted keyboard shortcuts', async ({ comfyPage }) => {
|
||||
// Open shortcuts panel
|
||||
await comfyPage.page
|
||||
.locator('button[aria-label*="Keyboard Shortcuts"]')
|
||||
@@ -152,7 +144,7 @@ test.describe('Bottom Panel Shortcuts', () => {
|
||||
expect(hasModifiers).toBeTruthy()
|
||||
})
|
||||
|
||||
test.skip('should maintain panel state when switching to terminal', async ({
|
||||
test('should maintain panel state when switching to terminal', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
// Open shortcuts panel first
|
||||
@@ -180,7 +172,7 @@ test.describe('Bottom Panel Shortcuts', () => {
|
||||
).toBeVisible()
|
||||
})
|
||||
|
||||
test.skip('should handle keyboard navigation', async ({ comfyPage }) => {
|
||||
test('should handle keyboard navigation', async ({ comfyPage }) => {
|
||||
// Open shortcuts panel
|
||||
await comfyPage.page
|
||||
.locator('button[aria-label*="Keyboard Shortcuts"]')
|
||||
@@ -206,7 +198,7 @@ test.describe('Bottom Panel Shortcuts', () => {
|
||||
).toHaveAttribute('aria-selected', 'true')
|
||||
})
|
||||
|
||||
test.skip('should close panel by clicking shortcuts button again', async ({
|
||||
test('should close panel by clicking shortcuts button again', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
// Open shortcuts panel
|
||||
@@ -224,7 +216,7 @@ test.describe('Bottom Panel Shortcuts', () => {
|
||||
await expect(comfyPage.page.locator('.bottom-panel')).not.toBeVisible()
|
||||
})
|
||||
|
||||
test.skip('should display shortcuts in organized columns', async ({
|
||||
test('should display shortcuts in organized columns', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
// Open shortcuts panel
|
||||
@@ -259,7 +251,7 @@ test.describe('Bottom Panel Shortcuts', () => {
|
||||
).toHaveAttribute('aria-selected', 'true')
|
||||
})
|
||||
|
||||
test.skip('should open settings dialog when clicking manage shortcuts button', async ({
|
||||
test('should open settings dialog when clicking manage shortcuts button', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
// Open shortcuts panel
|
||||
|
||||
@@ -65,7 +65,7 @@ test.describe('Change Tracker', () => {
|
||||
})
|
||||
})
|
||||
|
||||
test.skip('Can group multiple change actions into a single transaction', async ({
|
||||
test('Can group multiple change actions into a single transaction', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const node = (await comfyPage.getFirstNodeRef())!
|
||||
@@ -153,7 +153,7 @@ test.describe('Change Tracker', () => {
|
||||
await expect(node).toBeCollapsed()
|
||||
})
|
||||
|
||||
test.skip('Can detect changes in workflow.extra', async ({ comfyPage }) => {
|
||||
test('Can detect changes in workflow.extra', async ({ comfyPage }) => {
|
||||
expect(await comfyPage.getUndoQueueSize()).toBe(0)
|
||||
await comfyPage.page.evaluate(() => {
|
||||
window['app'].graph.extra.foo = 'bar'
|
||||
|
||||
@@ -152,7 +152,7 @@ const customColorPalettes: Record<string, Palette> = {
|
||||
}
|
||||
|
||||
test.describe('Color Palette', () => {
|
||||
test.skip('Can show custom color palette', async ({ comfyPage }) => {
|
||||
test('Can show custom color palette', async ({ comfyPage }) => {
|
||||
await comfyPage.setSetting('Comfy.CustomColorPalettes', customColorPalettes)
|
||||
// Reload to apply the new setting. Setting Comfy.CustomColorPalettes directly
|
||||
// doesn't update the store immediately.
|
||||
@@ -174,7 +174,7 @@ test.describe('Color Palette', () => {
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('default-color-palette.png')
|
||||
})
|
||||
|
||||
test.skip('Can add custom color palette', async ({ comfyPage }) => {
|
||||
test('Can add custom color palette', async ({ comfyPage }) => {
|
||||
await comfyPage.page.evaluate((p) => {
|
||||
window['app'].extensionManager.colorPalette.addCustomColorPalette(p)
|
||||
}, customColorPalettes.obsidian_dark)
|
||||
@@ -199,7 +199,7 @@ test.describe('Node Color Adjustments', () => {
|
||||
await comfyPage.loadWorkflow('nodes/every_node_color')
|
||||
})
|
||||
|
||||
test.skip('should adjust opacity via node opacity setting', async ({
|
||||
test('should adjust opacity via node opacity setting', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.setSetting('Comfy.Node.Opacity', 0.5)
|
||||
@@ -217,7 +217,7 @@ test.describe('Node Color Adjustments', () => {
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('node-opacity-1.png')
|
||||
})
|
||||
|
||||
test.skip('should persist color adjustments when changing themes', async ({
|
||||
test('should persist color adjustments when changing themes', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.setSetting('Comfy.Node.Opacity', 0.2)
|
||||
@@ -245,7 +245,7 @@ test.describe('Node Color Adjustments', () => {
|
||||
}
|
||||
})
|
||||
|
||||
test.skip('should lighten node colors when switching to light theme', async ({
|
||||
test('should lighten node colors when switching to light theme', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.setSetting('Comfy.ColorPalette', 'light')
|
||||
@@ -261,7 +261,7 @@ test.describe('Node Color Adjustments', () => {
|
||||
await node?.clickContextMenuOption('Colors')
|
||||
})
|
||||
|
||||
test.skip('should persist color adjustments when changing custom node colors', async ({
|
||||
test('should persist color adjustments when changing custom node colors', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.page
|
||||
@@ -272,7 +272,7 @@ test.describe('Node Color Adjustments', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test.skip('should persist color adjustments when removing custom node color', async ({
|
||||
test('should persist color adjustments when removing custom node color', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.page
|
||||
|
||||
@@ -7,7 +7,7 @@ test.beforeEach(async ({ comfyPage }) => {
|
||||
})
|
||||
|
||||
test.describe('Copy Paste', () => {
|
||||
test.skip('Can copy and paste node', async ({ comfyPage }) => {
|
||||
test('Can copy and paste node', async ({ comfyPage }) => {
|
||||
await comfyPage.clickEmptyLatentNode()
|
||||
await comfyPage.page.mouse.move(10, 10)
|
||||
await comfyPage.ctrlC()
|
||||
@@ -15,7 +15,7 @@ test.describe('Copy Paste', () => {
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('copied-node.png')
|
||||
})
|
||||
|
||||
test.skip('Can copy and paste node with link', async ({ comfyPage }) => {
|
||||
test('Can copy and paste node with link', async ({ comfyPage }) => {
|
||||
await comfyPage.clickTextEncodeNode1()
|
||||
await comfyPage.page.mouse.move(10, 10)
|
||||
await comfyPage.ctrlC()
|
||||
@@ -35,7 +35,7 @@ test.describe('Copy Paste', () => {
|
||||
expect(resultString).toBe(originalString + originalString)
|
||||
})
|
||||
|
||||
test.skip('Can copy and paste widget value', async ({ comfyPage }) => {
|
||||
test('Can copy and paste widget value', async ({ comfyPage }) => {
|
||||
// Copy width value (512) from empty latent node to KSampler's seed.
|
||||
// KSampler's seed
|
||||
await comfyPage.canvas.click({
|
||||
@@ -60,7 +60,7 @@ test.describe('Copy Paste', () => {
|
||||
/**
|
||||
* https://github.com/Comfy-Org/ComfyUI_frontend/issues/98
|
||||
*/
|
||||
test.skip('Paste in text area with node previously copied', async ({
|
||||
test('Paste in text area with node previously copied', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.clickEmptyLatentNode()
|
||||
@@ -77,7 +77,7 @@ test.describe('Copy Paste', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test.skip('Copy text area does not copy node', async ({ comfyPage }) => {
|
||||
test('Copy text area does not copy node', async ({ comfyPage }) => {
|
||||
const textBox = comfyPage.widgetTextBox
|
||||
await textBox.click()
|
||||
await textBox.inputValue()
|
||||
@@ -89,7 +89,7 @@ test.describe('Copy Paste', () => {
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('no-node-copied.png')
|
||||
})
|
||||
|
||||
test.skip('Copy node by dragging + alt', async ({ comfyPage }) => {
|
||||
test('Copy node by dragging + alt', async ({ comfyPage }) => {
|
||||
// TextEncodeNode1
|
||||
await comfyPage.page.mouse.move(618, 191)
|
||||
await comfyPage.page.keyboard.down('Alt')
|
||||
|
||||
@@ -27,7 +27,7 @@ test.describe('Custom Icons', () => {
|
||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Top')
|
||||
})
|
||||
|
||||
test.skip('sidebar tab icons use custom SVGs', async ({ comfyPage }) => {
|
||||
test('sidebar tab icons use custom SVGs', async ({ comfyPage }) => {
|
||||
// Find the icon in the sidebar
|
||||
const icon = comfyPage.page.locator(
|
||||
'.icon-\\[comfy--ai-model\\].side-bar-button-icon'
|
||||
|
||||
@@ -35,7 +35,7 @@ test.describe('Load workflow warning', () => {
|
||||
})
|
||||
})
|
||||
|
||||
test.skip('Does not report warning on undo/redo', async ({ comfyPage }) => {
|
||||
test('Does not report warning on undo/redo', async ({ comfyPage }) => {
|
||||
await comfyPage.setSetting('Comfy.NodeSearchBoxImpl', 'default')
|
||||
|
||||
await comfyPage.loadWorkflow('missing/missing_nodes')
|
||||
@@ -301,9 +301,7 @@ test.describe('Settings', () => {
|
||||
})
|
||||
|
||||
test.describe('Support', () => {
|
||||
test('Should open external zendesk link with OSS tag', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
test('Should open external zendesk link', async ({ comfyPage }) => {
|
||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Top')
|
||||
const pagePromise = comfyPage.page.context().waitForEvent('page')
|
||||
await comfyPage.menu.topbar.triggerTopbarCommand(['Help', 'Support'])
|
||||
@@ -311,10 +309,6 @@ test.describe('Support', () => {
|
||||
|
||||
await newPage.waitForLoadState('networkidle')
|
||||
await expect(newPage).toHaveURL(/.*support\.comfy\.org.*/)
|
||||
|
||||
const url = new URL(newPage.url())
|
||||
expect(url.searchParams.get('tf_42243568391700')).toBe('oss')
|
||||
|
||||
await newPage.close()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -29,9 +29,7 @@ test.describe('DOM Widget', () => {
|
||||
await expect(lastMultiline).not.toBeVisible()
|
||||
})
|
||||
|
||||
test.skip('Position update when entering focus mode', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
test('Position update when entering focus mode', async ({ comfyPage }) => {
|
||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Top')
|
||||
await comfyPage.executeCommand('Workspace.ToggleFocusMode')
|
||||
await comfyPage.nextFrame()
|
||||
|
||||
|
Before Width: | Height: | Size: 82 KiB After Width: | Height: | Size: 88 KiB |
@@ -7,7 +7,7 @@ test.beforeEach(async ({ comfyPage }) => {
|
||||
})
|
||||
|
||||
test.describe('Execution', () => {
|
||||
test.skip('Report error on unconnected slot', async ({ comfyPage }) => {
|
||||
test('Report error on unconnected slot', async ({ comfyPage }) => {
|
||||
await comfyPage.disconnectEdge()
|
||||
await comfyPage.clickEmptySpace()
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ test.describe('Graph Canvas Menu', () => {
|
||||
await comfyPage.setSetting('Comfy.Graph.CanvasMenu', true)
|
||||
})
|
||||
|
||||
test.skip('Can toggle link visibility', async ({ comfyPage }) => {
|
||||
test('Can toggle link visibility', async ({ comfyPage }) => {
|
||||
const button = comfyPage.page.getByTestId('toggle-link-visibility-button')
|
||||
await button.click()
|
||||
await comfyPage.nextFrame()
|
||||
@@ -39,7 +39,7 @@ test.describe('Graph Canvas Menu', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test.skip('Focus mode button is clickable and has correct test id', async ({
|
||||
test('Focus mode button is clickable and has correct test id', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const focusButton = comfyPage.page.getByTestId('focus-mode-button')
|
||||
@@ -51,7 +51,7 @@ test.describe('Graph Canvas Menu', () => {
|
||||
await comfyPage.nextFrame()
|
||||
})
|
||||
|
||||
test.skip('Zoom controls popup opens and closes', async ({ comfyPage }) => {
|
||||
test('Zoom controls popup opens and closes', async ({ comfyPage }) => {
|
||||
// Find the zoom button by its percentage text content
|
||||
const zoomButton = comfyPage.page.locator('button').filter({
|
||||
hasText: '%'
|
||||
|
||||
|
Before Width: | Height: | Size: 85 KiB After Width: | Height: | Size: 86 KiB |
|
Before Width: | Height: | Size: 99 KiB After Width: | Height: | Size: 100 KiB |
@@ -22,11 +22,11 @@ test.describe('Group Node', () => {
|
||||
await libraryTab.open()
|
||||
})
|
||||
|
||||
test.skip('Is added to node library sidebar', async ({ comfyPage }) => {
|
||||
test('Is added to node library sidebar', async ({ comfyPage }) => {
|
||||
expect(await libraryTab.getFolder('group nodes').count()).toBe(1)
|
||||
})
|
||||
|
||||
test.skip('Can be added to canvas using node library sidebar', async ({
|
||||
test('Can be added to canvas using node library sidebar', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const initialNodeCount = await comfyPage.getGraphNodesCount()
|
||||
@@ -39,7 +39,7 @@ test.describe('Group Node', () => {
|
||||
expect(await comfyPage.getGraphNodesCount()).toBe(initialNodeCount + 1)
|
||||
})
|
||||
|
||||
test.skip('Can be bookmarked and unbookmarked', async ({ comfyPage }) => {
|
||||
test('Can be bookmarked and unbookmarked', async ({ comfyPage }) => {
|
||||
await libraryTab.getFolder(groupNodeCategory).click()
|
||||
await libraryTab
|
||||
.getNode(groupNodeName)
|
||||
@@ -66,7 +66,7 @@ test.describe('Group Node', () => {
|
||||
).toHaveLength(0)
|
||||
})
|
||||
|
||||
test.skip('Displays preview on bookmark hover', async ({ comfyPage }) => {
|
||||
test('Displays preview on bookmark hover', async ({ comfyPage }) => {
|
||||
await libraryTab.getFolder(groupNodeCategory).click()
|
||||
await libraryTab
|
||||
.getNode(groupNodeName)
|
||||
@@ -261,14 +261,14 @@ test.describe('Group Node', () => {
|
||||
await groupNode.copy()
|
||||
})
|
||||
|
||||
test.skip('Copies and pastes group node within the same workflow', async ({
|
||||
test('Copies and pastes group node within the same workflow', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.ctrlV()
|
||||
await verifyNodeLoaded(comfyPage, 2)
|
||||
})
|
||||
|
||||
test.skip('Copies and pastes group node after clearing workflow', async ({
|
||||
test('Copies and pastes group node after clearing workflow', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
// Set setting
|
||||
@@ -281,7 +281,7 @@ test.describe('Group Node', () => {
|
||||
await verifyNodeLoaded(comfyPage, 1)
|
||||
})
|
||||
|
||||
test.skip('Copies and pastes group node into a newly created blank workflow', async ({
|
||||
test('Copies and pastes group node into a newly created blank workflow', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.menu.topbar.triggerTopbarCommand(['New'])
|
||||
@@ -289,7 +289,7 @@ test.describe('Group Node', () => {
|
||||
await verifyNodeLoaded(comfyPage, 1)
|
||||
})
|
||||
|
||||
test.skip('Copies and pastes group node across different workflows', async ({
|
||||
test('Copies and pastes group node across different workflows', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.loadWorkflow('default')
|
||||
@@ -297,7 +297,7 @@ test.describe('Group Node', () => {
|
||||
await verifyNodeLoaded(comfyPage, 1)
|
||||
})
|
||||
|
||||
test.skip('Serializes group node after copy and paste across workflows', async ({
|
||||
test('Serializes group node after copy and paste across workflows', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.menu.topbar.triggerTopbarCommand(['New'])
|
||||
|
||||
@@ -1,131 +0,0 @@
|
||||
import {
|
||||
comfyExpect as expect,
|
||||
comfyPageFixture as test
|
||||
} from '../fixtures/ComfyPage'
|
||||
|
||||
test.describe('History API v2', () => {
|
||||
const TEST_PROMPT_ID = 'test-prompt-id'
|
||||
const TEST_CLIENT_ID = 'test-client'
|
||||
|
||||
test('Can fetch history with new v2 format', async ({ comfyPage }) => {
|
||||
// Set up mocked history with tasks
|
||||
await comfyPage.setupHistory().withTask(['example.webp']).setupRoutes()
|
||||
|
||||
// Verify history_v2 API response format
|
||||
const result = await comfyPage.page.evaluate(async () => {
|
||||
try {
|
||||
const response = await window['app'].api.getHistory()
|
||||
return { success: true, data: response }
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch history:', error)
|
||||
return { success: false, error: error.message }
|
||||
}
|
||||
})
|
||||
|
||||
expect(result.success).toBe(true)
|
||||
expect(result.data).toHaveProperty('History')
|
||||
expect(Array.isArray(result.data.History)).toBe(true)
|
||||
expect(result.data.History.length).toBeGreaterThan(0)
|
||||
|
||||
const historyItem = result.data.History[0]
|
||||
|
||||
// Verify the new prompt structure (object instead of array)
|
||||
expect(historyItem.prompt).toHaveProperty('priority')
|
||||
expect(historyItem.prompt).toHaveProperty('prompt_id')
|
||||
expect(historyItem.prompt).toHaveProperty('extra_data')
|
||||
expect(typeof historyItem.prompt.priority).toBe('number')
|
||||
expect(typeof historyItem.prompt.prompt_id).toBe('string')
|
||||
expect(historyItem.prompt.extra_data).toHaveProperty('client_id')
|
||||
})
|
||||
|
||||
test.skip('Can load workflow from history using history_v2 endpoint', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
// Simple mock workflow for testing
|
||||
const mockWorkflow = {
|
||||
version: 0.4,
|
||||
nodes: [{ id: 1, type: 'TestNode', pos: [100, 100], size: [200, 100] }],
|
||||
links: [],
|
||||
groups: [],
|
||||
config: {},
|
||||
extra: {}
|
||||
}
|
||||
|
||||
// Set up history with workflow data
|
||||
await comfyPage
|
||||
.setupHistory()
|
||||
.withTask(['example.webp'], 'images', {
|
||||
prompt: {
|
||||
priority: 0,
|
||||
prompt_id: TEST_PROMPT_ID,
|
||||
extra_data: {
|
||||
client_id: TEST_CLIENT_ID,
|
||||
extra_pnginfo: { workflow: mockWorkflow }
|
||||
}
|
||||
}
|
||||
})
|
||||
.setupRoutes()
|
||||
|
||||
// Load initial workflow to clear canvas
|
||||
await comfyPage.loadWorkflow('simple_slider')
|
||||
await comfyPage.nextFrame()
|
||||
|
||||
// Load workflow from history
|
||||
const loadResult = await comfyPage.page.evaluate(async (promptId) => {
|
||||
try {
|
||||
const workflow =
|
||||
await window['app'].api.getWorkflowFromHistory(promptId)
|
||||
if (workflow) {
|
||||
await window['app'].loadGraphData(workflow)
|
||||
return { success: true }
|
||||
}
|
||||
return { success: false, error: 'No workflow found' }
|
||||
} catch (error) {
|
||||
console.error('Failed to load workflow from history:', error)
|
||||
return { success: false, error: error.message }
|
||||
}
|
||||
}, TEST_PROMPT_ID)
|
||||
|
||||
expect(loadResult.success).toBe(true)
|
||||
|
||||
// Verify workflow loaded correctly
|
||||
await comfyPage.nextFrame()
|
||||
const nodeInfo = await comfyPage.page.evaluate(() => {
|
||||
try {
|
||||
const graph = window['app'].graph
|
||||
return {
|
||||
success: true,
|
||||
nodeCount: graph.nodes?.length || 0,
|
||||
firstNodeType: graph.nodes?.[0]?.type || null
|
||||
}
|
||||
} catch (error) {
|
||||
return { success: false, error: error.message }
|
||||
}
|
||||
})
|
||||
|
||||
expect(nodeInfo.success).toBe(true)
|
||||
expect(nodeInfo.nodeCount).toBe(1)
|
||||
expect(nodeInfo.firstNodeType).toBe('TestNode')
|
||||
})
|
||||
|
||||
test('Handles missing workflow data gracefully', async ({ comfyPage }) => {
|
||||
// Set up empty history routes
|
||||
await comfyPage.setupHistory().setupRoutes()
|
||||
|
||||
// Test loading from history with invalid prompt_id
|
||||
const result = await comfyPage.page.evaluate(async () => {
|
||||
try {
|
||||
const workflow =
|
||||
await window['app'].api.getWorkflowFromHistory('invalid-id')
|
||||
return { success: true, workflow }
|
||||
} catch (error) {
|
||||
console.error('Expected error for invalid prompt_id:', error)
|
||||
return { success: false, error: error.message }
|
||||
}
|
||||
})
|
||||
|
||||
// Should handle gracefully without throwing
|
||||
expect(result.success).toBe(true)
|
||||
expect(result.workflow).toBeNull()
|
||||
})
|
||||
})
|
||||
@@ -3,10 +3,10 @@ import { expect } from '@playwright/test'
|
||||
import type { Position } from '@vueuse/core'
|
||||
|
||||
import {
|
||||
type ComfyPage,
|
||||
comfyPageFixture as test,
|
||||
testComfySnapToGridGridSize
|
||||
} from '../fixtures/ComfyPage'
|
||||
import type { ComfyPage } from '../fixtures/ComfyPage'
|
||||
import type { NodeReference } from '../fixtures/utils/litegraphUtils'
|
||||
|
||||
test.beforeEach(async ({ comfyPage }) => {
|
||||
@@ -14,7 +14,7 @@ test.beforeEach(async ({ comfyPage }) => {
|
||||
})
|
||||
|
||||
test.describe('Item Interaction', () => {
|
||||
test.skip('Can select/delete all items', async ({ comfyPage }) => {
|
||||
test('Can select/delete all items', async ({ comfyPage }) => {
|
||||
await comfyPage.loadWorkflow('groups/mixed_graph_items')
|
||||
await comfyPage.canvas.press('Control+a')
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('selected-all.png')
|
||||
@@ -22,9 +22,7 @@ test.describe('Item Interaction', () => {
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('deleted-all.png')
|
||||
})
|
||||
|
||||
test.skip('Can pin/unpin items with keyboard shortcut', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
test('Can pin/unpin items with keyboard shortcut', async ({ comfyPage }) => {
|
||||
await comfyPage.loadWorkflow('groups/mixed_graph_items')
|
||||
await comfyPage.canvas.press('Control+a')
|
||||
await comfyPage.canvas.press('KeyP')
|
||||
@@ -62,7 +60,7 @@ test.describe('Node Interaction', () => {
|
||||
})
|
||||
})
|
||||
|
||||
test.skip('@2x Can highlight selected', async ({ comfyPage }) => {
|
||||
test('@2x Can highlight selected', async ({ comfyPage }) => {
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('default.png')
|
||||
await comfyPage.clickTextEncodeNode1()
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('selected-node1.png')
|
||||
@@ -152,7 +150,7 @@ test.describe('Node Interaction', () => {
|
||||
})
|
||||
})
|
||||
|
||||
test.skip('Can drag node', async ({ comfyPage }) => {
|
||||
test('Can drag node', async ({ comfyPage }) => {
|
||||
await comfyPage.dragNode2()
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('dragged-node1.png')
|
||||
})
|
||||
@@ -165,7 +163,7 @@ test.describe('Node Interaction', () => {
|
||||
|
||||
// Test both directions of edge connection.
|
||||
;[{ reverse: false }, { reverse: true }].forEach(({ reverse }) => {
|
||||
test.skip(`Can disconnect/connect edge ${reverse ? 'reverse' : 'normal'}`, async ({
|
||||
test(`Can disconnect/connect edge ${reverse ? 'reverse' : 'normal'}`, async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.disconnectEdge()
|
||||
@@ -180,7 +178,7 @@ test.describe('Node Interaction', () => {
|
||||
})
|
||||
})
|
||||
|
||||
test.skip('Can move link', async ({ comfyPage }) => {
|
||||
test('Can move link', async ({ comfyPage }) => {
|
||||
await comfyPage.dragAndDrop(
|
||||
comfyPage.clipTextEncodeNode1InputSlot,
|
||||
comfyPage.emptySpace
|
||||
@@ -211,7 +209,7 @@ test.describe('Node Interaction', () => {
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('copied-link.png')
|
||||
})
|
||||
|
||||
test.skip('Auto snap&highlight when dragging link over node', async ({
|
||||
test('Auto snap&highlight when dragging link over node', async ({
|
||||
comfyPage,
|
||||
comfyMouse
|
||||
}) => {
|
||||
@@ -224,12 +222,12 @@ test.describe('Node Interaction', () => {
|
||||
})
|
||||
})
|
||||
|
||||
test.skip('Can adjust widget value', async ({ comfyPage }) => {
|
||||
test('Can adjust widget value', async ({ comfyPage }) => {
|
||||
await comfyPage.adjustWidgetValue()
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('adjusted-widget-value.png')
|
||||
})
|
||||
|
||||
test.skip('Link snap to slot', async ({ comfyPage }) => {
|
||||
test('Link snap to slot', async ({ comfyPage }) => {
|
||||
await comfyPage.loadWorkflow('links/snap_to_slot')
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('snap_to_slot.png')
|
||||
|
||||
@@ -246,9 +244,7 @@ test.describe('Node Interaction', () => {
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('snap_to_slot_linked.png')
|
||||
})
|
||||
|
||||
test.skip('Can batch move links by drag with shift', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
test('Can batch move links by drag with shift', async ({ comfyPage }) => {
|
||||
await comfyPage.loadWorkflow('links/batch_move_links')
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('batch_move_links.png')
|
||||
|
||||
@@ -270,7 +266,7 @@ test.describe('Node Interaction', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test.skip('Can batch disconnect links with ctrl+alt+click', async ({
|
||||
test('Can batch disconnect links with ctrl+alt+click', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const loadCheckpointClipSlotPos = {
|
||||
@@ -287,7 +283,7 @@ test.describe('Node Interaction', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test.skip('Can toggle dom widget node open/closed', async ({ comfyPage }) => {
|
||||
test('Can toggle dom widget node open/closed', async ({ comfyPage }) => {
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('default.png')
|
||||
await comfyPage.clickTextEncodeNodeToggler()
|
||||
await expect(comfyPage.canvas).toHaveScreenshot(
|
||||
@@ -300,7 +296,7 @@ test.describe('Node Interaction', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test.skip('Can close prompt dialog with canvas click (number widget)', async ({
|
||||
test('Can close prompt dialog with canvas click (number widget)', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const numberWidgetPos = {
|
||||
@@ -322,7 +318,7 @@ test.describe('Node Interaction', () => {
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('prompt-dialog-closed.png')
|
||||
})
|
||||
|
||||
test.skip('Can close prompt dialog with canvas click (text widget)', async ({
|
||||
test('Can close prompt dialog with canvas click (text widget)', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const textWidgetPos = {
|
||||
@@ -348,7 +344,7 @@ test.describe('Node Interaction', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test.skip('Can double click node title to edit', async ({ comfyPage }) => {
|
||||
test('Can double click node title to edit', async ({ comfyPage }) => {
|
||||
await comfyPage.loadWorkflow('nodes/single_ksampler')
|
||||
await comfyPage.canvas.dblclick({
|
||||
position: {
|
||||
@@ -376,7 +372,7 @@ test.describe('Node Interaction', () => {
|
||||
expect(await comfyPage.page.locator('.node-title-editor').count()).toBe(0)
|
||||
})
|
||||
|
||||
test.skip('Can group selected nodes', async ({ comfyPage }) => {
|
||||
test('Can group selected nodes', async ({ comfyPage }) => {
|
||||
await comfyPage.setSetting('Comfy.GroupSelectedNodes.Padding', 10)
|
||||
await comfyPage.select2Nodes()
|
||||
await comfyPage.page.keyboard.down('Control')
|
||||
@@ -389,7 +385,7 @@ test.describe('Node Interaction', () => {
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('group-selected-nodes.png')
|
||||
})
|
||||
|
||||
test.skip('Can fit group to contents', async ({ comfyPage }) => {
|
||||
test('Can fit group to contents', async ({ comfyPage }) => {
|
||||
await comfyPage.loadWorkflow('groups/oversized_group')
|
||||
await comfyPage.ctrlA()
|
||||
await comfyPage.nextFrame()
|
||||
@@ -398,7 +394,7 @@ test.describe('Node Interaction', () => {
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('group-fit-to-contents.png')
|
||||
})
|
||||
|
||||
test.skip('Can pin/unpin nodes', async ({ comfyPage }) => {
|
||||
test('Can pin/unpin nodes', async ({ comfyPage }) => {
|
||||
await comfyPage.select2Nodes()
|
||||
await comfyPage.executeCommand('Comfy.Canvas.ToggleSelectedNodes.Pin')
|
||||
await comfyPage.nextFrame()
|
||||
@@ -408,7 +404,7 @@ test.describe('Node Interaction', () => {
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('nodes-unpinned.png')
|
||||
})
|
||||
|
||||
test.skip('Can bypass/unbypass nodes with keyboard shortcut', async ({
|
||||
test('Can bypass/unbypass nodes with keyboard shortcut', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.select2Nodes()
|
||||
@@ -422,7 +418,7 @@ test.describe('Node Interaction', () => {
|
||||
})
|
||||
|
||||
test.describe('Group Interaction', () => {
|
||||
test.skip('Can double click group title to edit', async ({ comfyPage }) => {
|
||||
test('Can double click group title to edit', async ({ comfyPage }) => {
|
||||
await comfyPage.loadWorkflow('groups/single_group')
|
||||
await comfyPage.canvas.dblclick({
|
||||
position: {
|
||||
@@ -438,21 +434,21 @@ test.describe('Group Interaction', () => {
|
||||
})
|
||||
|
||||
test.describe('Canvas Interaction', () => {
|
||||
test.skip('Can zoom in/out', async ({ comfyPage }) => {
|
||||
test('Can zoom in/out', async ({ comfyPage }) => {
|
||||
await comfyPage.zoom(-100)
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('zoomed-in.png')
|
||||
await comfyPage.zoom(200)
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('zoomed-out.png')
|
||||
})
|
||||
|
||||
test.skip('Can zoom very far out', async ({ comfyPage }) => {
|
||||
test('Can zoom very far out', async ({ comfyPage }) => {
|
||||
await comfyPage.zoom(100, 12)
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('zoomed-very-far-out.png')
|
||||
await comfyPage.zoom(-100, 12)
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('zoomed-back-in.png')
|
||||
})
|
||||
|
||||
test.skip('Can zoom in/out with ctrl+shift+vertical-drag', async ({
|
||||
test('Can zoom in/out with ctrl+shift+vertical-drag', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.page.keyboard.down('Control')
|
||||
@@ -469,7 +465,7 @@ test.describe('Canvas Interaction', () => {
|
||||
await comfyPage.page.keyboard.up('Shift')
|
||||
})
|
||||
|
||||
test.skip('Can zoom in/out after decreasing canvas zoom speed setting', async ({
|
||||
test('Can zoom in/out after decreasing canvas zoom speed setting', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.setSetting('Comfy.Graph.ZoomSpeed', 1.05)
|
||||
@@ -484,7 +480,7 @@ test.describe('Canvas Interaction', () => {
|
||||
await comfyPage.setSetting('Comfy.Graph.ZoomSpeed', 1.1)
|
||||
})
|
||||
|
||||
test.skip('Can zoom in/out after increasing canvas zoom speed', async ({
|
||||
test('Can zoom in/out after increasing canvas zoom speed', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.setSetting('Comfy.Graph.ZoomSpeed', 1.5)
|
||||
@@ -499,12 +495,12 @@ test.describe('Canvas Interaction', () => {
|
||||
await comfyPage.setSetting('Comfy.Graph.ZoomSpeed', 1.1)
|
||||
})
|
||||
|
||||
test.skip('Can pan', async ({ comfyPage }) => {
|
||||
test('Can pan', async ({ comfyPage }) => {
|
||||
await comfyPage.pan({ x: 200, y: 200 })
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('panned.png')
|
||||
})
|
||||
|
||||
test.skip('Cursor style changes when panning', async ({ comfyPage }) => {
|
||||
test('Cursor style changes when panning', async ({ comfyPage }) => {
|
||||
const getCursorStyle = async () => {
|
||||
return await comfyPage.page.evaluate(() => {
|
||||
return (
|
||||
@@ -534,7 +530,7 @@ test.describe('Canvas Interaction', () => {
|
||||
})
|
||||
|
||||
// https://github.com/Comfy-Org/litegraph.js/pull/424
|
||||
test.skip('Properly resets dragging state after pan mode sequence', async ({
|
||||
test('Properly resets dragging state after pan mode sequence', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const getCursorStyle = async () => {
|
||||
@@ -570,10 +566,7 @@ test.describe('Canvas Interaction', () => {
|
||||
expect(await getCursorStyle()).toBe('default')
|
||||
})
|
||||
|
||||
test.skip('Can pan when dragging a link', async ({
|
||||
comfyPage,
|
||||
comfyMouse
|
||||
}) => {
|
||||
test('Can pan when dragging a link', async ({ comfyPage, comfyMouse }) => {
|
||||
const posSlot1 = comfyPage.clipTextEncodeNode1InputSlot
|
||||
await comfyMouse.move(posSlot1)
|
||||
const posEmpty = comfyPage.emptySpace
|
||||
@@ -593,7 +586,7 @@ test.describe('Canvas Interaction', () => {
|
||||
await comfyMouse.drop()
|
||||
})
|
||||
|
||||
test.skip('Can pan very far and back', async ({ comfyPage }) => {
|
||||
test('Can pan very far and back', async ({ comfyPage }) => {
|
||||
// intentionally slice the edge of where the clip text encode dom widgets are
|
||||
await comfyPage.pan({ x: -800, y: -300 }, { x: 1000, y: 10 })
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('panned-step-one.png')
|
||||
@@ -609,7 +602,7 @@ test.describe('Canvas Interaction', () => {
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('panned-back-to-one.png')
|
||||
})
|
||||
|
||||
test.skip('@mobile Can pan with touch', async ({ comfyPage }) => {
|
||||
test('@mobile Can pan with touch', async ({ comfyPage }) => {
|
||||
await comfyPage.closeMenu()
|
||||
await comfyPage.panWithTouch({ x: 200, y: 200 })
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('panned-touch.png')
|
||||
@@ -643,19 +636,19 @@ test.describe('Widget Interaction', () => {
|
||||
})
|
||||
|
||||
test.describe('Load workflow', () => {
|
||||
test.skip('Can load workflow with string node id', async ({ comfyPage }) => {
|
||||
test('Can load workflow with string node id', async ({ comfyPage }) => {
|
||||
await comfyPage.loadWorkflow('nodes/string_node_id')
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('string_node_id.png')
|
||||
})
|
||||
|
||||
test.skip('Can load workflow with ("STRING",) input node', async ({
|
||||
test('Can load workflow with ("STRING",) input node', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.loadWorkflow('inputs/string_input')
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('string_input.png')
|
||||
})
|
||||
|
||||
test.skip('Restore workflow on reload (switch workflow)', async ({
|
||||
test('Restore workflow on reload (switch workflow)', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.loadWorkflow('nodes/single_ksampler')
|
||||
@@ -664,7 +657,7 @@ test.describe('Load workflow', () => {
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('single_ksampler.png')
|
||||
})
|
||||
|
||||
test.skip('Restore workflow on reload (modify workflow)', async ({
|
||||
test('Restore workflow on reload (modify workflow)', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.loadWorkflow('nodes/single_ksampler')
|
||||
@@ -721,9 +714,7 @@ test.describe('Load workflow', () => {
|
||||
expect(activeWorkflowName).toEqual(workflowB)
|
||||
})
|
||||
|
||||
test.skip('Restores sidebar workflows after reload', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
test('Restores sidebar workflows after reload', async ({ comfyPage }) => {
|
||||
await comfyPage.setSetting(
|
||||
'Comfy.Workflow.WorkflowTabsPosition',
|
||||
'Sidebar'
|
||||
@@ -746,7 +737,7 @@ test.describe('Load workflow', () => {
|
||||
})
|
||||
})
|
||||
|
||||
test.skip('Auto fit view after loading workflow', async ({ comfyPage }) => {
|
||||
test('Auto fit view after loading workflow', async ({ comfyPage }) => {
|
||||
await comfyPage.setSetting('Comfy.EnableWorkflowViewRestore', false)
|
||||
await comfyPage.loadWorkflow('nodes/single_ksampler')
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('single_ksampler_fit.png')
|
||||
@@ -758,7 +749,7 @@ test.describe('Load duplicate workflow', () => {
|
||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Top')
|
||||
})
|
||||
|
||||
test.skip('A workflow can be loaded multiple times in a row', async ({
|
||||
test('A workflow can be loaded multiple times in a row', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.loadWorkflow('nodes/single_ksampler')
|
||||
@@ -847,7 +838,7 @@ test.describe('Canvas Navigation', () => {
|
||||
await comfyPage.setSetting('Comfy.Canvas.NavigationMode', 'legacy')
|
||||
})
|
||||
|
||||
test.skip('Left-click drag in empty area should pan canvas', async ({
|
||||
test('Left-click drag in empty area should pan canvas', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.dragAndDrop({ x: 50, y: 50 }, { x: 150, y: 150 })
|
||||
@@ -856,7 +847,7 @@ test.describe('Canvas Navigation', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test.skip('Middle-click drag should pan canvas', async ({ comfyPage }) => {
|
||||
test('Middle-click drag should pan canvas', async ({ comfyPage }) => {
|
||||
await comfyPage.page.mouse.move(50, 50)
|
||||
await comfyPage.page.mouse.down({ button: 'middle' })
|
||||
await comfyPage.page.mouse.move(150, 150)
|
||||
@@ -867,7 +858,7 @@ test.describe('Canvas Navigation', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test.skip('Mouse wheel should zoom in/out', async ({ comfyPage }) => {
|
||||
test('Mouse wheel should zoom in/out', async ({ comfyPage }) => {
|
||||
await comfyPage.page.mouse.move(400, 300)
|
||||
await comfyPage.page.mouse.wheel(0, -120)
|
||||
await comfyPage.nextFrame()
|
||||
@@ -882,9 +873,7 @@ test.describe('Canvas Navigation', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test.skip('Left-click on node should not pan canvas', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
test('Left-click on node should not pan canvas', async ({ comfyPage }) => {
|
||||
await comfyPage.clickTextEncodeNode1()
|
||||
const selectedCount = await comfyPage.getSelectedGraphNodesCount()
|
||||
expect(selectedCount).toBe(1)
|
||||
@@ -899,7 +888,7 @@ test.describe('Canvas Navigation', () => {
|
||||
await comfyPage.setSetting('Comfy.Canvas.NavigationMode', 'standard')
|
||||
})
|
||||
|
||||
test.skip('Left-click drag in empty area should select nodes', async ({
|
||||
test('Left-click drag in empty area should select nodes', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const clipNodes = await comfyPage.getNodeRefsByType('CLIPTextEncode')
|
||||
@@ -925,7 +914,7 @@ test.describe('Canvas Navigation', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test.skip('Middle-click drag should pan canvas', async ({ comfyPage }) => {
|
||||
test('Middle-click drag should pan canvas', async ({ comfyPage }) => {
|
||||
await comfyPage.page.mouse.move(50, 50)
|
||||
await comfyPage.page.mouse.down({ button: 'middle' })
|
||||
await comfyPage.page.mouse.move(150, 150)
|
||||
@@ -936,9 +925,7 @@ test.describe('Canvas Navigation', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test.skip('Ctrl + mouse wheel should zoom in/out', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
test('Ctrl + mouse wheel should zoom in/out', async ({ comfyPage }) => {
|
||||
await comfyPage.page.mouse.move(400, 300)
|
||||
await comfyPage.page.keyboard.down('Control')
|
||||
await comfyPage.page.mouse.wheel(0, -120)
|
||||
@@ -957,7 +944,7 @@ test.describe('Canvas Navigation', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test.skip('Left-click on node should select node (not start selection box)', async ({
|
||||
test('Left-click on node should select node (not start selection box)', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.clickTextEncodeNode1()
|
||||
@@ -968,9 +955,7 @@ test.describe('Canvas Navigation', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test.skip('Space + left-click drag should pan canvas', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
test('Space + left-click drag should pan canvas', async ({ comfyPage }) => {
|
||||
// Click canvas to focus it
|
||||
await comfyPage.page.click('canvas')
|
||||
await comfyPage.nextFrame()
|
||||
@@ -983,7 +968,7 @@ test.describe('Canvas Navigation', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test.skip('Space key overrides default left-click behavior', async ({
|
||||
test('Space key overrides default left-click behavior', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const clipNodes = await comfyPage.getNodeRefsByType('CLIPTextEncode')
|
||||
@@ -1029,7 +1014,7 @@ test.describe('Canvas Navigation', () => {
|
||||
})
|
||||
})
|
||||
|
||||
test.skip('Shift + mouse wheel should pan canvas horizontally', async ({
|
||||
test('Shift + mouse wheel should pan canvas horizontally', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.setSetting('Comfy.Canvas.MouseWheelScroll', 'panning')
|
||||
@@ -1067,7 +1052,7 @@ test.describe('Canvas Navigation', () => {
|
||||
})
|
||||
|
||||
test.describe('Edge Cases', () => {
|
||||
test.skip('Multiple modifier keys work correctly in legacy mode', async ({
|
||||
test('Multiple modifier keys work correctly in legacy mode', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.setSetting('Comfy.Canvas.NavigationMode', 'legacy')
|
||||
|
||||
@@ -27,9 +27,7 @@ test.describe('Canvas Event', () => {
|
||||
// See https://github.com/microsoft/playwright/issues/31580
|
||||
})
|
||||
|
||||
test.skip('Emit litegraph:canvas empty-double-click', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
test('Emit litegraph:canvas empty-double-click', async ({ comfyPage }) => {
|
||||
const eventPromise = comfyPage.page.evaluate(listenForEvent)
|
||||
const doubleClickPromise = comfyPage.doubleClickCanvas()
|
||||
const event = await eventPromise
|
||||
|
||||
@@ -25,7 +25,7 @@ test.describe('Load Workflow in Media', () => {
|
||||
// 'workflow.avif'
|
||||
]
|
||||
fileNames.forEach(async (fileName) => {
|
||||
test.skip(`Load workflow in ${fileName} (drop from filesystem)`, async ({
|
||||
test(`Load workflow in ${fileName} (drop from filesystem)`, async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.dragAndDropFile(`workflowInMedia/${fileName}`)
|
||||
@@ -37,7 +37,7 @@ test.describe('Load Workflow in Media', () => {
|
||||
'https://comfyanonymous.github.io/ComfyUI_examples/hidream/hidream_dev_example.png'
|
||||
]
|
||||
urls.forEach(async (url) => {
|
||||
test.skip(`Load workflow from URL ${url} (drop from different browser tabs)`, async ({
|
||||
test(`Load workflow from URL ${url} (drop from different browser tabs)`, async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.dragAndDropURL(url)
|
||||
|
||||
|
Before Width: | Height: | Size: 108 KiB After Width: | Height: | Size: 108 KiB |
@@ -7,7 +7,7 @@ test.beforeEach(async ({ comfyPage }) => {
|
||||
})
|
||||
|
||||
test.describe('LOD Threshold', () => {
|
||||
test.skip('Should switch to low quality mode at correct zoom threshold', async ({
|
||||
test('Should switch to low quality mode at correct zoom threshold', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
// Load a workflow with some nodes to render
|
||||
@@ -81,7 +81,7 @@ test.describe('LOD Threshold', () => {
|
||||
expect(zoomedInState.lowQuality).toBe(false)
|
||||
})
|
||||
|
||||
test.skip('Should update threshold when font size setting changes', async ({
|
||||
test('Should update threshold when font size setting changes', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.loadWorkflow('default')
|
||||
@@ -122,7 +122,7 @@ test.describe('LOD Threshold', () => {
|
||||
expect(afterZoom.lowQuality).toBe(true)
|
||||
})
|
||||
|
||||
test.skip('Should disable LOD when font size is set to 0', async ({
|
||||
test('Should disable LOD when font size is set to 0', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.loadWorkflow('default')
|
||||
@@ -149,7 +149,7 @@ test.describe('LOD Threshold', () => {
|
||||
expect(state.scale).toBeLessThan(0.2) // Very zoomed out
|
||||
})
|
||||
|
||||
test.skip('Should show visual difference between LOD on and off', async ({
|
||||
test('Should show visual difference between LOD on and off', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
// Load a workflow with text-heavy nodes for clear visual difference
|
||||
|
||||
@@ -7,7 +7,7 @@ test.describe('Menu', () => {
|
||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Top')
|
||||
})
|
||||
|
||||
test.skip('Can register sidebar tab', async ({ comfyPage }) => {
|
||||
test('Can register sidebar tab', async ({ comfyPage }) => {
|
||||
const initialChildrenCount = await comfyPage.menu.sideToolbar.evaluate(
|
||||
(el) => el.children.length
|
||||
)
|
||||
|
||||
@@ -9,7 +9,7 @@ test.beforeEach(async ({ comfyPage }) => {
|
||||
})
|
||||
|
||||
test.describe('Node Badge', () => {
|
||||
test.skip('Can add badge', async ({ comfyPage }) => {
|
||||
test('Can add badge', async ({ comfyPage }) => {
|
||||
await comfyPage.page.evaluate(() => {
|
||||
const LGraphBadge = window['LGraphBadge']
|
||||
const app = window['app'] as ComfyApp
|
||||
@@ -26,7 +26,7 @@ test.describe('Node Badge', () => {
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('node-badge.png')
|
||||
})
|
||||
|
||||
test.skip('Can add multiple badges', async ({ comfyPage }) => {
|
||||
test('Can add multiple badges', async ({ comfyPage }) => {
|
||||
await comfyPage.page.evaluate(() => {
|
||||
const LGraphBadge = window['LGraphBadge']
|
||||
const app = window['app'] as ComfyApp
|
||||
@@ -46,7 +46,7 @@ test.describe('Node Badge', () => {
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('node-badge-multiple.png')
|
||||
})
|
||||
|
||||
test.skip('Can add badge left-side', async ({ comfyPage }) => {
|
||||
test('Can add badge left-side', async ({ comfyPage }) => {
|
||||
await comfyPage.page.evaluate(() => {
|
||||
const LGraphBadge = window['LGraphBadge']
|
||||
const app = window['app'] as ComfyApp
|
||||
@@ -68,7 +68,7 @@ test.describe('Node Badge', () => {
|
||||
|
||||
test.describe('Node source badge', () => {
|
||||
Object.values(NodeBadgeMode).forEach(async (mode) => {
|
||||
test.skip(`Shows node badges (${mode})`, async ({ comfyPage }) => {
|
||||
test(`Shows node badges (${mode})`, async ({ comfyPage }) => {
|
||||
// Execution error workflow has both custom node and core node.
|
||||
await comfyPage.loadWorkflow('nodes/execution_error')
|
||||
await comfyPage.setSetting('Comfy.NodeBadge.NodeSourceBadgeMode', mode)
|
||||
@@ -81,7 +81,7 @@ test.describe('Node source badge', () => {
|
||||
})
|
||||
|
||||
test.describe('Node badge color', () => {
|
||||
test.skip('Can show node badge with unknown color palette', async ({
|
||||
test('Can show node badge with unknown color palette', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.setSetting(
|
||||
@@ -97,7 +97,7 @@ test.describe('Node badge color', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test.skip('Can show node badge with light color palette', async ({
|
||||
test('Can show node badge with light color palette', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.setSetting(
|
||||
|
||||
|
Before Width: | Height: | Size: 105 KiB After Width: | Height: | Size: 105 KiB |
@@ -9,27 +9,27 @@ test.beforeEach(async ({ comfyPage }) => {
|
||||
// If an input is optional by node definition, it should be shown as
|
||||
// a hollow circle no matter what shape it was defined in the workflow JSON.
|
||||
test.describe('Optional input', () => {
|
||||
test.skip('No shape specified', async ({ comfyPage }) => {
|
||||
test('No shape specified', async ({ comfyPage }) => {
|
||||
await comfyPage.loadWorkflow('inputs/optional_input_no_shape')
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('optional_input.png')
|
||||
})
|
||||
|
||||
test.skip('Wrong shape specified', async ({ comfyPage }) => {
|
||||
test('Wrong shape specified', async ({ comfyPage }) => {
|
||||
await comfyPage.loadWorkflow('inputs/optional_input_wrong_shape')
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('optional_input.png')
|
||||
})
|
||||
|
||||
test.skip('Correct shape specified', async ({ comfyPage }) => {
|
||||
test('Correct shape specified', async ({ comfyPage }) => {
|
||||
await comfyPage.loadWorkflow('inputs/optional_input_correct_shape')
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('optional_input.png')
|
||||
})
|
||||
|
||||
test.skip('Force input', async ({ comfyPage }) => {
|
||||
test('Force input', async ({ comfyPage }) => {
|
||||
await comfyPage.loadWorkflow('inputs/force_input')
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('force_input.png')
|
||||
})
|
||||
|
||||
test.skip('Default input', async ({ comfyPage }) => {
|
||||
test('Default input', async ({ comfyPage }) => {
|
||||
await comfyPage.loadWorkflow('inputs/default_input')
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('default_input.png')
|
||||
})
|
||||
@@ -65,18 +65,18 @@ test.describe('Optional input', () => {
|
||||
const renamedInput = inputs.find((w) => w.name === 'breadth')
|
||||
expect(renamedInput).toBeUndefined()
|
||||
})
|
||||
test.skip('slider', async ({ comfyPage }) => {
|
||||
test('slider', async ({ comfyPage }) => {
|
||||
await comfyPage.loadWorkflow('inputs/simple_slider')
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('simple_slider.png')
|
||||
})
|
||||
test.skip('unknown converted widget', async ({ comfyPage }) => {
|
||||
test('unknown converted widget', async ({ comfyPage }) => {
|
||||
await comfyPage.setSetting('Comfy.Workflow.ShowMissingNodesWarning', false)
|
||||
await comfyPage.loadWorkflow('missing/missing_nodes_converted_widget')
|
||||
await expect(comfyPage.canvas).toHaveScreenshot(
|
||||
'missing_nodes_converted_widget.png'
|
||||
)
|
||||
})
|
||||
test.skip('dynamically added input', async ({ comfyPage }) => {
|
||||
test('dynamically added input', async ({ comfyPage }) => {
|
||||
await comfyPage.loadWorkflow('inputs/dynamically_added_input')
|
||||
await expect(comfyPage.canvas).toHaveScreenshot(
|
||||
'dynamically_added_input.png'
|
||||
|
||||
@@ -27,9 +27,7 @@ test.describe('Node Help', () => {
|
||||
})
|
||||
|
||||
test.describe('Selection Toolbox', () => {
|
||||
test.skip('Should open help menu for selected node', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
test('Should open help menu for selected node', async ({ comfyPage }) => {
|
||||
// Load a workflow with a node
|
||||
await comfyPage.setSetting('Comfy.Canvas.SelectionToolbox', true)
|
||||
await comfyPage.loadWorkflow('default')
|
||||
@@ -66,9 +64,7 @@ test.describe('Node Help', () => {
|
||||
})
|
||||
|
||||
test.describe('Node Library Sidebar', () => {
|
||||
test.skip('Should open help menu from node library', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
test('Should open help menu from node library', async ({ comfyPage }) => {
|
||||
// Open the node library sidebar
|
||||
await comfyPage.menu.nodeLibraryTab.open()
|
||||
|
||||
@@ -101,7 +97,7 @@ test.describe('Node Help', () => {
|
||||
await expect(helpPage.locator('.node-help-content')).toBeVisible()
|
||||
})
|
||||
|
||||
test.skip('Should show node library tab when clicking back from help page', async ({
|
||||
test('Should show node library tab when clicking back from help page', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
// Open the node library sidebar
|
||||
@@ -149,7 +145,7 @@ test.describe('Node Help', () => {
|
||||
await comfyPage.setSetting('Comfy.Canvas.SelectionToolbox', true)
|
||||
})
|
||||
|
||||
test.skip('Should display loading state while fetching help', async ({
|
||||
test('Should display loading state while fetching help', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
// Mock slow network response
|
||||
@@ -180,7 +176,7 @@ test.describe('Node Help', () => {
|
||||
await expect(helpPage).toContainText('Test Help Content')
|
||||
})
|
||||
|
||||
test.skip('Should display fallback content when help file not found', async ({
|
||||
test('Should display fallback content when help file not found', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
// Mock 404 response for help files
|
||||
@@ -209,7 +205,7 @@ test.describe('Node Help', () => {
|
||||
await expect(helpPage).toContainText('Outputs')
|
||||
})
|
||||
|
||||
test.skip('Should render markdown with images correctly', async ({
|
||||
test('Should render markdown with images correctly', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
// Mock response with markdown containing images
|
||||
@@ -255,7 +251,7 @@ test.describe('Node Help', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test.skip('Should render video elements with source tags in markdown', async ({
|
||||
test('Should render video elements with source tags in markdown', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
// Mock response with video elements
|
||||
@@ -316,7 +312,7 @@ test.describe('Node Help', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test.skip('Should handle custom node documentation paths', async ({
|
||||
test('Should handle custom node documentation paths', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
// First load workflow with custom node
|
||||
@@ -369,9 +365,7 @@ This is documentation for a custom node.
|
||||
}
|
||||
})
|
||||
|
||||
test.skip('Should sanitize dangerous HTML content', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
test('Should sanitize dangerous HTML content', async ({ comfyPage }) => {
|
||||
// Mock response with potentially dangerous content
|
||||
await comfyPage.page.route('**/docs/KSampler/en.md', async (route) => {
|
||||
await route.fulfill({
|
||||
@@ -430,7 +424,7 @@ This is documentation for a custom node.
|
||||
await expect(helpPage.locator('img[alt="Safe Image"]')).toBeVisible()
|
||||
})
|
||||
|
||||
test.skip('Should handle locale-specific documentation', async ({
|
||||
test('Should handle locale-specific documentation', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
// Mock different responses for different locales
|
||||
@@ -474,9 +468,7 @@ This is English documentation.
|
||||
await comfyPage.setSetting('Comfy.Locale', 'en')
|
||||
})
|
||||
|
||||
test.skip('Should handle network errors gracefully', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
test('Should handle network errors gracefully', async ({ comfyPage }) => {
|
||||
// Mock network error
|
||||
await comfyPage.page.route('**/docs/**/*.md', async (route) => {
|
||||
await route.abort('failed')
|
||||
@@ -502,7 +494,7 @@ This is English documentation.
|
||||
expect(content).toBeTruthy()
|
||||
})
|
||||
|
||||
test.skip('Should update help content when switching between nodes', async ({
|
||||
test('Should update help content when switching between nodes', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
// Mock different help content for different nodes
|
||||
|
||||
@@ -14,9 +14,7 @@ test.describe('Node search box', () => {
|
||||
await comfyPage.setSetting('Comfy.NodeSearchBoxImpl', 'default')
|
||||
})
|
||||
|
||||
test.skip(`Can trigger on empty canvas double click`, async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
test(`Can trigger on empty canvas double click`, async ({ comfyPage }) => {
|
||||
await comfyPage.doubleClickCanvas()
|
||||
await expect(comfyPage.searchBox.input).toHaveCount(1)
|
||||
})
|
||||
@@ -48,14 +46,14 @@ test.describe('Node search box', () => {
|
||||
await expect(comfyPage.searchBox.input).toBeVisible()
|
||||
})
|
||||
|
||||
test.skip('Can add node', async ({ comfyPage }) => {
|
||||
test('Can add node', async ({ comfyPage }) => {
|
||||
await comfyPage.doubleClickCanvas()
|
||||
await expect(comfyPage.searchBox.input).toHaveCount(1)
|
||||
await comfyPage.searchBox.fillAndSelectFirstNode('KSampler')
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('added-node.png')
|
||||
})
|
||||
|
||||
test.skip('Can auto link node', async ({ comfyPage }) => {
|
||||
test('Can auto link node', async ({ comfyPage }) => {
|
||||
await comfyPage.disconnectEdge()
|
||||
// Select the second item as the first item is always reroute
|
||||
await comfyPage.searchBox.fillAndSelectFirstNode('CLIPTextEncode', {
|
||||
@@ -64,7 +62,7 @@ test.describe('Node search box', () => {
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('auto-linked-node.png')
|
||||
})
|
||||
|
||||
test.skip('Can auto link batch moved node', async ({ comfyPage }) => {
|
||||
test('Can auto link batch moved node', async ({ comfyPage }) => {
|
||||
await comfyPage.loadWorkflow('links/batch_move_links')
|
||||
|
||||
const outputSlot1Pos = {
|
||||
@@ -88,7 +86,7 @@ test.describe('Node search box', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test.skip('Link release connecting to node with no slots', async ({
|
||||
test('Link release connecting to node with no slots', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.disconnectEdge()
|
||||
@@ -100,9 +98,7 @@ test.describe('Node search box', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test.skip('Has correct aria-labels on search results', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
test('Has correct aria-labels on search results', async ({ comfyPage }) => {
|
||||
const node = 'Load Checkpoint'
|
||||
await comfyPage.doubleClickCanvas()
|
||||
await comfyPage.searchBox.input.waitFor({ state: 'visible' })
|
||||
@@ -154,7 +150,7 @@ test.describe('Node search box', () => {
|
||||
await comfyPage.doubleClickCanvas()
|
||||
})
|
||||
|
||||
test.skip('Can add filter', async ({ comfyPage }) => {
|
||||
test('Can add filter', async ({ comfyPage }) => {
|
||||
await comfyPage.searchBox.addFilter('MODEL', 'Input Type')
|
||||
await expectFilterChips(comfyPage, ['MODEL'])
|
||||
})
|
||||
@@ -201,13 +197,13 @@ test.describe('Node search box', () => {
|
||||
await expect(comfyPage.searchBox.input).toBeVisible()
|
||||
})
|
||||
|
||||
test.skip('Can add multiple filters', async ({ comfyPage }) => {
|
||||
test('Can add multiple filters', async ({ comfyPage }) => {
|
||||
await comfyPage.searchBox.addFilter('MODEL', 'Input Type')
|
||||
await comfyPage.searchBox.addFilter('CLIP', 'Output Type')
|
||||
await expectFilterChips(comfyPage, ['MODEL', 'CLIP'])
|
||||
})
|
||||
|
||||
test.skip('Can remove filter', async ({ comfyPage }) => {
|
||||
test('Can remove filter', async ({ comfyPage }) => {
|
||||
await comfyPage.searchBox.addFilter('MODEL', 'Input Type')
|
||||
await comfyPage.searchBox.removeFilter(0)
|
||||
await expectFilterChips(comfyPage, [])
|
||||
@@ -220,7 +216,7 @@ test.describe('Node search box', () => {
|
||||
await comfyPage.searchBox.addFilter('utils', 'Category')
|
||||
})
|
||||
|
||||
test.skip('Can remove first filter', async ({ comfyPage }) => {
|
||||
test('Can remove first filter', async ({ comfyPage }) => {
|
||||
await comfyPage.searchBox.removeFilter(0)
|
||||
await expectFilterChips(comfyPage, ['CLIP', 'utils'])
|
||||
await comfyPage.searchBox.removeFilter(0)
|
||||
@@ -229,12 +225,12 @@ test.describe('Node search box', () => {
|
||||
await expectFilterChips(comfyPage, [])
|
||||
})
|
||||
|
||||
test.skip('Can remove middle filter', async ({ comfyPage }) => {
|
||||
test('Can remove middle filter', async ({ comfyPage }) => {
|
||||
await comfyPage.searchBox.removeFilter(1)
|
||||
await expectFilterChips(comfyPage, ['MODEL', 'utils'])
|
||||
})
|
||||
|
||||
test.skip('Can remove last filter', async ({ comfyPage }) => {
|
||||
test('Can remove last filter', async ({ comfyPage }) => {
|
||||
await comfyPage.searchBox.removeFilter(2)
|
||||
await expectFilterChips(comfyPage, ['MODEL', 'CLIP'])
|
||||
})
|
||||
@@ -246,14 +242,12 @@ test.describe('Node search box', () => {
|
||||
await comfyPage.doubleClickCanvas()
|
||||
})
|
||||
|
||||
test.skip('focuses input after adding a filter', async ({ comfyPage }) => {
|
||||
test('focuses input after adding a filter', async ({ comfyPage }) => {
|
||||
await comfyPage.searchBox.addFilter('MODEL', 'Input Type')
|
||||
await expect(comfyPage.searchBox.input).toHaveFocus()
|
||||
})
|
||||
|
||||
test.skip('focuses input after removing a filter', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
test('focuses input after removing a filter', async ({ comfyPage }) => {
|
||||
await comfyPage.searchBox.addFilter('MODEL', 'Input Type')
|
||||
await comfyPage.searchBox.removeFilter(0)
|
||||
await expect(comfyPage.searchBox.input).toHaveFocus()
|
||||
@@ -268,7 +262,7 @@ test.describe('Release context menu', () => {
|
||||
await comfyPage.setSetting('Comfy.NodeSearchBoxImpl', 'default')
|
||||
})
|
||||
|
||||
test.skip('Can trigger on link release', async ({ comfyPage }) => {
|
||||
test('Can trigger on link release', async ({ comfyPage }) => {
|
||||
await comfyPage.disconnectEdge()
|
||||
await comfyPage.page.mouse.move(10, 10)
|
||||
await comfyPage.nextFrame()
|
||||
@@ -277,7 +271,7 @@ test.describe('Release context menu', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test.skip('Can search and add node from context menu', async ({
|
||||
test('Can search and add node from context menu', async ({
|
||||
comfyPage,
|
||||
comfyMouse
|
||||
}) => {
|
||||
|
||||
@@ -7,7 +7,7 @@ test.beforeEach(async ({ comfyPage }) => {
|
||||
})
|
||||
|
||||
test.describe('Note Node', () => {
|
||||
test.skip('Can load node nodes', async ({ comfyPage }) => {
|
||||
test('Can load node nodes', async ({ comfyPage }) => {
|
||||
await comfyPage.loadWorkflow('nodes/note_nodes')
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('note_nodes.png')
|
||||
})
|
||||
|
||||
@@ -8,14 +8,14 @@ test.beforeEach(async ({ comfyPage }) => {
|
||||
})
|
||||
|
||||
test.describe('Primitive Node', () => {
|
||||
test.skip('Can load with correct size', async ({ comfyPage }) => {
|
||||
test('Can load with correct size', async ({ comfyPage }) => {
|
||||
await comfyPage.loadWorkflow('primitive/primitive_node')
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('primitive_node.png')
|
||||
})
|
||||
|
||||
// When link is dropped on widget, it should automatically convert the widget
|
||||
// to input.
|
||||
test.skip('Can connect to widget', async ({ comfyPage }) => {
|
||||
test('Can connect to widget', async ({ comfyPage }) => {
|
||||
await comfyPage.loadWorkflow('primitive/primitive_node_unconnected')
|
||||
const primitiveNode: NodeReference = await comfyPage.getNodeRefById(1)
|
||||
const ksamplerNode: NodeReference = await comfyPage.getNodeRefById(2)
|
||||
@@ -26,7 +26,7 @@ test.describe('Primitive Node', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test.skip('Can connect to dom widget', async ({ comfyPage }) => {
|
||||
test('Can connect to dom widget', async ({ comfyPage }) => {
|
||||
await comfyPage.loadWorkflow(
|
||||
'primitive/primitive_node_unconnected_dom_widget'
|
||||
)
|
||||
@@ -38,7 +38,7 @@ test.describe('Primitive Node', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test.skip('Can connect to static primitive node', async ({ comfyPage }) => {
|
||||
test('Can connect to static primitive node', async ({ comfyPage }) => {
|
||||
await comfyPage.loadWorkflow('primitive/static_primitive_unconnected')
|
||||
const primitiveNode: NodeReference = await comfyPage.getNodeRefById(1)
|
||||
const ksamplerNode: NodeReference = await comfyPage.getNodeRefById(2)
|
||||
|
||||
|
Before Width: | Height: | Size: 100 KiB |
@@ -7,7 +7,7 @@ test.describe('Release Notifications', () => {
|
||||
await comfyPage.setSetting('Comfy.UseNewMenu', 'Top')
|
||||
})
|
||||
|
||||
test.skip('should show help center with release information', async ({
|
||||
test('should show help center with release information', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
// Mock release API with test data instead of empty array
|
||||
@@ -63,7 +63,7 @@ test.describe('Release Notifications', () => {
|
||||
await expect(helpMenu).not.toBeVisible()
|
||||
})
|
||||
|
||||
test.skip('should not show release notifications when mocked (default behavior)', async ({
|
||||
test('should not show release notifications when mocked (default behavior)', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
// Use default setup (mockReleases: true)
|
||||
@@ -94,9 +94,7 @@ test.describe('Release Notifications', () => {
|
||||
).not.toBeVisible()
|
||||
})
|
||||
|
||||
test.skip('should handle release API errors gracefully', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
test('should handle release API errors gracefully', async ({ comfyPage }) => {
|
||||
// Mock API to return an error
|
||||
await comfyPage.page.route('**/releases**', async (route) => {
|
||||
const url = route.request().url()
|
||||
@@ -133,7 +131,7 @@ test.describe('Release Notifications', () => {
|
||||
).toBeVisible()
|
||||
})
|
||||
|
||||
test.skip('should hide "What\'s New" section when notifications are disabled', async ({
|
||||
test('should hide "What\'s New" section when notifications are disabled', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
// Disable version update notifications
|
||||
@@ -221,7 +219,7 @@ test.describe('Release Notifications', () => {
|
||||
expect(apiCallCount).toBe(0)
|
||||
})
|
||||
|
||||
test.skip('should show "What\'s New" section when notifications are enabled', async ({
|
||||
test('should show "What\'s New" section when notifications are enabled', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
// Enable version update notifications (default behavior)
|
||||
@@ -274,7 +272,7 @@ test.describe('Release Notifications', () => {
|
||||
).toBeVisible()
|
||||
})
|
||||
|
||||
test.skip('should toggle "What\'s New" section when setting changes', async ({
|
||||
test('should toggle "What\'s New" section when setting changes', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
// Mock release API with test data
|
||||
@@ -329,7 +327,7 @@ test.describe('Release Notifications', () => {
|
||||
await expect(whatsNewSection).not.toBeVisible()
|
||||
})
|
||||
|
||||
test.skip('should handle edge case with empty releases and disabled notifications', async ({
|
||||
test('should handle edge case with empty releases and disabled notifications', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
// Disable notifications
|
||||
|
||||
@@ -77,7 +77,7 @@ test.describe('Remote COMBO Widget', () => {
|
||||
await comfyPage.page.unroute('**/api/models/checkpoints**')
|
||||
})
|
||||
|
||||
test.skip('lazy loads options when widget is added from node library', async ({
|
||||
test('lazy loads options when widget is added from node library', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const nodeName = 'Remote Widget Node'
|
||||
@@ -104,9 +104,7 @@ test.describe('Remote COMBO Widget', () => {
|
||||
expect(widgetOptions).toEqual(mockOptions)
|
||||
})
|
||||
|
||||
test.skip('applies query parameters from input spec', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
test('applies query parameters from input spec', async ({ comfyPage }) => {
|
||||
const nodeName = 'Remote Widget Node With Sort Query Param'
|
||||
await addRemoteWidgetNode(comfyPage, nodeName)
|
||||
await waitForWidgetUpdate(comfyPage)
|
||||
@@ -115,7 +113,7 @@ test.describe('Remote COMBO Widget', () => {
|
||||
expect(widgetOptions).toEqual([...mockOptions].sort())
|
||||
})
|
||||
|
||||
test.skip('handles empty list of options', async ({ comfyPage }) => {
|
||||
test('handles empty list of options', async ({ comfyPage }) => {
|
||||
await comfyPage.page.route(
|
||||
'**/api/models/checkpoints**',
|
||||
async (route) => {
|
||||
@@ -130,7 +128,7 @@ test.describe('Remote COMBO Widget', () => {
|
||||
expect(widgetOptions).toEqual([])
|
||||
})
|
||||
|
||||
test.skip('falls back to default value when non-200 response', async ({
|
||||
test('falls back to default value when non-200 response', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.page.route(
|
||||
@@ -167,7 +165,7 @@ test.describe('Remote COMBO Widget', () => {
|
||||
expect(requestWasMade).toBe(false)
|
||||
})
|
||||
|
||||
test.skip('fetches options immediately after widget is added to graph', async ({
|
||||
test('fetches options immediately after widget is added to graph', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const requestPromise = comfyPage.page.waitForRequest((request) =>
|
||||
@@ -180,7 +178,7 @@ test.describe('Remote COMBO Widget', () => {
|
||||
})
|
||||
|
||||
test.describe('Refresh Behavior', () => {
|
||||
test.skip('refresh button is visible in selection toolbar when node is selected', async ({
|
||||
test('refresh button is visible in selection toolbar when node is selected', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.setSetting('Comfy.Canvas.SelectionToolbox', true)
|
||||
@@ -199,7 +197,7 @@ test.describe('Remote COMBO Widget', () => {
|
||||
).toBeVisible()
|
||||
})
|
||||
|
||||
test.skip('refreshes options when TTL expires', async ({ comfyPage }) => {
|
||||
test('refreshes options when TTL expires', async ({ comfyPage }) => {
|
||||
// Fulfill each request with a unique timestamp
|
||||
await comfyPage.page.route(
|
||||
'**/api/models/checkpoints**',
|
||||
@@ -230,7 +228,7 @@ test.describe('Remote COMBO Widget', () => {
|
||||
expect(refreshedOptions).not.toEqual(initialOptions)
|
||||
})
|
||||
|
||||
test.skip('does not refresh when TTL is not set', async ({ comfyPage }) => {
|
||||
test('does not refresh when TTL is not set', async ({ comfyPage }) => {
|
||||
let requestCount = 0
|
||||
await comfyPage.page.route(
|
||||
'**/api/models/checkpoints**',
|
||||
@@ -253,7 +251,7 @@ test.describe('Remote COMBO Widget', () => {
|
||||
expect(requestCount).toBe(1) // Should only make initial request
|
||||
})
|
||||
|
||||
test.skip('retries failed requests with backoff', async ({ comfyPage }) => {
|
||||
test('retries failed requests with backoff', async ({ comfyPage }) => {
|
||||
const timestamps: number[] = []
|
||||
await comfyPage.page.route(
|
||||
'**/api/models/checkpoints**',
|
||||
@@ -280,9 +278,7 @@ test.describe('Remote COMBO Widget', () => {
|
||||
expect(intervals[1]).toBeGreaterThan(intervals[0])
|
||||
})
|
||||
|
||||
test.skip('clicking refresh button forces a refresh', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
test('clicking refresh button forces a refresh', async ({ comfyPage }) => {
|
||||
await comfyPage.page.route(
|
||||
'**/api/models/checkpoints**',
|
||||
async (route) => {
|
||||
@@ -308,7 +304,7 @@ test.describe('Remote COMBO Widget', () => {
|
||||
expect(refreshedOptions).not.toEqual(initialOptions)
|
||||
})
|
||||
|
||||
test.skip('control_after_refresh is applied after refresh', async ({
|
||||
test('control_after_refresh is applied after refresh', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const options = [
|
||||
@@ -344,7 +340,7 @@ test.describe('Remote COMBO Widget', () => {
|
||||
})
|
||||
|
||||
test.describe('Cache Behavior', () => {
|
||||
test.skip('reuses cached data between widgets with same params', async ({
|
||||
test('reuses cached data between widgets with same params', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
let requestCount = 0
|
||||
|
||||
@@ -12,7 +12,7 @@ test.describe('Reroute Node', () => {
|
||||
await comfyPage.setupWorkflowsDirectory({})
|
||||
})
|
||||
|
||||
test.skip('loads from inserted workflow', async ({ comfyPage }) => {
|
||||
test('loads from inserted workflow', async ({ comfyPage }) => {
|
||||
const workflowName = 'single_connected_reroute_node.json'
|
||||
await comfyPage.setupWorkflowsDirectory({
|
||||
[workflowName]: 'links/single_connected_reroute_node.json'
|
||||
@@ -44,12 +44,12 @@ test.describe('LiteGraph Native Reroute Node', () => {
|
||||
await comfyPage.setSetting('LiteGraph.Reroute.SplineOffset', 80)
|
||||
})
|
||||
|
||||
test.skip('loads from workflow', async ({ comfyPage }) => {
|
||||
test('loads from workflow', async ({ comfyPage }) => {
|
||||
await comfyPage.loadWorkflow('reroute/native_reroute')
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('native_reroute.png')
|
||||
})
|
||||
|
||||
test.skip('@2x @0.5x Can add reroute by alt clicking on link', async ({
|
||||
test('@2x @0.5x Can add reroute by alt clicking on link', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const loadCheckpointNode = (
|
||||
@@ -75,7 +75,7 @@ test.describe('LiteGraph Native Reroute Node', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test.skip('Can add reroute by clicking middle of link context menu', async ({
|
||||
test('Can add reroute by clicking middle of link context menu', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const loadCheckpointNode = (
|
||||
@@ -102,7 +102,7 @@ test.describe('LiteGraph Native Reroute Node', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test.skip('Can delete link that is connected to two reroutes', async ({
|
||||
test('Can delete link that is connected to two reroutes', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
// https://github.com/Comfy-Org/ComfyUI_frontend/issues/4695
|
||||
|
||||
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 29 KiB |
@@ -8,7 +8,7 @@ test.beforeEach(async ({ comfyPage }) => {
|
||||
})
|
||||
|
||||
test.describe('Canvas Right Click Menu', () => {
|
||||
test.skip('Can add node', async ({ comfyPage }) => {
|
||||
test('Can add node', async ({ comfyPage }) => {
|
||||
await comfyPage.rightClickCanvas()
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('right-click-menu.png')
|
||||
await comfyPage.page.getByText('Add Node').click()
|
||||
@@ -20,7 +20,7 @@ test.describe('Canvas Right Click Menu', () => {
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('add-node-node-added.png')
|
||||
})
|
||||
|
||||
test.skip('Can add group', async ({ comfyPage }) => {
|
||||
test('Can add group', async ({ comfyPage }) => {
|
||||
await comfyPage.rightClickCanvas()
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('right-click-menu.png')
|
||||
await comfyPage.page.getByText('Add Group', { exact: true }).click()
|
||||
@@ -28,7 +28,7 @@ test.describe('Canvas Right Click Menu', () => {
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('add-group-group-added.png')
|
||||
})
|
||||
|
||||
test.skip('Can convert to group node', async ({ comfyPage }) => {
|
||||
test('Can convert to group node', async ({ comfyPage }) => {
|
||||
await comfyPage.select2Nodes()
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('selected-2-nodes.png')
|
||||
await comfyPage.rightClickCanvas()
|
||||
@@ -44,7 +44,7 @@ test.describe('Canvas Right Click Menu', () => {
|
||||
})
|
||||
|
||||
test.describe('Node Right Click Menu', () => {
|
||||
test.skip('Can open properties panel', async ({ comfyPage }) => {
|
||||
test('Can open properties panel', async ({ comfyPage }) => {
|
||||
await comfyPage.rightClickEmptyLatentNode()
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('right-click-node.png')
|
||||
await comfyPage.page.getByText('Properties Panel').click()
|
||||
@@ -54,7 +54,7 @@ test.describe('Node Right Click Menu', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test.skip('Can collapse', async ({ comfyPage }) => {
|
||||
test('Can collapse', async ({ comfyPage }) => {
|
||||
await comfyPage.rightClickEmptyLatentNode()
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('right-click-node.png')
|
||||
await comfyPage.page.getByText('Collapse').click()
|
||||
@@ -64,7 +64,7 @@ test.describe('Node Right Click Menu', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test.skip('Can collapse (Node Badge)', async ({ comfyPage }) => {
|
||||
test('Can collapse (Node Badge)', async ({ comfyPage }) => {
|
||||
await comfyPage.setSetting(
|
||||
'Comfy.NodeBadge.NodeIdBadgeMode',
|
||||
NodeBadgeMode.ShowAll
|
||||
@@ -82,7 +82,7 @@ test.describe('Node Right Click Menu', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test.skip('Can bypass', async ({ comfyPage }) => {
|
||||
test('Can bypass', async ({ comfyPage }) => {
|
||||
await comfyPage.rightClickEmptyLatentNode()
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('right-click-node.png')
|
||||
await comfyPage.page.getByText('Bypass').click()
|
||||
@@ -92,7 +92,7 @@ test.describe('Node Right Click Menu', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test.skip('Can pin and unpin', async ({ comfyPage }) => {
|
||||
test('Can pin and unpin', async ({ comfyPage }) => {
|
||||
await comfyPage.rightClickEmptyLatentNode()
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('right-click-node.png')
|
||||
await comfyPage.page.click('.litemenu-entry:has-text("Pin")')
|
||||
@@ -111,7 +111,7 @@ test.describe('Node Right Click Menu', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test.skip('Can move after unpin', async ({ comfyPage }) => {
|
||||
test('Can move after unpin', async ({ comfyPage }) => {
|
||||
await comfyPage.rightClickEmptyLatentNode()
|
||||
await comfyPage.page.click('.litemenu-entry:has-text("Pin")')
|
||||
await comfyPage.nextFrame()
|
||||
@@ -125,7 +125,7 @@ test.describe('Node Right Click Menu', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test.skip('Can pin/unpin selected nodes', async ({ comfyPage }) => {
|
||||
test('Can pin/unpin selected nodes', async ({ comfyPage }) => {
|
||||
await comfyPage.select2Nodes()
|
||||
await comfyPage.page.keyboard.down('Control')
|
||||
await comfyPage.rightClickEmptyLatentNode()
|
||||
|
||||
@@ -15,7 +15,7 @@ test.describe('Selection Toolbox', () => {
|
||||
await comfyPage.setSetting('Comfy.Canvas.SelectionToolbox', true)
|
||||
})
|
||||
|
||||
test.skip('shows selection toolbox', async ({ comfyPage }) => {
|
||||
test('shows selection toolbox', async ({ comfyPage }) => {
|
||||
// By default, selection toolbox should be enabled
|
||||
await expect(comfyPage.selectionToolbox).not.toBeVisible()
|
||||
|
||||
@@ -30,7 +30,7 @@ test.describe('Selection Toolbox', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test.skip('shows at correct position when node is pasted', async ({
|
||||
test('shows at correct position when node is pasted', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.loadWorkflow('nodes/single_ksampler')
|
||||
@@ -66,9 +66,7 @@ test.describe('Selection Toolbox', () => {
|
||||
await expect(comfyPage.selectionToolbox).not.toBeVisible()
|
||||
})
|
||||
|
||||
test.skip('shows border only with multiple selections', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
test('shows border only with multiple selections', async ({ comfyPage }) => {
|
||||
// Select single node
|
||||
await comfyPage.selectNodes(['KSampler'])
|
||||
|
||||
@@ -96,7 +94,7 @@ test.describe('Selection Toolbox', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test.skip('displays bypass button in toolbox when nodes are selected', async ({
|
||||
test('displays bypass button in toolbox when nodes are selected', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
// A group + a KSampler node
|
||||
|
||||
|
Before Width: | Height: | Size: 102 KiB After Width: | Height: | Size: 102 KiB |
|
Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 104 KiB |
|
Before Width: | Height: | Size: 99 KiB After Width: | Height: | Size: 99 KiB |
@@ -72,7 +72,7 @@ test.describe('Selection Toolbox - More Options Submenus', () => {
|
||||
throw new Error('Could not open More Options menu - popover not showing')
|
||||
}
|
||||
|
||||
test.skip('opens Node Info from More Options menu', async ({ comfyPage }) => {
|
||||
test('opens Node Info from More Options menu', async ({ comfyPage }) => {
|
||||
await openMoreOptions(comfyPage)
|
||||
const nodeInfoButton = comfyPage.page.getByText('Node Info', {
|
||||
exact: true
|
||||
@@ -82,7 +82,7 @@ test.describe('Selection Toolbox - More Options Submenus', () => {
|
||||
await comfyPage.nextFrame()
|
||||
})
|
||||
|
||||
test.skip('changes node shape via Shape submenu', async ({ comfyPage }) => {
|
||||
test('changes node shape via Shape submenu', async ({ comfyPage }) => {
|
||||
const nodeRef = (await comfyPage.getNodeRefsByTitle('KSampler'))[0]
|
||||
const initialShape = await nodeRef.getProperty<number>('shape')
|
||||
|
||||
@@ -99,9 +99,7 @@ test.describe('Selection Toolbox - More Options Submenus', () => {
|
||||
expect(newShape).toBe(1)
|
||||
})
|
||||
|
||||
test.skip('changes node color via Color submenu swatch', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
test('changes node color via Color submenu swatch', async ({ comfyPage }) => {
|
||||
const nodeRef = (await comfyPage.getNodeRefsByTitle('KSampler'))[0]
|
||||
const initialColor = await nodeRef.getProperty<string | undefined>('color')
|
||||
|
||||
@@ -119,7 +117,7 @@ test.describe('Selection Toolbox - More Options Submenus', () => {
|
||||
}
|
||||
})
|
||||
|
||||
test.skip('renames a node using Rename action', async ({ comfyPage }) => {
|
||||
test('renames a node using Rename action', async ({ comfyPage }) => {
|
||||
const nodeRef = (await comfyPage.getNodeRefsByTitle('KSampler'))[0]
|
||||
await openMoreOptions(comfyPage)
|
||||
await comfyPage.page
|
||||
@@ -136,7 +134,7 @@ test.describe('Selection Toolbox - More Options Submenus', () => {
|
||||
expect(newTitle).toBe('RenamedNode')
|
||||
})
|
||||
|
||||
test.skip('closes More Options menu when clicking outside', async ({
|
||||
test('closes More Options menu when clicking outside', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await openMoreOptions(comfyPage)
|
||||
@@ -153,7 +151,7 @@ test.describe('Selection Toolbox - More Options Submenus', () => {
|
||||
).not.toBeVisible()
|
||||
})
|
||||
|
||||
test.skip('closes More Options menu when clicking the button again (toggle)', async ({
|
||||
test('closes More Options menu when clicking the button again (toggle)', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await openMoreOptions(comfyPage)
|
||||
|
||||
@@ -12,7 +12,7 @@ test.describe('Node library sidebar', () => {
|
||||
await tab.open()
|
||||
})
|
||||
|
||||
test.skip('Node preview and drag to canvas', async ({ comfyPage }) => {
|
||||
test('Node preview and drag to canvas', async ({ comfyPage }) => {
|
||||
const tab = comfyPage.menu.nodeLibraryTab
|
||||
await tab.getFolder('sampling').click()
|
||||
|
||||
@@ -49,7 +49,7 @@ test.describe('Node library sidebar', () => {
|
||||
expect(await comfyPage.getGraphNodesCount()).toBe(count + 1)
|
||||
})
|
||||
|
||||
test.skip('Bookmark node', async ({ comfyPage }) => {
|
||||
test('Bookmark node', async ({ comfyPage }) => {
|
||||
const tab = comfyPage.menu.nodeLibraryTab
|
||||
await tab.getFolder('sampling').click()
|
||||
|
||||
@@ -68,7 +68,7 @@ test.describe('Node library sidebar', () => {
|
||||
expect(await comfyPage.page.isVisible('.node-lib-node-preview')).toBe(true)
|
||||
})
|
||||
|
||||
test.skip('Ignores unrecognized node', async ({ comfyPage }) => {
|
||||
test('Ignores unrecognized node', async ({ comfyPage }) => {
|
||||
await comfyPage.setSetting('Comfy.NodeLibrary.Bookmarks.V2', ['foo'])
|
||||
|
||||
const tab = comfyPage.menu.nodeLibraryTab
|
||||
@@ -76,13 +76,13 @@ test.describe('Node library sidebar', () => {
|
||||
expect(await tab.getNode('foo').count()).toBe(0)
|
||||
})
|
||||
|
||||
test.skip('Displays empty bookmarks folder', async ({ comfyPage }) => {
|
||||
test('Displays empty bookmarks folder', async ({ comfyPage }) => {
|
||||
await comfyPage.setSetting('Comfy.NodeLibrary.Bookmarks.V2', ['foo/'])
|
||||
const tab = comfyPage.menu.nodeLibraryTab
|
||||
expect(await tab.getFolder('foo').count()).toBe(1)
|
||||
})
|
||||
|
||||
test.skip('Can add new bookmark folder', async ({ comfyPage }) => {
|
||||
test('Can add new bookmark folder', async ({ comfyPage }) => {
|
||||
const tab = comfyPage.menu.nodeLibraryTab
|
||||
await tab.newFolderButton.click()
|
||||
const textInput = comfyPage.page.locator('.editable-text input')
|
||||
@@ -95,7 +95,7 @@ test.describe('Node library sidebar', () => {
|
||||
).toEqual(['New Folder/'])
|
||||
})
|
||||
|
||||
test.skip('Can add nested bookmark folder', async ({ comfyPage }) => {
|
||||
test('Can add nested bookmark folder', async ({ comfyPage }) => {
|
||||
await comfyPage.setSetting('Comfy.NodeLibrary.Bookmarks.V2', ['foo/'])
|
||||
const tab = comfyPage.menu.nodeLibraryTab
|
||||
|
||||
@@ -112,7 +112,7 @@ test.describe('Node library sidebar', () => {
|
||||
).toEqual(['foo/', 'foo/bar/'])
|
||||
})
|
||||
|
||||
test.skip('Can delete bookmark folder', async ({ comfyPage }) => {
|
||||
test('Can delete bookmark folder', async ({ comfyPage }) => {
|
||||
await comfyPage.setSetting('Comfy.NodeLibrary.Bookmarks.V2', ['foo/'])
|
||||
const tab = comfyPage.menu.nodeLibraryTab
|
||||
|
||||
@@ -124,7 +124,7 @@ test.describe('Node library sidebar', () => {
|
||||
).toEqual([])
|
||||
})
|
||||
|
||||
test.skip('Can rename bookmark folder', async ({ comfyPage }) => {
|
||||
test('Can rename bookmark folder', async ({ comfyPage }) => {
|
||||
await comfyPage.setSetting('Comfy.NodeLibrary.Bookmarks.V2', ['foo/'])
|
||||
const tab = comfyPage.menu.nodeLibraryTab
|
||||
|
||||
@@ -140,7 +140,7 @@ test.describe('Node library sidebar', () => {
|
||||
).toEqual(['bar/'])
|
||||
})
|
||||
|
||||
test.skip('Can add bookmark by dragging node to bookmark folder', async ({
|
||||
test('Can add bookmark by dragging node to bookmark folder', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.setSetting('Comfy.NodeLibrary.Bookmarks.V2', ['foo/'])
|
||||
@@ -155,7 +155,7 @@ test.describe('Node library sidebar', () => {
|
||||
).toEqual(['foo/', 'foo/KSamplerAdvanced'])
|
||||
})
|
||||
|
||||
test.skip('Can add bookmark by clicking bookmark button', async ({
|
||||
test('Can add bookmark by clicking bookmark button', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const tab = comfyPage.menu.nodeLibraryTab
|
||||
@@ -166,9 +166,7 @@ test.describe('Node library sidebar', () => {
|
||||
).toEqual(['KSamplerAdvanced'])
|
||||
})
|
||||
|
||||
test.skip('Can unbookmark node (Top level bookmark)', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
test('Can unbookmark node (Top level bookmark)', async ({ comfyPage }) => {
|
||||
await comfyPage.setSetting('Comfy.NodeLibrary.Bookmarks.V2', [
|
||||
'KSamplerAdvanced'
|
||||
])
|
||||
@@ -179,9 +177,7 @@ test.describe('Node library sidebar', () => {
|
||||
).toEqual([])
|
||||
})
|
||||
|
||||
test.skip('Can unbookmark node (Library node bookmark)', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
test('Can unbookmark node (Library node bookmark)', async ({ comfyPage }) => {
|
||||
await comfyPage.setSetting('Comfy.NodeLibrary.Bookmarks.V2', [
|
||||
'KSamplerAdvanced'
|
||||
])
|
||||
@@ -196,7 +192,7 @@ test.describe('Node library sidebar', () => {
|
||||
await comfyPage.getSetting('Comfy.NodeLibrary.Bookmarks.V2')
|
||||
).toEqual([])
|
||||
})
|
||||
test.skip('Can customize icon', async ({ comfyPage }) => {
|
||||
test('Can customize icon', async ({ comfyPage }) => {
|
||||
await comfyPage.setSetting('Comfy.NodeLibrary.Bookmarks.V2', ['foo/'])
|
||||
const tab = comfyPage.menu.nodeLibraryTab
|
||||
await tab.getFolder('foo').click({ button: 'right' })
|
||||
@@ -219,7 +215,7 @@ test.describe('Node library sidebar', () => {
|
||||
})
|
||||
})
|
||||
// If color is left as default, it should not be saved
|
||||
test.skip('Can customize icon (default field)', async ({ comfyPage }) => {
|
||||
test('Can customize icon (default field)', async ({ comfyPage }) => {
|
||||
await comfyPage.setSetting('Comfy.NodeLibrary.Bookmarks.V2', ['foo/'])
|
||||
const tab = comfyPage.menu.nodeLibraryTab
|
||||
await tab.getFolder('foo').click({ button: 'right' })
|
||||
@@ -238,7 +234,7 @@ test.describe('Node library sidebar', () => {
|
||||
})
|
||||
})
|
||||
|
||||
test.skip('Can customize bookmark color after interacting with color options', async ({
|
||||
test('Can customize bookmark color after interacting with color options', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
// Open customization dialog
|
||||
@@ -278,7 +274,7 @@ test.describe('Node library sidebar', () => {
|
||||
await expect(setting['foo/'].color).not.toBe('')
|
||||
})
|
||||
|
||||
test.skip('Can rename customized bookmark folder', async ({ comfyPage }) => {
|
||||
test('Can rename customized bookmark folder', async ({ comfyPage }) => {
|
||||
await comfyPage.setSetting('Comfy.NodeLibrary.Bookmarks.V2', ['foo/'])
|
||||
await comfyPage.setSetting('Comfy.NodeLibrary.BookmarksCustomization', {
|
||||
'foo/': {
|
||||
@@ -307,7 +303,7 @@ test.describe('Node library sidebar', () => {
|
||||
})
|
||||
})
|
||||
|
||||
test.skip('Can delete customized bookmark folder', async ({ comfyPage }) => {
|
||||
test('Can delete customized bookmark folder', async ({ comfyPage }) => {
|
||||
await comfyPage.setSetting('Comfy.NodeLibrary.Bookmarks.V2', ['foo/'])
|
||||
await comfyPage.setSetting('Comfy.NodeLibrary.BookmarksCustomization', {
|
||||
'foo/': {
|
||||
@@ -327,7 +323,7 @@ test.describe('Node library sidebar', () => {
|
||||
).toEqual({})
|
||||
})
|
||||
|
||||
test.skip('Can filter nodes in both trees', async ({ comfyPage }) => {
|
||||
test('Can filter nodes in both trees', async ({ comfyPage }) => {
|
||||
await comfyPage.setSetting('Comfy.NodeLibrary.Bookmarks.V2', [
|
||||
'foo/',
|
||||
'foo/KSamplerAdvanced',
|
||||
|
||||
@@ -16,7 +16,7 @@ test.describe('Workflows sidebar', () => {
|
||||
await comfyPage.setupWorkflowsDirectory({})
|
||||
})
|
||||
|
||||
test.skip('Can create new blank workflow', async ({ comfyPage }) => {
|
||||
test('Can create new blank workflow', async ({ comfyPage }) => {
|
||||
const tab = comfyPage.menu.workflowsTab
|
||||
expect(await tab.getOpenedWorkflowNames()).toEqual([
|
||||
'*Unsaved Workflow.json'
|
||||
@@ -29,7 +29,7 @@ test.describe('Workflows sidebar', () => {
|
||||
])
|
||||
})
|
||||
|
||||
test.skip('Can show top level saved workflows', async ({ comfyPage }) => {
|
||||
test('Can show top level saved workflows', async ({ comfyPage }) => {
|
||||
await comfyPage.setupWorkflowsDirectory({
|
||||
'workflow1.json': 'default.json',
|
||||
'workflow2.json': 'default.json'
|
||||
@@ -42,7 +42,7 @@ test.describe('Workflows sidebar', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test.skip('Can duplicate workflow', async ({ comfyPage }) => {
|
||||
test('Can duplicate workflow', async ({ comfyPage }) => {
|
||||
const tab = comfyPage.menu.workflowsTab
|
||||
await comfyPage.menu.topbar.saveWorkflow('workflow1.json')
|
||||
|
||||
@@ -72,7 +72,7 @@ test.describe('Workflows sidebar', () => {
|
||||
])
|
||||
})
|
||||
|
||||
test.skip('Can open workflow after insert', async ({ comfyPage }) => {
|
||||
test('Can open workflow after insert', async ({ comfyPage }) => {
|
||||
await comfyPage.setupWorkflowsDirectory({
|
||||
'workflow1.json': 'nodes/single_ksampler.json'
|
||||
})
|
||||
@@ -91,7 +91,7 @@ test.describe('Workflows sidebar', () => {
|
||||
expect((await comfyPage.getNodes()).length).toEqual(1)
|
||||
})
|
||||
|
||||
test.skip('Can rename nested workflow from opened workflow item', async ({
|
||||
test('Can rename nested workflow from opened workflow item', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.setupWorkflowsDirectory({
|
||||
@@ -117,7 +117,7 @@ test.describe('Workflows sidebar', () => {
|
||||
])
|
||||
})
|
||||
|
||||
test.skip('Can save workflow as', async ({ comfyPage }) => {
|
||||
test('Can save workflow as', async ({ comfyPage }) => {
|
||||
await comfyPage.executeCommand('Comfy.NewBlankWorkflow')
|
||||
await comfyPage.menu.topbar.saveWorkflowAs('workflow3.json')
|
||||
expect(await comfyPage.menu.workflowsTab.getOpenedWorkflowNames()).toEqual([
|
||||
@@ -133,7 +133,7 @@ test.describe('Workflows sidebar', () => {
|
||||
])
|
||||
})
|
||||
|
||||
test.skip('Exported workflow does not contain localized slot names', async ({
|
||||
test('Exported workflow does not contain localized slot names', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.loadWorkflow('default')
|
||||
@@ -153,7 +153,7 @@ test.describe('Workflows sidebar', () => {
|
||||
}
|
||||
})
|
||||
|
||||
test.skip('Can export same workflow with different locales', async ({
|
||||
test('Can export same workflow with different locales', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.loadWorkflow('default')
|
||||
@@ -185,9 +185,8 @@ test.describe('Workflows sidebar', () => {
|
||||
expect(downloadedContent).toEqual(downloadedContentZh)
|
||||
})
|
||||
|
||||
test.skip('Can save workflow as with same name', async ({ comfyPage }) => {
|
||||
test('Can save workflow as with same name', async ({ comfyPage }) => {
|
||||
await comfyPage.menu.topbar.saveWorkflow('workflow5.json')
|
||||
await comfyPage.nextFrame()
|
||||
expect(await comfyPage.menu.workflowsTab.getOpenedWorkflowNames()).toEqual([
|
||||
'workflow5.json'
|
||||
])
|
||||
@@ -200,7 +199,7 @@ test.describe('Workflows sidebar', () => {
|
||||
])
|
||||
})
|
||||
|
||||
test.skip('Can save temporary workflow with unmodified name', async ({
|
||||
test('Can save temporary workflow with unmodified name', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
expect(await comfyPage.isCurrentWorkflowModified()).toBe(false)
|
||||
@@ -214,9 +213,7 @@ test.describe('Workflows sidebar', () => {
|
||||
expect(await comfyPage.isCurrentWorkflowModified()).toBe(false)
|
||||
})
|
||||
|
||||
test.skip('Can overwrite other workflows with save as', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
test('Can overwrite other workflows with save as', async ({ comfyPage }) => {
|
||||
const topbar = comfyPage.menu.topbar
|
||||
await topbar.saveWorkflow('workflow1.json')
|
||||
await topbar.saveWorkflowAs('workflow2.json')
|
||||
@@ -242,7 +239,7 @@ test.describe('Workflows sidebar', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test.skip('Does not report warning when switching between opened workflows', async ({
|
||||
test('Does not report warning when switching between opened workflows', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.loadWorkflow('missing/missing_nodes')
|
||||
@@ -260,7 +257,7 @@ test.describe('Workflows sidebar', () => {
|
||||
).not.toBeVisible()
|
||||
})
|
||||
|
||||
test.skip('Can close saved-workflows from the open workflows section', async ({
|
||||
test('Can close saved-workflows from the open workflows section', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.menu.topbar.saveWorkflow(
|
||||
@@ -275,7 +272,7 @@ test.describe('Workflows sidebar', () => {
|
||||
])
|
||||
})
|
||||
|
||||
test.skip('Can close saved workflow with command', async ({ comfyPage }) => {
|
||||
test('Can close saved workflow with command', async ({ comfyPage }) => {
|
||||
const tab = comfyPage.menu.workflowsTab
|
||||
await comfyPage.menu.topbar.saveWorkflow('workflow1.json')
|
||||
await comfyPage.executeCommand('Workspace.CloseWorkflow')
|
||||
@@ -284,9 +281,7 @@ test.describe('Workflows sidebar', () => {
|
||||
])
|
||||
})
|
||||
|
||||
test.skip('Can delete workflows (confirm disabled)', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
test('Can delete workflows (confirm disabled)', async ({ comfyPage }) => {
|
||||
await comfyPage.setSetting('Comfy.Workflow.ConfirmDelete', false)
|
||||
|
||||
const { topbar, workflowsTab } = comfyPage.menu
|
||||
@@ -305,7 +300,7 @@ test.describe('Workflows sidebar', () => {
|
||||
])
|
||||
})
|
||||
|
||||
test.skip('Can delete workflows', async ({ comfyPage }) => {
|
||||
test('Can delete workflows', async ({ comfyPage }) => {
|
||||
const { topbar, workflowsTab } = comfyPage.menu
|
||||
|
||||
const filename = 'workflow18.json'
|
||||
@@ -323,9 +318,7 @@ test.describe('Workflows sidebar', () => {
|
||||
])
|
||||
})
|
||||
|
||||
test.skip('Can duplicate workflow from context menu', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
test('Can duplicate workflow from context menu', async ({ comfyPage }) => {
|
||||
await comfyPage.setupWorkflowsDirectory({
|
||||
'workflow1.json': 'default.json'
|
||||
})
|
||||
@@ -344,9 +337,7 @@ test.describe('Workflows sidebar', () => {
|
||||
])
|
||||
})
|
||||
|
||||
test.skip('Can drop workflow from workflows sidebar', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
test('Can drop workflow from workflows sidebar', async ({ comfyPage }) => {
|
||||
await comfyPage.setupWorkflowsDirectory({
|
||||
'workflow1.json': 'default.json'
|
||||
})
|
||||
|
||||
@@ -468,9 +468,7 @@ test.describe('Subgraph Operations', () => {
|
||||
expect(finalNodeCount).toBe(initialNodeCount + 1)
|
||||
})
|
||||
|
||||
test.skip('Can undo and redo operations in subgraph', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
test('Can undo and redo operations in subgraph', async ({ comfyPage }) => {
|
||||
await comfyPage.loadWorkflow('subgraphs/basic-subgraph')
|
||||
|
||||
const subgraphNode = await comfyPage.getNodeRefById('2')
|
||||
@@ -685,7 +683,7 @@ test.describe('Subgraph Operations', () => {
|
||||
expect(widgetCount).toBe(0)
|
||||
})
|
||||
|
||||
test.skip('Multiple promoted widgets are handled correctly', async ({
|
||||
test('Multiple promoted widgets are handled correctly', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.loadWorkflow(
|
||||
|
||||
@@ -69,7 +69,7 @@ test.describe('Templates', () => {
|
||||
}
|
||||
})
|
||||
|
||||
test.skip('Can load template workflows', async ({ comfyPage }) => {
|
||||
test('Can load template workflows', async ({ comfyPage }) => {
|
||||
// Clear the workflow
|
||||
await comfyPage.menu.workflowsTab.open()
|
||||
await comfyPage.executeCommand('Comfy.NewBlankWorkflow')
|
||||
|
||||
|
Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 68 KiB |
@@ -12,9 +12,7 @@ test.describe('Vue Node Groups', () => {
|
||||
await comfyPage.vueNodes.waitForNodes()
|
||||
})
|
||||
|
||||
test.skip('should allow creating groups with hotkey', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
test('should allow creating groups with hotkey', async ({ comfyPage }) => {
|
||||
await comfyPage.page.getByText('Load Checkpoint').click()
|
||||
await comfyPage.page.getByText('KSampler').click({ modifiers: ['Control'] })
|
||||
await comfyPage.page.keyboard.press(CREATE_GROUP_HOTKEY)
|
||||
@@ -24,7 +22,7 @@ test.describe('Vue Node Groups', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test.skip('should allow fitting group to contents', async ({ comfyPage }) => {
|
||||
test('should allow fitting group to contents', async ({ comfyPage }) => {
|
||||
await comfyPage.setup()
|
||||
await comfyPage.loadWorkflow('groups/oversized_group')
|
||||
await comfyPage.ctrlA()
|
||||
|
||||
|
Before Width: | Height: | Size: 99 KiB After Width: | Height: | Size: 95 KiB |
|
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 47 KiB |
@@ -9,7 +9,7 @@ test.describe('Vue Nodes Canvas Pan', () => {
|
||||
await comfyPage.vueNodes.waitForNodes()
|
||||
})
|
||||
|
||||
test.skip('@mobile Can pan with touch', async ({ comfyPage }) => {
|
||||
test('@mobile Can pan with touch', async ({ comfyPage }) => {
|
||||
await comfyPage.panWithTouch({ x: 64, y: 64 }, { x: 256, y: 256 })
|
||||
await expect(comfyPage.canvas).toHaveScreenshot(
|
||||
'vue-nodes-paned-with-touch.png'
|
||||
|
||||
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 11 KiB |
@@ -9,7 +9,7 @@ test.describe('Vue Nodes Zoom', () => {
|
||||
await comfyPage.vueNodes.waitForNodes()
|
||||
})
|
||||
|
||||
test.skip('should not capture drag while zooming with ctrl+shift+drag', async ({
|
||||
test('should not capture drag while zooming with ctrl+shift+drag', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const checkpointNode = comfyPage.vueNodes.getNodeByTitle('Load Checkpoint')
|
||||
|
||||
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 15 KiB |
@@ -1,7 +1,6 @@
|
||||
import type { Locator, Page } from '@playwright/test'
|
||||
|
||||
import type { NodeId } from '../../../../../src/platform/workflow/validation/schemas/workflowSchema'
|
||||
import { getSlotKey } from '../../../../../src/renderer/core/layout/slots/slotIdentifier'
|
||||
import {
|
||||
comfyExpect as expect,
|
||||
comfyPageFixture as test
|
||||
@@ -67,8 +66,11 @@ function slotLocator(
|
||||
slotIndex: number,
|
||||
isInput: boolean
|
||||
) {
|
||||
const key = getSlotKey(String(nodeId), slotIndex, isInput)
|
||||
return page.locator(`[data-slot-key="${key}"]`)
|
||||
const type = isInput ? 'input' : 'output'
|
||||
const id = String(nodeId)
|
||||
return page.locator(
|
||||
`[data-node-id="${id}"][data-slot-type="${type}"][data-slot-index="${slotIndex}"]`
|
||||
)
|
||||
}
|
||||
|
||||
async function expectVisibleAll(...locators: Locator[]) {
|
||||
@@ -109,7 +111,7 @@ test.describe('Vue Node Link Interaction', () => {
|
||||
await fitToViewInstant(comfyPage)
|
||||
})
|
||||
|
||||
test.skip('should show a link dragging out from a slot when dragging on a slot', async ({
|
||||
test('should show a link dragging out from a slot when dragging on a slot', async ({
|
||||
comfyPage,
|
||||
comfyMouse
|
||||
}) => {
|
||||
@@ -218,7 +220,7 @@ test.describe('Vue Node Link Interaction', () => {
|
||||
expect(await samplerInput.getLinkCount()).toBe(0)
|
||||
})
|
||||
|
||||
test.skip('should reuse the existing origin when dragging an input link', async ({
|
||||
test('should reuse the existing origin when dragging an input link', async ({
|
||||
comfyPage,
|
||||
comfyMouse
|
||||
}) => {
|
||||
@@ -255,7 +257,7 @@ test.describe('Vue Node Link Interaction', () => {
|
||||
await comfyMouse.drop()
|
||||
})
|
||||
|
||||
test.skip('ctrl+alt drag from an input starts a fresh link', async ({
|
||||
test('ctrl+alt drag from an input starts a fresh link', async ({
|
||||
comfyPage,
|
||||
comfyMouse
|
||||
}) => {
|
||||
@@ -395,7 +397,7 @@ test.describe('Vue Node Link Interaction', () => {
|
||||
expect(await vaeInput.getLinkCount()).toBe(1)
|
||||
})
|
||||
|
||||
test.skip('rerouted input drag preview remains anchored to reroute', async ({
|
||||
test('rerouted input drag preview remains anchored to reroute', async ({
|
||||
comfyPage,
|
||||
comfyMouse
|
||||
}) => {
|
||||
@@ -480,7 +482,7 @@ test.describe('Vue Node Link Interaction', () => {
|
||||
expect(linkDetails?.parentId).not.toBeNull()
|
||||
})
|
||||
|
||||
test.skip('rerouted output shift-drag preview remains anchored to reroute', async ({
|
||||
test('rerouted output shift-drag preview remains anchored to reroute', async ({
|
||||
comfyPage,
|
||||
comfyMouse
|
||||
}) => {
|
||||
@@ -639,7 +641,7 @@ test.describe('Vue Node Link Interaction', () => {
|
||||
})
|
||||
})
|
||||
|
||||
test.skip('shift-dragging an output with multiple links should drag all links', async ({
|
||||
test('shift-dragging an output with multiple links should drag all links', async ({
|
||||
comfyPage,
|
||||
comfyMouse
|
||||
}) => {
|
||||
@@ -694,7 +696,7 @@ test.describe('Vue Node Link Interaction', () => {
|
||||
}
|
||||
})
|
||||
|
||||
test.skip('should snap to node center while dragging and link on drop', async ({
|
||||
test('should snap to node center while dragging and link on drop', async ({
|
||||
comfyPage,
|
||||
comfyMouse
|
||||
}) => {
|
||||
@@ -743,7 +745,7 @@ test.describe('Vue Node Link Interaction', () => {
|
||||
expect(linked?.targetId).toBe(samplerNode.id)
|
||||
})
|
||||
|
||||
test.skip('should snap to a specific compatible slot when targeting it', async ({
|
||||
test('should snap to a specific compatible slot when targeting it', async ({
|
||||
comfyPage,
|
||||
comfyMouse
|
||||
}) => {
|
||||
|
||||
|
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 51 KiB |
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 46 KiB |
@@ -1,8 +1,8 @@
|
||||
import {
|
||||
type ComfyPage,
|
||||
comfyExpect as expect,
|
||||
comfyPageFixture as test
|
||||
} from '../../../../fixtures/ComfyPage'
|
||||
import type { ComfyPage } from '../../../../fixtures/ComfyPage'
|
||||
import type { Position } from '../../../../fixtures/types'
|
||||
|
||||
test.describe('Vue Node Moving', () => {
|
||||
@@ -29,7 +29,7 @@ test.describe('Vue Node Moving', () => {
|
||||
expect(diffY).toBeGreaterThan(0)
|
||||
}
|
||||
|
||||
test.skip('should allow moving nodes by dragging', async ({ comfyPage }) => {
|
||||
test('should allow moving nodes by dragging', async ({ comfyPage }) => {
|
||||
const loadCheckpointHeaderPos = await getLoadCheckpointHeaderPos(comfyPage)
|
||||
await comfyPage.dragAndDrop(loadCheckpointHeaderPos, {
|
||||
x: 256,
|
||||
@@ -42,7 +42,7 @@ test.describe('Vue Node Moving', () => {
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('vue-node-moved-node.png')
|
||||
})
|
||||
|
||||
test.skip('@mobile should allow moving nodes by dragging on touch devices', async ({
|
||||
test('@mobile should allow moving nodes by dragging on touch devices', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
// Disable minimap (gets in way of the node on small screens)
|
||||
|
||||
|
Before Width: | Height: | Size: 84 KiB After Width: | Height: | Size: 73 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 9.3 KiB |
|
Before Width: | Height: | Size: 96 KiB |
@@ -11,7 +11,7 @@ test.describe('Vue Node Custom Colors', () => {
|
||||
await comfyPage.vueNodes.waitForNodes()
|
||||
})
|
||||
|
||||
test.skip('displays color picker button and allows color selection', async ({
|
||||
test('displays color picker button and allows color selection', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
const loadCheckpointNode = comfyPage.page.locator('[data-node-id]').filter({
|
||||
@@ -30,14 +30,14 @@ test.describe('Vue Node Custom Colors', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test.skip('should load node colors from workflow', async ({ comfyPage }) => {
|
||||
test('should load node colors from workflow', async ({ comfyPage }) => {
|
||||
await comfyPage.loadWorkflow('nodes/every_node_color')
|
||||
await expect(comfyPage.canvas).toHaveScreenshot(
|
||||
'vue-node-custom-colors-dark-all-colors.png'
|
||||
)
|
||||
})
|
||||
|
||||
test.skip('should show brightened node colors on light theme', async ({
|
||||
test('should show brightened node colors on light theme', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.setSetting('Comfy.ColorPalette', 'light')
|
||||
|
||||
|
Before Width: | Height: | Size: 97 KiB After Width: | Height: | Size: 91 KiB |
|
Before Width: | Height: | Size: 97 KiB After Width: | Height: | Size: 88 KiB |
|
Before Width: | Height: | Size: 98 KiB After Width: | Height: | Size: 89 KiB |
@@ -3,7 +3,7 @@ import {
|
||||
comfyPageFixture as test
|
||||
} from '../../../fixtures/ComfyPage'
|
||||
|
||||
const ERROR_CLASS = /border-node-stroke-error/
|
||||
const ERROR_CLASS = /border-error/
|
||||
|
||||
test.describe('Vue Node Error', () => {
|
||||
test.beforeEach(async ({ comfyPage }) => {
|
||||
@@ -17,21 +17,16 @@ test.describe('Vue Node Error', () => {
|
||||
await comfyPage.setup()
|
||||
await comfyPage.loadWorkflow('missing/missing_nodes')
|
||||
|
||||
// Close missing nodes warning dialog
|
||||
await comfyPage.page.getByRole('button', { name: 'Close' }).click()
|
||||
await comfyPage.page.waitForSelector('.comfy-missing-nodes', {
|
||||
state: 'hidden'
|
||||
})
|
||||
|
||||
// Expect error state on missing unknown node
|
||||
const unknownNode = comfyPage.page.locator('[data-node-id]').filter({
|
||||
hasText: 'UNKNOWN NODE'
|
||||
})
|
||||
await expect(unknownNode).toHaveClass(ERROR_CLASS)
|
||||
})
|
||||
|
||||
test('should display error state when node causes execution error', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.setup()
|
||||
await comfyPage.loadWorkflow('nodes/execution_error')
|
||||
await comfyPage.runButton.click()
|
||||
|
||||
const raiseErrorNode = comfyPage.vueNodes.getNodeByTitle('Raise Error')
|
||||
await expect(raiseErrorNode).toHaveClass(ERROR_CLASS)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -13,9 +13,7 @@ test.describe('Vue Nodes - LOD', () => {
|
||||
await comfyPage.loadWorkflow('default')
|
||||
})
|
||||
|
||||
test.skip('should toggle LOD based on zoom threshold', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
test('should toggle LOD based on zoom threshold', async ({ comfyPage }) => {
|
||||
await comfyPage.vueNodes.waitForNodes()
|
||||
|
||||
const initialNodeCount = await comfyPage.vueNodes.getNodeCount()
|
||||
|
||||
|
Before Width: | Height: | Size: 95 KiB After Width: | Height: | Size: 107 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 41 KiB |
|
Before Width: | Height: | Size: 95 KiB After Width: | Height: | Size: 107 KiB |
|
Before Width: | Height: | Size: 97 KiB |
@@ -9,7 +9,7 @@ test.describe('Vue Upload Widgets', () => {
|
||||
await comfyPage.vueNodes.waitForNodes()
|
||||
})
|
||||
|
||||
test.skip('should hide canvas-only upload buttons', async ({ comfyPage }) => {
|
||||
test('should hide canvas-only upload buttons', async ({ comfyPage }) => {
|
||||
await comfyPage.setup()
|
||||
await comfyPage.loadWorkflow('widgets/all_load_widgets')
|
||||
await comfyPage.vueNodes.waitForNodes()
|
||||
|
||||
|
Before Width: | Height: | Size: 82 KiB After Width: | Height: | Size: 68 KiB |
@@ -7,7 +7,7 @@ test.beforeEach(async ({ comfyPage }) => {
|
||||
})
|
||||
|
||||
test.describe('Combo text widget', () => {
|
||||
test.skip('Truncates text when resized', async ({ comfyPage }) => {
|
||||
test('Truncates text when resized', async ({ comfyPage }) => {
|
||||
await comfyPage.resizeLoadCheckpointNode(0.2, 1)
|
||||
await expect(comfyPage.canvas).toHaveScreenshot(
|
||||
'load-checkpoint-resized-min-width.png'
|
||||
@@ -19,16 +19,14 @@ test.describe('Combo text widget', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test.skip("Doesn't truncate when space still available", async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
test("Doesn't truncate when space still available", async ({ comfyPage }) => {
|
||||
await comfyPage.resizeEmptyLatentNode(0.8, 0.8)
|
||||
await expect(comfyPage.canvas).toHaveScreenshot(
|
||||
'empty-latent-resized-80-percent.png'
|
||||
)
|
||||
})
|
||||
|
||||
test.skip('Can revert to full text', async ({ comfyPage }) => {
|
||||
test('Can revert to full text', async ({ comfyPage }) => {
|
||||
await comfyPage.resizeLoadCheckpointNode(0.8, 1, true)
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('resized-to-original.png')
|
||||
})
|
||||
@@ -82,7 +80,7 @@ test.describe('Combo text widget', () => {
|
||||
})
|
||||
|
||||
test.describe('Boolean widget', () => {
|
||||
test.skip('Can toggle', async ({ comfyPage }) => {
|
||||
test('Can toggle', async ({ comfyPage }) => {
|
||||
await comfyPage.loadWorkflow('widgets/boolean_widget')
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('boolean_widget.png')
|
||||
const node = (await comfyPage.getFirstNodeRef())!
|
||||
@@ -95,7 +93,7 @@ test.describe('Boolean widget', () => {
|
||||
})
|
||||
|
||||
test.describe('Slider widget', () => {
|
||||
test.skip('Can drag adjust value', async ({ comfyPage }) => {
|
||||
test('Can drag adjust value', async ({ comfyPage }) => {
|
||||
await comfyPage.loadWorkflow('inputs/simple_slider')
|
||||
await comfyPage.page.waitForTimeout(300)
|
||||
const node = (await comfyPage.getFirstNodeRef())!
|
||||
@@ -117,7 +115,7 @@ test.describe('Slider widget', () => {
|
||||
})
|
||||
|
||||
test.describe('Number widget', () => {
|
||||
test.skip('Can drag adjust value', async ({ comfyPage }) => {
|
||||
test('Can drag adjust value', async ({ comfyPage }) => {
|
||||
await comfyPage.loadWorkflow('widgets/seed_widget')
|
||||
await comfyPage.page.waitForTimeout(300)
|
||||
|
||||
@@ -139,7 +137,7 @@ test.describe('Number widget', () => {
|
||||
})
|
||||
|
||||
test.describe('Dynamic widget manipulation', () => {
|
||||
test.skip('Auto expand node when widget is added dynamically', async ({
|
||||
test('Auto expand node when widget is added dynamically', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.loadWorkflow('nodes/single_ksampler')
|
||||
@@ -155,12 +153,12 @@ test.describe('Dynamic widget manipulation', () => {
|
||||
})
|
||||
|
||||
test.describe('Image widget', () => {
|
||||
test.skip('Can load image', async ({ comfyPage }) => {
|
||||
test('Can load image', async ({ comfyPage }) => {
|
||||
await comfyPage.loadWorkflow('widgets/load_image_widget')
|
||||
await expect(comfyPage.canvas).toHaveScreenshot('load_image_widget.png')
|
||||
})
|
||||
|
||||
test.skip('Can drag and drop image', async ({ comfyPage }) => {
|
||||
test('Can drag and drop image', async ({ comfyPage }) => {
|
||||
await comfyPage.loadWorkflow('widgets/load_image_widget')
|
||||
|
||||
// Get position of the load image node
|
||||
@@ -184,7 +182,7 @@ test.describe('Image widget', () => {
|
||||
expect(filename).toBe('image32x32.webp')
|
||||
})
|
||||
|
||||
test.skip('Can change image by changing the filename combo value', async ({
|
||||
test('Can change image by changing the filename combo value', async ({
|
||||
comfyPage
|
||||
}) => {
|
||||
await comfyPage.loadWorkflow('widgets/load_image_widget')
|
||||
@@ -322,7 +320,7 @@ test.describe('Animated image widget', () => {
|
||||
})
|
||||
|
||||
test.describe('Load audio widget', () => {
|
||||
test.skip('Can load audio', async ({ comfyPage }) => {
|
||||
test('Can load audio', async ({ comfyPage }) => {
|
||||
await comfyPage.loadWorkflow('widgets/load_audio_widget')
|
||||
// Wait for the audio widget to be rendered in the DOM
|
||||
await comfyPage.page.waitForSelector('.comfy-audio', { state: 'attached' })
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// For more info, see https://github.com/storybookjs/eslint-plugin-storybook#configuration-flat-config-format
|
||||
import pluginJs from '@eslint/js'
|
||||
import pluginI18n from '@intlify/eslint-plugin-vue-i18n'
|
||||
import { createTypeScriptImportResolver } from 'eslint-import-resolver-typescript'
|
||||
import { importX } from 'eslint-plugin-import-x'
|
||||
import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'
|
||||
import storybook from 'eslint-plugin-storybook'
|
||||
@@ -24,17 +23,10 @@ const commonGlobals = {
|
||||
} as const
|
||||
|
||||
const settings = {
|
||||
'import-x/resolver-next': [
|
||||
createTypeScriptImportResolver({
|
||||
alwaysTryTypes: true,
|
||||
project: [
|
||||
'./tsconfig.json',
|
||||
'./apps/*/tsconfig.json',
|
||||
'./packages/*/tsconfig.json'
|
||||
],
|
||||
noWarnOnMultipleProjects: true
|
||||
})
|
||||
],
|
||||
'import/resolver': {
|
||||
typescript: true,
|
||||
node: true
|
||||
},
|
||||
tailwindcss: {
|
||||
config: `${import.meta.dirname}/packages/design-system/src/css/style.css`,
|
||||
functions: ['cn', 'clsx', 'tw']
|
||||
@@ -75,8 +67,11 @@ export default defineConfig([
|
||||
...commonParserOptions,
|
||||
projectService: {
|
||||
allowDefaultProject: [
|
||||
'vite.config.mts',
|
||||
'vite.electron.config.mts',
|
||||
'vite.types.config.mts'
|
||||
'vite.types.config.mts',
|
||||
'playwright.config.ts',
|
||||
'playwright.i18n.config.ts'
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -254,17 +249,5 @@ export default defineConfig([
|
||||
rules: {
|
||||
'no-console': 'off'
|
||||
}
|
||||
},
|
||||
{
|
||||
files: ['scripts/**/*.js'],
|
||||
languageOptions: {
|
||||
globals: {
|
||||
...globals.node
|
||||
}
|
||||
},
|
||||
rules: {
|
||||
'@typescript-eslint/no-floating-promises': 'off',
|
||||
'no-console': 'off'
|
||||
}
|
||||
}
|
||||
])
|
||||
|
||||
13
global.d.ts
vendored
@@ -5,19 +5,6 @@ declare const __ALGOLIA_APP_ID__: string
|
||||
declare const __ALGOLIA_API_KEY__: string
|
||||
declare const __USE_PROD_CONFIG__: boolean
|
||||
|
||||
interface Window {
|
||||
__CONFIG__: {
|
||||
mixpanel_token?: string
|
||||
subscription_required?: boolean
|
||||
server_health_alert?: {
|
||||
message: string
|
||||
tooltip?: string
|
||||
severity?: 'info' | 'warning' | 'error'
|
||||
badge?: string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface Navigator {
|
||||
/**
|
||||
* Used by the electron API. This is a WICG non-standard API, but is guaranteed to exist in Electron.
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>ComfyUI</title>
|
||||
<!-- All assets should be loaded from the root no matter the initial path -->
|
||||
<base href="/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
|
||||
<link rel="stylesheet" type="text/css" href="materialdesignicons.min.css" />
|
||||
<link rel="stylesheet" type="text/css" href="user.css" />
|
||||
@@ -14,10 +12,10 @@
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
<!-- Status bar style (eg. black or transparent) -->
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
||||
|
||||
|
||||
<link rel="manifest" href="manifest.json">
|
||||
</head>
|
||||
|
||||
|
||||
<body class="litegraph grid">
|
||||
<div id="vue-app"></div>
|
||||
<script type="module" src="src/main.ts"></script>
|
||||
|
||||
@@ -12,10 +12,6 @@ const config: KnipConfig = {
|
||||
],
|
||||
project: ['**/*.{js,ts,vue}', '*.{js,ts,mts}']
|
||||
},
|
||||
'apps/desktop-ui': {
|
||||
entry: ['src/main.ts', 'src/i18n.ts'],
|
||||
project: ['src/**/*.{js,ts,vue}', '*.{js,ts,mts}']
|
||||
},
|
||||
'packages/tailwind-utils': {
|
||||
project: ['src/**/*.{js,ts}']
|
||||
},
|
||||
@@ -24,7 +20,6 @@ const config: KnipConfig = {
|
||||
project: ['src/**/*.{js,ts}', '*.{js,ts,mts}']
|
||||
},
|
||||
'packages/registry-types': {
|
||||
entry: ['src/comfyRegistryTypes.ts'],
|
||||
project: ['src/**/*.{js,ts}']
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
export default {
|
||||
'./**/*.js': 'pnpm exec eslint --cache --fix',
|
||||
'./**/*.js': (stagedFiles) => formatAndEslint(stagedFiles),
|
||||
|
||||
'./**/*.{ts,tsx,vue,mts}': [
|
||||
'pnpm exec eslint --cache --fix',
|
||||
'pnpm exec prettier --cache --write'
|
||||
'./**/*.{ts,tsx,vue,mts}': (stagedFiles) => [
|
||||
...formatAndEslint(stagedFiles),
|
||||
'pnpm typecheck'
|
||||
]
|
||||
}
|
||||
|
||||
function formatAndEslint(fileNames) {
|
||||
return [
|
||||
`pnpm exec eslint --cache --fix ${fileNames.join(' ')}`,
|
||||
`pnpm exec prettier --cache --write ${fileNames.join(' ')}`
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
export default {
|
||||
'./**/*.js': (stagedFiles) => formatAndEslint(stagedFiles),
|
||||
|
||||
'./**/*.{ts,tsx,vue,mts}': (stagedFiles) => [
|
||||
...formatAndEslint(stagedFiles),
|
||||
'pnpm typecheck'
|
||||
]
|
||||
}
|
||||
|
||||
function formatAndEslint(fileNames) {
|
||||
return [
|
||||
`pnpm exec eslint --cache --fix ${fileNames.join(' ')}`,
|
||||
`pnpm exec prettier --cache --write ${fileNames.join(' ')}`
|
||||
]
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@comfyorg/comfyui-frontend",
|
||||
"private": true,
|
||||
"version": "1.29.1",
|
||||
"version": "1.29.0",
|
||||
"type": "module",
|
||||
"repository": "https://github.com/Comfy-Org/ComfyUI_frontend",
|
||||
"homepage": "https://comfy.org",
|
||||
@@ -85,7 +85,6 @@
|
||||
"jsdom": "catalog:",
|
||||
"knip": "catalog:",
|
||||
"lint-staged": "catalog:",
|
||||
"mixpanel-browser": "catalog:",
|
||||
"nx": "catalog:",
|
||||
"postcss-html": "catalog:",
|
||||
"prettier": "catalog:",
|
||||
|
||||
@@ -85,10 +85,6 @@
|
||||
--color-bypass: #6a246a;
|
||||
--color-error: #962a2a;
|
||||
|
||||
--color-comfy-menu-secondary: var(--comfy-menu-secondary-bg);
|
||||
--text-xxxs: 0.5625rem;
|
||||
--text-xxxs--line-height: calc(1 / 0.5625);
|
||||
|
||||
--color-blue-selection: rgb(from var(--color-blue-100) r g b / 0.3);
|
||||
--color-node-hover-100: rgb(from var(--color-charcoal-100) r g b/ 0.15);
|
||||
--color-node-hover-200: rgb(from var(--color-charcoal-100) r g b/ 0.1);
|
||||
@@ -133,7 +129,6 @@
|
||||
|
||||
/* --- */
|
||||
|
||||
--accent-primary: var(--color-charcoal-700);
|
||||
--backdrop: var(--color-white);
|
||||
--dialog-surface: var(--color-neutral-200);
|
||||
--node-component-border: var(--color-gray-400);
|
||||
@@ -159,20 +154,13 @@
|
||||
from var(--color-zinc-500) r g b / 10%
|
||||
);
|
||||
--node-component-widget-skeleton-surface: var(--color-zinc-300);
|
||||
--node-stroke: var(--color-gray-400);
|
||||
--node-stroke-selected: var(--color-accent-primary);
|
||||
--node-stroke-error: var(--color-error);
|
||||
--node-stroke-executing: var(--color-blue-100);
|
||||
--node-stroke: var(--color-stone-100);
|
||||
}
|
||||
|
||||
.dark-theme {
|
||||
--accent-primary: var(--color-pure-white);
|
||||
--backdrop: var(--color-neutral-900);
|
||||
--dialog-surface: var(--color-neutral-700);
|
||||
--node-component-border: var(--color-stone-200);
|
||||
--node-component-border-error: var(--color-danger-100);
|
||||
--node-component-border-executing: var(--color-blue-500);
|
||||
--node-component-border-selected: var(--color-charcoal-200);
|
||||
--node-component-header-icon: var(--color-slate-300);
|
||||
--node-component-header-surface: var(--color-charcoal-800);
|
||||
--node-component-outline: var(--color-white);
|
||||
@@ -188,10 +176,7 @@
|
||||
--node-component-tooltip-border: var(--color-slate-300);
|
||||
--node-component-tooltip-surface: var(--color-charcoal-800);
|
||||
--node-component-widget-skeleton-surface: var(--color-zinc-800);
|
||||
--node-stroke: var(--color-stone-200);
|
||||
--node-stroke-selected: var(--color-pure-white);
|
||||
--node-stroke-error: var(--color-error);
|
||||
--node-stroke-executing: var(--color-blue-100);
|
||||
--node-stroke: var(--color-slate-100);
|
||||
}
|
||||
|
||||
@theme inline {
|
||||
@@ -229,9 +214,6 @@
|
||||
--node-component-widget-skeleton-surface
|
||||
);
|
||||
--color-node-stroke: var(--node-stroke);
|
||||
--color-node-stroke-selected: var(--node-stroke-selected);
|
||||
--color-node-stroke-error: var(--node-stroke-error);
|
||||
--color-node-stroke-executing: var(--node-stroke-executing);
|
||||
}
|
||||
|
||||
@custom-variant dark-theme {
|
||||
@@ -1065,7 +1047,7 @@ audio.comfy-audio.empty-audio-widget {
|
||||
}
|
||||
|
||||
.isLOD .lg-node-header {
|
||||
border-radius: 0;
|
||||
border-radius: 0px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="none"><path fill="currentColor" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.3" d="m4 2 9.333 6L4 14V2Z"/></svg>
|
||||
|
Before Width: | Height: | Size: 221 B |
@@ -14,9 +14,9 @@
|
||||
"./networkUtil": "./src/networkUtil.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^1.11.0"
|
||||
"axios": "catalog:"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5.9.2"
|
||||
"typescript": "catalog:"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ export function formatSize(value?: number) {
|
||||
* - filename: 'file'
|
||||
* - suffix: 'txt'
|
||||
*/
|
||||
export function getFilenameDetails(fullFilename: string) {
|
||||
function getFilenameDetails(fullFilename: string) {
|
||||
if (fullFilename.includes('.')) {
|
||||
return {
|
||||
filename: fullFilename.split('.').slice(0, -1).join('.'),
|
||||
@@ -451,26 +451,3 @@ export function stringToLocale(locale: string): SupportedLocale {
|
||||
? (locale as SupportedLocale)
|
||||
: 'en'
|
||||
}
|
||||
|
||||
export function formatDuration(milliseconds: number): string {
|
||||
if (!milliseconds || milliseconds < 0) return '0s'
|
||||
|
||||
const totalSeconds = Math.floor(milliseconds / 1000)
|
||||
const hours = Math.floor(totalSeconds / 3600)
|
||||
const minutes = Math.floor((totalSeconds % 3600) / 60)
|
||||
const remainingSeconds = Math.floor(totalSeconds % 60)
|
||||
|
||||
const parts: string[] = []
|
||||
|
||||
if (hours > 0) {
|
||||
parts.push(`${hours}h`)
|
||||
}
|
||||
if (minutes > 0) {
|
||||
parts.push(`${minutes}m`)
|
||||
}
|
||||
if (remainingSeconds > 0 || parts.length === 0) {
|
||||
parts.push(`${remainingSeconds}s`)
|
||||
}
|
||||
|
||||
return parts.join(' ')
|
||||
}
|
||||
|
||||
196
pnpm-lock.yaml
generated
@@ -183,9 +183,6 @@ catalogs:
|
||||
lint-staged:
|
||||
specifier: ^15.2.7
|
||||
version: 15.2.7
|
||||
mixpanel-browser:
|
||||
specifier: ^2.71.0
|
||||
version: 2.71.0
|
||||
nx:
|
||||
specifier: 21.4.1
|
||||
version: 21.4.1
|
||||
@@ -197,7 +194,7 @@ catalogs:
|
||||
version: 1.8.0
|
||||
prettier:
|
||||
specifier: ^3.3.2
|
||||
version: 3.6.2
|
||||
version: 3.3.2
|
||||
primeicons:
|
||||
specifier: ^7.0.0
|
||||
version: 7.0.0
|
||||
@@ -254,7 +251,7 @@ catalogs:
|
||||
version: 3.5.13
|
||||
vue-component-type-helpers:
|
||||
specifier: ^3.0.7
|
||||
version: 3.1.1
|
||||
version: 3.1.0
|
||||
vue-eslint-parser:
|
||||
specifier: ^10.2.0
|
||||
version: 10.2.0
|
||||
@@ -476,7 +473,7 @@ importers:
|
||||
version: 21.4.1(@babel/traverse@7.28.3)(@playwright/test@1.52.0)(@zkochan/js-yaml@0.0.7)(eslint@9.35.0(jiti@2.4.2))(nx@21.4.1)(typescript@5.9.2)
|
||||
'@nx/storybook':
|
||||
specifier: 'catalog:'
|
||||
version: 21.4.1(@babel/traverse@7.28.3)(@zkochan/js-yaml@0.0.7)(eslint@9.35.0(jiti@2.4.2))(nx@21.4.1)(storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.6.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)))(typescript@5.9.2)
|
||||
version: 21.4.1(@babel/traverse@7.28.3)(@zkochan/js-yaml@0.0.7)(eslint@9.35.0(jiti@2.4.2))(nx@21.4.1)(storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.3.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)))(typescript@5.9.2)
|
||||
'@nx/vite':
|
||||
specifier: 'catalog:'
|
||||
version: 21.4.1(@babel/traverse@7.28.3)(nx@21.4.1)(typescript@5.9.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2))(vitest@3.2.4)
|
||||
@@ -488,19 +485,19 @@ importers:
|
||||
version: 1.52.0
|
||||
'@storybook/addon-docs':
|
||||
specifier: 'catalog:'
|
||||
version: 9.1.1(@types/react@19.1.9)(storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.6.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)))
|
||||
version: 9.1.1(@types/react@19.1.9)(storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.3.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)))
|
||||
'@storybook/vue3':
|
||||
specifier: 'catalog:'
|
||||
version: 9.1.1(storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.6.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)))(vue@3.5.13(typescript@5.9.2))
|
||||
version: 9.1.1(storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.3.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)))(vue@3.5.13(typescript@5.9.2))
|
||||
'@storybook/vue3-vite':
|
||||
specifier: 'catalog:'
|
||||
version: 9.1.1(storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.6.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)))(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2))(vue@3.5.13(typescript@5.9.2))
|
||||
version: 9.1.1(storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.3.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)))(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2))(vue@3.5.13(typescript@5.9.2))
|
||||
'@tailwindcss/vite':
|
||||
specifier: 'catalog:'
|
||||
version: 4.1.12(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2))
|
||||
'@trivago/prettier-plugin-sort-imports':
|
||||
specifier: 'catalog:'
|
||||
version: 5.2.2(@vue/compiler-sfc@3.5.13)(prettier@3.6.2)
|
||||
version: 5.2.2(@vue/compiler-sfc@3.5.13)(prettier@3.3.2)
|
||||
'@types/eslint-plugin-tailwindcss':
|
||||
specifier: 'catalog:'
|
||||
version: 3.17.0
|
||||
@@ -548,10 +545,10 @@ importers:
|
||||
version: 4.16.1(@typescript-eslint/utils@8.44.0(eslint@9.35.0(jiti@2.4.2))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint@9.35.0(jiti@2.4.2))
|
||||
eslint-plugin-prettier:
|
||||
specifier: 'catalog:'
|
||||
version: 5.5.4(eslint-config-prettier@10.1.8(eslint@9.35.0(jiti@2.4.2)))(eslint@9.35.0(jiti@2.4.2))(prettier@3.6.2)
|
||||
version: 5.5.4(eslint-config-prettier@10.1.8(eslint@9.35.0(jiti@2.4.2)))(eslint@9.35.0(jiti@2.4.2))(prettier@3.3.2)
|
||||
eslint-plugin-storybook:
|
||||
specifier: 'catalog:'
|
||||
version: 9.1.6(eslint@9.35.0(jiti@2.4.2))(storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.6.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)))(typescript@5.9.2)
|
||||
version: 9.1.6(eslint@9.35.0(jiti@2.4.2))(storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.3.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)))(typescript@5.9.2)
|
||||
eslint-plugin-tailwindcss:
|
||||
specifier: 'catalog:'
|
||||
version: 4.0.0-beta.0(tailwindcss@4.1.12)
|
||||
@@ -585,9 +582,6 @@ importers:
|
||||
lint-staged:
|
||||
specifier: 'catalog:'
|
||||
version: 15.2.7
|
||||
mixpanel-browser:
|
||||
specifier: 'catalog:'
|
||||
version: 2.71.0
|
||||
nx:
|
||||
specifier: 'catalog:'
|
||||
version: 21.4.1
|
||||
@@ -596,10 +590,10 @@ importers:
|
||||
version: 1.8.0
|
||||
prettier:
|
||||
specifier: 'catalog:'
|
||||
version: 3.6.2
|
||||
version: 3.3.2
|
||||
storybook:
|
||||
specifier: 'catalog:'
|
||||
version: 9.1.6(@testing-library/dom@10.4.1)(prettier@3.6.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2))
|
||||
version: 9.1.6(@testing-library/dom@10.4.1)(prettier@3.3.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2))
|
||||
stylelint:
|
||||
specifier: 'catalog:'
|
||||
version: 16.24.0(typescript@5.9.2)
|
||||
@@ -647,7 +641,7 @@ importers:
|
||||
version: 3.2.4(@types/debug@4.1.12)(@types/node@20.14.10)(@vitest/ui@3.2.4)(happy-dom@15.11.0)(jsdom@26.1.0)(lightningcss@1.30.1)(terser@5.39.2)
|
||||
vue-component-type-helpers:
|
||||
specifier: 'catalog:'
|
||||
version: 3.1.1
|
||||
version: 3.1.0
|
||||
vue-eslint-parser:
|
||||
specifier: 'catalog:'
|
||||
version: 10.2.0(eslint@9.35.0(jiti@2.4.2))
|
||||
@@ -746,11 +740,11 @@ importers:
|
||||
packages/shared-frontend-utils:
|
||||
dependencies:
|
||||
axios:
|
||||
specifier: ^1.11.0
|
||||
specifier: 'catalog:'
|
||||
version: 1.11.0
|
||||
devDependencies:
|
||||
typescript:
|
||||
specifier: ^5.9.2
|
||||
specifier: 'catalog:'
|
||||
version: 5.9.2
|
||||
|
||||
packages/tailwind-utils:
|
||||
@@ -2183,21 +2177,6 @@ packages:
|
||||
'@microsoft/tsdoc@0.15.1':
|
||||
resolution: {integrity: sha512-4aErSrCR/On/e5G2hDP0wjooqDdauzEbIq8hIkIe5pXV0rtWJZvdCEKL0ykZxex+IxIwBp0eGeV48hQN07dXtw==}
|
||||
|
||||
'@mixpanel/rrdom@2.0.0-alpha.18.2':
|
||||
resolution: {integrity: sha512-vX/tbnS14ZzzatC7vOyvAm9tOLU8tof0BuppBlphzEx1YHTSw8DQiAmyAc0AmXidchLV0W+cUHV/WsehPLh2hQ==}
|
||||
|
||||
'@mixpanel/rrweb-snapshot@2.0.0-alpha.18.2':
|
||||
resolution: {integrity: sha512-2kSnjZZ3QZ9zOz/isOt8s54mXUUDgXk/u0eEi/rE0xBWDeuA0NHrBcqiMc+w4F/yWWUpo5F5zcuPeYpc6ufAsw==}
|
||||
|
||||
'@mixpanel/rrweb-types@2.0.0-alpha.18.2':
|
||||
resolution: {integrity: sha512-ucIYe1mfJ2UksvXW+d3bOySTB2/0yUSqQJlUydvbBz6OO2Bhq3nJHyLXV9ExkgUMZm1ZyDcvvmNUd1+5tAXlpA==}
|
||||
|
||||
'@mixpanel/rrweb-utils@2.0.0-alpha.18.2':
|
||||
resolution: {integrity: sha512-OomKIB6GTx5xvCLJ7iic2khT/t/tnCJUex13aEqsbSqIT/UzUUsqf+LTrgUK5ex+f6odmkCNjre2y5jvpNqn+g==}
|
||||
|
||||
'@mixpanel/rrweb@2.0.0-alpha.18.2':
|
||||
resolution: {integrity: sha512-J3dVTEu6Z4p8di7y9KKvUooNuBjX97DdG6XGWoPEPi07A9512h9M8MEtvlY3mK0PGfuC0Mz5Pv/Ws6gjGYfKQg==}
|
||||
|
||||
'@napi-rs/wasm-runtime@0.2.12':
|
||||
resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==}
|
||||
|
||||
@@ -3017,9 +2996,6 @@ packages:
|
||||
'@types/chai@5.2.2':
|
||||
resolution: {integrity: sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==}
|
||||
|
||||
'@types/css-font-loading-module@0.0.7':
|
||||
resolution: {integrity: sha512-nl09VhutdjINdWyXxHWN/w9zlNCfr60JUqJbd24YXUuCwgeL0TpFSdElCwb6cxfB6ybE19Gjj4g0jsgkXxKv1Q==}
|
||||
|
||||
'@types/debug@4.1.12':
|
||||
resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==}
|
||||
|
||||
@@ -3518,9 +3494,6 @@ packages:
|
||||
'@webgpu/types@0.1.51':
|
||||
resolution: {integrity: sha512-ktR3u64NPjwIViNCck+z9QeyN0iPkQCUOQ07ZCV1RzlkfP+olLTeEZ95O1QHS+v4w9vJeY9xj/uJuSphsHy5rQ==}
|
||||
|
||||
'@xstate/fsm@1.6.5':
|
||||
resolution: {integrity: sha512-b5o1I6aLNeYlU/3CPlj/Z91ybk1gUsKT+5NAJI+2W4UjvS5KLG28K9v5UvNoFVjHV8PajVZ00RH3vnjyQO7ZAw==}
|
||||
|
||||
'@xterm/addon-fit@0.10.0':
|
||||
resolution: {integrity: sha512-UFYkDm4HUahf2lnEyHvio51TNGiLK66mqP2JoATy7hRZeXaGMRDr00JiSF7m63vR5WKATF605yEggJKsw0JpMQ==}
|
||||
peerDependencies:
|
||||
@@ -3803,10 +3776,6 @@ packages:
|
||||
balanced-match@2.0.0:
|
||||
resolution: {integrity: sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==}
|
||||
|
||||
base64-arraybuffer@1.0.2:
|
||||
resolution: {integrity: sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==}
|
||||
engines: {node: '>= 0.6.0'}
|
||||
|
||||
base64-js@1.5.1:
|
||||
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
|
||||
|
||||
@@ -4917,6 +4886,9 @@ packages:
|
||||
get-tsconfig@4.10.1:
|
||||
resolution: {integrity: sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==}
|
||||
|
||||
get-tsconfig@4.7.5:
|
||||
resolution: {integrity: sha512-ZCuZCnlqNzjb4QprAzXKdpp/gh6KTxSJuw3IBsPnV/7fV4NxC9ckB+vPTt8w7fJA0TaSD7c55BR47JD6MEDyDw==}
|
||||
|
||||
glob-parent@5.1.2:
|
||||
resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
|
||||
engines: {node: '>= 6'}
|
||||
@@ -5994,9 +5966,6 @@ packages:
|
||||
mitt@3.0.1:
|
||||
resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==}
|
||||
|
||||
mixpanel-browser@2.71.0:
|
||||
resolution: {integrity: sha512-jKmDXe68/oQFgk/9ns9Z36bA0CJ31PH8Y77XTLLGfJvhsUPbvu+7Se9e281NejZF6+OMqx7cE+zFxToozYyNrA==}
|
||||
|
||||
mkdirp@3.0.1:
|
||||
resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==}
|
||||
engines: {node: '>=10'}
|
||||
@@ -6024,6 +5993,11 @@ packages:
|
||||
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
|
||||
hasBin: true
|
||||
|
||||
nanoid@3.3.8:
|
||||
resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==}
|
||||
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
|
||||
hasBin: true
|
||||
|
||||
nanoid@5.1.5:
|
||||
resolution: {integrity: sha512-Ir/+ZpE9fDsNH0hQ3C68uyThDXzYcim2EqcZ8zn8Chtt1iylPT9xXJB0kPCnqzgcEGikO9RxSrh63MsmVCU7Fw==}
|
||||
engines: {node: ^18 || >=20}
|
||||
@@ -6371,6 +6345,10 @@ packages:
|
||||
postcss-value-parser@4.2.0:
|
||||
resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}
|
||||
|
||||
postcss@8.5.1:
|
||||
resolution: {integrity: sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==}
|
||||
engines: {node: ^10 || ^12 || >=14}
|
||||
|
||||
postcss@8.5.6:
|
||||
resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==}
|
||||
engines: {node: ^10 || ^12 || >=14}
|
||||
@@ -6383,8 +6361,8 @@ packages:
|
||||
resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==}
|
||||
engines: {node: '>=6.0.0'}
|
||||
|
||||
prettier@3.6.2:
|
||||
resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==}
|
||||
prettier@3.3.2:
|
||||
resolution: {integrity: sha512-rAVeHYMcv8ATV5d508CFdn+8/pHPpXeIid1DdrPwXnaAdH7cqjVbpJaT5eq4yRAFU/lsbwYwSF/n5iNrdJHPQA==}
|
||||
engines: {node: '>=14'}
|
||||
hasBin: true
|
||||
|
||||
@@ -7472,6 +7450,9 @@ packages:
|
||||
vue-component-type-helpers@2.2.12:
|
||||
resolution: {integrity: sha512-YbGqHZ5/eW4SnkPNR44mKVc6ZKQoRs/Rux1sxC6rdwXb4qpbOSYfDr9DsTHolOTGmIKgM9j141mZbBeg05R1pw==}
|
||||
|
||||
vue-component-type-helpers@3.1.0:
|
||||
resolution: {integrity: sha512-cC1pYNRZkSS1iCvdlaMbbg2sjDwxX098FucEjtz9Yig73zYjWzQsnMe5M9H8dRNv55hAIDGUI29hF2BEUA4FMQ==}
|
||||
|
||||
vue-component-type-helpers@3.1.1:
|
||||
resolution: {integrity: sha512-B0kHv7qX6E7+kdc5nsaqjdGZ1KwNKSUQDWGy7XkTYT7wFsOpkEyaJ1Vq79TjwrrtuLRgizrTV7PPuC4rRQo+vw==}
|
||||
|
||||
@@ -9488,29 +9469,6 @@ snapshots:
|
||||
|
||||
'@microsoft/tsdoc@0.15.1': {}
|
||||
|
||||
'@mixpanel/rrdom@2.0.0-alpha.18.2':
|
||||
dependencies:
|
||||
'@mixpanel/rrweb-snapshot': 2.0.0-alpha.18.2
|
||||
|
||||
'@mixpanel/rrweb-snapshot@2.0.0-alpha.18.2':
|
||||
dependencies:
|
||||
postcss: 8.5.6
|
||||
|
||||
'@mixpanel/rrweb-types@2.0.0-alpha.18.2': {}
|
||||
|
||||
'@mixpanel/rrweb-utils@2.0.0-alpha.18.2': {}
|
||||
|
||||
'@mixpanel/rrweb@2.0.0-alpha.18.2':
|
||||
dependencies:
|
||||
'@mixpanel/rrdom': 2.0.0-alpha.18.2
|
||||
'@mixpanel/rrweb-snapshot': 2.0.0-alpha.18.2
|
||||
'@mixpanel/rrweb-types': 2.0.0-alpha.18.2
|
||||
'@mixpanel/rrweb-utils': 2.0.0-alpha.18.2
|
||||
'@types/css-font-loading-module': 0.0.7
|
||||
'@xstate/fsm': 1.6.5
|
||||
base64-arraybuffer: 1.0.2
|
||||
mitt: 3.0.1
|
||||
|
||||
'@napi-rs/wasm-runtime@0.2.12':
|
||||
dependencies:
|
||||
'@emnapi/core': 1.4.5
|
||||
@@ -9687,7 +9645,7 @@ snapshots:
|
||||
- typescript
|
||||
- verdaccio
|
||||
|
||||
'@nx/storybook@21.4.1(@babel/traverse@7.28.3)(@zkochan/js-yaml@0.0.7)(eslint@9.35.0(jiti@2.4.2))(nx@21.4.1)(storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.6.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)))(typescript@5.9.2)':
|
||||
'@nx/storybook@21.4.1(@babel/traverse@7.28.3)(@zkochan/js-yaml@0.0.7)(eslint@9.35.0(jiti@2.4.2))(nx@21.4.1)(storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.3.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)))(typescript@5.9.2)':
|
||||
dependencies:
|
||||
'@nx/cypress': 21.4.1(@babel/traverse@7.28.3)(@zkochan/js-yaml@0.0.7)(eslint@9.35.0(jiti@2.4.2))(nx@21.4.1)(typescript@5.9.2)
|
||||
'@nx/devkit': 21.4.1(nx@21.4.1)
|
||||
@@ -9695,7 +9653,7 @@ snapshots:
|
||||
'@nx/js': 21.4.1(@babel/traverse@7.28.3)(nx@21.4.1)
|
||||
'@phenomnomnominal/tsquery': 5.0.1(typescript@5.9.2)
|
||||
semver: 7.7.2
|
||||
storybook: 9.1.6(@testing-library/dom@10.4.1)(prettier@3.6.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2))
|
||||
storybook: 9.1.6(@testing-library/dom@10.4.1)(prettier@3.3.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2))
|
||||
tslib: 2.8.1
|
||||
transitivePeerDependencies:
|
||||
- '@babel/traverse'
|
||||
@@ -10046,29 +10004,29 @@ snapshots:
|
||||
|
||||
'@sindresorhus/merge-streams@4.0.0': {}
|
||||
|
||||
'@storybook/addon-docs@9.1.1(@types/react@19.1.9)(storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.6.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)))':
|
||||
'@storybook/addon-docs@9.1.1(@types/react@19.1.9)(storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.3.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)))':
|
||||
dependencies:
|
||||
'@mdx-js/react': 3.1.0(@types/react@19.1.9)(react@19.1.1)
|
||||
'@storybook/csf-plugin': 9.1.1(storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.6.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)))
|
||||
'@storybook/csf-plugin': 9.1.1(storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.3.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)))
|
||||
'@storybook/icons': 1.4.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||
'@storybook/react-dom-shim': 9.1.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.6.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)))
|
||||
'@storybook/react-dom-shim': 9.1.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.3.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)))
|
||||
react: 19.1.1
|
||||
react-dom: 19.1.1(react@19.1.1)
|
||||
storybook: 9.1.6(@testing-library/dom@10.4.1)(prettier@3.6.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2))
|
||||
storybook: 9.1.6(@testing-library/dom@10.4.1)(prettier@3.3.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2))
|
||||
ts-dedent: 2.2.0
|
||||
transitivePeerDependencies:
|
||||
- '@types/react'
|
||||
|
||||
'@storybook/builder-vite@9.1.1(storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.6.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)))(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2))':
|
||||
'@storybook/builder-vite@9.1.1(storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.3.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)))(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2))':
|
||||
dependencies:
|
||||
'@storybook/csf-plugin': 9.1.1(storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.6.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)))
|
||||
storybook: 9.1.6(@testing-library/dom@10.4.1)(prettier@3.6.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2))
|
||||
'@storybook/csf-plugin': 9.1.1(storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.3.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)))
|
||||
storybook: 9.1.6(@testing-library/dom@10.4.1)(prettier@3.3.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2))
|
||||
ts-dedent: 2.2.0
|
||||
vite: 5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)
|
||||
|
||||
'@storybook/csf-plugin@9.1.1(storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.6.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)))':
|
||||
'@storybook/csf-plugin@9.1.1(storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.3.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)))':
|
||||
dependencies:
|
||||
storybook: 9.1.6(@testing-library/dom@10.4.1)(prettier@3.6.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2))
|
||||
storybook: 9.1.6(@testing-library/dom@10.4.1)(prettier@3.3.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2))
|
||||
unplugin: 1.16.1
|
||||
|
||||
'@storybook/global@5.0.0': {}
|
||||
@@ -10078,19 +10036,19 @@ snapshots:
|
||||
react: 19.1.1
|
||||
react-dom: 19.1.1(react@19.1.1)
|
||||
|
||||
'@storybook/react-dom-shim@9.1.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.6.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)))':
|
||||
'@storybook/react-dom-shim@9.1.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.3.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)))':
|
||||
dependencies:
|
||||
react: 19.1.1
|
||||
react-dom: 19.1.1(react@19.1.1)
|
||||
storybook: 9.1.6(@testing-library/dom@10.4.1)(prettier@3.6.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2))
|
||||
storybook: 9.1.6(@testing-library/dom@10.4.1)(prettier@3.3.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2))
|
||||
|
||||
'@storybook/vue3-vite@9.1.1(storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.6.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)))(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2))(vue@3.5.13(typescript@5.9.2))':
|
||||
'@storybook/vue3-vite@9.1.1(storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.3.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)))(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2))(vue@3.5.13(typescript@5.9.2))':
|
||||
dependencies:
|
||||
'@storybook/builder-vite': 9.1.1(storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.6.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)))(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2))
|
||||
'@storybook/vue3': 9.1.1(storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.6.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)))(vue@3.5.13(typescript@5.9.2))
|
||||
'@storybook/builder-vite': 9.1.1(storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.3.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)))(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2))
|
||||
'@storybook/vue3': 9.1.1(storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.3.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)))(vue@3.5.13(typescript@5.9.2))
|
||||
find-package-json: 1.2.0
|
||||
magic-string: 0.30.19
|
||||
storybook: 9.1.6(@testing-library/dom@10.4.1)(prettier@3.6.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2))
|
||||
storybook: 9.1.6(@testing-library/dom@10.4.1)(prettier@3.3.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2))
|
||||
typescript: 5.9.2
|
||||
vite: 5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)
|
||||
vue-component-meta: 2.2.12(typescript@5.9.2)
|
||||
@@ -10098,10 +10056,10 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- vue
|
||||
|
||||
'@storybook/vue3@9.1.1(storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.6.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)))(vue@3.5.13(typescript@5.9.2))':
|
||||
'@storybook/vue3@9.1.1(storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.3.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)))(vue@3.5.13(typescript@5.9.2))':
|
||||
dependencies:
|
||||
'@storybook/global': 5.0.0
|
||||
storybook: 9.1.6(@testing-library/dom@10.4.1)(prettier@3.6.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2))
|
||||
storybook: 9.1.6(@testing-library/dom@10.4.1)(prettier@3.3.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2))
|
||||
type-fest: 2.19.0
|
||||
vue: 3.5.13(typescript@5.9.2)
|
||||
vue-component-type-helpers: 3.1.1
|
||||
@@ -10366,7 +10324,7 @@ snapshots:
|
||||
'@tiptap/extension-text-style': 2.10.4(@tiptap/core@2.10.4(@tiptap/pm@2.10.4))
|
||||
'@tiptap/pm': 2.10.4
|
||||
|
||||
'@trivago/prettier-plugin-sort-imports@5.2.2(@vue/compiler-sfc@3.5.13)(prettier@3.6.2)':
|
||||
'@trivago/prettier-plugin-sort-imports@5.2.2(@vue/compiler-sfc@3.5.13)(prettier@3.3.2)':
|
||||
dependencies:
|
||||
'@babel/generator': 7.28.3
|
||||
'@babel/parser': 7.28.4
|
||||
@@ -10374,7 +10332,7 @@ snapshots:
|
||||
'@babel/types': 7.28.4
|
||||
javascript-natural-sort: 0.7.1
|
||||
lodash: 4.17.21
|
||||
prettier: 3.6.2
|
||||
prettier: 3.3.2
|
||||
optionalDependencies:
|
||||
'@vue/compiler-sfc': 3.5.13
|
||||
transitivePeerDependencies:
|
||||
@@ -10399,8 +10357,6 @@ snapshots:
|
||||
dependencies:
|
||||
'@types/deep-eql': 4.0.2
|
||||
|
||||
'@types/css-font-loading-module@0.0.7': {}
|
||||
|
||||
'@types/debug@4.1.12':
|
||||
dependencies:
|
||||
'@types/ms': 2.1.0
|
||||
@@ -10994,8 +10950,6 @@ snapshots:
|
||||
|
||||
'@webgpu/types@0.1.51': {}
|
||||
|
||||
'@xstate/fsm@1.6.5': {}
|
||||
|
||||
'@xterm/addon-fit@0.10.0(@xterm/xterm@5.5.0)':
|
||||
dependencies:
|
||||
'@xterm/xterm': 5.5.0
|
||||
@@ -11312,8 +11266,6 @@ snapshots:
|
||||
|
||||
balanced-match@2.0.0: {}
|
||||
|
||||
base64-arraybuffer@1.0.2: {}
|
||||
|
||||
base64-js@1.5.1: {}
|
||||
|
||||
better-opn@3.0.2:
|
||||
@@ -12158,20 +12110,20 @@ snapshots:
|
||||
- supports-color
|
||||
optional: true
|
||||
|
||||
eslint-plugin-prettier@5.5.4(eslint-config-prettier@10.1.8(eslint@9.35.0(jiti@2.4.2)))(eslint@9.35.0(jiti@2.4.2))(prettier@3.6.2):
|
||||
eslint-plugin-prettier@5.5.4(eslint-config-prettier@10.1.8(eslint@9.35.0(jiti@2.4.2)))(eslint@9.35.0(jiti@2.4.2))(prettier@3.3.2):
|
||||
dependencies:
|
||||
eslint: 9.35.0(jiti@2.4.2)
|
||||
prettier: 3.6.2
|
||||
prettier: 3.3.2
|
||||
prettier-linter-helpers: 1.0.0
|
||||
synckit: 0.11.11
|
||||
optionalDependencies:
|
||||
eslint-config-prettier: 10.1.8(eslint@9.35.0(jiti@2.4.2))
|
||||
|
||||
eslint-plugin-storybook@9.1.6(eslint@9.35.0(jiti@2.4.2))(storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.6.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)))(typescript@5.9.2):
|
||||
eslint-plugin-storybook@9.1.6(eslint@9.35.0(jiti@2.4.2))(storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.3.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)))(typescript@5.9.2):
|
||||
dependencies:
|
||||
'@typescript-eslint/utils': 8.44.0(eslint@9.35.0(jiti@2.4.2))(typescript@5.9.2)
|
||||
eslint: 9.35.0(jiti@2.4.2)
|
||||
storybook: 9.1.6(@testing-library/dom@10.4.1)(prettier@3.6.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2))
|
||||
storybook: 9.1.6(@testing-library/dom@10.4.1)(prettier@3.3.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2))
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
- typescript
|
||||
@@ -12610,6 +12562,10 @@ snapshots:
|
||||
dependencies:
|
||||
resolve-pkg-maps: 1.0.0
|
||||
|
||||
get-tsconfig@4.7.5:
|
||||
dependencies:
|
||||
resolve-pkg-maps: 1.0.0
|
||||
|
||||
glob-parent@5.1.2:
|
||||
dependencies:
|
||||
is-glob: 4.0.3
|
||||
@@ -13882,10 +13838,6 @@ snapshots:
|
||||
|
||||
mitt@3.0.1: {}
|
||||
|
||||
mixpanel-browser@2.71.0:
|
||||
dependencies:
|
||||
'@mixpanel/rrweb': 2.0.0-alpha.18.2
|
||||
|
||||
mkdirp@3.0.1: {}
|
||||
|
||||
mlly@1.8.0:
|
||||
@@ -13908,6 +13860,8 @@ snapshots:
|
||||
|
||||
nanoid@3.3.11: {}
|
||||
|
||||
nanoid@3.3.8: {}
|
||||
|
||||
nanoid@5.1.5: {}
|
||||
|
||||
napi-postinstall@0.3.3: {}
|
||||
@@ -14287,14 +14241,14 @@ snapshots:
|
||||
dependencies:
|
||||
htmlparser2: 8.0.2
|
||||
js-tokens: 9.0.1
|
||||
postcss: 8.5.6
|
||||
postcss-safe-parser: 6.0.0(postcss@8.5.6)
|
||||
postcss: 8.5.1
|
||||
postcss-safe-parser: 6.0.0(postcss@8.5.1)
|
||||
|
||||
postcss-resolve-nested-selector@0.1.6: {}
|
||||
|
||||
postcss-safe-parser@6.0.0(postcss@8.5.6):
|
||||
postcss-safe-parser@6.0.0(postcss@8.5.1):
|
||||
dependencies:
|
||||
postcss: 8.5.6
|
||||
postcss: 8.5.1
|
||||
|
||||
postcss-safe-parser@7.0.1(postcss@8.5.6):
|
||||
dependencies:
|
||||
@@ -14312,6 +14266,12 @@ snapshots:
|
||||
|
||||
postcss-value-parser@4.2.0: {}
|
||||
|
||||
postcss@8.5.1:
|
||||
dependencies:
|
||||
nanoid: 3.3.8
|
||||
picocolors: 1.1.1
|
||||
source-map-js: 1.2.1
|
||||
|
||||
postcss@8.5.6:
|
||||
dependencies:
|
||||
nanoid: 3.3.11
|
||||
@@ -14324,7 +14284,7 @@ snapshots:
|
||||
dependencies:
|
||||
fast-diff: 1.3.0
|
||||
|
||||
prettier@3.6.2: {}
|
||||
prettier@3.3.2: {}
|
||||
|
||||
pretty-format@27.5.1:
|
||||
dependencies:
|
||||
@@ -14990,7 +14950,7 @@ snapshots:
|
||||
internal-slot: 1.1.0
|
||||
optional: true
|
||||
|
||||
storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.6.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)):
|
||||
storybook@9.1.6(@testing-library/dom@10.4.1)(prettier@3.3.2)(vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2)):
|
||||
dependencies:
|
||||
'@storybook/global': 5.0.0
|
||||
'@testing-library/jest-dom': 6.6.4
|
||||
@@ -15005,7 +14965,7 @@ snapshots:
|
||||
semver: 7.7.2
|
||||
ws: 8.18.3
|
||||
optionalDependencies:
|
||||
prettier: 3.6.2
|
||||
prettier: 3.3.2
|
||||
transitivePeerDependencies:
|
||||
- '@testing-library/dom'
|
||||
- bufferutil
|
||||
@@ -15327,7 +15287,7 @@ snapshots:
|
||||
tsx@4.19.4:
|
||||
dependencies:
|
||||
esbuild: 0.25.5
|
||||
get-tsconfig: 4.10.1
|
||||
get-tsconfig: 4.7.5
|
||||
optionalDependencies:
|
||||
fsevents: 2.3.3
|
||||
|
||||
@@ -15679,7 +15639,7 @@ snapshots:
|
||||
vite@5.4.19(@types/node@20.14.10)(lightningcss@1.30.1)(terser@5.39.2):
|
||||
dependencies:
|
||||
esbuild: 0.21.5
|
||||
postcss: 8.5.6
|
||||
postcss: 8.5.1
|
||||
rollup: 4.22.4
|
||||
optionalDependencies:
|
||||
'@types/node': 20.14.10
|
||||
@@ -15744,6 +15704,8 @@ snapshots:
|
||||
|
||||
vue-component-type-helpers@2.2.12: {}
|
||||
|
||||
vue-component-type-helpers@3.1.0: {}
|
||||
|
||||
vue-component-type-helpers@3.1.1: {}
|
||||
|
||||
vue-demi@0.14.10(vue@3.5.13(typescript@5.9.2)):
|
||||
|
||||
@@ -94,7 +94,6 @@ catalog:
|
||||
zod: ^3.23.8
|
||||
zod-to-json-schema: ^3.24.1
|
||||
zod-validation-error: ^3.3.0
|
||||
mixpanel-browser: ^2.71.0
|
||||
|
||||
cleanupUnusedCatalogs: true
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 18 KiB |
365
src/api/auth.ts
@@ -1,365 +0,0 @@
|
||||
import * as Sentry from '@sentry/vue'
|
||||
import { isEmpty } from 'es-toolkit/compat'
|
||||
|
||||
import { api } from '@/scripts/api'
|
||||
|
||||
interface UserCloudStatus {
|
||||
status: 'active' | 'waitlisted'
|
||||
}
|
||||
|
||||
const ONBOARDING_SURVEY_KEY = 'onboarding_survey'
|
||||
|
||||
/**
|
||||
* Helper function to capture API errors with Sentry
|
||||
*/
|
||||
function captureApiError(
|
||||
error: Error,
|
||||
endpoint: string,
|
||||
errorType: 'http_error' | 'network_error',
|
||||
httpStatus?: number,
|
||||
operation?: string,
|
||||
extraContext?: Record<string, any>
|
||||
) {
|
||||
const tags: Record<string, any> = {
|
||||
api_endpoint: endpoint,
|
||||
error_type: errorType
|
||||
}
|
||||
|
||||
if (httpStatus !== undefined) {
|
||||
tags.http_status = httpStatus
|
||||
}
|
||||
|
||||
if (operation) {
|
||||
tags.operation = operation
|
||||
}
|
||||
|
||||
const sentryOptions: any = {
|
||||
tags,
|
||||
extra: extraContext ? { ...extraContext } : undefined
|
||||
}
|
||||
|
||||
Sentry.captureException(error, sentryOptions)
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to check if error is already handled HTTP error
|
||||
*/
|
||||
function isHttpError(error: unknown, errorMessagePrefix: string): boolean {
|
||||
return error instanceof Error && error.message.startsWith(errorMessagePrefix)
|
||||
}
|
||||
|
||||
export async function getUserCloudStatus(): Promise<UserCloudStatus> {
|
||||
try {
|
||||
const response = await api.fetchApi('/user', {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
})
|
||||
if (!response.ok) {
|
||||
const error = new Error(`Failed to get user: ${response.statusText}`)
|
||||
captureApiError(
|
||||
error,
|
||||
'/user',
|
||||
'http_error',
|
||||
response.status,
|
||||
undefined,
|
||||
{
|
||||
api: {
|
||||
method: 'GET',
|
||||
endpoint: '/user',
|
||||
status_code: response.status,
|
||||
status_text: response.statusText
|
||||
}
|
||||
}
|
||||
)
|
||||
throw error
|
||||
}
|
||||
|
||||
return response.json()
|
||||
} catch (error) {
|
||||
// Only capture network errors (not HTTP errors we already captured)
|
||||
if (!isHttpError(error, 'Failed to get user:')) {
|
||||
captureApiError(error as Error, '/user', 'network_error')
|
||||
}
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
export async function getInviteCodeStatus(
|
||||
inviteCode: string
|
||||
): Promise<{ claimed: boolean; expired: boolean }> {
|
||||
try {
|
||||
const response = await api.fetchApi(
|
||||
`/invite_code/${encodeURIComponent(inviteCode)}/status`,
|
||||
{
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
}
|
||||
)
|
||||
if (!response.ok) {
|
||||
const error = new Error(
|
||||
`Failed to get invite code status: ${response.statusText}`
|
||||
)
|
||||
captureApiError(
|
||||
error,
|
||||
'/invite_code/{code}/status',
|
||||
'http_error',
|
||||
response.status,
|
||||
undefined,
|
||||
{
|
||||
api: {
|
||||
method: 'GET',
|
||||
endpoint: `/invite_code/${inviteCode}/status`,
|
||||
status_code: response.status,
|
||||
status_text: response.statusText
|
||||
},
|
||||
extra: {
|
||||
invite_code_length: inviteCode.length
|
||||
},
|
||||
route_template: '/invite_code/{code}/status',
|
||||
route_actual: `/invite_code/${inviteCode}/status`
|
||||
}
|
||||
)
|
||||
throw error
|
||||
}
|
||||
|
||||
return response.json()
|
||||
} catch (error) {
|
||||
// Only capture network errors (not HTTP errors we already captured)
|
||||
if (!isHttpError(error, 'Failed to get invite code status:')) {
|
||||
captureApiError(
|
||||
error as Error,
|
||||
'/invite_code/{code}/status',
|
||||
'network_error',
|
||||
undefined,
|
||||
undefined,
|
||||
{
|
||||
route_template: '/invite_code/{code}/status',
|
||||
route_actual: `/invite_code/${inviteCode}/status`
|
||||
}
|
||||
)
|
||||
}
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
export async function getSurveyCompletedStatus(): Promise<boolean> {
|
||||
try {
|
||||
const response = await api.fetchApi(`/settings/${ONBOARDING_SURVEY_KEY}`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
})
|
||||
if (!response.ok) {
|
||||
// Not an error case - survey not completed is a valid state
|
||||
Sentry.addBreadcrumb({
|
||||
category: 'auth',
|
||||
message: 'Survey status check returned non-ok response',
|
||||
level: 'info',
|
||||
data: {
|
||||
status: response.status,
|
||||
endpoint: `/settings/${ONBOARDING_SURVEY_KEY}`
|
||||
}
|
||||
})
|
||||
return false
|
||||
}
|
||||
const data = await response.json()
|
||||
// Check if data exists and is not empty
|
||||
return !isEmpty(data.value)
|
||||
} catch (error) {
|
||||
// Network error - still capture it as it's not thrown from above
|
||||
Sentry.captureException(error, {
|
||||
tags: {
|
||||
api_endpoint: '/settings/{key}',
|
||||
error_type: 'network_error'
|
||||
},
|
||||
extra: {
|
||||
route_template: '/settings/{key}',
|
||||
route_actual: `/settings/${ONBOARDING_SURVEY_KEY}`
|
||||
},
|
||||
level: 'warning'
|
||||
})
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// @ts-expect-error - Unused function kept for future use
|
||||
async function postSurveyStatus(): Promise<void> {
|
||||
try {
|
||||
const response = await api.fetchApi(`/settings/${ONBOARDING_SURVEY_KEY}`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({ [ONBOARDING_SURVEY_KEY]: undefined })
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
const error = new Error(
|
||||
`Failed to post survey status: ${response.statusText}`
|
||||
)
|
||||
captureApiError(
|
||||
error,
|
||||
'/settings/{key}',
|
||||
'http_error',
|
||||
response.status,
|
||||
'post_survey_status',
|
||||
{
|
||||
route_template: '/settings/{key}',
|
||||
route_actual: `/settings/${ONBOARDING_SURVEY_KEY}`
|
||||
}
|
||||
)
|
||||
throw error
|
||||
}
|
||||
} catch (error) {
|
||||
// Only capture network errors (not HTTP errors we already captured)
|
||||
if (!isHttpError(error, 'Failed to post survey status:')) {
|
||||
captureApiError(
|
||||
error as Error,
|
||||
'/settings/{key}',
|
||||
'network_error',
|
||||
undefined,
|
||||
'post_survey_status',
|
||||
{
|
||||
route_template: '/settings/{key}',
|
||||
route_actual: `/settings/${ONBOARDING_SURVEY_KEY}`
|
||||
}
|
||||
)
|
||||
}
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
export async function submitSurvey(
|
||||
survey: Record<string, unknown>
|
||||
): Promise<void> {
|
||||
try {
|
||||
Sentry.addBreadcrumb({
|
||||
category: 'auth',
|
||||
message: 'Submitting survey',
|
||||
level: 'info',
|
||||
data: {
|
||||
survey_fields: Object.keys(survey)
|
||||
}
|
||||
})
|
||||
|
||||
const response = await api.fetchApi('/settings', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({ [ONBOARDING_SURVEY_KEY]: survey })
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
const error = new Error(`Failed to submit survey: ${response.statusText}`)
|
||||
captureApiError(
|
||||
error,
|
||||
'/settings',
|
||||
'http_error',
|
||||
response.status,
|
||||
'submit_survey',
|
||||
{
|
||||
survey: {
|
||||
field_count: Object.keys(survey).length,
|
||||
field_names: Object.keys(survey)
|
||||
}
|
||||
}
|
||||
)
|
||||
throw error
|
||||
}
|
||||
|
||||
// Log successful survey submission
|
||||
Sentry.addBreadcrumb({
|
||||
category: 'auth',
|
||||
message: 'Survey submitted successfully',
|
||||
level: 'info'
|
||||
})
|
||||
} catch (error) {
|
||||
// Only capture network errors (not HTTP errors we already captured)
|
||||
if (!isHttpError(error, 'Failed to submit survey:')) {
|
||||
captureApiError(
|
||||
error as Error,
|
||||
'/settings',
|
||||
'network_error',
|
||||
undefined,
|
||||
'submit_survey'
|
||||
)
|
||||
}
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
export async function claimInvite(
|
||||
code: string
|
||||
): Promise<{ success: boolean; message: string }> {
|
||||
try {
|
||||
Sentry.addBreadcrumb({
|
||||
category: 'auth',
|
||||
message: 'Attempting to claim invite',
|
||||
level: 'info',
|
||||
data: {
|
||||
code_length: code.length
|
||||
}
|
||||
})
|
||||
|
||||
const res = await api.fetchApi(
|
||||
`/invite_code/${encodeURIComponent(code)}/claim`,
|
||||
{
|
||||
method: 'POST'
|
||||
}
|
||||
)
|
||||
|
||||
if (!res.ok) {
|
||||
const error = new Error(
|
||||
`Failed to claim invite: ${res.status} ${res.statusText}`
|
||||
)
|
||||
captureApiError(
|
||||
error,
|
||||
'/invite_code/{code}/claim',
|
||||
'http_error',
|
||||
res.status,
|
||||
'claim_invite',
|
||||
{
|
||||
invite: {
|
||||
code_length: code.length,
|
||||
status_code: res.status,
|
||||
status_text: res.statusText
|
||||
},
|
||||
route_template: '/invite_code/{code}/claim',
|
||||
route_actual: `/invite_code/${encodeURIComponent(code)}/claim`
|
||||
}
|
||||
)
|
||||
throw error
|
||||
}
|
||||
|
||||
// Log successful invite claim
|
||||
Sentry.addBreadcrumb({
|
||||
category: 'auth',
|
||||
message: 'Invite claimed successfully',
|
||||
level: 'info'
|
||||
})
|
||||
|
||||
return res.json()
|
||||
} catch (error) {
|
||||
// Only capture network errors (not HTTP errors we already captured)
|
||||
if (!isHttpError(error, 'Failed to claim invite:')) {
|
||||
captureApiError(
|
||||
error as Error,
|
||||
'/invite_code/{code}/claim',
|
||||
'network_error',
|
||||
undefined,
|
||||
'claim_invite',
|
||||
{
|
||||
route_template: '/invite_code/{code}/claim',
|
||||
route_actual: `/invite_code/${encodeURIComponent(code)}/claim`
|
||||
}
|
||||
)
|
||||
}
|
||||
throw error
|
||||
}
|
||||
}
|
||||
@@ -1,64 +1,29 @@
|
||||
/**
|
||||
* Utility functions for downloading files
|
||||
*/
|
||||
import { isCloud } from '@/platform/distribution/types'
|
||||
|
||||
// Constants
|
||||
const DEFAULT_DOWNLOAD_FILENAME = 'download.png'
|
||||
|
||||
/**
|
||||
* Trigger a download by creating a temporary anchor element
|
||||
* @param href - The URL or blob URL to download
|
||||
* @param filename - The filename to suggest to the browser
|
||||
*/
|
||||
function triggerLinkDownload(href: string, filename: string): void {
|
||||
const link = document.createElement('a')
|
||||
link.href = href
|
||||
link.download = filename
|
||||
link.style.display = 'none'
|
||||
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
document.body.removeChild(link)
|
||||
}
|
||||
|
||||
/**
|
||||
* Download a file from a URL by creating a temporary anchor element
|
||||
* @param url - The URL of the file to download (must be a valid URL string)
|
||||
* @param filename - Optional filename override (will use URL filename or default if not provided)
|
||||
* @throws {Error} If the URL is invalid or empty
|
||||
*/
|
||||
export function downloadFile(url: string, filename?: string): void {
|
||||
export const downloadFile = (url: string, filename?: string): void => {
|
||||
if (!url || typeof url !== 'string' || url.trim().length === 0) {
|
||||
throw new Error('Invalid URL provided for download')
|
||||
}
|
||||
|
||||
const inferredFilename =
|
||||
const link = document.createElement('a')
|
||||
link.href = url
|
||||
link.download =
|
||||
filename || extractFilenameFromUrl(url) || DEFAULT_DOWNLOAD_FILENAME
|
||||
|
||||
if (isCloud) {
|
||||
// Assets from cross-origin (e.g., GCS) cannot be downloaded this way
|
||||
void downloadViaBlobFetch(url, inferredFilename).catch((error) => {
|
||||
console.error('Failed to download file', error)
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
triggerLinkDownload(url, inferredFilename)
|
||||
}
|
||||
|
||||
/**
|
||||
* Download a Blob by creating a temporary object URL and anchor element
|
||||
* @param filename - The filename to suggest to the browser
|
||||
* @param blob - The Blob to download
|
||||
*/
|
||||
export function downloadBlob(filename: string, blob: Blob): void {
|
||||
const url = URL.createObjectURL(blob)
|
||||
|
||||
triggerLinkDownload(url, filename)
|
||||
|
||||
// Revoke on the next microtask to give the browser time to start the download
|
||||
queueMicrotask(() => URL.revokeObjectURL(url))
|
||||
// Trigger download
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
document.body.removeChild(link)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -74,15 +39,3 @@ const extractFilenameFromUrl = (url: string): string | null => {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
const downloadViaBlobFetch = async (
|
||||
href: string,
|
||||
filename: string
|
||||
): Promise<void> => {
|
||||
const response = await fetch(href)
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch ${href}: ${response.status}`)
|
||||
}
|
||||
const blob = await response.blob()
|
||||
downloadBlob(filename, blob)
|
||||
}
|
||||
|
||||