Skip to content

✨ Allow user-defined sorting or grouping - #1089

Merged
gwennlbh merged 38 commits into
mainfrom
custom-group-by-sort-by
Jan 22, 2026
Merged

gwennlbh merged 38 commits into
mainfrom
custom-group-by-sort-by

Conversation

@gwennlbh

@gwennlbh gwennlbh commented Jan 17, 2026 •

Copy link
Copy Markdown
Member

Closes #925

TODO:

  • E2E test
  • fix label-less metadata not shown in picker (see crop in groupby metadata submenus) crop was not set as groupable in protocol v52

Later:

@gwennlbh
gwennlbh force-pushed the custom-group-by-sort-by branch 5 times, most recently from be22818 to 26d8f54 Compare January 17, 2026 19:47
@gwennlbh
gwennlbh had a problem deploying to #1089: Unit tests report January 17, 2026 19:48 — with GitHub Actions Failure
@gwennlbh
gwennlbh temporarily deployed to #1089: App preview January 17, 2026 19:48 — with GitHub Actions Inactive
@gwennlbh
gwennlbh force-pushed the custom-group-by-sort-by branch from 26d8f54 to 9a1e71e Compare January 18, 2026 00:11
@gwennlbh
gwennlbh had a problem deploying to #1089: Unit tests report January 18, 2026 00:12 — with GitHub Actions Failure
@gwennlbh
gwennlbh force-pushed the custom-group-by-sort-by branch from 9a1e71e to 9917b9b Compare January 18, 2026 00:13
@gwennlbh
gwennlbh had a problem deploying to #1089: Unit tests report January 18, 2026 00:15 — with GitHub Actions Failure
@gwennlbh
gwennlbh force-pushed the custom-group-by-sort-by branch from 9917b9b to 61f1f88 Compare January 18, 2026 00:17
@gwennlbh
gwennlbh temporarily deployed to #1089: App preview January 18, 2026 00:19 — with GitHub Actions Inactive
@gwennlbh
gwennlbh had a problem deploying to #1089: Unit tests report January 18, 2026 00:19 — with GitHub Actions Failure
@gwennlbh
gwennlbh force-pushed the custom-group-by-sort-by branch from 61f1f88 to 2d35ecf Compare January 18, 2026 17:33
@gwennlbh
gwennlbh had a problem deploying to #1089: Unit tests report January 18, 2026 18:39 — with GitHub Actions Failure
@gwennlbh
gwennlbh temporarily deployed to #1089: App preview January 18, 2026 18:39 — with GitHub Actions Inactive
@gwennlbh
gwennlbh temporarily deployed to #1089: App preview January 18, 2026 18:52 — with GitHub Actions Inactive
@gwennlbh
gwennlbh had a problem deploying to #1089: Unit tests report January 18, 2026 18:52 — with GitHub Actions Failure
@gwennlbh
gwennlbh force-pushed the custom-group-by-sort-by branch from 8e5f932 to ffa16d5 Compare January 18, 2026 18:56
@gwennlbh
gwennlbh temporarily deployed to #1089: Unit tests report January 18, 2026 18:57 — with GitHub Actions Inactive
@gwennlbh
gwennlbh temporarily deployed to #1089: App preview January 18, 2026 18:57 — with GitHub Actions Inactive
@gwennlbh
gwennlbh temporarily deployed to #1089: Unit tests report January 18, 2026 18:58 — with GitHub Actions Inactive
@gwennlbh
gwennlbh temporarily deployed to #1089: Test coverage report January 18, 2026 18:59 — with GitHub Actions Inactive
@github-actions

github-actions Bot commented Jan 18, 2026 •

Copy link
Copy Markdown
Contributor

✅ Coverage is up by 0.66% (from 43.66% to 44.32%)

@github-actions

github-actions Bot commented Jan 18, 2026 •

Copy link
Copy Markdown
Contributor

Bundle Stats

Hey there, this message comes from a GitHub action that helps you and reviewers to understand how these changes affect the size of this project's bundle.

As this PR is updated, I'll keep you updated on how the bundle size is impacted.

Total

Files count Total bundle size % Changed
215 → 216 46.38 MB → 46.4 MB (+14.21 kB)
12.08 MB → 12.09 MB (gzip)
+0.03%

Changeset

No files were changed

View detailed bundle breakdown

Added

Asset File Size % Changed
_app/immutable/nodes/16.A7kLrl5r.js 0 B → 409.65 kB (+409.65 kB)
0 B → 143.9 kB (gzip)
-
_app/immutable/chunks/DeApfJqy.js 0 B → 389.87 kB (+389.87 kB)
0 B → 107.57 kB (gzip)
-
_app/immutable/chunks/BgbD-SFL.js 0 B → 330.41 kB (+330.41 kB)
0 B → 141.12 kB (gzip)
-
_app/immutable/chunks/DCNNlHqi.js 0 B → 106.63 kB (+106.63 kB)
0 B → 47.85 kB (gzip)
-
_app/immutable/chunks/DsPXQ0zC.js 0 B → 96.29 kB (+96.29 kB)
0 B → 35.17 kB (gzip)
-
_app/immutable/chunks/AgF3bVlb.js 0 B → 70.3 kB (+70.3 kB)
0 B → 26.72 kB (gzip)
-
_app/immutable/chunks/DZP24NaT.js 0 B → 65.87 kB (+65.87 kB)
0 B → 30.11 kB (gzip)
-
_app/immutable/chunks/CQPs5m48.js 0 B → 59.89 kB (+59.89 kB)
0 B → 26.61 kB (gzip)
-
_app/immutable/chunks/BtCNOWOo.js 0 B → 54.94 kB (+54.94 kB)
0 B → 26.58 kB (gzip)
-
_app/immutable/chunks/p3uDzF0H.js 0 B → 33.77 kB (+33.77 kB)
0 B → 12.98 kB (gzip)
-
_app/immutable/nodes/18.OUQFz221.js 0 B → 32.33 kB (+32.33 kB)
0 B → 13.31 kB (gzip)
-
_app/immutable/nodes/13.De-AVG-V.js 0 B → 29.8 kB (+29.8 kB)
0 B → 10.83 kB (gzip)
-
_app/immutable/chunks/DAkzSwNS.js 0 B → 25.74 kB (+25.74 kB)
0 B → 11.45 kB (gzip)
-
_app/immutable/chunks/DT-7UekT.js 0 B → 25.51 kB (+25.51 kB)
0 B → 12.47 kB (gzip)
-
_app/immutable/nodes/2.BcSV47OJ.js 0 B → 25.05 kB (+25.05 kB)
0 B → 11.09 kB (gzip)
-
_app/immutable/chunks/CdqXxxWn.js 0 B → 23.94 kB (+23.94 kB)
0 B → 9.57 kB (gzip)
-
_app/immutable/chunks/iE9-RDim.js 0 B → 16.26 kB (+16.26 kB)
0 B → 5.93 kB (gzip)
-
_app/immutable/nodes/26.CzTZZYUw.js 0 B → 14.97 kB (+14.97 kB)
0 B → 6.04 kB (gzip)
-
_app/immutable/chunks/ChK4yKOo.js 0 B → 13.47 kB (+13.47 kB)
0 B → 5.43 kB (gzip)
-
_app/immutable/chunks/DPyZ3ZS3.js 0 B → 13.35 kB (+13.35 kB)
0 B → 4.75 kB (gzip)
-
_app/immutable/nodes/5.Cg3Q1kso.js 0 B → 10.69 kB (+10.69 kB)
0 B → 4.4 kB (gzip)
-
_app/immutable/nodes/3.BvOA7mTm.js 0 B → 9.95 kB (+9.95 kB)
0 B → 4.34 kB (gzip)
-
_app/immutable/entry/app.rzARbP9o.js 0 B → 8.54 kB (+8.54 kB)
0 B → 2.67 kB (gzip)
-
_app/immutable/nodes/25.CSRJ1HTv.js 0 B → 8.12 kB (+8.12 kB)
0 B → 3.09 kB (gzip)
-
_app/immutable/chunks/CSzl_iKe.js 0 B → 7.44 kB (+7.44 kB)
0 B → 4.26 kB (gzip)
-
_app/immutable/nodes/30.C86DzqG6.js 0 B → 7.32 kB (+7.32 kB)
0 B → 3.52 kB (gzip)
-
_app/immutable/chunks/BLb2aiWe.js 0 B → 6.71 kB (+6.71 kB)
0 B → 3.29 kB (gzip)
-
_app/immutable/chunks/DQ_TPpYO.js 0 B → 6.57 kB (+6.57 kB)
0 B → 3.25 kB (gzip)
-
_app/immutable/nodes/17.DwmQ1Xks.js 0 B → 6.27 kB (+6.27 kB)
0 B → 2.99 kB (gzip)
-
_app/immutable/chunks/Dud1u_el.js 0 B → 5.9 kB (+5.9 kB)
0 B → 2.79 kB (gzip)
-
_app/immutable/chunks/BlMwKDoE.js 0 B → 5.89 kB (+5.89 kB)
0 B → 2.94 kB (gzip)
-
_app/immutable/assets/_layout.BPWI82Hy.css 0 B → 5.66 kB (+5.66 kB)
0 B → 1.5 kB (gzip)
-
_app/immutable/chunks/C1aAvYhY.js 0 B → 5.54 kB (+5.54 kB)
0 B → 1.82 kB (gzip)
-
_app/immutable/chunks/Be_Pszap.js 0 B → 5.5 kB (+5.5 kB)
0 B → 2.51 kB (gzip)
-
_app/immutable/nodes/29.BoOJfjR0.js 0 B → 5.32 kB (+5.32 kB)
0 B → 2.66 kB (gzip)
-
_app/immutable/chunks/F7yV4Evm.js 0 B → 4.83 kB (+4.83 kB)
0 B → 1.99 kB (gzip)
-
_app/immutable/chunks/D4MkanpK.js 0 B → 4.39 kB (+4.39 kB)
0 B → 2.01 kB (gzip)
-
_app/immutable/nodes/28.BBOdYpEg.js 0 B → 4.31 kB (+4.31 kB)
0 B → 1.99 kB (gzip)
-
_app/immutable/nodes/19.WyiAnD1s.js 0 B → 4.26 kB (+4.26 kB)
0 B → 1.97 kB (gzip)
-
_app/immutable/nodes/11.Cwv4b2Zk.js 0 B → 3.97 kB (+3.97 kB)
0 B → 1.9 kB (gzip)
-
_app/immutable/chunks/Czh9ua7l.js 0 B → 3.87 kB (+3.87 kB)
0 B → 1.99 kB (gzip)
-
_app/immutable/chunks/BYSP0TVq.js 0 B → 3.44 kB (+3.44 kB)
0 B → 1.47 kB (gzip)
-
_app/immutable/chunks/DEggfmHN.js 0 B → 3.44 kB (+3.44 kB)
0 B → 1.6 kB (gzip)
-
_app/immutable/nodes/7.DYOGuMkd.js 0 B → 3.26 kB (+3.26 kB)
0 B → 1.74 kB (gzip)
-
_app/immutable/nodes/23.CvaRYsDT.js 0 B → 2.99 kB (+2.99 kB)
0 B → 1.25 kB (gzip)
-
_app/immutable/chunks/CQuQX8Xe.js 0 B → 2.84 kB (+2.84 kB)
0 B → 1.21 kB (gzip)
-
_app/immutable/nodes/14.BLsGaK5v.js 0 B → 2.71 kB (+2.71 kB)
0 B → 1.5 kB (gzip)
-
_app/immutable/nodes/12.CGYHhAmp.js 0 B → 2.65 kB (+2.65 kB)
0 B → 1.47 kB (gzip)
-
_app/immutable/chunks/cRCxHGxA.js 0 B → 2.52 kB (+2.52 kB)
0 B → 1.09 kB (gzip)
-
_app/immutable/nodes/20.CEyJ3UYT.js 0 B → 2.51 kB (+2.51 kB)
0 B → 1.05 kB (gzip)
-
_app/immutable/chunks/wXR-mlcD.js 0 B → 2.33 kB (+2.33 kB)
0 B → 1.34 kB (gzip)
-
_app/immutable/chunks/DuCxD5m6.js 0 B → 2.23 kB (+2.23 kB)
0 B → 1.12 kB (gzip)
-
_app/immutable/chunks/GyhVtNyV.js 0 B → 2.13 kB (+2.13 kB)
0 B → 1.19 kB (gzip)
-
_app/immutable/chunks/DModlayj.js 0 B → 2.12 kB (+2.12 kB)
0 B → 936 B (gzip)
-
_app/immutable/chunks/XqnqIUdi.js 0 B → 2.1 kB (+2.1 kB)
0 B → 1.42 kB (gzip)
-
_app/immutable/chunks/Co6vNb0Q.js 0 B → 2.08 kB (+2.08 kB)
0 B → 1.14 kB (gzip)
-
_app/immutable/assets/AreaObservations.BUAje0Dk.css 0 B → 1.9 kB (+1.9 kB)
0 B → 568 B (gzip)
-
_app/immutable/nodes/27.COLP4wFo.js 0 B → 1.74 kB (+1.74 kB)
0 B → 1.06 kB (gzip)
-
_app/immutable/nodes/22.C_AJ_yey.js 0 B → 1.74 kB (+1.74 kB)
0 B → 972 B (gzip)
-
_app/immutable/chunks/MoRr4ga3.js 0 B → 1.58 kB (+1.58 kB)
0 B → 819 B (gzip)
-
_app/immutable/chunks/DbbnX7bR.js 0 B → 1.55 kB (+1.55 kB)
0 B → 760 B (gzip)
-
_app/immutable/nodes/4.B9ubI7-A.js 0 B → 1.5 kB (+1.5 kB)
0 B → 883 B (gzip)
-
_app/immutable/chunks/Bfii5q_X.js 0 B → 1.4 kB (+1.4 kB)
0 B → 760 B (gzip)
-
_app/immutable/chunks/nfgn53ii.js 0 B → 1.4 kB (+1.4 kB)
0 B → 607 B (gzip)
-
_app/immutable/chunks/C1tSBIAr.js 0 B → 1.29 kB (+1.29 kB)
0 B → 899 B (gzip)
-
_app/immutable/nodes/31.BGwq6RRA.js 0 B → 1.17 kB (+1.17 kB)
0 B → 562 B (gzip)
-
_app/immutable/chunks/PSlMpm62.js 0 B → 1.17 kB (+1.17 kB)
0 B → 718 B (gzip)
-
_app/immutable/chunks/CcN_7mcL.js 0 B → 1.11 kB (+1.11 kB)
0 B → 576 B (gzip)
-
_app/immutable/chunks/DjvUFegr.js 0 B → 1.08 kB (+1.08 kB)
0 B → 526 B (gzip)
-
_app/immutable/chunks/DkgQdc-B.js 0 B → 1.07 kB (+1.07 kB)
0 B → 636 B (gzip)
-
_app/immutable/chunks/CAlYqcvo.js 0 B → 1.02 kB (+1.02 kB)
0 B → 407 B (gzip)
-
_app/immutable/chunks/DIZehVWx.js 0 B → 936 B (+936 B)
0 B → 520 B (gzip)
-
_app/immutable/chunks/B497PTvx.js 0 B → 892 B (+892 B)
0 B → 489 B (gzip)
-
_app/immutable/chunks/CT0aiy88.js 0 B → 891 B (+891 B)
0 B → 441 B (gzip)
-
_app/immutable/nodes/1.D019gdFX.js 0 B → 889 B (+889 B)
0 B → 574 B (gzip)
-
_app/immutable/chunks/BdXFvyhZ.js 0 B → 881 B (+881 B)
0 B → 533 B (gzip)
-
_app/immutable/chunks/CDJr9D2I.js 0 B → 879 B (+879 B)
0 B → 518 B (gzip)
-
_app/immutable/chunks/C8lVmhRd.js 0 B → 848 B (+848 B)
0 B → 439 B (gzip)
-
_app/immutable/chunks/Dn3OkNAu.js 0 B → 846 B (+846 B)
0 B → 534 B (gzip)
-
_app/immutable/chunks/DHDdPBYY.js 0 B → 841 B (+841 B)
0 B → 479 B (gzip)
-
_app/immutable/chunks/DL9HRecy.js 0 B → 776 B (+776 B)
0 B → 460 B (gzip)
-
_app/immutable/chunks/BzXUQaZW.js 0 B → 757 B (+757 B)
0 B → 592 B (gzip)
-
_app/immutable/chunks/Bx0cyn8q.js 0 B → 726 B (+726 B)
0 B → 390 B (gzip)
-
_app/immutable/assets/DropdownMenu.Cs63yTjv.css 0 B → 712 B (+712 B)
0 B → 377 B (gzip)
-
_app/immutable/chunks/BfcZ5gVq.js 0 B → 701 B (+701 B)
0 B → 377 B (gzip)
-
_app/immutable/chunks/DTruYEjw.js 0 B → 678 B (+678 B)
0 B → 469 B (gzip)
-
_app/immutable/chunks/DvoPCqxK.js 0 B → 650 B (+650 B)
0 B → 443 B (gzip)
-
_app/immutable/chunks/BVr2L6qY.js 0 B → 650 B (+650 B)
0 B → 441 B (gzip)
-
_app/immutable/chunks/CarGYN3B.js 0 B → 641 B (+641 B)
0 B → 424 B (gzip)
-
_app/immutable/chunks/DhlM17BC.js 0 B → 621 B (+621 B)
0 B → 435 B (gzip)
-
_app/immutable/chunks/DoegkK3q.js 0 B → 617 B (+617 B)
0 B → 435 B (gzip)
-
_app/immutable/chunks/BnCk3ED8.js 0 B → 611 B (+611 B)
0 B → 401 B (gzip)
-
_app/immutable/chunks/CltBvVwF.js 0 B → 605 B (+605 B)
0 B → 387 B (gzip)
-
_app/immutable/chunks/BrqZx_ae.js 0 B → 588 B (+588 B)
0 B → 418 B (gzip)
-
_app/immutable/chunks/K2qgPpSv.js 0 B → 587 B (+587 B)
0 B → 407 B (gzip)
-
_app/immutable/chunks/CMhAvRSy.js 0 B → 582 B (+582 B)
0 B → 413 B (gzip)
-
_app/immutable/chunks/CGrGbOwQ.js 0 B → 568 B (+568 B)
0 B → 372 B (gzip)
-
_app/immutable/chunks/DzAEI1Kz.js 0 B → 560 B (+560 B)
0 B → 393 B (gzip)
-
_app/immutable/chunks/DT-vThLh.js 0 B → 558 B (+558 B)
0 B → 409 B (gzip)
-
_app/immutable/chunks/2i6XmD6g.js 0 B → 547 B (+547 B)
0 B → 402 B (gzip)
-
_app/immutable/chunks/CIFPyU_Z.js 0 B → 545 B (+545 B)
0 B → 318 B (gzip)
-
_app/immutable/chunks/yOJPvbtV.js 0 B → 539 B (+539 B)
0 B → 398 B (gzip)
-
_app/immutable/chunks/D41t8BPD.js 0 B → 537 B (+537 B)
0 B → 392 B (gzip)
-
_app/immutable/chunks/BQSoEZr0.js 0 B → 536 B (+536 B)
0 B → 352 B (gzip)
-
_app/immutable/chunks/Dd9HmVfI.js 0 B → 524 B (+524 B)
0 B → 351 B (gzip)
-
_app/immutable/chunks/BNHarAyR.js 0 B → 522 B (+522 B)
0 B → 347 B (gzip)
-
app/immutable/chunks/C8H46ks.js 0 B → 520 B (+520 B)
0 B → 385 B (gzip)
-
_app/immutable/chunks/DtKcgUE7.js 0 B → 520 B (+520 B)
0 B → 387 B (gzip)
-
_app/immutable/chunks/aDmlZIE0.js 0 B → 518 B (+518 B)
0 B → 320 B (gzip)
-
_app/immutable/chunks/HajTbEEg.js 0 B → 513 B (+513 B)
0 B → 326 B (gzip)
-
_app/immutable/chunks/Dpu8fCXy.js 0 B → 492 B (+492 B)
0 B → 354 B (gzip)
-
_app/immutable/chunks/BpmBLvPO.js 0 B → 488 B (+488 B)
0 B → 361 B (gzip)
-
_app/immutable/chunks/Bc5oYN8P.js 0 B → 484 B (+484 B)
0 B → 372 B (gzip)
-
_app/immutable/chunks/CZOiKUnz.js 0 B → 481 B (+481 B)
0 B → 368 B (gzip)
-
_app/immutable/chunks/CLbVCPP8.js 0 B → 469 B (+469 B)
0 B → 355 B (gzip)
-
_app/immutable/chunks/DbcH4EyF.js 0 B → 467 B (+467 B)
0 B → 357 B (gzip)
-
_app/immutable/chunks/D7DTERvX.js 0 B → 466 B (+466 B)
0 B → 278 B (gzip)
-
_app/immutable/chunks/VywocGAY.js 0 B → 461 B (+461 B)
0 B → 356 B (gzip)
-
_app/immutable/chunks/DLp1PZgc.js 0 B → 438 B (+438 B)
0 B → 341 B (gzip)
-
_app/immutable/chunks/DdWikKeT.js 0 B → 437 B (+437 B)
0 B → 265 B (gzip)
-
_app/immutable/chunks/CQ9iwEEh.js 0 B → 419 B (+419 B)
0 B → 326 B (gzip)
-
_app/immutable/chunks/DLRd3mJH.js 0 B → 377 B (+377 B)
0 B → 248 B (gzip)
-
_app/immutable/chunks/BnrL4TBO.js 0 B → 374 B (+374 B)
0 B → 288 B (gzip)
-
_app/immutable/nodes/8.CJ3KAzvP.js 0 B → 328 B (+328 B)
0 B → 241 B (gzip)
-
_app/immutable/chunks/JIdlij4u.js 0 B → 325 B (+325 B)
0 B → 257 B (gzip)
-
_app/immutable/chunks/CZtqHjUZ.js 0 B → 289 B (+289 B)
0 B → 218 B (gzip)
-
_app/immutable/nodes/6.D3zUuLzA.js 0 B → 285 B (+285 B)
0 B → 213 B (gzip)
-
_app/immutable/nodes/9.DIDI_Eyd.js 0 B → 283 B (+283 B)
0 B → 213 B (gzip)
-
_app/immutable/chunks/CAqPo6Td.js 0 B → 272 B (+272 B)
0 B → 174 B (gzip)
-
_app/immutable/nodes/0.C_fleTf0.js 0 B → 257 B (+257 B)
0 B → 221 B (gzip)
-
_app/immutable/chunks/C6ZPS0Dy.js 0 B → 240 B (+240 B)
0 B → 174 B (gzip)
-
_app/immutable/nodes/24.hS-hPOua.js 0 B → 230 B (+230 B)
0 B → 202 B (gzip)
-
_app/immutable/chunks/CSn1aUIw.js 0 B → 210 B (+210 B)
0 B → 170 B (gzip)
-
_app/immutable/chunks/B7AhtfvW.js 0 B → 185 B (+185 B)
0 B → 170 B (gzip)
-
_app/immutable/chunks/O7Z4mlhg.js 0 B → 138 B (+138 B)
0 B → 130 B (gzip)
-
_app/immutable/chunks/tu7KUWxd.js 0 B → 136 B (+136 B)
0 B → 134 B (gzip)
-
_app/immutable/entry/start.UPilp1q5.js 0 B → 126 B (+126 B)
0 B → 133 B (gzip)
-
_app/immutable/chunks/CF2SBdhC.js 0 B → 124 B (+124 B)
0 B → 122 B (gzip)
-
_app/immutable/nodes/15.CsixqNRh.js 0 B → 112 B (+112 B)
0 B → 122 B (gzip)
-
_app/immutable/chunks/BNUQVwre.js 0 B → 110 B (+110 B)
0 B → 118 B (gzip)
-
_app/immutable/chunks/DzxvSvME.js 0 B → 96 B (+96 B)
0 B → 104 B (gzip)
-
_app/immutable/chunks/B2CrHUF-.js 0 B → 74 B (+74 B)
0 B → 93 B (gzip)
-
_app/immutable/assets/arrow-left-s-line.AJW81_Sc.css 0 B → 59 B (+59 B)
0 B → 78 B (gzip)
-

Removed

Asset File Size % Changed
_app/immutable/nodes/16.BIvpMGN9.js 409.46 kB → 0 B (-409.46 kB)
143.82 kB → 0 B (gzip)
-100%
_app/immutable/chunks/BwHmUFLS.js 389.87 kB → 0 B (-389.87 kB)
107.57 kB → 0 B (gzip)
-100%
_app/immutable/chunks/BU8B9RD3.js 329.04 kB → 0 B (-329.04 kB)
140.72 kB → 0 B (gzip)
-100%
_app/immutable/chunks/DAANccr5.js 102.07 kB → 0 B (-102.07 kB)
45.95 kB → 0 B (gzip)
-100%
_app/immutable/chunks/CbX4StfB.js 95.73 kB → 0 B (-95.73 kB)
34.92 kB → 0 B (gzip)
-100%
_app/immutable/chunks/-F_gFVxD.js 70.29 kB → 0 B (-70.29 kB)
26.72 kB → 0 B (gzip)
-100%
_app/immutable/chunks/BtQrBqsp.js 65.87 kB → 0 B (-65.87 kB)
30.11 kB → 0 B (gzip)
-100%
_app/immutable/chunks/DVtVVteV.js 57.45 kB → 0 B (-57.45 kB)
25.56 kB → 0 B (gzip)
-100%
_app/immutable/chunks/BfRouyFh.js 54.94 kB → 0 B (-54.94 kB)
26.59 kB → 0 B (gzip)
-100%
_app/immutable/nodes/18.B0DkQEE2.js 32.33 kB → 0 B (-32.33 kB)
13.31 kB → 0 B (gzip)
-100%
_app/immutable/nodes/13.AuS4DN_r.js 29.78 kB → 0 B (-29.78 kB)
10.83 kB → 0 B (gzip)
-100%
_app/immutable/chunks/Cup80Ddu.js 26.21 kB → 0 B (-26.21 kB)
10.49 kB → 0 B (gzip)
-100%
_app/immutable/chunks/DaNBzuW6.js 25.74 kB → 0 B (-25.74 kB)
11.45 kB → 0 B (gzip)
-100%
_app/immutable/chunks/BK4BMcVI.js 25.51 kB → 0 B (-25.51 kB)
12.47 kB → 0 B (gzip)
-100%
_app/immutable/nodes/2.Dx5WC_pg.js 25.05 kB → 0 B (-25.05 kB)
11.09 kB → 0 B (gzip)
-100%
_app/immutable/chunks/BjlLRYIt.js 19.36 kB → 0 B (-19.36 kB)
7.5 kB → 0 B (gzip)
-100%
_app/immutable/chunks/ChU_o052.js 17.96 kB → 0 B (-17.96 kB)
6.19 kB → 0 B (gzip)
-100%
_app/immutable/chunks/Gw92a-UQ.js 16.29 kB → 0 B (-16.29 kB)
5.93 kB → 0 B (gzip)
-100%
_app/immutable/nodes/26.BBk9oc6x.js 14.97 kB → 0 B (-14.97 kB)
6.04 kB → 0 B (gzip)
-100%
_app/immutable/chunks/BIX0rRw7.js 13.49 kB → 0 B (-13.49 kB)
5.43 kB → 0 B (gzip)
-100%
_app/immutable/nodes/5.Fd-G0ZsA.js 10.69 kB → 0 B (-10.69 kB)
4.4 kB → 0 B (gzip)
-100%
_app/immutable/nodes/3.puLAvLvj.js 10.04 kB → 0 B (-10.04 kB)
4.36 kB → 0 B (gzip)
-100%
_app/immutable/entry/app.D4_rJAxp.js 8.54 kB → 0 B (-8.54 kB)
2.67 kB → 0 B (gzip)
-100%
_app/immutable/nodes/25.DJfGGvji.js 8.12 kB → 0 B (-8.12 kB)
3.09 kB → 0 B (gzip)
-100%
_app/immutable/chunks/sL0K3fN-.js 7.44 kB → 0 B (-7.44 kB)
4.26 kB → 0 B (gzip)
-100%
_app/immutable/nodes/30.BFPEBBFO.js 7.23 kB → 0 B (-7.23 kB)
3.48 kB → 0 B (gzip)
-100%
_app/immutable/chunks/D8yTUGhC.js 6.71 kB → 0 B (-6.71 kB)
3.29 kB → 0 B (gzip)
-100%
_app/immutable/chunks/Cf1-bY1Q.js 6.49 kB → 0 B (-6.49 kB)
3.21 kB → 0 B (gzip)
-100%
_app/immutable/chunks/Bw4JNYoy.js 6.41 kB → 0 B (-6.41 kB)
2.48 kB → 0 B (gzip)
-100%
_app/immutable/nodes/17.CLOnpo8V.js 6.27 kB → 0 B (-6.27 kB)
2.99 kB → 0 B (gzip)
-100%
_app/immutable/chunks/B0stWMtE.js 5.9 kB → 0 B (-5.9 kB)
2.78 kB → 0 B (gzip)
-100%
_app/immutable/chunks/DuWL0vUv.js 5.89 kB → 0 B (-5.89 kB)
2.94 kB → 0 B (gzip)
-100%
_app/immutable/assets/_layout.DVvbJCBh.css 5.65 kB → 0 B (-5.65 kB)
1.5 kB → 0 B (gzip)
-100%
_app/immutable/chunks/CMm1XSRX.js 5.5 kB → 0 B (-5.5 kB)
2.51 kB → 0 B (gzip)
-100%
_app/immutable/nodes/29.BxITVGO6.js 5.03 kB → 0 B (-5.03 kB)
2.53 kB → 0 B (gzip)
-100%
_app/immutable/chunks/CvsD8UHe.js 4.83 kB → 0 B (-4.83 kB)
1.99 kB → 0 B (gzip)
-100%
_app/immutable/chunks/CYxvMQRj.js 4.39 kB → 0 B (-4.39 kB)
2.01 kB → 0 B (gzip)
-100%
_app/immutable/nodes/28.C4gnjfI4.js 4.31 kB → 0 B (-4.31 kB)
1.99 kB → 0 B (gzip)
-100%
_app/immutable/nodes/19.DSCFXD8B.js 4.26 kB → 0 B (-4.26 kB)
1.96 kB → 0 B (gzip)
-100%
_app/immutable/nodes/11.DLxB59mI.js 4.04 kB → 0 B (-4.04 kB)
1.95 kB → 0 B (gzip)
-100%
_app/immutable/chunks/DiWcCiSd.js 3.87 kB → 0 B (-3.87 kB)
1.99 kB → 0 B (gzip)
-100%
_app/immutable/chunks/DR9EdeJi.js 3.84 kB → 0 B (-3.84 kB)
1.52 kB → 0 B (gzip)
-100%
app/immutable/chunks/7VNnUGG.js 3.44 kB → 0 B (-3.44 kB)
1.47 kB → 0 B (gzip)
-100%
_app/immutable/chunks/BAQJBgK7.js 3.44 kB → 0 B (-3.44 kB)
1.6 kB → 0 B (gzip)
-100%
_app/immutable/nodes/7.Cz0h90PE.js 3.26 kB → 0 B (-3.26 kB)
1.75 kB → 0 B (gzip)
-100%
_app/immutable/nodes/23.Du3vu9kv.js 2.99 kB → 0 B (-2.99 kB)
1.25 kB → 0 B (gzip)
-100%
_app/immutable/nodes/12.BkAKRAqP.js 2.76 kB → 0 B (-2.76 kB)
1.53 kB → 0 B (gzip)
-100%
_app/immutable/nodes/14.BHdXaTGT.js 2.73 kB → 0 B (-2.73 kB)
1.5 kB → 0 B (gzip)
-100%
_app/immutable/chunks/BfFagUct.js 2.52 kB → 0 B (-2.52 kB)
1.09 kB → 0 B (gzip)
-100%
_app/immutable/nodes/20.CcWBlr5R.js 2.51 kB → 0 B (-2.51 kB)
1.05 kB → 0 B (gzip)
-100%
_app/immutable/chunks/Cv5PH1NZ.js 2.33 kB → 0 B (-2.33 kB)
1.34 kB → 0 B (gzip)
-100%
_app/immutable/chunks/DLjSwf2L.js 2.3 kB → 0 B (-2.3 kB)
1.3 kB → 0 B (gzip)
-100%
_app/immutable/chunks/K9QUWDvO.js 2.29 kB → 0 B (-2.29 kB)
1.14 kB → 0 B (gzip)
-100%
_app/immutable/chunks/DsBbfKvh.js 2.2 kB → 0 B (-2.2 kB)
977 B → 0 B (gzip)
-100%
_app/immutable/chunks/6zgynhY4.js 2.13 kB → 0 B (-2.13 kB)
1.19 kB → 0 B (gzip)
-100%
_app/immutable/chunks/CgamuiiR.js 2.1 kB → 0 B (-2.1 kB)
1.42 kB → 0 B (gzip)
-100%
_app/immutable/nodes/27.ty6gFU5e.js 1.74 kB → 0 B (-1.74 kB)
1.06 kB → 0 B (gzip)
-100%
_app/immutable/nodes/22.CJMwTAID.js 1.74 kB → 0 B (-1.74 kB)
974 B → 0 B (gzip)
-100%
_app/immutable/assets/AreaObservations.Dbm3QfsR.css 1.63 kB → 0 B (-1.63 kB)
521 B → 0 B (gzip)
-100%
_app/immutable/chunks/CpvLZCy-.js 1.58 kB → 0 B (-1.58 kB)
815 B → 0 B (gzip)
-100%
_app/immutable/chunks/DkObws5w.js 1.58 kB → 0 B (-1.58 kB)
952 B → 0 B (gzip)
-100%
_app/immutable/chunks/DqacvikL.js 1.55 kB → 0 B (-1.55 kB)
765 B → 0 B (gzip)
-100%
_app/immutable/nodes/4.Bkwfwf0Q.js 1.5 kB → 0 B (-1.5 kB)
883 B → 0 B (gzip)
-100%
_app/immutable/chunks/B396oin3.js 1.4 kB → 0 B (-1.4 kB)
762 B → 0 B (gzip)
-100%
_app/immutable/chunks/CKkDXmQl.js 1.4 kB → 0 B (-1.4 kB)
607 B → 0 B (gzip)
-100%
_app/immutable/nodes/31.CRB8Lq7G.js 1.17 kB → 0 B (-1.17 kB)
562 B → 0 B (gzip)
-100%
_app/immutable/chunks/DblKFMJ7.js 1.17 kB → 0 B (-1.17 kB)
718 B → 0 B (gzip)
-100%
_app/immutable/chunks/8o9uEBVT.js 1.11 kB → 0 B (-1.11 kB)
576 B → 0 B (gzip)
-100%
_app/immutable/chunks/D_WIeVGO.js 1.08 kB → 0 B (-1.08 kB)
526 B → 0 B (gzip)
-100%
_app/immutable/chunks/BUuOmezJ.js 1.07 kB → 0 B (-1.07 kB)
636 B → 0 B (gzip)
-100%
_app/immutable/chunks/CKGO9siM.js 1.01 kB → 0 B (-1.01 kB)
406 B → 0 B (gzip)
-100%
_app/immutable/chunks/BL5CKBOk.js 936 B → 0 B (-936 B)
520 B → 0 B (gzip)
-100%
_app/immutable/chunks/REfynWsD.js 892 B → 0 B (-892 B)
489 B → 0 B (gzip)
-100%
_app/immutable/chunks/ChNofzWt.js 891 B → 0 B (-891 B)
441 B → 0 B (gzip)
-100%
_app/immutable/nodes/1.CadwbiJy.js 889 B → 0 B (-889 B)
574 B → 0 B (gzip)
-100%
_app/immutable/chunks/CTa3VcET.js 881 B → 0 B (-881 B)
533 B → 0 B (gzip)
-100%
_app/immutable/chunks/BGHfxDdT.js 879 B → 0 B (-879 B)
518 B → 0 B (gzip)
-100%
_app/immutable/chunks/D7KtSJj7.js 848 B → 0 B (-848 B)
439 B → 0 B (gzip)
-100%
_app/immutable/chunks/DDnq3p5T.js 846 B → 0 B (-846 B)
534 B → 0 B (gzip)
-100%
_app/immutable/chunks/DvkKVfti.js 804 B → 0 B (-804 B)
452 B → 0 B (gzip)
-100%
_app/immutable/chunks/D1ESK_wy.js 776 B → 0 B (-776 B)
460 B → 0 B (gzip)
-100%
_app/immutable/chunks/DE9xidcW.js 701 B → 0 B (-701 B)
377 B → 0 B (gzip)
-100%
_app/immutable/chunks/2m5fj3vx.js 689 B → 0 B (-689 B)
360 B → 0 B (gzip)
-100%
_app/immutable/chunks/D5ilb8A7.js 678 B → 0 B (-678 B)
469 B → 0 B (gzip)
-100%
_app/immutable/chunks/BzOrQNNn.js 641 B → 0 B (-641 B)
424 B → 0 B (gzip)
-100%
_app/immutable/chunks/CyBn-tVx.js 613 B → 0 B (-613 B)
414 B → 0 B (gzip)
-100%
_app/immutable/chunks/CPqgAJYp.js 613 B → 0 B (-613 B)
413 B → 0 B (gzip)
-100%
_app/immutable/chunks/ByvuWBPs.js 611 B → 0 B (-611 B)
401 B → 0 B (gzip)
-100%
_app/immutable/chunks/wdVNnjoY.js 605 B → 0 B (-605 B)
387 B → 0 B (gzip)
-100%
_app/immutable/chunks/DtZ0vr8R.js 584 B → 0 B (-584 B)
410 B → 0 B (gzip)
-100%
_app/immutable/chunks/B_6md9PG.js 580 B → 0 B (-580 B)
407 B → 0 B (gzip)
-100%
_app/immutable/assets/DropdownMenu.MWzSw6F0.css 557 B → 0 B (-557 B)
304 B → 0 B (gzip)
-100%
_app/immutable/chunks/DKKrFqiH.js 551 B → 0 B (-551 B)
392 B → 0 B (gzip)
-100%
_app/immutable/chunks/C21ijWDc.js 550 B → 0 B (-550 B)
380 B → 0 B (gzip)
-100%
_app/immutable/chunks/BOgcPPfP.js 545 B → 0 B (-545 B)
318 B → 0 B (gzip)
-100%
app/immutable/chunks/DJoOqv3.js 545 B → 0 B (-545 B)
387 B → 0 B (gzip)
-100%
_app/immutable/chunks/Df_lxiRT.js 536 B → 0 B (-536 B)
352 B → 0 B (gzip)
-100%
_app/immutable/chunks/Dh1-4aaA.js 531 B → 0 B (-531 B)
345 B → 0 B (gzip)
-100%
_app/immutable/chunks/-xqLbGsv.js 524 B → 0 B (-524 B)
351 B → 0 B (gzip)
-100%
_app/immutable/chunks/BLfkkyTy.js 523 B → 0 B (-523 B)
362 B → 0 B (gzip)
-100%
_app/immutable/chunks/Bg4Ni83P.js 522 B → 0 B (-522 B)
347 B → 0 B (gzip)
-100%
_app/immutable/chunks/D2EdAa-W.js 521 B → 0 B (-521 B)
383 B → 0 B (gzip)
-100%
_app/immutable/chunks/D3PsXshV.js 518 B → 0 B (-518 B)
320 B → 0 B (gzip)
-100%
_app/immutable/chunks/D2JgMQ8q.js 510 B → 0 B (-510 B)
377 B → 0 B (gzip)
-100%
_app/immutable/chunks/CSYbj_FL.js 502 B → 0 B (-502 B)
370 B → 0 B (gzip)
-100%
_app/immutable/chunks/B7l0To16.js 500 B → 0 B (-500 B)
367 B → 0 B (gzip)
-100%
_app/immutable/chunks/BaJhntkc.js 492 B → 0 B (-492 B)
354 B → 0 B (gzip)
-100%
_app/immutable/chunks/DuPCnGGU.js 483 B → 0 B (-483 B)
356 B → 0 B (gzip)
-100%
_app/immutable/chunks/0oFeVwuH.js 483 B → 0 B (-483 B)
361 B → 0 B (gzip)
-100%
_app/immutable/chunks/BP3rxBJ3.js 466 B → 0 B (-466 B)
278 B → 0 B (gzip)
-100%
_app/immutable/chunks/75nzIWHE.js 451 B → 0 B (-451 B)
335 B → 0 B (gzip)
-100%
_app/immutable/chunks/CN-XS2WN.js 447 B → 0 B (-447 B)
344 B → 0 B (gzip)
-100%
_app/immutable/chunks/CPpe_7RL.js 444 B → 0 B (-444 B)
340 B → 0 B (gzip)
-100%
_app/immutable/chunks/Bq7NDd4B.js 437 B → 0 B (-437 B)
265 B → 0 B (gzip)
-100%
_app/immutable/chunks/Bx_yO3dM.js 432 B → 0 B (-432 B)
328 B → 0 B (gzip)
-100%
_app/immutable/chunks/DwiZxRDe.js 430 B → 0 B (-430 B)
332 B → 0 B (gzip)
-100%
_app/immutable/chunks/DShUqE8i.js 424 B → 0 B (-424 B)
329 B → 0 B (gzip)
-100%
_app/immutable/chunks/C2IX9EDg.js 401 B → 0 B (-401 B)
315 B → 0 B (gzip)
-100%
_app/immutable/chunks/hFci9Gn4.js 396 B → 0 B (-396 B)
288 B → 0 B (gzip)
-100%
_app/immutable/chunks/Uj4D1cDq.js 382 B → 0 B (-382 B)
299 B → 0 B (gzip)
-100%
_app/immutable/chunks/rIhx3FPg.js 377 B → 0 B (-377 B)
248 B → 0 B (gzip)
-100%
_app/immutable/chunks/BIkWmN95.js 374 B → 0 B (-374 B)
288 B → 0 B (gzip)
-100%
_app/immutable/nodes/8.Z9lOtd79.js 328 B → 0 B (-328 B)
241 B → 0 B (gzip)
-100%
_app/immutable/chunks/DGx7p2Ee.js 325 B → 0 B (-325 B)
257 B → 0 B (gzip)
-100%
_app/immutable/chunks/DP-XA445.js 289 B → 0 B (-289 B)
218 B → 0 B (gzip)
-100%
_app/immutable/nodes/6.eUPnrw3E.js 285 B → 0 B (-285 B)
214 B → 0 B (gzip)
-100%
_app/immutable/nodes/9.BE054Q8k.js 283 B → 0 B (-283 B)
213 B → 0 B (gzip)
-100%
_app/immutable/chunks/yP74RMak.js 272 B → 0 B (-272 B)
174 B → 0 B (gzip)
-100%
_app/immutable/nodes/0.CDAGtPrO.js 257 B → 0 B (-257 B)
221 B → 0 B (gzip)
-100%
_app/immutable/chunks/CyH1usJM.js 240 B → 0 B (-240 B)
174 B → 0 B (gzip)
-100%
_app/immutable/nodes/24.CVOkSr0K.js 230 B → 0 B (-230 B)
202 B → 0 B (gzip)
-100%
_app/immutable/chunks/CJPmdx8s.js 210 B → 0 B (-210 B)
170 B → 0 B (gzip)
-100%
_app/immutable/chunks/D4fDEf1g.js 185 B → 0 B (-185 B)
170 B → 0 B (gzip)
-100%
_app/immutable/chunks/B-sF4vDz.js 138 B → 0 B (-138 B)
130 B → 0 B (gzip)
-100%
_app/immutable/chunks/DHFGj22C.js 136 B → 0 B (-136 B)
134 B → 0 B (gzip)
-100%
_app/immutable/chunks/ClwSbRJx.js 124 B → 0 B (-124 B)
122 B → 0 B (gzip)
-100%
_app/immutable/nodes/15.C_G7nfXC.js 112 B → 0 B (-112 B)
122 B → 0 B (gzip)
-100%
_app/immutable/chunks/DxItblp1.js 110 B → 0 B (-110 B)
118 B → 0 B (gzip)
-100%
_app/immutable/chunks/CSpJnAzH.js 96 B → 0 B (-96 B)
104 B → 0 B (gzip)
-100%
_app/immutable/entry/start.bV1oFZMY.js 83 B → 0 B (-83 B)
93 B → 0 B (gzip)
-100%
_app/immutable/chunks/B8JVsEC9.js 74 B → 0 B (-74 B)
93 B → 0 B (gzip)
-100%
_app/immutable/assets/arrow-right-s-line.AJW81_Sc.css 59 B → 0 B (-59 B)
78 B → 0 B (gzip)
-100%

Bigger

Asset File Size % Changed
_app/immutable/nodes/21.DPzO339H.js 99 B → 139 B (+40 B)
100 B → 140 B (gzip)
+40.40%
_app/immutable/chunks/DvXcruuH.js 30 B → 67 B (+37 B)
50 B → 87 B (gzip)
+123.33%
_app/immutable/chunks/PPVm8Dsz.js 1.2 kB → 1.24 kB (+37 B)
714 B → 747 B (gzip)
+3.00%

Smaller

Asset File Size % Changed
_app/immutable/workers/start-[hash].js 1.71 MB → 1.7 MB (-2.4 kB)
583.31 kB → 582.74 kB (gzip)
-0.14%

@gwennlbh
gwennlbh temporarily deployed to #1089: End-to-end tests report January 18, 2026 19:10 — with GitHub Actions Inactive
gwennlbh added a commit that referenced this pull request Jan 18, 2026
Not needed after all (we just had to parse more date formats when
deserializing from database, namely fractional seconds), but could be
useful in the future:

diff --git a/package.json b/package.json
index 8c1357b..9c7c19650 100644
--- a/package.json
+++ b/package.json
@@ -58,7 +58,8 @@
 		"test:ui": "vitest --ui",
 		"test:watch": "vitest",
 		"wuchale": "wuchale",
-		"all-contributors": "all-contributors --files README.md --files README_FR.md"
+		"all-contributors": "all-contributors --files README.md --files README_FR.md",
+		"test:update-fixtures": "playwright test tasks/exports.spec.js && playwright test tasks/dbdump.spec.js && playwright --update-snapshots '\\w+.spec.(js|ts)'"
 	},
 	"devDependencies": {
 		"@actions/core": "^2.0.2",
diff --git a/tests/tasks/dbdump.spec.js b/tests/tasks/dbdump.spec.js
index 464e91f..329e329e8 100644
--- a/tests/tasks/dbdump.spec.js
+++ b/tests/tasks/dbdump.spec.js
@@ -1,6 +1,3 @@
-import { readFile, writeFile } from 'node:fs/promises';
-import JSZip from 'jszip';
-
 import { exampleProtocol, expect, test } from '../fixtures.js';
 import {
 	confirmDeletionModal,
@@ -9,8 +6,7 @@ import {
 	importPhotos,
 	importProtocol,
 	importResults,
-	newSession,
-	sessionMetadataSectionFor
+	newSession
 } from '../utils.js';

 test.skip(
@@ -18,162 +14,28 @@ test.skip(
 	'Skipping database dumps and exports on CI, these are meant as easy way to (re)create dump fixtures locally only.'
 );

-test.describe('Database dumps', () => {
-	test('basic', async ({ page }) => {
-		await goToProtocolManagement(page);
-		await importProtocol(page, 'arthropods.light.cigaleprotocol.json');
-
-		await importResults(page, 'exports/correct.zip');
-		await dumpDatabase(page, 'basic.devalue');
-	});
+test('basic', async ({ page }) => {
+	await goToProtocolManagement(page);
+	await importProtocol(page, 'arthropods.light.cigaleprotocol.json');

-	test('kitchensink-protocol', async ({ page, app }) => {
-		await goToProtocolManagement(page);
-		await importProtocol(page, 'kitchensink.cigaleprotocol.yaml');
-		await page
-			.getByRole('listitem')
-			.filter({ hasText: exampleProtocol.id })
-			.getByRole('button', { name: 'Supprimer' })
-			.click();
-		await confirmDeletionModal(page, { type: exampleProtocol.name });
-		await expect(page.getByText('Protocole supprimé')).toBeVisible();
-		await newSession(page);
-
-		await app.tabs.go('import');
-		await importPhotos({ page }, 'cyan.jpeg', 'leaf.jpeg');
-		await page.waitForTimeout(2_000);
-		await dumpDatabase(page, 'kitchensink-protocol.devalue');
-	});
+	await importResults(page, 'exports/correct.zip');
+	await dumpDatabase(page, 'basic.devalue');
 });

-test.describe('Exports', () => {
-	/**
-	 * @PARAM {object} param0
-	 * @PARAM {import('@playwright/test').Page} param0.page
-	 * @PARAM {import('../fixtures.js').AppFixture} param0.app
-	 */
-	async function prepare({ page, app }) {
-		await newSession(page, { name: 'Testing session' });
-
-		await page.getByTestId('goto-current-session').click();
-		await page.waitForURL((u) => u.hash.startsWith('#/sessions/'));
-		await sessionMetadataSectionFor(page, 'Durée de prospection')
-			.getByRole('textbox')
-			.first()
-			.fill('54');
-		await sessionMetadataSectionFor(page, 'Durée de prospection')
-			.getByRole('textbox')
-			.first()
-			.blur();
-		await sessionMetadataSectionFor(page, 'Vent')
-			.getByRole('radiogroup')
-			.getByRole('radio', { name: 'Modéré' })
-			.check();
-
-		await app.tabs.go('import');
-		await importPhotos(
-			{ page },
-			'cyan.jpeg',
-			'leaf.jpeg',
-			'lil-fella.jpeg',
-			'with-exif-gps.jpeg'
-		);
-		await expect(page.getByText(/Analyse….|En attente/)).toHaveCount(0, {
-			timeout: 30_000
-		});
-
-		await app.tabs.go('crop');
-		await page.getByText('lil-fella.jpeg').click();
-		await page.getByRole('button', { name: 'Continuer' }).click();
-		await page.waitForTimeout(1000);
-		await page.getByRole('button', { name: 'Autres photos Esc' }).click();
-
-		await app.tabs.go('classify');
-		await page.waitForTimeout(1000);
-		await expect(page.getByText('Chargement du modèle de classification')).toHaveCount(0, {
-			timeout: 10_000
-		});
-		await page.waitForTimeout(5_000);
-		await expect(page.getByText(/Analyse…|En attente/)).toHaveCount(0, { timeout: 10_000 });
-	}
-
-	test('correct', async ({ page, app }) => {
-		await prepare({ page, app });
-		await app.tabs.go('results');
-		await page.getByText('Métadonnées, images recadrées et images originales').click();
-		await page.getByRole('button', { name: 'results.zip' }).click();
-		const download = await page.waitForEvent('download');
-		await download.saveAs('./tests/fixtures/exports/correct.zip');
-	});
-
-	test('no-originals', async ({ page, app }) => {
-		await prepare({ page, app });
-		await app.tabs.go('results');
-		await page.getByText('Métadonnées et images recadrées').click();
-		await page.getByRole('button', { name: 'results.zip' }).click();
-		const download = await page.waitForEvent('download');
-		await download.saveAs('./tests/fixtures/exports/no-originals.zip');
-	});
-
-	test('no-analysis', async ({ page, app }) => {
-		await prepare({ page, app });
-		await app.tabs.go('results');
-		await page.getByText('Métadonnées, images recadrées et images originales').click();
-		await page.getByRole('button', { name: 'results.zip' }).click();
-		const download = await page.waitForEvent('download');
-		const zipPath = './tests/fixtures/exports/no-analysis.zip';
-		await download.saveAs(zipPath);
-
-		// Remove analysis.json from the zip
-		const zipData = await readFile(zipPath);
-		const zip = await JSZip.loadAsync(zipData);
-		zip.remove('analysis.json');
-		const newZipData = await zip.generateAsync({ type: 'nodebuffer' });
-		await writeFile(zipPath, newZipData);
-	});
-
-	test('invalid-json-analysis', async ({ page, app }) => {
-		await prepare({ page, app });
-		await app.tabs.go('results');
-		await page.getByText('Métadonnées, images recadrées et images originales').click();
-		await page.getByRole('button', { name: 'results.zip' }).click();
-		const download = await page.waitForEvent('download');
-		const zipPath = './tests/fixtures/exports/invalid-json-analysis.zip';
-		await download.saveAs(zipPath);
-
-		// Erase first '{' from analysis.json
-		const zipData = await readFile(zipPath);
-		const zip = await JSZip.loadAsync(zipData);
-		const analysisFile = zip.file('analysis.json');
-		if (!analysisFile) throw new Error('analysis.json not found in zip');
-		const analysisContent = await analysisFile.async('string');
-		const modifiedContent = analysisContent.replace('{', '');
-		zip.file('analysis.json', modifiedContent);
-		const newZipData = await zip.generateAsync({ type: 'nodebuffer' });
-		await writeFile(zipPath, newZipData);
-	});
-
-	test('wrong-protocol', async ({ page, app }) => {
-		await prepare({ page, app });
-		await app.tabs.go('results');
-		await page.getByText('Métadonnées, images recadrées et images originales').click();
-		await page.getByRole('button', { name: 'results.zip' }).click();
-		const download = await page.waitForEvent('download');
-		await download.saveAs('./tests/fixtures/exports/wrong-protocol.zip');
-
-		// Change protocol ID in analysis.json
-		const zipPath = './tests/fixtures/exports/wrong-protocol.zip';
-		const zipData = await readFile(zipPath);
-		const zip = await JSZip.loadAsync(zipData);
-		const analysisFile = zip.file('analysis.json');
-		if (!analysisFile) throw new Error('analysis.json not found in zip');
-		const analysisContent = await analysisFile.async('string');
-		const modifiedContent = analysisContent.replace(
-			'"protocol": "io.github.cigaleapp.arthropods.example.light"',
-			'"protocol": "io.github.cigaleapp.arthropods.example.wrong"'
-		);
-		zip.file('analysis.json', modifiedContent);
-		const newZipData = await zip.generateAsync({ type: 'nodebuffer' });
-		await writeFile(zipPath, newZipData);
-	});
+test('kitchensink-protocol', async ({ page, app }) => {
+	await goToProtocolManagement(page);
+	await importProtocol(page, 'kitchensink.cigaleprotocol.yaml');
+	await page
+		.getByRole('listitem')
+		.filter({ hasText: exampleProtocol.id })
+		.getByRole('button', { name: 'Supprimer' })
+		.click();
+	await confirmDeletionModal(page, { type: exampleProtocol.name });
+	await expect(page.getByText('Protocole supprimé')).toBeVisible();
+	await newSession(page);
+
+	await app.tabs.go('import');
+	await importPhotos({ page }, 'cyan.jpeg', 'leaf.jpeg');
+	await page.waitForTimeout(2_000);
+	await dumpDatabase(page, 'kitchensink-protocol.devalue');
 });
diff --git a/tests/tasks/exports.spec.js b/tests/tasks/exports.spec.js
new file mode 100644
index 000000000..5ced92a59
--- /dev/null
+++ b/tests/tasks/exports.spec.js
@@ -0,0 +1,134 @@
+import { readFile, writeFile } from 'node:fs/promises';
+import JSZip from 'jszip';
+
+import { expect, test } from '../fixtures.js';
+import { importPhotos, newSession, sessionMetadataSectionFor } from '../utils.js';
+
+test.skip(
+	Boolean(process.env.CI),
+	'Skipping database dumps and exports on CI, these are meant as easy way to (re)create dump fixtures locally only.'
+);
+
+/**
+ * @PARAM {object} param0
+ * @PARAM {import('@playwright/test').Page} param0.page
+ * @PARAM {import('../fixtures.js').AppFixture} param0.app
+ */
+async function prepare({ page, app }) {
+	await newSession(page, { name: 'Testing session' });
+
+	await page.getByTestId('goto-current-session').click();
+	await page.waitForURL((u) => u.hash.startsWith('#/sessions/'));
+	await sessionMetadataSectionFor(page, 'Durée de prospection')
+		.getByRole('textbox')
+		.first()
+		.fill('54');
+	await sessionMetadataSectionFor(page, 'Durée de prospection')
+		.getByRole('textbox')
+		.first()
+		.blur();
+	await sessionMetadataSectionFor(page, 'Vent')
+		.getByRole('radiogroup')
+		.getByRole('radio', { name: 'Modéré' })
+		.check();
+
+	await app.tabs.go('import');
+	await importPhotos({ page }, 'cyan.jpeg', 'leaf.jpeg', 'lil-fella.jpeg', 'with-exif-gps.jpeg');
+	await expect(page.getByText(/Analyse….|En attente/)).toHaveCount(0, {
+		timeout: 30_000
+	});
+
+	await app.tabs.go('crop');
+	await page.getByText('lil-fella.jpeg').click();
+	await page.getByRole('button', { name: 'Continuer' }).click();
+	await page.waitForTimeout(1000);
+	await page.getByRole('button', { name: 'Autres photos Esc' }).click();
+
+	await app.tabs.go('classify');
+	await page.waitForTimeout(1000);
+	await expect(page.getByText('Chargement du modèle de classification')).toHaveCount(0, {
+		timeout: 10_000
+	});
+	await page.waitForTimeout(5_000);
+	await expect(page.getByText(/Analyse…|En attente/)).toHaveCount(0, { timeout: 10_000 });
+}
+
+test('correct', async ({ page, app }) => {
+	await prepare({ page, app });
+	await app.tabs.go('results');
+	await page.getByText('Métadonnées, images recadrées et images originales').click();
+	await page.getByRole('button', { name: 'results.zip' }).click();
+	const download = await page.waitForEvent('download');
+	await download.saveAs('./tests/fixtures/exports/correct.zip');
+});
+
+test('no-originals', async ({ page, app }) => {
+	await prepare({ page, app });
+	await app.tabs.go('results');
+	await page.getByText('Métadonnées et images recadrées').click();
+	await page.getByRole('button', { name: 'results.zip' }).click();
+	const download = await page.waitForEvent('download');
+	await download.saveAs('./tests/fixtures/exports/no-originals.zip');
+});
+
+test('no-analysis', async ({ page, app }) => {
+	await prepare({ page, app });
+	await app.tabs.go('results');
+	await page.getByText('Métadonnées, images recadrées et images originales').click();
+	await page.getByRole('button', { name: 'results.zip' }).click();
+	const download = await page.waitForEvent('download');
+	const zipPath = './tests/fixtures/exports/no-analysis.zip';
+	await download.saveAs(zipPath);
+
+	// Remove analysis.json from the zip
+	const zipData = await readFile(zipPath);
+	const zip = await JSZip.loadAsync(zipData);
+	zip.remove('analysis.json');
+	const newZipData = await zip.generateAsync({ type: 'nodebuffer' });
+	await writeFile(zipPath, newZipData);
+});
+
+test('invalid-json-analysis', async ({ page, app }) => {
+	await prepare({ page, app });
+	await app.tabs.go('results');
+	await page.getByText('Métadonnées, images recadrées et images originales').click();
+	await page.getByRole('button', { name: 'results.zip' }).click();
+	const download = await page.waitForEvent('download');
+	const zipPath = './tests/fixtures/exports/invalid-json-analysis.zip';
+	await download.saveAs(zipPath);
+
+	// Erase first '{' from analysis.json
+	const zipData = await readFile(zipPath);
+	const zip = await JSZip.loadAsync(zipData);
+	const analysisFile = zip.file('analysis.json');
+	if (!analysisFile) throw new Error('analysis.json not found in zip');
+	const analysisContent = await analysisFile.async('string');
+	const modifiedContent = analysisContent.replace('{', '');
+	zip.file('analysis.json', modifiedContent);
+	const newZipData = await zip.generateAsync({ type: 'nodebuffer' });
+	await writeFile(zipPath, newZipData);
+});
+
+test('wrong-protocol', async ({ page, app }) => {
+	await prepare({ page, app });
+	await app.tabs.go('results');
+	await page.getByText('Métadonnées, images recadrées et images originales').click();
+	await page.getByRole('button', { name: 'results.zip' }).click();
+	const download = await page.waitForEvent('download');
+	await download.saveAs('./tests/fixtures/exports/wrong-protocol.zip');
+
+	// Change protocol ID in analysis.json
+	const zipPath = './tests/fixtures/exports/wrong-protocol.zip';
+	const zipData = await readFile(zipPath);
+	const zip = await JSZip.loadAsync(zipData);
+	const analysisFile = zip.file('analysis.json');
+	if (!analysisFile) throw new Error('analysis.json not found in zip');
+	const analysisContent = await analysisFile.async('string');
+	const modifiedContent = analysisContent.replace(
+		'"protocol": "io.github.cigaleapp.arthropods.example.light"',
+		'"protocol": "io.github.cigaleapp.arthropods.example.wrong"'
+	);
+	zip.file('analysis.json', modifiedContent);
+	const newZipData = await zip.generateAsync({ type: 'nodebuffer' });
+	await writeFile(zipPath, newZipData);
+});

This branch was successfully deployed

5 active deployments
#1089: End-to-end tests report — 638f9120 Deployed Jan 22, 2026 by gwennlbh via E2E / Combine reports #4786
#1089: Test coverage report — 638f9120 Deployed Jan 22, 2026 by gwennlbh via Check coverage / Upload coverage report #4786
#1089: Unit tests report — 638f9120 Deployed Jan 22, 2026 by gwennlbh via Check coverage / Test #4786
#1089: App preview — 638f9120 Deployed Jan 22, 2026 by gwennlbh via PR Preview / Build & Deploy #4786
#1089: node_modules analysis — 638f9120 Deployed Jan 22, 2026 by gwennlbh via analyze #1721
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Custom group by & sort by

2 participants