From 2ed058795bf0ce968134aa8db18b0dd86b54c30e Mon Sep 17 00:00:00 2001
From: Seefs <40468931+seefs001@users.noreply.github.com>
Date: Mon, 12 Oct 2026 18:19:04 +0800
Subject: [PATCH] fix(ci): stabilize checks and configure Bun dependency
updates (#7765)
---
.github/dependabot.yml | 6 ++
.github/workflows/ci.yml | 6 +-
controller/security_account_test.go | 5 ++
controller/security_enrollment_test.go | 13 +++-
web/bun.lock | 37 ++--------
web/package.json | 7 +-
.../components/ui/__tests__/markdown.test.tsx | 68 +++++++++++++++++++
.../__tests__/model-mapping-editor.test.tsx | 16 ++---
8 files changed, 113 insertions(+), 45 deletions(-)
create mode 100644 .github/dependabot.yml
create mode 100644 web/src/components/ui/__tests__/markdown.test.tsx
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 000000000..92902fb1a
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,6 @@
+version: 2
+updates:
+ - package-ecosystem: "bun"
+ directory: "/web"
+ schedule:
+ interval: "weekly"
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index ca3407224..c20540c07 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -11,6 +11,8 @@ concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
+# Checkout the event commit in both jobs. A PR merge ref can disappear while
+# queued runs start, and a base branch can advance after the merge event.
jobs:
backend:
name: Backend vet, build, and test
@@ -25,7 +27,7 @@ jobs:
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
repository: ${{ github.event.pull_request.base.repo.full_name }}
- ref: ${{ github.event.action == 'closed' && github.event.pull_request.base.ref || github.ref }}
+ ref: ${{ github.sha }}
- name: Set up Go
uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0
@@ -80,7 +82,7 @@ jobs:
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
repository: ${{ github.event.pull_request.base.repo.full_name }}
- ref: ${{ github.event.action == 'closed' && github.event.pull_request.base.ref || github.ref }}
+ ref: ${{ github.sha }}
- name: Set up Bun
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
diff --git a/controller/security_account_test.go b/controller/security_account_test.go
index 37e29a24c..23a088286 100644
--- a/controller/security_account_test.go
+++ b/controller/security_account_test.go
@@ -234,6 +234,11 @@ func TestSecurityAccountDeletionConcurrentRequestsHaveOneWinner(t *testing.T) {
require.NoError(t, model.DB.Unscoped().First(&deleted, user.Id).Error)
assert.True(t, deleted.DeletedAt.Valid)
assert.Equal(t, identity.UserAuthVersion+1, deleted.AuthVersion)
+ _, _, err := service.ValidateLoginSession(identity)
+ assert.Error(t, err)
+ count, err := model.CountActiveUserSessions(user.Id, time.Now().Unix())
+ require.NoError(t, err)
+ assert.Zero(t, count)
}
type securityMailbox struct {
diff --git a/controller/security_enrollment_test.go b/controller/security_enrollment_test.go
index 3f44b4a7d..bb5a8e064 100644
--- a/controller/security_enrollment_test.go
+++ b/controller/security_enrollment_test.go
@@ -32,6 +32,7 @@ import (
"github.com/QuantumNous/new-api/setting/system_setting"
"github.com/fxamacker/cbor/v2"
"github.com/gin-gonic/gin"
+ "github.com/glebarez/sqlite"
"github.com/golang-jwt/jwt/v5"
"github.com/pquerna/otp/totp"
"github.com/stretchr/testify/assert"
@@ -54,7 +55,17 @@ func setupSecurityEnrollmentTest(t *testing.T) (*model.User, service.AuthIdentit
dialect = "sqlite"
}
dsn := os.Getenv("TEST_" + strings.ToUpper(dialect) + "_DSN")
- db, _ := newAuditTestDatabase(t, dialect, dsn)
+ var db *gorm.DB
+ if dialect == "sqlite" {
+ // Match common.SQLitePath: take the writer lock before transaction reads.
+ // Deferred transactions can fail to upgrade their read lock when concurrent
+ // proof consumption overlaps account deletion or session revocation.
+ var err error
+ db, err = gorm.Open(sqlite.Open(t.TempDir()+"/security.db?_pragma=busy_timeout(30000)&_pragma=journal_mode(WAL)&_txlock=immediate"), &gorm.Config{})
+ require.NoError(t, err)
+ } else {
+ db, _ = newAuditTestDatabase(t, dialect, dsn)
+ }
logDB, _ := newAuditTestDatabase(t, dialect, dsn)
db.Logger = logger.Default.LogMode(logger.Silent)
logDB.Logger = logger.Default.LogMode(logger.Silent)
diff --git a/web/bun.lock b/web/bun.lock
index 22f73e5b8..d8cb685c8 100644
--- a/web/bun.lock
+++ b/web/bun.lock
@@ -31,11 +31,11 @@
"clsx": "^2.1.1",
"cmdk": "^1.1.1",
"dayjs": "^1.11.21",
- "dompurify": "3.4.13",
+ "dompurify": "3.4.16",
"i18next": "^26.3.4",
"i18next-browser-languagedetector": "^8.2.1",
"input-otp": "^1.4.2",
- "katex": "^0.17.0",
+ "katex": "^0.18.2",
"lucide-react": "^1.23.0",
"marked": "^18.0.5",
"motion": "^12.42.2",
@@ -95,11 +95,12 @@
},
"overrides": {
"brace-expansion": "2.1.1",
- "dompurify": "3.4.13",
+ "dompurify": "3.4.16",
"fast-uri": "3.1.2",
"hono": "4.12.22",
"ip-address": "10.2.0",
"js-cookie": "3.0.7",
+ "katex": "0.18.2",
"mermaid": "11.15.0",
"minimist": "1.2.8",
"postcss": "8.5.15",
@@ -1011,10 +1012,6 @@
"@types/validate-npm-package-name": ["@types/validate-npm-package-name@4.0.2", "", {}, "sha512-lrpDziQipxCEeK5kWxvljWYhUvOiB2A9izZd9B2AFarYAkqZshb4lPbRs7zKEic6eGtH8V/2qJW+dPp9OtF6bw=="],
- "@types/whatwg-mimetype": ["@types/whatwg-mimetype@3.0.2", "", {}, "sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA=="],
-
- "@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="],
-
"@typescript/native-preview": ["@typescript/native-preview@7.0.0-dev.20260707.2", "", { "optionalDependencies": { "@typescript/native-preview-darwin-arm64": "7.0.0-dev.20260707.2", "@typescript/native-preview-darwin-x64": "7.0.0-dev.20260707.2", "@typescript/native-preview-linux-arm": "7.0.0-dev.20260707.2", "@typescript/native-preview-linux-arm64": "7.0.0-dev.20260707.2", "@typescript/native-preview-linux-x64": "7.0.0-dev.20260707.2", "@typescript/native-preview-win32-arm64": "7.0.0-dev.20260707.2", "@typescript/native-preview-win32-x64": "7.0.0-dev.20260707.2" }, "bin": { "tsgo": "bin/tsgo" } }, "sha512-oUGp+Rep/hqMhPunyinsALUwSlzHINSxitifPiSaeqoKOKD2OlR9NE3TaPqwsl4NlGslsOSUXI1JotWQzpYCPg=="],
"@typescript/native-preview-darwin-arm64": ["@typescript/native-preview-darwin-arm64@7.0.0-dev.20260707.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-wny2pgKjGbiZtnOIHVa3tXC1UfDqxNEFzyPGmiqybedG8hipG2Nfp0l5UxbaKCjkLacUpH/W5bP2hBOMVhCOzg=="],
@@ -1165,8 +1162,6 @@
"buffer-from": ["buffer-from@1.1.2", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="],
- "buffer-image-size": ["buffer-image-size@0.6.4", "", { "dependencies": { "@types/node": "*" } }, "sha512-nEh+kZOPY1w+gcCMobZ6ETUp9WfibndnosbpwB1iJk/8Gt5ZF2bhS6+B6bPYz424KtwsR6Rflc3tCz1/ghX2dQ=="],
-
"bundle-name": ["bundle-name@4.1.0", "", { "dependencies": { "run-applescript": "^7.0.0" } }, "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q=="],
"bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="],
@@ -1385,7 +1380,7 @@
"dom-accessibility-api": ["dom-accessibility-api@0.6.3", "", {}, "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w=="],
- "dompurify": ["dompurify@3.4.13", "", { "optionalDependencies": { "@types/trusted-types": "^2.0.7" } }, "sha512-2vmYIoqjze2d+kakP8S/nS5shfsl587kzwEjcGlTdiksUVgFHnFCsLYDVj/JNqJVOQZGSYBTmuycv0PodwmnMQ=="],
+ "dompurify": ["dompurify@3.4.16", "", { "optionalDependencies": { "@types/trusted-types": "^2.0.7" } }, "sha512-sqo+pNp3qRhCIpbgRi1y8Tgk27Bo2Ry7w0dC1NBeNTdZChWjz9Xb/KOoZbRP/R6pQZ80Qw8YhXw13hWWBbMRnQ=="],
"dot-prop": ["dot-prop@6.0.1", "", { "dependencies": { "is-obj": "^2.0.0" } }, "sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA=="],
@@ -1569,8 +1564,6 @@
"hachure-fill": ["hachure-fill@0.5.2", "", {}, "sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg=="],
- "happy-dom": ["happy-dom@20.11.1", "", { "dependencies": { "@types/node": ">=20.0.0", "@types/whatwg-mimetype": "^3.0.2", "@types/ws": "^8.18.1", "buffer-image-size": "^0.6.4", "entities": "^7.0.1", "whatwg-mimetype": "^3.0.0", "ws": "^8.21.0" } }, "sha512-XSt8tMzbW9ymE7687xztkO1ckR7qJNQ3LywY9vlYGhGi3zXrGBHuUo2Cl1ztZaICW+1eAGdkLbj6iwVqDT33kg=="],
-
"has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="],
"has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="],
@@ -1741,7 +1734,7 @@
"jsonfile": ["jsonfile@6.2.1", "", { "dependencies": { "universalify": "^2.0.0" }, "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q=="],
- "katex": ["katex@0.17.0", "", { "dependencies": { "commander": "^8.3.0" }, "bin": { "katex": "cli.js" } }, "sha512-Vdw0ATsQ9V+LuegM/BTwQqV/6cTl5lbGcIrU+BCgLxyf6bo38ybOr372tuSIxir3CN720flu1meYR6XzNMwQnw=="],
+ "katex": ["katex@0.18.2", "", { "dependencies": { "commander": "^8.3.0" }, "bin": { "katex": "cli.js" } }, "sha512-3snve4y0SXTMequLim1FMiPvLGElySam0blN5xQZBqEY3lOJz1TnuaaiugnBZBqHzlSAGmFTYL7MCQkORuLKCA=="],
"khroma": ["khroma@2.1.0", "", {}, "sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw=="],
@@ -2583,8 +2576,6 @@
"wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="],
- "ws": ["ws@8.21.1", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+0NTnW77fFN/DjQi6k/Sq/Yvk4Sgajw7urW8V+asjXnRgDs9gyGkdb7EzgfhA4goXsRIZKE28fzIXBHEzhuiWw=="],
-
"wsl-utils": ["wsl-utils@0.3.1", "", { "dependencies": { "is-wsl": "^3.1.0", "powershell-utils": "^0.1.0" } }, "sha512-g/eziiSUNBSsdDJtCLB8bdYEUMj4jR7AGeUo96p/3dTafgjHhpF4RiCFPiRILwjQoDXx5MqkBr4fwWtR3Ky4Wg=="],
"xml-name-validator": ["xml-name-validator@5.0.0", "", {}, "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg=="],
@@ -2733,10 +2724,6 @@
"geojson-dissolve/@turf/meta": ["@turf/meta@3.14.0", "", {}, "sha512-OtXqLQuR9hlQ/HkAF/OdzRea7E0eZK1ay8y8CBXkoO2R6v34CsDrWYLMSo0ZzMsaQDpKo76NPP2GGo+PyG1cSg=="],
- "happy-dom/entities": ["entities@7.0.1", "", {}, "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA=="],
-
- "happy-dom/whatwg-mimetype": ["whatwg-mimetype@3.0.0", "", {}, "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q=="],
-
"hast-util-from-html/parse5": ["parse5@7.3.0", "", { "dependencies": { "entities": "^6.0.0" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="],
"hast-util-raw/parse5": ["parse5@7.3.0", "", { "dependencies": { "entities": "^6.0.0" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="],
@@ -2757,12 +2744,8 @@
"mermaid/es-toolkit": ["es-toolkit@1.49.0", "", {}, "sha512-G5iZ6Pc/FNRY/soKZHC+TxGDD83rHUDXxzaWhGCX44vAv/tMs56WMusnm/KMNK+luUPsgA9U28cGr4RDlSzL2g=="],
- "mermaid/katex": ["katex@0.16.47", "", { "dependencies": { "commander": "^8.3.0" }, "bin": { "katex": "cli.js" } }, "sha512-Eeo8Ys1doU1z+x8AZsPpQu+p/QcZBI5PeOo7QGQdy2x2m0MU/hYagBbGOmXwr5KVbEfVuWv9LpnQWeehogurjg=="],
-
"mermaid/marked": ["marked@16.4.2", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA=="],
- "micromark-extension-math/katex": ["katex@0.16.47", "", { "dependencies": { "commander": "^8.3.0" }, "bin": { "katex": "cli.js" } }, "sha512-Eeo8Ys1doU1z+x8AZsPpQu+p/QcZBI5PeOo7QGQdy2x2m0MU/hYagBbGOmXwr5KVbEfVuWv9LpnQWeehogurjg=="],
-
"micromatch/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="],
"npm-run-path/path-key": ["path-key@4.0.0", "", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="],
@@ -2785,8 +2768,6 @@
"recharts/es-toolkit": ["es-toolkit@1.49.0", "", {}, "sha512-G5iZ6Pc/FNRY/soKZHC+TxGDD83rHUDXxzaWhGCX44vAv/tMs56WMusnm/KMNK+luUPsgA9U28cGr4RDlSzL2g=="],
- "rehype-katex/katex": ["katex@0.16.47", "", { "dependencies": { "commander": "^8.3.0" }, "bin": { "katex": "cli.js" } }, "sha512-Eeo8Ys1doU1z+x8AZsPpQu+p/QcZBI5PeOo7QGQdy2x2m0MU/hYagBbGOmXwr5KVbEfVuWv9LpnQWeehogurjg=="],
-
"restore-cursor/onetime": ["onetime@7.0.0", "", { "dependencies": { "mimic-function": "^5.0.0" } }, "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ=="],
"rolldown/@oxc-project/types": ["@oxc-project/types@0.139.0", "", {}, "sha512-r9gHphtCs+1M7J0pw6Sn/hh/Wpa/iQrOOkrNAlVLF/gHq+/CJmHIWKKUUhdWjcD6CIa8idarspCsASiXCXvFUw=="],
@@ -2853,12 +2834,6 @@
"hast-util-raw/parse5/entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="],
- "mermaid/katex/commander": ["commander@8.3.0", "", {}, "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww=="],
-
- "micromark-extension-math/katex/commander": ["commander@8.3.0", "", {}, "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww=="],
-
- "rehype-katex/katex/commander": ["commander@8.3.0", "", {}, "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww=="],
-
"send/mime-types/mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="],
"simplify-geojson/concat-stream/readable-stream": ["readable-stream@1.1.14", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.1", "isarray": "0.0.1", "string_decoder": "~0.10.x" } }, "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ=="],
diff --git a/web/package.json b/web/package.json
index 34ac9bb7d..a21d106c9 100644
--- a/web/package.json
+++ b/web/package.json
@@ -50,11 +50,11 @@
"clsx": "^2.1.1",
"cmdk": "^1.1.1",
"dayjs": "^1.11.21",
- "dompurify": "3.4.13",
+ "dompurify": "3.4.16",
"i18next": "^26.3.4",
"i18next-browser-languagedetector": "^8.2.1",
"input-otp": "^1.4.2",
- "katex": "^0.17.0",
+ "katex": "^0.18.2",
"lucide-react": "^1.23.0",
"marked": "^18.0.5",
"motion": "^12.42.2",
@@ -112,11 +112,12 @@
},
"overrides": {
"brace-expansion": "2.1.1",
- "dompurify": "3.4.13",
+ "dompurify": "3.4.16",
"fast-uri": "3.1.2",
"hono": "4.12.22",
"ip-address": "10.2.0",
"js-cookie": "3.0.7",
+ "katex": "0.18.2",
"mermaid": "11.15.0",
"minimist": "1.2.8",
"postcss": "8.5.15",
diff --git a/web/src/components/ui/__tests__/markdown.test.tsx b/web/src/components/ui/__tests__/markdown.test.tsx
new file mode 100644
index 000000000..6d4171ae1
--- /dev/null
+++ b/web/src/components/ui/__tests__/markdown.test.tsx
@@ -0,0 +1,68 @@
+/*
+Copyright (C) 2023-2026 QuantumNous
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with this program. If not, see .
+
+For commercial licensing, please contact support@quantumnous.com
+*/
+import { render } from '@testing-library/react'
+import { expect, test } from 'vitest'
+
+import { Markdown } from '../markdown'
+
+test.each([
+ ['inline', 'Formula: $$\\frac{1}{2}$$.'],
+ ['display', '$$\n\\frac{1}{2}\n$$'],
+ ['fenced', '```math\n\\frac{1}{2}\n```'],
+])('preserves MathML and visual output when rendering %s math', (_, source) => {
+ const view = render({source})
+ const math = view.getByRole('math', { hidden: true })
+
+ expect(math.querySelector('mfrac')).not.toBeNull()
+ expect(math.querySelector('annotation')).toHaveTextContent('\\frac{1}{2}')
+ expect(view.container.querySelector('.katex-html')).toHaveAttribute(
+ 'aria-hidden',
+ 'true'
+ )
+})
+
+test('does not load math resources when trust is inherited from a polluted prototype', () => {
+ const originalTrust = Object.getOwnPropertyDescriptor(
+ Object.prototype,
+ 'trust'
+ )
+
+ try {
+ Object.defineProperty(Object.prototype, 'trust', {
+ configurable: true,
+ value: true,
+ writable: true,
+ })
+
+ const view = render(
+
+ {'$$\\includegraphics{https://example.com/untrusted.png}$$'}
+
+ )
+
+ expect(view.container.querySelector('img')).toBeNull()
+ expect(view.container).toHaveTextContent('\\includegraphics')
+ } finally {
+ if (originalTrust) {
+ Object.defineProperty(Object.prototype, 'trust', originalTrust)
+ } else {
+ Reflect.deleteProperty(Object.prototype, 'trust')
+ }
+ }
+})
diff --git a/web/src/features/channels/components/__tests__/model-mapping-editor.test.tsx b/web/src/features/channels/components/__tests__/model-mapping-editor.test.tsx
index 907e15b96..2c4ab8965 100644
--- a/web/src/features/channels/components/__tests__/model-mapping-editor.test.tsx
+++ b/web/src/features/channels/components/__tests__/model-mapping-editor.test.tsx
@@ -190,15 +190,15 @@ test('a draft request with only the upstream name focuses the request field, and
draftRequest={{ from: '', to: 'up-1', token: 1 }}
/>
)
- await waitFor(() =>
- expect(
- screen.getAllByRole('combobox', { name: 'Request Model Name' })
- ).toHaveLength(2)
- )
+ // The row is committed before its passive effect moves focus.
+ await waitFor(() => {
+ const requestFields = screen.getAllByRole('combobox', {
+ name: 'Request Model Name',
+ })
+ expect(requestFields).toHaveLength(2)
+ expect(requestFields[1]).toHaveFocus()
+ })
expect(screen.getByDisplayValue('up-1')).toBeVisible()
- expect(
- screen.getAllByRole('combobox', { name: 'Request Model Name' })[1]
- ).toHaveFocus()
view.rerender(