mirror of
https://github.com/apache/superset.git
synced 2026-05-20 07:15:17 +00:00
Compare commits
5 Commits
tdd/issue-
...
docs/dashb
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ca885b2341 | ||
|
|
ac5e8f1308 | ||
|
|
f98edc351e | ||
|
|
4ceefb7e40 | ||
|
|
1b9f06c840 |
168
docs/admin_docs/configuration/dashboard-performance.mdx
Normal file
168
docs/admin_docs/configuration/dashboard-performance.mdx
Normal file
@@ -0,0 +1,168 @@
|
||||
---
|
||||
title: Dashboard Performance
|
||||
hide_title: true
|
||||
sidebar_position: 5
|
||||
version: 1
|
||||
---
|
||||
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
-->
|
||||
|
||||
# Dashboard Performance
|
||||
|
||||
A dashboard's perceived speed is determined by three independent things: how
|
||||
many charts have to render, how many queries the backend can execute
|
||||
concurrently, and how quickly the underlying data warehouse can return
|
||||
results. Superset gives you levers for the first two; the third belongs to
|
||||
your warehouse. This page covers the dashboard-side levers and the practical
|
||||
guidance around them.
|
||||
|
||||
## Is there a maximum chart count per dashboard?
|
||||
|
||||
**No hard limit is enforced** — Superset has no configuration key that
|
||||
caps the number of charts on a dashboard. In practice, dashboards behave
|
||||
well up to a few dozen charts. Beyond that, you'll typically feel friction
|
||||
on the initial load and during cross-filter / time-range updates, even with
|
||||
the lazy-loading optimizations described below.
|
||||
|
||||
Rough thresholds to keep in mind:
|
||||
|
||||
- **Under ~25 charts**: usually no perceptible problem.
|
||||
- **25–50 charts**: still fine, but you start to want tabs to break the
|
||||
page into chunks the user actually looks at.
|
||||
- **Over ~50 charts**: split into multiple dashboards or use tabs
|
||||
aggressively. The bottleneck is rarely Superset itself — it's the
|
||||
warehouse executing dozens of queries in parallel and the browser
|
||||
rendering dozens of chart frames.
|
||||
|
||||
These are guidelines, not guarantees. A dashboard of 100 sparkline-style
|
||||
charts hitting a fast cache behaves very differently from a dashboard of
|
||||
20 heavy aggregations against a cold warehouse.
|
||||
|
||||
## Lazy rendering — `DASHBOARD_VIRTUALIZATION`
|
||||
|
||||
Superset's dashboard layout is virtualized at the row level. Charts that
|
||||
are far below the user's current scroll position are not rendered (and
|
||||
therefore don't fetch data) until the user scrolls them into view, and they
|
||||
are unmounted again if scrolled well past. This is on by default.
|
||||
|
||||
**Feature flag**: `DASHBOARD_VIRTUALIZATION` (default: `True`)
|
||||
|
||||
The flag is `stable` and marked for path-to-deprecation — meaning the
|
||||
behavior will eventually be non-optional, but the flag still exists so
|
||||
operators can disable it if a specific layout misbehaves.
|
||||
|
||||
**Behavior** (from `superset-frontend/src/dashboard/components/gridComponents/Row/Row.tsx`):
|
||||
|
||||
- A chart is rendered when its row scrolls within **1 viewport height** of
|
||||
the visible area.
|
||||
- A chart is unmounted when its row scrolls more than **4 viewport
|
||||
heights** away from the visible area.
|
||||
- Tabs that aren't currently selected don't render their content at all
|
||||
(see below).
|
||||
- The unmounting half is skipped in **embedded** mode (so an embedded
|
||||
dashboard keeps its charts mounted once they've been seen, which avoids
|
||||
re-fetching on scroll-up). Both halves are skipped for **headless /
|
||||
bot** rendering (so screenshot / report jobs load every chart).
|
||||
|
||||
## Deferred data fetch — `DASHBOARD_VIRTUALIZATION_DEFER_DATA`
|
||||
|
||||
By default, `DASHBOARD_VIRTUALIZATION` controls *rendering* — but charts
|
||||
that don't render also don't fetch data, because Superset's chart
|
||||
components issue their data request on mount. `DASHBOARD_VIRTUALIZATION_DEFER_DATA`
|
||||
is a supplementary flag that further defers the data request itself, useful
|
||||
for backends where opening a connection or compiling a query is expensive
|
||||
even if the result is later thrown away.
|
||||
|
||||
**Feature flag**: `DASHBOARD_VIRTUALIZATION_DEFER_DATA` (default: `False`)
|
||||
|
||||
Enable this if you see warehouse load spike on dashboard *open* even
|
||||
though most charts are off-screen.
|
||||
|
||||
## Per-tab lazy loading
|
||||
|
||||
**This is on by default and has no flag.** A tab's content is not rendered
|
||||
until the user activates that tab, so charts inside an unselected tab do
|
||||
not fetch data on dashboard open. When the user clicks the tab, that
|
||||
tab's charts mount and fetch in the normal way.
|
||||
|
||||
Practically: tabs are the single most effective tool for a large
|
||||
dashboard. Splitting 60 charts across 4 tabs effectively turns dashboard
|
||||
open into "load ~15 charts," and the remaining ones lazy-load only if the
|
||||
user goes looking.
|
||||
|
||||
## Is there a switch to cap concurrent chart queries?
|
||||
|
||||
**No.** Superset does not implement a frontend-side concurrent-request
|
||||
limiter. Each chart issues its own data request when it mounts, and the
|
||||
browser handles parallelism (typically ~6 in-flight HTTP requests per
|
||||
origin, then the rest queue). Backend throughput is bounded by your
|
||||
Gunicorn worker count for synchronous query execution, or by your Celery
|
||||
worker pool when [async queries](./async-queries-celery.mdx) are enabled.
|
||||
|
||||
If you need to throttle warehouse load, the right place is:
|
||||
|
||||
1. The warehouse itself (connection pool / concurrency limits).
|
||||
2. Superset's Celery configuration (smaller worker pool when async
|
||||
queries are on).
|
||||
3. Splitting heavy charts across tabs or separate dashboards (each
|
||||
dashboard load only fetches what's visible).
|
||||
|
||||
## Splitting strategies
|
||||
|
||||
When a dashboard outgrows comfortable performance, the options in order
|
||||
of effort:
|
||||
|
||||
**1. Move sections into tabs.** Same dashboard, but only the active tab's
|
||||
charts fetch. This is the cheapest change and often the only one needed.
|
||||
|
||||
**2. Cache aggressively.** A Redis cache backend (see
|
||||
[Caching](./cache.mdx)) means repeat dashboard loads serve from cache
|
||||
rather than re-hitting the warehouse. This is especially impactful for
|
||||
dashboards opened by many users in close succession.
|
||||
|
||||
**3. Enable async queries.** [Async query execution](./async-queries-celery.mdx)
|
||||
via Celery decouples query duration from request lifetime, so a slow
|
||||
chart doesn't block the page. The user sees other charts come in as
|
||||
their queries complete.
|
||||
|
||||
**4. Split into multiple dashboards.** Group related charts into purpose-
|
||||
specific dashboards rather than one mega-dashboard. Link them from a
|
||||
landing dashboard or a navigation menu.
|
||||
|
||||
**5. Pre-aggregate at the warehouse level.** If the same expensive
|
||||
aggregation appears across many charts, materialize it as a view or
|
||||
scheduled table in the warehouse so each chart query is a cheap lookup.
|
||||
|
||||
## Operational notes
|
||||
|
||||
- The feature flags above are set in `superset_config.py`, e.g.:
|
||||
|
||||
```python
|
||||
FEATURE_FLAGS = {
|
||||
"DASHBOARD_VIRTUALIZATION": True,
|
||||
"DASHBOARD_VIRTUALIZATION_DEFER_DATA": True,
|
||||
}
|
||||
```
|
||||
|
||||
- See [Feature Flags](./feature-flags.mdx) for the full list of supported
|
||||
flags and their lifecycle stages.
|
||||
- Report and screenshot jobs (alerts, scheduled reports, dashboard
|
||||
exports) intentionally bypass row virtualization so the rendered
|
||||
artifact includes every chart, not just the ones above the fold.
|
||||
@@ -89,7 +89,7 @@
|
||||
"remark-import-partial": "^0.0.2",
|
||||
"reselect": "^5.2.0",
|
||||
"storybook": "^8.6.18",
|
||||
"swagger-ui-react": "^5.32.5",
|
||||
"swagger-ui-react": "^5.32.6",
|
||||
"swc-loader": "^0.2.7",
|
||||
"tinycolor2": "^1.4.2",
|
||||
"unist-util-visit": "^5.1.0"
|
||||
@@ -127,7 +127,8 @@
|
||||
"resolutions": {
|
||||
"react-redux": "^9.2.0",
|
||||
"@reduxjs/toolkit": "^2.5.0",
|
||||
"baseline-browser-mapping": "^2.9.19"
|
||||
"baseline-browser-mapping": "^2.9.19",
|
||||
"swagger-client": "3.37.3"
|
||||
},
|
||||
"packageManager": "yarn@1.22.22+sha1.ac34549e6aa8e7ead463a7407e1c7390f61a6610"
|
||||
}
|
||||
|
||||
471
docs/yarn.lock
471
docs/yarn.lock
@@ -3898,59 +3898,6 @@
|
||||
"@swagger-api/apidom-error" "^1.11.0"
|
||||
"@swaggerexpert/json-pointer" "^2.10.1"
|
||||
|
||||
"@swagger-api/apidom-ns-api-design-systems@^1.11.0":
|
||||
version "1.11.0"
|
||||
resolved "https://registry.yarnpkg.com/@swagger-api/apidom-ns-api-design-systems/-/apidom-ns-api-design-systems-1.11.0.tgz#c9bee1def674b6b12559a97da243c9358e4f5d13"
|
||||
integrity sha512-IskDsUkUtNas4guoChRKKkw0wOst64nRA24WuIjLf8ztfBdcl/oqx/cgy8pwWCUqNYvL9L3+sD5HeuokqMrySw==
|
||||
dependencies:
|
||||
"@babel/runtime-corejs3" "^7.26.10"
|
||||
"@swagger-api/apidom-core" "^1.11.0"
|
||||
"@swagger-api/apidom-error" "^1.11.0"
|
||||
"@swagger-api/apidom-ns-openapi-3-1" "^1.11.0"
|
||||
"@types/ramda" "~0.30.0"
|
||||
ramda "~0.30.0"
|
||||
ramda-adjunct "^5.0.0"
|
||||
ts-mixer "^6.0.3"
|
||||
|
||||
"@swagger-api/apidom-ns-arazzo-1@^1.11.0":
|
||||
version "1.11.0"
|
||||
resolved "https://registry.yarnpkg.com/@swagger-api/apidom-ns-arazzo-1/-/apidom-ns-arazzo-1-1.11.0.tgz#ceda21f89fe970d8c6e2504e0ea644f864eabae1"
|
||||
integrity sha512-n+aGSlLHyrpmCaBa9DBZkIqnNVzYAYSa010MvAwhlwtW3EbFYNwYWinbTwLqCd3leN6XWTvQYCvk0/k7/9Cq4A==
|
||||
dependencies:
|
||||
"@babel/runtime-corejs3" "^7.26.10"
|
||||
"@swagger-api/apidom-core" "^1.11.0"
|
||||
"@swagger-api/apidom-ns-json-schema-2020-12" "^1.11.0"
|
||||
"@types/ramda" "~0.30.0"
|
||||
ramda "~0.30.0"
|
||||
ramda-adjunct "^5.0.0"
|
||||
ts-mixer "^6.0.3"
|
||||
|
||||
"@swagger-api/apidom-ns-asyncapi-2@^1.11.0":
|
||||
version "1.11.0"
|
||||
resolved "https://registry.yarnpkg.com/@swagger-api/apidom-ns-asyncapi-2/-/apidom-ns-asyncapi-2-1.11.0.tgz#9e7dcb40c6de940c6b4ad23c8413864aa3286127"
|
||||
integrity sha512-SHh3naFZlXFI0gG36tNYvJ/VO8aZsjnXIQAqJHfOE6rrpl5msJrdDatmNczh+57WPZxEZA+KTXWCqNKdeu3G3Q==
|
||||
dependencies:
|
||||
"@babel/runtime-corejs3" "^7.26.10"
|
||||
"@swagger-api/apidom-core" "^1.11.0"
|
||||
"@swagger-api/apidom-ns-json-schema-draft-7" "^1.11.0"
|
||||
"@types/ramda" "~0.30.0"
|
||||
ramda "~0.30.0"
|
||||
ramda-adjunct "^5.0.0"
|
||||
ts-mixer "^6.0.3"
|
||||
|
||||
"@swagger-api/apidom-ns-asyncapi-3@^1.11.0":
|
||||
version "1.11.0"
|
||||
resolved "https://registry.yarnpkg.com/@swagger-api/apidom-ns-asyncapi-3/-/apidom-ns-asyncapi-3-1.11.0.tgz#92a3f5b78f795f651114a5d7e572dadd051e62b0"
|
||||
integrity sha512-4vrgNYDj68hgmgZj1eGBaBr5xqIETWn4jAioiRHek4jV1FLvmxCs3nC2nYs8CzQqqJ1bqirdiirrUpqhaQvTEA==
|
||||
dependencies:
|
||||
"@babel/runtime-corejs3" "^7.26.10"
|
||||
"@swagger-api/apidom-core" "^1.11.0"
|
||||
"@swagger-api/apidom-ns-asyncapi-2" "^1.11.0"
|
||||
"@types/ramda" "~0.30.0"
|
||||
ramda "~0.30.0"
|
||||
ramda-adjunct "^5.0.0"
|
||||
ts-mixer "^6.0.3"
|
||||
|
||||
"@swagger-api/apidom-ns-json-schema-2019-09@^1.11.0":
|
||||
version "1.11.0"
|
||||
resolved "https://registry.yarnpkg.com/@swagger-api/apidom-ns-json-schema-2019-09/-/apidom-ns-json-schema-2019-09-1.11.0.tgz#18531aa5a09192d2f296474f458b493e64f40d5c"
|
||||
@@ -4020,20 +3967,6 @@
|
||||
ramda-adjunct "^5.0.0"
|
||||
ts-mixer "^6.0.4"
|
||||
|
||||
"@swagger-api/apidom-ns-openapi-2@^1.11.0":
|
||||
version "1.11.0"
|
||||
resolved "https://registry.yarnpkg.com/@swagger-api/apidom-ns-openapi-2/-/apidom-ns-openapi-2-1.11.0.tgz#89b97db3173589cab4b9c57650a6d75638f870bb"
|
||||
integrity sha512-cAIPJhLxm/nj1kzneNySeaTahY+hH5gkGNsgbmifGnLPsC5YOOfEVMKLj18IREdXqdnxJgRbsI9Azl4g09TPkg==
|
||||
dependencies:
|
||||
"@babel/runtime-corejs3" "^7.26.10"
|
||||
"@swagger-api/apidom-core" "^1.11.0"
|
||||
"@swagger-api/apidom-error" "^1.11.0"
|
||||
"@swagger-api/apidom-ns-json-schema-draft-4" "^1.11.0"
|
||||
"@types/ramda" "~0.30.0"
|
||||
ramda "~0.30.0"
|
||||
ramda-adjunct "^5.0.0"
|
||||
ts-mixer "^6.0.3"
|
||||
|
||||
"@swagger-api/apidom-ns-openapi-3-0@^1.11.0":
|
||||
version "1.11.0"
|
||||
resolved "https://registry.yarnpkg.com/@swagger-api/apidom-ns-openapi-3-0/-/apidom-ns-openapi-3-0-1.11.0.tgz#4b44fb8a292439f4966bedef7d0c484926ece0e5"
|
||||
@@ -4081,285 +4014,10 @@
|
||||
ramda-adjunct "^5.0.0"
|
||||
ts-mixer "^6.0.3"
|
||||
|
||||
"@swagger-api/apidom-parser-adapter-api-design-systems-json@^1.11.0":
|
||||
version "1.11.0"
|
||||
resolved "https://registry.yarnpkg.com/@swagger-api/apidom-parser-adapter-api-design-systems-json/-/apidom-parser-adapter-api-design-systems-json-1.11.0.tgz#b7c757fadafe6bbcfb12f897b9f7432795de9460"
|
||||
integrity sha512-0OdwcnV/QF+Vs3Vj0dTmlRHEp9WQg9aBvWWl8Fq25OviyDhGGRpqgkEAOjtVYCH3XyZ1Xz+jhIDOdd5pxBajsA==
|
||||
dependencies:
|
||||
"@babel/runtime-corejs3" "^7.26.10"
|
||||
"@swagger-api/apidom-core" "^1.11.0"
|
||||
"@swagger-api/apidom-ns-api-design-systems" "^1.11.0"
|
||||
"@swagger-api/apidom-parser-adapter-json" "^1.11.0"
|
||||
"@types/ramda" "~0.30.0"
|
||||
ramda "~0.30.0"
|
||||
ramda-adjunct "^5.0.0"
|
||||
|
||||
"@swagger-api/apidom-parser-adapter-api-design-systems-yaml@^1.11.0":
|
||||
version "1.11.0"
|
||||
resolved "https://registry.yarnpkg.com/@swagger-api/apidom-parser-adapter-api-design-systems-yaml/-/apidom-parser-adapter-api-design-systems-yaml-1.11.0.tgz#7dd524f0241fd9997e8959a071a3e8b2cf58f1f6"
|
||||
integrity sha512-K714DT6nFW+ZM9LTo+c120zkUjsEcIFO2DU+0cnzReRyenb1x6RZe+uOqTt7iWohnnWp2FV/j0exd/mCsxW65Q==
|
||||
dependencies:
|
||||
"@babel/runtime-corejs3" "^7.26.10"
|
||||
"@swagger-api/apidom-core" "^1.11.0"
|
||||
"@swagger-api/apidom-ns-api-design-systems" "^1.11.0"
|
||||
"@swagger-api/apidom-parser-adapter-yaml-1-2" "^1.11.0"
|
||||
"@types/ramda" "~0.30.0"
|
||||
ramda "~0.30.0"
|
||||
ramda-adjunct "^5.0.0"
|
||||
|
||||
"@swagger-api/apidom-parser-adapter-arazzo-json-1@^1.11.0":
|
||||
version "1.11.0"
|
||||
resolved "https://registry.yarnpkg.com/@swagger-api/apidom-parser-adapter-arazzo-json-1/-/apidom-parser-adapter-arazzo-json-1-1.11.0.tgz#8b8bafdab34b4a917a2cb68aeb0e9e94ad511fc2"
|
||||
integrity sha512-z9K6XEr3AafV2EA+1pfW+8VoMCCSSpm2IU7oUTjSnhxRb5t/DZR4Qg8FEK8tRKdS2BO2kFFLb2xikrY3Qx8B+g==
|
||||
dependencies:
|
||||
"@babel/runtime-corejs3" "^7.26.10"
|
||||
"@swagger-api/apidom-core" "^1.11.0"
|
||||
"@swagger-api/apidom-ns-arazzo-1" "^1.11.0"
|
||||
"@swagger-api/apidom-parser-adapter-json" "^1.11.0"
|
||||
"@types/ramda" "~0.30.0"
|
||||
ramda "~0.30.0"
|
||||
ramda-adjunct "^5.0.0"
|
||||
|
||||
"@swagger-api/apidom-parser-adapter-arazzo-yaml-1@^1.11.0":
|
||||
version "1.11.0"
|
||||
resolved "https://registry.yarnpkg.com/@swagger-api/apidom-parser-adapter-arazzo-yaml-1/-/apidom-parser-adapter-arazzo-yaml-1-1.11.0.tgz#2a21d25375dae5bfd76ef331a581ad84fd00ccdd"
|
||||
integrity sha512-HPb7Wzr+cj0IJkRRlqsK1tNCQXivuGRP4iB2yek16sQZXo2eqSUZ3j3Lz/WwWgnN/FWGAODm4bj9+EhGQ11TnA==
|
||||
dependencies:
|
||||
"@babel/runtime-corejs3" "^7.26.10"
|
||||
"@swagger-api/apidom-core" "^1.11.0"
|
||||
"@swagger-api/apidom-ns-arazzo-1" "^1.11.0"
|
||||
"@swagger-api/apidom-parser-adapter-yaml-1-2" "^1.11.0"
|
||||
"@types/ramda" "~0.30.0"
|
||||
ramda "~0.30.0"
|
||||
ramda-adjunct "^5.0.0"
|
||||
|
||||
"@swagger-api/apidom-parser-adapter-asyncapi-json-2@^1.11.0":
|
||||
version "1.11.0"
|
||||
resolved "https://registry.yarnpkg.com/@swagger-api/apidom-parser-adapter-asyncapi-json-2/-/apidom-parser-adapter-asyncapi-json-2-1.11.0.tgz#b9c6efff4ece06882a3aba91ed787b307618d6fe"
|
||||
integrity sha512-sQenLXZRmTDQehe3JCSQpz6jpE3DhMQ0aoe2gpNqo23Gt/4oeW6nAP2h49q9Ne+CHPp0ApFUUyIXF7UTmbUWqA==
|
||||
dependencies:
|
||||
"@babel/runtime-corejs3" "^7.26.10"
|
||||
"@swagger-api/apidom-core" "^1.11.0"
|
||||
"@swagger-api/apidom-ns-asyncapi-2" "^1.11.0"
|
||||
"@swagger-api/apidom-parser-adapter-json" "^1.11.0"
|
||||
"@types/ramda" "~0.30.0"
|
||||
ramda "~0.30.0"
|
||||
ramda-adjunct "^5.0.0"
|
||||
|
||||
"@swagger-api/apidom-parser-adapter-asyncapi-json-3@^1.11.0":
|
||||
version "1.11.0"
|
||||
resolved "https://registry.yarnpkg.com/@swagger-api/apidom-parser-adapter-asyncapi-json-3/-/apidom-parser-adapter-asyncapi-json-3-1.11.0.tgz#af21567e11a9e2eaf84748d5a5a93436c19bc71a"
|
||||
integrity sha512-aGnG3AYp4Qsimn1FOP0B9leYCJAQVockzHqyJj30xiNAXquBMXr6lq3L2/AEsmpDGv/x/++YJ4p2ggSxy12QNw==
|
||||
dependencies:
|
||||
"@babel/runtime-corejs3" "^7.26.10"
|
||||
"@swagger-api/apidom-core" "^1.11.0"
|
||||
"@swagger-api/apidom-ns-asyncapi-3" "^1.11.0"
|
||||
"@swagger-api/apidom-parser-adapter-json" "^1.11.0"
|
||||
"@types/ramda" "~0.30.0"
|
||||
ramda "~0.30.0"
|
||||
ramda-adjunct "^5.0.0"
|
||||
|
||||
"@swagger-api/apidom-parser-adapter-asyncapi-yaml-2@^1.11.0":
|
||||
version "1.11.0"
|
||||
resolved "https://registry.yarnpkg.com/@swagger-api/apidom-parser-adapter-asyncapi-yaml-2/-/apidom-parser-adapter-asyncapi-yaml-2-1.11.0.tgz#c8d2670275331f075714bdf6afb99a00aa53d3e4"
|
||||
integrity sha512-iIRlB8B46UPiu0EkKhq1TvwloBgObASJ5ROx8rhT5+Pj+BBegE+KIY02EUKwcz5FgXJrH3XcltLiI7ZA68347Q==
|
||||
dependencies:
|
||||
"@babel/runtime-corejs3" "^7.26.10"
|
||||
"@swagger-api/apidom-core" "^1.11.0"
|
||||
"@swagger-api/apidom-ns-asyncapi-2" "^1.11.0"
|
||||
"@swagger-api/apidom-parser-adapter-yaml-1-2" "^1.11.0"
|
||||
"@types/ramda" "~0.30.0"
|
||||
ramda "~0.30.0"
|
||||
ramda-adjunct "^5.0.0"
|
||||
|
||||
"@swagger-api/apidom-parser-adapter-asyncapi-yaml-3@^1.11.0":
|
||||
version "1.11.0"
|
||||
resolved "https://registry.yarnpkg.com/@swagger-api/apidom-parser-adapter-asyncapi-yaml-3/-/apidom-parser-adapter-asyncapi-yaml-3-1.11.0.tgz#aaaf47cd034228d453d6426b3afcf2b429a3a497"
|
||||
integrity sha512-BF2ZyQYMUNrjP1nMneX6ZD2IWBLycWpxg3yllXDCJtfdQT/IMzldIPKCNI9qoBE57lM6j2hpy+Jd86QJk20t2w==
|
||||
dependencies:
|
||||
"@babel/runtime-corejs3" "^7.26.10"
|
||||
"@swagger-api/apidom-core" "^1.11.0"
|
||||
"@swagger-api/apidom-ns-asyncapi-3" "^1.11.0"
|
||||
"@swagger-api/apidom-parser-adapter-yaml-1-2" "^1.11.0"
|
||||
"@types/ramda" "~0.30.0"
|
||||
ramda "~0.30.0"
|
||||
ramda-adjunct "^5.0.0"
|
||||
|
||||
"@swagger-api/apidom-parser-adapter-json@^1.11.0":
|
||||
version "1.11.0"
|
||||
resolved "https://registry.yarnpkg.com/@swagger-api/apidom-parser-adapter-json/-/apidom-parser-adapter-json-1.11.0.tgz#81e5919a2e4139492c0f910f8413557eb059de8a"
|
||||
integrity sha512-DObW0LxYwif0erzGoXiEAZ6ecc/18LIEKxjEAc5Bw2M5I0C/iGW4y/UxAywihGvhMEo1gOvdO6w9Jh6UnuPVmA==
|
||||
dependencies:
|
||||
"@babel/runtime-corejs3" "^7.26.10"
|
||||
"@swagger-api/apidom-ast" "^1.11.0"
|
||||
"@swagger-api/apidom-core" "^1.11.0"
|
||||
"@swagger-api/apidom-error" "^1.11.0"
|
||||
"@types/ramda" "~0.30.0"
|
||||
ramda "~0.30.0"
|
||||
ramda-adjunct "^5.0.0"
|
||||
tree-sitter "=0.21.1"
|
||||
tree-sitter-json "=0.24.8"
|
||||
web-tree-sitter "=0.24.5"
|
||||
|
||||
"@swagger-api/apidom-parser-adapter-openapi-json-2@^1.11.0":
|
||||
version "1.11.0"
|
||||
resolved "https://registry.yarnpkg.com/@swagger-api/apidom-parser-adapter-openapi-json-2/-/apidom-parser-adapter-openapi-json-2-1.11.0.tgz#c48a0a51b4381ebf5b7c3406b90d9a25dfa48a49"
|
||||
integrity sha512-dREUHAEHVry9aSGjqDpYF9Wzm1lgUkV6EgoYDflyQ9HxgCwhucDPFmUgI7UaR0G6bplnJumMcZXh1I1TGn1v7Q==
|
||||
dependencies:
|
||||
"@babel/runtime-corejs3" "^7.26.10"
|
||||
"@swagger-api/apidom-core" "^1.11.0"
|
||||
"@swagger-api/apidom-ns-openapi-2" "^1.11.0"
|
||||
"@swagger-api/apidom-parser-adapter-json" "^1.11.0"
|
||||
"@types/ramda" "~0.30.0"
|
||||
ramda "~0.30.0"
|
||||
ramda-adjunct "^5.0.0"
|
||||
|
||||
"@swagger-api/apidom-parser-adapter-openapi-json-3-0@^1.11.0":
|
||||
version "1.11.0"
|
||||
resolved "https://registry.yarnpkg.com/@swagger-api/apidom-parser-adapter-openapi-json-3-0/-/apidom-parser-adapter-openapi-json-3-0-1.11.0.tgz#e432f87615bdd5652aa7c299454ede2cbec46f4a"
|
||||
integrity sha512-U/NZpvuj9IpUS48zF2tYbgW2AtTw6Yi6kXNiHUtgUEomxYdb6XQeKLDGvgeWjgAgfUROohakcH+wx713VCGxfQ==
|
||||
dependencies:
|
||||
"@babel/runtime-corejs3" "^7.26.10"
|
||||
"@swagger-api/apidom-core" "^1.11.0"
|
||||
"@swagger-api/apidom-ns-openapi-3-0" "^1.11.0"
|
||||
"@swagger-api/apidom-parser-adapter-json" "^1.11.0"
|
||||
"@types/ramda" "~0.30.0"
|
||||
ramda "~0.30.0"
|
||||
ramda-adjunct "^5.0.0"
|
||||
|
||||
"@swagger-api/apidom-parser-adapter-openapi-json-3-1@^1.11.0":
|
||||
version "1.11.0"
|
||||
resolved "https://registry.yarnpkg.com/@swagger-api/apidom-parser-adapter-openapi-json-3-1/-/apidom-parser-adapter-openapi-json-3-1-1.11.0.tgz#1c5043a00620235c0b175d03a1da4a17d41294fd"
|
||||
integrity sha512-fYarNeaz39oKZ6VwqwON+IeJszidZGPvUYDfggLaar81NGimrz07y1U+DhAf96IX3qgUa2J6Fu3Bv1r57hs6Ng==
|
||||
dependencies:
|
||||
"@babel/runtime-corejs3" "^7.26.10"
|
||||
"@swagger-api/apidom-core" "^1.11.0"
|
||||
"@swagger-api/apidom-ns-openapi-3-1" "^1.11.0"
|
||||
"@swagger-api/apidom-parser-adapter-json" "^1.11.0"
|
||||
"@types/ramda" "~0.30.0"
|
||||
ramda "~0.30.0"
|
||||
ramda-adjunct "^5.0.0"
|
||||
|
||||
"@swagger-api/apidom-parser-adapter-openapi-json-3-2@^1.11.0":
|
||||
version "1.11.0"
|
||||
resolved "https://registry.yarnpkg.com/@swagger-api/apidom-parser-adapter-openapi-json-3-2/-/apidom-parser-adapter-openapi-json-3-2-1.11.0.tgz#29bcb3388b9a452a78a01d2491e5891009755c2d"
|
||||
integrity sha512-jtMoAH3R73bQUc4D2cJTUUvO4iJz9CV1W4+zoU/gT2l6h8Ji5EhZH0/VyynUk4J6mW/GdwxUN/q5z2P/DtSmfA==
|
||||
dependencies:
|
||||
"@babel/runtime-corejs3" "^7.26.10"
|
||||
"@swagger-api/apidom-core" "^1.11.0"
|
||||
"@swagger-api/apidom-ns-openapi-3-2" "^1.11.0"
|
||||
"@swagger-api/apidom-parser-adapter-json" "^1.11.0"
|
||||
"@types/ramda" "~0.30.0"
|
||||
ramda "~0.30.0"
|
||||
ramda-adjunct "^5.0.0"
|
||||
|
||||
"@swagger-api/apidom-parser-adapter-openapi-yaml-2@^1.11.0":
|
||||
version "1.11.0"
|
||||
resolved "https://registry.yarnpkg.com/@swagger-api/apidom-parser-adapter-openapi-yaml-2/-/apidom-parser-adapter-openapi-yaml-2-1.11.0.tgz#08a94606d3b636075aacb9af80b5fd25b4b15782"
|
||||
integrity sha512-e8L4kHahgkOIzCCSGs5jTahXLInERNr37teSLS4SuqYgSVWr9AVXuNvpHNYGeMECD8briGIGfAAtnZChCGYrEA==
|
||||
dependencies:
|
||||
"@babel/runtime-corejs3" "^7.26.10"
|
||||
"@swagger-api/apidom-core" "^1.11.0"
|
||||
"@swagger-api/apidom-ns-openapi-2" "^1.11.0"
|
||||
"@swagger-api/apidom-parser-adapter-yaml-1-2" "^1.11.0"
|
||||
"@types/ramda" "~0.30.0"
|
||||
ramda "~0.30.0"
|
||||
ramda-adjunct "^5.0.0"
|
||||
|
||||
"@swagger-api/apidom-parser-adapter-openapi-yaml-3-0@^1.11.0":
|
||||
version "1.11.0"
|
||||
resolved "https://registry.yarnpkg.com/@swagger-api/apidom-parser-adapter-openapi-yaml-3-0/-/apidom-parser-adapter-openapi-yaml-3-0-1.11.0.tgz#55c35ac58764b20890456707c9176ef48f20d741"
|
||||
integrity sha512-s+AXnNzLeAk28jUAeXwTSR1AlX+TXIAt2GfFgWUAV+SFw2OhRpoKYLzItN3n2UsHselqHvfyUL9xNCJBZleQtQ==
|
||||
dependencies:
|
||||
"@babel/runtime-corejs3" "^7.26.10"
|
||||
"@swagger-api/apidom-core" "^1.11.0"
|
||||
"@swagger-api/apidom-ns-openapi-3-0" "^1.11.0"
|
||||
"@swagger-api/apidom-parser-adapter-yaml-1-2" "^1.11.0"
|
||||
"@types/ramda" "~0.30.0"
|
||||
ramda "~0.30.0"
|
||||
ramda-adjunct "^5.0.0"
|
||||
|
||||
"@swagger-api/apidom-parser-adapter-openapi-yaml-3-1@^1.11.0":
|
||||
version "1.11.0"
|
||||
resolved "https://registry.yarnpkg.com/@swagger-api/apidom-parser-adapter-openapi-yaml-3-1/-/apidom-parser-adapter-openapi-yaml-3-1-1.11.0.tgz#a6dc3316738f3175d896a832701c349ec416c11a"
|
||||
integrity sha512-xyUyehHhB+BSOAT7mYGqmcEozuLKxmx1Hug97O9SVgNU8QTClc95+VWrAHhJbn8juPR6y2vSwm/wrQDwb4yq7w==
|
||||
dependencies:
|
||||
"@babel/runtime-corejs3" "^7.26.10"
|
||||
"@swagger-api/apidom-core" "^1.11.0"
|
||||
"@swagger-api/apidom-ns-openapi-3-1" "^1.11.0"
|
||||
"@swagger-api/apidom-parser-adapter-yaml-1-2" "^1.11.0"
|
||||
"@types/ramda" "~0.30.0"
|
||||
ramda "~0.30.0"
|
||||
ramda-adjunct "^5.0.0"
|
||||
|
||||
"@swagger-api/apidom-parser-adapter-openapi-yaml-3-2@^1.11.0":
|
||||
version "1.11.0"
|
||||
resolved "https://registry.yarnpkg.com/@swagger-api/apidom-parser-adapter-openapi-yaml-3-2/-/apidom-parser-adapter-openapi-yaml-3-2-1.11.0.tgz#bc83e0d8ec0a54c67c8a77109be56c5eef5be169"
|
||||
integrity sha512-u7Y98zdjEs+0Upa8TdxOsb7z8hYJmLz9lVleRiB7rqysVga6oSDI5NAFdLVqMB6uAUuFi/tyiuiFT4Qosfd6Vw==
|
||||
dependencies:
|
||||
"@babel/runtime-corejs3" "^7.26.10"
|
||||
"@swagger-api/apidom-core" "^1.11.0"
|
||||
"@swagger-api/apidom-ns-openapi-3-2" "^1.11.0"
|
||||
"@swagger-api/apidom-parser-adapter-yaml-1-2" "^1.11.0"
|
||||
"@types/ramda" "~0.30.0"
|
||||
ramda "~0.30.0"
|
||||
ramda-adjunct "^5.0.0"
|
||||
|
||||
"@swagger-api/apidom-parser-adapter-yaml-1-2@^1.11.0":
|
||||
version "1.11.0"
|
||||
resolved "https://registry.yarnpkg.com/@swagger-api/apidom-parser-adapter-yaml-1-2/-/apidom-parser-adapter-yaml-1-2-1.11.0.tgz#6bca1a605127d904bde0419bfd2df880c6051757"
|
||||
integrity sha512-FZK9KfwiTnNc+imxg7Wu2ktKhXCYPeFQZ1uZJzJL/hk1n+zyPfRY/4Aue4HzDcG8+wbItd3dRjKClFanVZAXoA==
|
||||
dependencies:
|
||||
"@babel/runtime-corejs3" "^7.26.10"
|
||||
"@swagger-api/apidom-ast" "^1.11.0"
|
||||
"@swagger-api/apidom-core" "^1.11.0"
|
||||
"@swagger-api/apidom-error" "^1.11.0"
|
||||
"@tree-sitter-grammars/tree-sitter-yaml" "=0.7.1"
|
||||
"@types/ramda" "~0.30.0"
|
||||
ramda "~0.30.0"
|
||||
ramda-adjunct "^5.0.0"
|
||||
tree-sitter "=0.22.4"
|
||||
web-tree-sitter "=0.24.5"
|
||||
|
||||
"@swagger-api/apidom-reference@^1.11.0":
|
||||
version "1.11.0"
|
||||
resolved "https://registry.yarnpkg.com/@swagger-api/apidom-reference/-/apidom-reference-1.11.0.tgz#9deaff0a93e46058c090946394dbb83975a86a51"
|
||||
integrity sha512-ftqegYrxxl9UwQFbdVOtXIqNolVd25M5u53X8fP96Wx6lEVr5Ed7B6+dzch8ttCUmKeoLIeagvt76b6BoYtnLw==
|
||||
dependencies:
|
||||
"@babel/runtime-corejs3" "^7.26.10"
|
||||
"@swagger-api/apidom-core" "^1.11.0"
|
||||
"@swagger-api/apidom-error" "^1.11.0"
|
||||
"@types/ramda" "~0.30.0"
|
||||
axios "^1.15.0"
|
||||
minimatch "^10.2.1"
|
||||
ramda "~0.30.0"
|
||||
ramda-adjunct "^5.0.0"
|
||||
optionalDependencies:
|
||||
"@swagger-api/apidom-json-pointer" "^1.11.0"
|
||||
"@swagger-api/apidom-ns-arazzo-1" "^1.11.0"
|
||||
"@swagger-api/apidom-ns-asyncapi-2" "^1.11.0"
|
||||
"@swagger-api/apidom-ns-openapi-2" "^1.11.0"
|
||||
"@swagger-api/apidom-ns-openapi-3-0" "^1.11.0"
|
||||
"@swagger-api/apidom-ns-openapi-3-1" "^1.11.0"
|
||||
"@swagger-api/apidom-ns-openapi-3-2" "^1.11.0"
|
||||
"@swagger-api/apidom-parser-adapter-api-design-systems-json" "^1.11.0"
|
||||
"@swagger-api/apidom-parser-adapter-api-design-systems-yaml" "^1.11.0"
|
||||
"@swagger-api/apidom-parser-adapter-arazzo-json-1" "^1.11.0"
|
||||
"@swagger-api/apidom-parser-adapter-arazzo-yaml-1" "^1.11.0"
|
||||
"@swagger-api/apidom-parser-adapter-asyncapi-json-2" "^1.11.0"
|
||||
"@swagger-api/apidom-parser-adapter-asyncapi-json-3" "^1.11.0"
|
||||
"@swagger-api/apidom-parser-adapter-asyncapi-yaml-2" "^1.11.0"
|
||||
"@swagger-api/apidom-parser-adapter-asyncapi-yaml-3" "^1.11.0"
|
||||
"@swagger-api/apidom-parser-adapter-json" "^1.11.0"
|
||||
"@swagger-api/apidom-parser-adapter-openapi-json-2" "^1.11.0"
|
||||
"@swagger-api/apidom-parser-adapter-openapi-json-3-0" "^1.11.0"
|
||||
"@swagger-api/apidom-parser-adapter-openapi-json-3-1" "^1.11.0"
|
||||
"@swagger-api/apidom-parser-adapter-openapi-json-3-2" "^1.11.0"
|
||||
"@swagger-api/apidom-parser-adapter-openapi-yaml-2" "^1.11.0"
|
||||
"@swagger-api/apidom-parser-adapter-openapi-yaml-3-0" "^1.11.0"
|
||||
"@swagger-api/apidom-parser-adapter-openapi-yaml-3-1" "^1.11.0"
|
||||
"@swagger-api/apidom-parser-adapter-openapi-yaml-3-2" "^1.11.0"
|
||||
"@swagger-api/apidom-parser-adapter-yaml-1-2" "^1.11.0"
|
||||
|
||||
"@swaggerexpert/cookie@^2.0.2":
|
||||
version "2.0.2"
|
||||
@@ -4543,14 +4201,6 @@
|
||||
dependencies:
|
||||
defer-to-connect "^2.0.1"
|
||||
|
||||
"@tree-sitter-grammars/tree-sitter-yaml@=0.7.1":
|
||||
version "0.7.1"
|
||||
resolved "https://registry.npmjs.org/@tree-sitter-grammars/tree-sitter-yaml/-/tree-sitter-yaml-0.7.1.tgz"
|
||||
integrity sha512-AynBwkIoQCTgjDR33bDUp9Mqq+YTco0is3n5hRApMqG9of/6A4eQsfC1/uSEeHSUyMQSYawcAWamsexnVpIP4Q==
|
||||
dependencies:
|
||||
node-addon-api "^8.3.1"
|
||||
node-gyp-build "^4.8.4"
|
||||
|
||||
"@tybys/wasm-util@^0.10.1":
|
||||
version "0.10.1"
|
||||
resolved "https://registry.yarnpkg.com/@tybys/wasm-util/-/wasm-util-0.10.1.tgz#ecddd3205cf1e2d5274649ff0eedd2991ed7f414"
|
||||
@@ -5479,13 +5129,6 @@ address@^1.0.1:
|
||||
resolved "https://registry.npmjs.org/address/-/address-1.2.2.tgz"
|
||||
integrity sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA==
|
||||
|
||||
agent-base@6:
|
||||
version "6.0.2"
|
||||
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77"
|
||||
integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==
|
||||
dependencies:
|
||||
debug "4"
|
||||
|
||||
aggregate-error@^3.0.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz"
|
||||
@@ -5847,11 +5490,6 @@ async@3.2.6:
|
||||
resolved "https://registry.npmjs.org/async/-/async-3.2.6.tgz"
|
||||
integrity sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==
|
||||
|
||||
asynckit@^0.4.0:
|
||||
version "0.4.0"
|
||||
resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz"
|
||||
integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==
|
||||
|
||||
autolinker@^3.11.0:
|
||||
version "3.16.2"
|
||||
resolved "https://registry.npmjs.org/autolinker/-/autolinker-3.16.2.tgz"
|
||||
@@ -5878,16 +5516,6 @@ available-typed-arrays@^1.0.7:
|
||||
dependencies:
|
||||
possible-typed-array-names "^1.0.0"
|
||||
|
||||
axios@^1.15.0:
|
||||
version "1.16.1"
|
||||
resolved "https://registry.yarnpkg.com/axios/-/axios-1.16.1.tgz#517e29291d19d6e8cf919ff264f4fe157261ba12"
|
||||
integrity sha512-caYkukvroVPO8KrzuJEb50Hm07KwfBZPEC3VeFHTsqWHvKTsy54hjJz9BS/cdaypROE2rH6xvm9mHX4fgWkr3A==
|
||||
dependencies:
|
||||
follow-redirects "^1.16.0"
|
||||
form-data "^4.0.5"
|
||||
https-proxy-agent "^5.0.1"
|
||||
proxy-from-env "^2.1.0"
|
||||
|
||||
babel-loader@^9.2.1:
|
||||
version "9.2.1"
|
||||
resolved "https://registry.npmjs.org/babel-loader/-/babel-loader-9.2.1.tgz"
|
||||
@@ -6424,13 +6052,6 @@ combine-promises@^1.1.0:
|
||||
resolved "https://registry.npmjs.org/combine-promises/-/combine-promises-1.2.0.tgz"
|
||||
integrity sha512-VcQB1ziGD0NXrhKxiwyNbCDmRzs/OShMs2GqW2DlU2A/Sd0nQxE1oWDAE5O0ygSx5mgQOn9eIFh7yKPgFRVkPQ==
|
||||
|
||||
combined-stream@^1.0.8:
|
||||
version "1.0.8"
|
||||
resolved "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz"
|
||||
integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
|
||||
dependencies:
|
||||
delayed-stream "~1.0.0"
|
||||
|
||||
comma-separated-tokens@^2.0.0:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz"
|
||||
@@ -7355,11 +6976,6 @@ delaunator@5:
|
||||
dependencies:
|
||||
robust-predicates "^3.0.2"
|
||||
|
||||
delayed-stream@~1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz"
|
||||
integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==
|
||||
|
||||
depd@2.0.0, depd@~2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz"
|
||||
@@ -8404,7 +8020,7 @@ flatted@^3.2.9:
|
||||
resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.4.2.tgz#f5c23c107f0f37de8dbdf24f13722b3b98d52726"
|
||||
integrity sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==
|
||||
|
||||
follow-redirects@^1.0.0, follow-redirects@^1.16.0:
|
||||
follow-redirects@^1.0.0:
|
||||
version "1.16.0"
|
||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.16.0.tgz#28474a159d3b9d11ef62050a14ed60e4df6d61bc"
|
||||
integrity sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==
|
||||
@@ -8426,17 +8042,6 @@ form-data-encoder@^2.1.2:
|
||||
resolved "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.4.tgz"
|
||||
integrity sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==
|
||||
|
||||
form-data@^4.0.5:
|
||||
version "4.0.5"
|
||||
resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.5.tgz#b49e48858045ff4cbf6b03e1805cebcad3679053"
|
||||
integrity sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==
|
||||
dependencies:
|
||||
asynckit "^0.4.0"
|
||||
combined-stream "^1.0.8"
|
||||
es-set-tostringtag "^2.1.0"
|
||||
hasown "^2.0.2"
|
||||
mime-types "^2.1.12"
|
||||
|
||||
format@^0.2.0:
|
||||
version "0.2.2"
|
||||
resolved "https://registry.npmjs.org/format/-/format-0.2.2.tgz"
|
||||
@@ -9098,14 +8703,6 @@ http2-wrapper@^2.1.10:
|
||||
quick-lru "^5.1.1"
|
||||
resolve-alpn "^1.2.0"
|
||||
|
||||
https-proxy-agent@^5.0.1:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6"
|
||||
integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==
|
||||
dependencies:
|
||||
agent-base "6"
|
||||
debug "4"
|
||||
|
||||
human-signals@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz"
|
||||
@@ -11162,7 +10759,7 @@ mime-types@2.1.18:
|
||||
dependencies:
|
||||
mime-db "~1.33.0"
|
||||
|
||||
mime-types@^2.1.12, mime-types@^2.1.27, mime-types@~2.1.17, mime-types@~2.1.24, mime-types@~2.1.34:
|
||||
mime-types@^2.1.27, mime-types@~2.1.17, mime-types@~2.1.24, mime-types@~2.1.34:
|
||||
version "2.1.35"
|
||||
resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz"
|
||||
integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
|
||||
@@ -11228,7 +10825,7 @@ minimatch@3.1.5, minimatch@^3.1.1, minimatch@^3.1.2:
|
||||
dependencies:
|
||||
brace-expansion "^1.1.7"
|
||||
|
||||
minimatch@^10.2.1, minimatch@^10.2.2:
|
||||
minimatch@^10.2.2:
|
||||
version "10.2.5"
|
||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.2.5.tgz#bd48687a0be38ed2961399105600f832095861d1"
|
||||
integrity sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==
|
||||
@@ -11335,14 +10932,9 @@ node-addon-api@^7.0.0:
|
||||
resolved "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz"
|
||||
integrity sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==
|
||||
|
||||
node-addon-api@^8.0.0, node-addon-api@^8.2.2, node-addon-api@^8.3.0, node-addon-api@^8.3.1:
|
||||
version "8.5.0"
|
||||
resolved "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.5.0.tgz"
|
||||
integrity sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A==
|
||||
|
||||
node-domexception@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz"
|
||||
resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5"
|
||||
integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==
|
||||
|
||||
node-emoji@^2.1.0:
|
||||
@@ -11357,7 +10949,7 @@ node-emoji@^2.1.0:
|
||||
|
||||
node-fetch-commonjs@^3.3.2:
|
||||
version "3.3.2"
|
||||
resolved "https://registry.npmjs.org/node-fetch-commonjs/-/node-fetch-commonjs-3.3.2.tgz"
|
||||
resolved "https://registry.yarnpkg.com/node-fetch-commonjs/-/node-fetch-commonjs-3.3.2.tgz#0dd0fd4c4a314c5234f496ff7b5d9ce5a6c8feaa"
|
||||
integrity sha512-VBlAiynj3VMLrotgwOS3OyECFxas5y7ltLcK4t41lMUZeaK15Ym4QRkqN0EQKAFL42q9i21EPKjzLUPfltR72A==
|
||||
dependencies:
|
||||
node-domexception "^1.0.0"
|
||||
@@ -11377,11 +10969,6 @@ node-fetch@^2.6.1:
|
||||
dependencies:
|
||||
whatwg-url "^5.0.0"
|
||||
|
||||
node-gyp-build@^4.8.0, node-gyp-build@^4.8.2, node-gyp-build@^4.8.4:
|
||||
version "4.8.4"
|
||||
resolved "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz"
|
||||
integrity sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==
|
||||
|
||||
node-readfiles@^0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.npmjs.org/node-readfiles/-/node-readfiles-0.2.0.tgz"
|
||||
@@ -12670,11 +12257,6 @@ proxy-addr@~2.0.7:
|
||||
forwarded "0.2.0"
|
||||
ipaddr.js "1.9.1"
|
||||
|
||||
proxy-from-env@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-2.1.0.tgz#a7487568adad577cfaaa7e88c49cab3ab3081aba"
|
||||
integrity sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==
|
||||
|
||||
punycode@^1.4.1:
|
||||
version "1.4.1"
|
||||
resolved "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz"
|
||||
@@ -14450,7 +14032,7 @@ svgo@^3.0.2, svgo@^3.2.0:
|
||||
picocolors "^1.0.0"
|
||||
sax "^1.5.0"
|
||||
|
||||
swagger-client@^3.37.3:
|
||||
swagger-client@3.37.3, swagger-client@^3.37.4:
|
||||
version "3.37.3"
|
||||
resolved "https://registry.yarnpkg.com/swagger-client/-/swagger-client-3.37.3.tgz#dbe3f0d22c367d4bc04cc7ddaf5224e116d46074"
|
||||
integrity sha512-PZv5smQPnPwfP6mnkq96fOp/RNDKBqd8vfwE4UuwA229wsesj20yd7RadXx+9uLBC3c0H6cu/H+bnbMTWG6oUQ==
|
||||
@@ -14475,10 +14057,10 @@ swagger-client@^3.37.3:
|
||||
ramda "^0.30.1"
|
||||
ramda-adjunct "^5.1.0"
|
||||
|
||||
swagger-ui-react@^5.32.5:
|
||||
version "5.32.5"
|
||||
resolved "https://registry.yarnpkg.com/swagger-ui-react/-/swagger-ui-react-5.32.5.tgz#c4650972c71aaf4107a5f742e9c45615eba6857f"
|
||||
integrity sha512-u86Qx36C5FvmJFVGMF3s62dxR3l0EfUmlylJVqCJ4vL0tvfd38kNdCQan9app6Y+C25uqVAjGLYu2w87UMD35Q==
|
||||
swagger-ui-react@^5.32.6:
|
||||
version "5.32.6"
|
||||
resolved "https://registry.yarnpkg.com/swagger-ui-react/-/swagger-ui-react-5.32.6.tgz#00c3f99a5b1f6c0debb2ee0589018dfc79ba1d4a"
|
||||
integrity sha512-2q2kXd6eDR+syyWV5HE2CkWANyr2MHPkNezG4M7fC0FPlBUZEsNgyA/2dcb9dIwgE5xd995dO42h89fNMF5/ng==
|
||||
dependencies:
|
||||
"@babel/runtime-corejs3" "^7.27.1"
|
||||
"@scarf/scarf" "=1.4.0"
|
||||
@@ -14509,7 +14091,7 @@ swagger-ui-react@^5.32.5:
|
||||
reselect "^5.1.1"
|
||||
serialize-error "^8.1.0"
|
||||
sha.js "^2.4.12"
|
||||
swagger-client "^3.37.3"
|
||||
swagger-client "^3.37.4"
|
||||
url-parse "^1.5.10"
|
||||
xml "=1.0.1"
|
||||
xml-but-prettier "^1.0.1"
|
||||
@@ -14679,30 +14261,6 @@ tree-dump@^1.0.3:
|
||||
resolved "https://registry.npmjs.org/tree-dump/-/tree-dump-1.1.0.tgz"
|
||||
integrity sha512-rMuvhU4MCDbcbnleZTFezWsaZXRFemSqAM+7jPnzUl1fo9w3YEKOxAeui0fz3OI4EU4hf23iyA7uQRVko+UaBA==
|
||||
|
||||
tree-sitter-json@=0.24.8:
|
||||
version "0.24.8"
|
||||
resolved "https://registry.npmjs.org/tree-sitter-json/-/tree-sitter-json-0.24.8.tgz"
|
||||
integrity sha512-Tc9ZZYwHyWZ3Tt1VEw7Pa2scu1YO7/d2BCBbKTx5hXwig3UfdQjsOPkPyLpDJOn/m1UBEWYAtSdGAwCSyagBqQ==
|
||||
dependencies:
|
||||
node-addon-api "^8.2.2"
|
||||
node-gyp-build "^4.8.2"
|
||||
|
||||
tree-sitter@=0.21.1:
|
||||
version "0.21.1"
|
||||
resolved "https://registry.yarnpkg.com/tree-sitter/-/tree-sitter-0.21.1.tgz#fbb34c09056700814af0e1e37688e06463ba04c4"
|
||||
integrity sha512-7dxoA6kYvtgWw80265MyqJlkRl4yawIjO7S5MigytjELkX43fV2WsAXzsNfO7sBpPPCF5Gp0+XzHk0DwLCq3xQ==
|
||||
dependencies:
|
||||
node-addon-api "^8.0.0"
|
||||
node-gyp-build "^4.8.0"
|
||||
|
||||
tree-sitter@=0.22.4:
|
||||
version "0.22.4"
|
||||
resolved "https://registry.npmjs.org/tree-sitter/-/tree-sitter-0.22.4.tgz"
|
||||
integrity sha512-usbHZP9/oxNsUY65MQUsduGRqDHQOou1cagUSwjhoSYAmSahjQDAVsh9s+SlZkn8X8+O1FULRGwHu7AFP3kjzg==
|
||||
dependencies:
|
||||
node-addon-api "^8.3.0"
|
||||
node-gyp-build "^4.8.4"
|
||||
|
||||
trim-lines@^3.0.0:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz"
|
||||
@@ -15322,14 +14880,9 @@ web-namespaces@^2.0.0:
|
||||
|
||||
web-streams-polyfill@^3.0.3:
|
||||
version "3.3.3"
|
||||
resolved "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz"
|
||||
resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz#2073b91a2fdb1fbfbd401e7de0ac9f8214cecb4b"
|
||||
integrity sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==
|
||||
|
||||
web-tree-sitter@=0.24.5:
|
||||
version "0.24.5"
|
||||
resolved "https://registry.npmjs.org/web-tree-sitter/-/web-tree-sitter-0.24.5.tgz"
|
||||
integrity sha512-+J/2VSHN8J47gQUAvF8KDadrfz6uFYVjxoxbKWDoXVsH2u7yLdarCnIURnrMA6uSRkgX3SdmqM5BOoQjPdSh5w==
|
||||
|
||||
webidl-conversions@^3.0.0:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz"
|
||||
|
||||
8131
superset-frontend/package-lock.json
generated
8131
superset-frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -174,7 +174,7 @@
|
||||
"dom-to-pdf": "^0.3.2",
|
||||
"echarts": "^5.6.0",
|
||||
"fast-glob": "^3.3.2",
|
||||
"fs-extra": "^11.3.4",
|
||||
"fs-extra": "^11.3.5",
|
||||
"fuse.js": "^7.3.0",
|
||||
"geolib": "^3.3.14",
|
||||
"geostyler": "^18.5.1",
|
||||
@@ -330,7 +330,7 @@
|
||||
"eslint-plugin-no-only-tests": "^3.4.0",
|
||||
"eslint-plugin-prettier": "^5.5.5",
|
||||
"eslint-plugin-react-prefer-function-component": "^5.0.0",
|
||||
"eslint-plugin-react-you-might-not-need-an-effect": "^0.10.0",
|
||||
"eslint-plugin-react-you-might-not-need-an-effect": "^0.10.1",
|
||||
"eslint-plugin-storybook": "^0.8.0",
|
||||
"eslint-plugin-testing-library": "^7.16.2",
|
||||
"eslint-plugin-theme-colors": "file:eslint-rules/eslint-plugin-theme-colors",
|
||||
@@ -340,8 +340,8 @@
|
||||
"html-webpack-plugin": "^5.6.7",
|
||||
"http-server": "^14.1.1",
|
||||
"imports-loader": "^5.0.0",
|
||||
"jest": "^30.3.0",
|
||||
"jest-environment-jsdom": "^29.7.0",
|
||||
"jest": "^30.4.2",
|
||||
"jest-environment-jsdom": "^30.4.1",
|
||||
"jest-html-reporter": "^4.4.0",
|
||||
"jest-websocket-mock": "^2.5.0",
|
||||
"js-yaml-loader": "^1.2.2",
|
||||
@@ -412,7 +412,14 @@
|
||||
"@luma.gl/gltf": "~9.2.5",
|
||||
"@luma.gl/shadertools": "~9.2.5",
|
||||
"@luma.gl/webgl": "~9.2.5",
|
||||
"fast-xml-parser": "^5.8.0"
|
||||
"fast-xml-parser": "^5.8.0",
|
||||
"jest-mock": "^30.4.0",
|
||||
"jest-runtime": "^30.4.0",
|
||||
"@jest/globals": "^30.4.0",
|
||||
"@jest/types": "^30.4.0",
|
||||
"jest-util": "^30.4.0",
|
||||
"jest-circus": "^30.4.0",
|
||||
"jest-environment-node": "^30.4.0"
|
||||
},
|
||||
"readme": "ERROR: No README data found!",
|
||||
"scarfSettings": {
|
||||
|
||||
@@ -154,7 +154,7 @@ test('accepts custom style props', () => {
|
||||
render(<DropdownContainer items={generateItems(2)} style={customStyle} />);
|
||||
|
||||
const container = screen.getByTestId('container');
|
||||
expect(container).toHaveStyle('background-color: red');
|
||||
expect(container).toHaveStyle('background-color: rgb(255, 0, 0)');
|
||||
expect(container).toHaveStyle('padding: 10px');
|
||||
});
|
||||
|
||||
|
||||
@@ -553,22 +553,26 @@ describe('SupersetClientClass', () => {
|
||||
});
|
||||
|
||||
describe('when unauthorized', () => {
|
||||
let originalLocation: any;
|
||||
let authSpy: jest.SpyInstance;
|
||||
let locationSpy: jest.SpyInstance;
|
||||
let mockHrefValue: string;
|
||||
const mockRequestUrl = 'https://host/get/url';
|
||||
const mockRequestPath = '/get/url';
|
||||
const mockRequestSearch = '?param=1¶m=2';
|
||||
const mockHref = mockRequestUrl + mockRequestSearch;
|
||||
|
||||
beforeEach(() => {
|
||||
originalLocation = window.location;
|
||||
// @ts-expect-error
|
||||
delete window.location;
|
||||
window.location = {
|
||||
mockHrefValue = mockHref;
|
||||
locationSpy = jest.spyOn(window, 'location', 'get').mockReturnValue({
|
||||
pathname: mockRequestPath,
|
||||
search: mockRequestSearch,
|
||||
href: mockHref,
|
||||
} as unknown as Location;
|
||||
get href() {
|
||||
return mockHrefValue;
|
||||
},
|
||||
set href(v: string) {
|
||||
mockHrefValue = v;
|
||||
},
|
||||
} as unknown as Location);
|
||||
authSpy = jest
|
||||
.spyOn(SupersetClientClass.prototype, 'ensureAuth')
|
||||
.mockImplementation();
|
||||
@@ -578,7 +582,7 @@ describe('SupersetClientClass', () => {
|
||||
|
||||
afterEach(() => {
|
||||
authSpy.mockReset();
|
||||
window.location = originalLocation;
|
||||
locationSpy.mockRestore();
|
||||
});
|
||||
|
||||
test('should redirect', async () => {
|
||||
@@ -599,11 +603,17 @@ describe('SupersetClientClass', () => {
|
||||
test('should not redirect again if already on login page', async () => {
|
||||
const client = new SupersetClientClass({});
|
||||
|
||||
window.location = {
|
||||
href: '/login?next=something',
|
||||
mockHrefValue = '/login?next=something';
|
||||
locationSpy.mockReturnValue({
|
||||
get href() {
|
||||
return mockHrefValue;
|
||||
},
|
||||
set href(v: string) {
|
||||
mockHrefValue = v;
|
||||
},
|
||||
pathname: '/login',
|
||||
search: '?next=something',
|
||||
} as unknown as Location;
|
||||
} as unknown as Location);
|
||||
|
||||
let error;
|
||||
try {
|
||||
|
||||
@@ -360,18 +360,26 @@ describe('callApi()', () => {
|
||||
});
|
||||
|
||||
describe('caching', () => {
|
||||
const origLocation = window.location;
|
||||
let locationSpy: jest.SpyInstance;
|
||||
let mockProtocol = 'https:';
|
||||
|
||||
beforeAll(() => {
|
||||
Object.defineProperty(window, 'location', { value: {} });
|
||||
locationSpy = jest.spyOn(window, 'location', 'get').mockReturnValue({
|
||||
get protocol() {
|
||||
return mockProtocol;
|
||||
},
|
||||
set protocol(v: string) {
|
||||
mockProtocol = v;
|
||||
},
|
||||
} as Location);
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
Object.defineProperty(window, 'location', { value: origLocation });
|
||||
locationSpy.mockRestore();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
window.location.protocol = 'https:';
|
||||
mockProtocol = 'https:';
|
||||
await caches.delete(constants.CACHE_KEY);
|
||||
});
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ describe('updateTextNode(node, options)', () => {
|
||||
test('handles setting font', () => {
|
||||
const node = updateTextNode(createTextNode(), {
|
||||
style: {
|
||||
font: 'italic 30px Lobster 700',
|
||||
font: 'italic 700 30px Lobster',
|
||||
},
|
||||
});
|
||||
expect(node.getAttribute('class')).toEqual('');
|
||||
|
||||
@@ -630,9 +630,11 @@ describe('plugin-chart-table', () => {
|
||||
);
|
||||
|
||||
expect(getComputedStyle(screen.getByTitle('2467063')).background).toBe(
|
||||
'rgba(172, 225, 196, 1)',
|
||||
'rgb(172, 225, 196)',
|
||||
);
|
||||
expect(getComputedStyle(screen.getByTitle('2467')).background).toBe(
|
||||
'rgba(0, 0, 0, 0)',
|
||||
);
|
||||
expect(getComputedStyle(screen.getByTitle('2467')).background).toBe('');
|
||||
});
|
||||
|
||||
test('render cell without color', () => {
|
||||
@@ -669,12 +671,14 @@ describe('plugin-chart-table', () => {
|
||||
}),
|
||||
);
|
||||
expect(getComputedStyle(screen.getByTitle('2467')).background).toBe(
|
||||
'rgba(172, 225, 196, 0.812)',
|
||||
'rgba(172, 225, 196, 0.81)',
|
||||
);
|
||||
expect(getComputedStyle(screen.getByTitle('2467063')).background).toBe(
|
||||
'',
|
||||
'rgba(0, 0, 0, 0)',
|
||||
);
|
||||
expect(getComputedStyle(screen.getByText('N/A')).background).toBe(
|
||||
'rgba(0, 0, 0, 0)',
|
||||
);
|
||||
expect(getComputedStyle(screen.getByText('N/A')).background).toBe('');
|
||||
});
|
||||
|
||||
test('preserves muted null styling when no formatter resolves text color', () => {
|
||||
@@ -1059,10 +1063,10 @@ describe('plugin-chart-table', () => {
|
||||
);
|
||||
|
||||
expect(getComputedStyle(screen.getByText('Joe')).background).toBe(
|
||||
'rgba(172, 225, 196, 1)',
|
||||
'rgb(172, 225, 196)',
|
||||
);
|
||||
expect(getComputedStyle(screen.getByText('Michael')).background).toBe(
|
||||
'',
|
||||
'rgba(0, 0, 0, 0)',
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1090,9 +1094,11 @@ describe('plugin-chart-table', () => {
|
||||
}),
|
||||
);
|
||||
expect(getComputedStyle(screen.getByText('Maria')).background).toBe(
|
||||
'rgba(172, 225, 196, 1)',
|
||||
'rgb(172, 225, 196)',
|
||||
);
|
||||
expect(getComputedStyle(screen.getByText('Joe')).background).toBe(
|
||||
'rgba(0, 0, 0, 0)',
|
||||
);
|
||||
expect(getComputedStyle(screen.getByText('Joe')).background).toBe('');
|
||||
});
|
||||
|
||||
test('render color with string column color formatter (operator containing)', () => {
|
||||
@@ -1119,9 +1125,11 @@ describe('plugin-chart-table', () => {
|
||||
}),
|
||||
);
|
||||
expect(getComputedStyle(screen.getByText('Michael')).background).toBe(
|
||||
'rgba(172, 225, 196, 1)',
|
||||
'rgb(172, 225, 196)',
|
||||
);
|
||||
expect(getComputedStyle(screen.getByText('Joe')).background).toBe(
|
||||
'rgba(0, 0, 0, 0)',
|
||||
);
|
||||
expect(getComputedStyle(screen.getByText('Joe')).background).toBe('');
|
||||
});
|
||||
|
||||
test('render color with string column color formatter (operator not containing)', () => {
|
||||
@@ -1148,10 +1156,10 @@ describe('plugin-chart-table', () => {
|
||||
}),
|
||||
);
|
||||
expect(getComputedStyle(screen.getByText('Joe')).background).toBe(
|
||||
'rgba(172, 225, 196, 1)',
|
||||
'rgb(172, 225, 196)',
|
||||
);
|
||||
expect(getComputedStyle(screen.getByText('Michael')).background).toBe(
|
||||
'',
|
||||
'rgba(0, 0, 0, 0)',
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1179,10 +1187,10 @@ describe('plugin-chart-table', () => {
|
||||
}),
|
||||
);
|
||||
expect(getComputedStyle(screen.getByText('Joe')).background).toBe(
|
||||
'rgba(172, 225, 196, 1)',
|
||||
'rgb(172, 225, 196)',
|
||||
);
|
||||
expect(getComputedStyle(screen.getByText('Michael')).background).toBe(
|
||||
'',
|
||||
'rgba(0, 0, 0, 0)',
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1209,13 +1217,13 @@ describe('plugin-chart-table', () => {
|
||||
}),
|
||||
);
|
||||
expect(getComputedStyle(screen.getByText('Joe')).background).toBe(
|
||||
'rgba(172, 225, 196, 1)',
|
||||
'rgb(172, 225, 196)',
|
||||
);
|
||||
expect(getComputedStyle(screen.getByText('Michael')).background).toBe(
|
||||
'rgba(172, 225, 196, 1)',
|
||||
'rgb(172, 225, 196)',
|
||||
);
|
||||
expect(getComputedStyle(screen.getByText('Maria')).background).toBe(
|
||||
'rgba(172, 225, 196, 1)',
|
||||
'rgb(172, 225, 196)',
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1243,9 +1251,11 @@ describe('plugin-chart-table', () => {
|
||||
}),
|
||||
);
|
||||
expect(getComputedStyle(screen.getByText('true')).background).toBe(
|
||||
'rgba(172, 225, 196, 1)',
|
||||
'rgb(172, 225, 196)',
|
||||
);
|
||||
expect(getComputedStyle(screen.getByText('false')).background).toBe(
|
||||
'rgba(0, 0, 0, 0)',
|
||||
);
|
||||
expect(getComputedStyle(screen.getByText('false')).background).toBe('');
|
||||
});
|
||||
|
||||
test('render color with boolean column color formatter (operator is false)', () => {
|
||||
@@ -1272,9 +1282,11 @@ describe('plugin-chart-table', () => {
|
||||
}),
|
||||
);
|
||||
expect(getComputedStyle(screen.getByText('false')).background).toBe(
|
||||
'rgba(172, 225, 196, 1)',
|
||||
'rgb(172, 225, 196)',
|
||||
);
|
||||
expect(getComputedStyle(screen.getByText('true')).background).toBe(
|
||||
'rgba(0, 0, 0, 0)',
|
||||
);
|
||||
expect(getComputedStyle(screen.getByText('true')).background).toBe('');
|
||||
});
|
||||
|
||||
test('render color with boolean column color formatter (operator is null)', () => {
|
||||
@@ -1301,10 +1313,14 @@ describe('plugin-chart-table', () => {
|
||||
}),
|
||||
);
|
||||
expect(getComputedStyle(screen.getByText('N/A')).background).toBe(
|
||||
'rgba(172, 225, 196, 1)',
|
||||
'rgb(172, 225, 196)',
|
||||
);
|
||||
expect(getComputedStyle(screen.getByText('true')).background).toBe(
|
||||
'rgba(0, 0, 0, 0)',
|
||||
);
|
||||
expect(getComputedStyle(screen.getByText('false')).background).toBe(
|
||||
'rgba(0, 0, 0, 0)',
|
||||
);
|
||||
expect(getComputedStyle(screen.getByText('true')).background).toBe('');
|
||||
expect(getComputedStyle(screen.getByText('false')).background).toBe('');
|
||||
});
|
||||
|
||||
test('render color with boolean column color formatter (operator is not null)', () => {
|
||||
@@ -1333,12 +1349,14 @@ describe('plugin-chart-table', () => {
|
||||
const trueElements = screen.getAllByText('true');
|
||||
const falseElements = screen.getAllByText('false');
|
||||
expect(getComputedStyle(trueElements[0]).background).toBe(
|
||||
'rgba(172, 225, 196, 1)',
|
||||
'rgb(172, 225, 196)',
|
||||
);
|
||||
expect(getComputedStyle(falseElements[0]).background).toBe(
|
||||
'rgba(172, 225, 196, 1)',
|
||||
'rgb(172, 225, 196)',
|
||||
);
|
||||
expect(getComputedStyle(screen.getByText('N/A')).background).toBe(
|
||||
'rgba(0, 0, 0, 0)',
|
||||
);
|
||||
expect(getComputedStyle(screen.getByText('N/A')).background).toBe('');
|
||||
});
|
||||
|
||||
test('render color with column color formatter to entire row', () => {
|
||||
@@ -1367,13 +1385,13 @@ describe('plugin-chart-table', () => {
|
||||
);
|
||||
|
||||
expect(getComputedStyle(screen.getByText('Michael')).background).toBe(
|
||||
'rgba(172, 225, 196, 1)',
|
||||
'rgb(172, 225, 196)',
|
||||
);
|
||||
expect(getComputedStyle(screen.getByTitle('2467063')).background).toBe(
|
||||
'rgba(172, 225, 196, 1)',
|
||||
'rgb(172, 225, 196)',
|
||||
);
|
||||
expect(getComputedStyle(screen.getByTitle('0.123456')).background).toBe(
|
||||
'rgba(172, 225, 196, 1)',
|
||||
'rgb(172, 225, 196)',
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1644,7 +1662,9 @@ describe('plugin-chart-table', () => {
|
||||
expect(getComputedStyle(screen.getByTitle('2467063')).background).toBe(
|
||||
'rgb(172, 225, 196)',
|
||||
);
|
||||
expect(getComputedStyle(screen.getByTitle('2467')).background).toBe('');
|
||||
expect(getComputedStyle(screen.getByTitle('2467')).background).toBe(
|
||||
'rgba(0, 0, 0, 0)',
|
||||
);
|
||||
});
|
||||
|
||||
test('render color with useGradient true returns gradient color', () => {
|
||||
@@ -1674,9 +1694,11 @@ describe('plugin-chart-table', () => {
|
||||
|
||||
// When useGradient is true, should return gradient color with opacity
|
||||
expect(getComputedStyle(screen.getByTitle('2467063')).background).toBe(
|
||||
'rgba(172, 225, 196, 1)',
|
||||
'rgb(172, 225, 196)',
|
||||
);
|
||||
expect(getComputedStyle(screen.getByTitle('2467')).background).toBe(
|
||||
'rgba(0, 0, 0, 0)',
|
||||
);
|
||||
expect(getComputedStyle(screen.getByTitle('2467')).background).toBe('');
|
||||
});
|
||||
|
||||
test('render color with useGradient undefined defaults to gradient (backward compatibility)', () => {
|
||||
@@ -1705,9 +1727,11 @@ describe('plugin-chart-table', () => {
|
||||
|
||||
// When useGradient is undefined, should default to gradient for backward compatibility
|
||||
expect(getComputedStyle(screen.getByTitle('2467063')).background).toBe(
|
||||
'rgba(172, 225, 196, 1)',
|
||||
'rgb(172, 225, 196)',
|
||||
);
|
||||
expect(getComputedStyle(screen.getByTitle('2467')).background).toBe(
|
||||
'rgba(0, 0, 0, 0)',
|
||||
);
|
||||
expect(getComputedStyle(screen.getByTitle('2467')).background).toBe('');
|
||||
});
|
||||
|
||||
test('render color with useGradient false and None operator returns solid color', () => {
|
||||
|
||||
@@ -19,6 +19,38 @@
|
||||
|
||||
import JSDOMEnvironment from 'jest-environment-jsdom';
|
||||
|
||||
// jest-environment-jsdom 30 bundles jsdom 26, which marks window.location as
|
||||
// [LegacyUnforgeable] (configurable: false). jest 30's spyOn now strictly
|
||||
// checks the configurable flag and throws when it's false, breaking every test
|
||||
// that uses jest.spyOn(window, 'location', 'get').
|
||||
//
|
||||
// We intercept Object.defineProperties at module-load time (before any JSDOM
|
||||
// instance is created). The interceptor makes window.location configurable
|
||||
// every time jsdom creates a new Window, restoring the ability to spy on it.
|
||||
// This file is only required by Jest in the test environment so the
|
||||
// monkey-patch is safe.
|
||||
const _originalDefineProperties = Object.defineProperties.bind(Object);
|
||||
(Object as any).defineProperties = function (
|
||||
obj: object,
|
||||
props: PropertyDescriptorMap,
|
||||
) {
|
||||
if (
|
||||
props !== null &&
|
||||
typeof props === 'object' &&
|
||||
Object.prototype.hasOwnProperty.call(props, 'location') &&
|
||||
(props as any).location?.configurable === false
|
||||
) {
|
||||
// Allow jest.spyOn(window, 'location', 'get') to work in tests by making
|
||||
// the property configurable. This deviates from the browser spec's
|
||||
// [LegacyUnforgeable] requirement but is acceptable in a test environment.
|
||||
props = {
|
||||
...props,
|
||||
location: { ...(props as any).location, configurable: true },
|
||||
};
|
||||
}
|
||||
return _originalDefineProperties(obj, props);
|
||||
};
|
||||
|
||||
// https://github.com/facebook/jest/blob/v29.4.3/website/versioned_docs/version-29.4/Configuration.md#testenvironment-string
|
||||
export default class FixJSDOMEnvironment extends JSDOMEnvironment {
|
||||
constructor(...args: ConstructorParameters<typeof JSDOMEnvironment>) {
|
||||
|
||||
@@ -582,16 +582,25 @@ describe('async actions', () => {
|
||||
|
||||
// eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks
|
||||
describe('runQuery with query params', () => {
|
||||
const { location } = window;
|
||||
let locationSpy: jest.SpyInstance;
|
||||
|
||||
beforeAll(() => {
|
||||
delete (window as any).location;
|
||||
(window as any).location = new URL('http://localhost/sqllab/?foo=bar');
|
||||
const u = new URL('http://localhost/sqllab/?foo=bar');
|
||||
locationSpy = jest.spyOn(window, 'location', 'get').mockReturnValue({
|
||||
href: u.href,
|
||||
pathname: u.pathname,
|
||||
search: u.search,
|
||||
hash: u.hash,
|
||||
origin: u.origin,
|
||||
host: u.host,
|
||||
hostname: u.hostname,
|
||||
port: u.port,
|
||||
protocol: u.protocol,
|
||||
} as Location);
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
delete (window as any).location;
|
||||
window.location = location;
|
||||
locationSpy.mockRestore();
|
||||
});
|
||||
|
||||
const makeRequest = () => {
|
||||
|
||||
@@ -244,11 +244,9 @@ test('reads database from localStorage when URL has db param', () => {
|
||||
|
||||
jest.spyOn(localStorageHelpers, 'getItem').mockReturnValue(localStorageDb);
|
||||
|
||||
const originalLocation = window.location;
|
||||
Object.defineProperty(window, 'location', {
|
||||
value: { search: '?db=true' },
|
||||
writable: true,
|
||||
});
|
||||
const locationSpy = jest
|
||||
.spyOn(window, 'location', 'get')
|
||||
.mockReturnValue({ ...window.location, search: '?db=true' } as Location);
|
||||
|
||||
const store = mockStore(createInitialState());
|
||||
const { result, rerender } = renderHook(
|
||||
@@ -269,10 +267,7 @@ test('reads database from localStorage when URL has db param', () => {
|
||||
null,
|
||||
);
|
||||
|
||||
Object.defineProperty(window, 'location', {
|
||||
value: originalLocation,
|
||||
writable: true,
|
||||
});
|
||||
locationSpy.mockRestore();
|
||||
});
|
||||
|
||||
test('returns null db when dbId does not exist in databases', () => {
|
||||
|
||||
@@ -45,15 +45,23 @@ const createProps = () => ({
|
||||
submenuKey: 'share',
|
||||
});
|
||||
|
||||
const originalLocation = window.location;
|
||||
|
||||
const postDashboardPermalinkMockUrl = `http://localhost/api/v1/dashboard/${DASHBOARD_ID}/permalink`;
|
||||
|
||||
let hrefValue = '';
|
||||
let locationSpy: jest.SpyInstance;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
// @ts-expect-error
|
||||
delete window.location;
|
||||
window.location = { href: '' } as any;
|
||||
hrefValue = '';
|
||||
locationSpy = jest.spyOn(window, 'location', 'get').mockReturnValue({
|
||||
...window.location,
|
||||
get href() {
|
||||
return hrefValue;
|
||||
},
|
||||
set href(v: string) {
|
||||
hrefValue = v;
|
||||
},
|
||||
} as Location);
|
||||
fetchMock.clearHistory().removeRoutes();
|
||||
fetchMock.post(
|
||||
postDashboardPermalinkMockUrl,
|
||||
@@ -63,7 +71,7 @@ beforeEach(() => {
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
window.location = originalLocation;
|
||||
locationSpy.mockRestore();
|
||||
window.featureFlags = {};
|
||||
fetchMock.clearHistory().removeRoutes();
|
||||
});
|
||||
|
||||
@@ -18,19 +18,18 @@
|
||||
*/
|
||||
import extractUrlParams from './extractUrlParams';
|
||||
|
||||
const originalWindowLocation = window.location;
|
||||
|
||||
// eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks
|
||||
describe('extractUrlParams', () => {
|
||||
let locationSpy: jest.SpyInstance;
|
||||
|
||||
beforeAll(() => {
|
||||
// @ts-expect-error
|
||||
delete window.location;
|
||||
// @ts-expect-error
|
||||
window.location = { search: '?edit=true&abc=123' };
|
||||
locationSpy = jest
|
||||
.spyOn(window, 'location', 'get')
|
||||
.mockReturnValue({ search: '?edit=true&abc=123' } as Location);
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
window.location = originalWindowLocation;
|
||||
locationSpy.mockRestore();
|
||||
});
|
||||
|
||||
test('returns all urlParams', () => {
|
||||
|
||||
@@ -29,9 +29,8 @@ describe('getChartIdsFromLayout', () => {
|
||||
},
|
||||
};
|
||||
|
||||
const globalLocation = window.location;
|
||||
afterEach(() => {
|
||||
window.location = globalLocation;
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
test('should encode filters', () => {
|
||||
@@ -93,16 +92,11 @@ describe('getChartIdsFromLayout', () => {
|
||||
});
|
||||
|
||||
test('should preserve unknown filters', () => {
|
||||
const windowSpy = jest.spyOn(window, 'window', 'get');
|
||||
windowSpy.mockImplementation(
|
||||
() =>
|
||||
({
|
||||
location: {
|
||||
origin: 'https://localhost',
|
||||
search: '?unknown_param=value',
|
||||
},
|
||||
}) as unknown as Window & typeof globalThis,
|
||||
);
|
||||
jest.spyOn(window, 'location', 'get').mockReturnValue({
|
||||
...window.location,
|
||||
origin: 'https://localhost',
|
||||
search: '?unknown_param=value',
|
||||
} as Location);
|
||||
const urlWithStandalone = getDashboardUrl({
|
||||
pathname: 'path',
|
||||
filters: {},
|
||||
@@ -112,7 +106,6 @@ describe('getChartIdsFromLayout', () => {
|
||||
expect(urlWithStandalone).toBe(
|
||||
`path?unknown_param=value&standalone=${DashboardStandaloneMode.HideNav}`,
|
||||
);
|
||||
windowSpy.mockRestore();
|
||||
});
|
||||
|
||||
test('should pass through a router-relative pathname unchanged', () => {
|
||||
@@ -122,21 +115,18 @@ describe('getChartIdsFromLayout', () => {
|
||||
hash: '',
|
||||
standalone: DashboardStandaloneMode.HideNav,
|
||||
});
|
||||
expect(url).toBe(`/dashboard/1/?standalone=${DashboardStandaloneMode.HideNav}`);
|
||||
expect(url).toBe(
|
||||
`/dashboard/1/?standalone=${DashboardStandaloneMode.HideNav}`,
|
||||
);
|
||||
});
|
||||
|
||||
test('should process native filters key', () => {
|
||||
const windowSpy = jest.spyOn(window, 'window', 'get');
|
||||
windowSpy.mockImplementation(
|
||||
() =>
|
||||
({
|
||||
location: {
|
||||
origin: 'https://localhost',
|
||||
search:
|
||||
'?preselect_filters=%7B%7D&native_filters_key=024380498jdkjf-2094838',
|
||||
},
|
||||
}) as unknown as Window & typeof globalThis,
|
||||
);
|
||||
jest.spyOn(window, 'location', 'get').mockReturnValue({
|
||||
...window.location,
|
||||
origin: 'https://localhost',
|
||||
search:
|
||||
'?preselect_filters=%7B%7D&native_filters_key=024380498jdkjf-2094838',
|
||||
} as Location);
|
||||
|
||||
const urlWithNativeFilters = getDashboardUrl({
|
||||
pathname: 'path',
|
||||
@@ -146,6 +136,5 @@ describe('getChartIdsFromLayout', () => {
|
||||
expect(urlWithNativeFilters).toBe(
|
||||
'path?preselect_filters=%7B%7D&native_filters_key=024380498jdkjf-2094838',
|
||||
);
|
||||
windowSpy.mockRestore();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -120,6 +120,11 @@ fetchMock.get('glob:*/api/v1/chart/*', {
|
||||
});
|
||||
|
||||
const defaultPath = '/explore/';
|
||||
|
||||
afterEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
const renderWithRouter = ({
|
||||
search = '',
|
||||
overridePathname,
|
||||
@@ -134,11 +139,10 @@ const renderWithRouter = ({
|
||||
history?: ReturnType<typeof createMemoryHistory>;
|
||||
} = {}) => {
|
||||
const path = overridePathname ?? defaultPath;
|
||||
Object.defineProperty(window, 'location', {
|
||||
get() {
|
||||
return { pathname: path, search };
|
||||
},
|
||||
});
|
||||
jest.spyOn(window, 'location', 'get').mockReturnValue({
|
||||
pathname: path,
|
||||
search,
|
||||
} as Location);
|
||||
const history =
|
||||
existingHistory ??
|
||||
createMemoryHistory({ initialEntries: [`${path}${search}`] });
|
||||
|
||||
@@ -46,14 +46,8 @@ jest.mock('src/components/Datasource/components/DatasourceEditor', () => ({
|
||||
),
|
||||
}));
|
||||
|
||||
let originalLocation: Location;
|
||||
|
||||
beforeEach(() => {
|
||||
originalLocation = window.location;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
window.location = originalLocation;
|
||||
jest.restoreAllMocks();
|
||||
|
||||
try {
|
||||
const unmatched = fetchMock.callHistory.calls('unmatched');
|
||||
@@ -539,10 +533,10 @@ test('should show missing params state', () => {
|
||||
});
|
||||
|
||||
test('should show missing dataset state', () => {
|
||||
// @ts-expect-error - overriding window.location for test
|
||||
delete window.location;
|
||||
// @ts-expect-error - overriding window.location for test
|
||||
window.location = { search: '?slice_id=152' };
|
||||
jest.spyOn(window, 'location', 'get').mockReturnValue({
|
||||
...window.location,
|
||||
search: '?slice_id=152',
|
||||
} as Location);
|
||||
const props = createProps({ datasource: fallbackExploreInitialData.dataset });
|
||||
render(<DatasourceControl {...props} />, { useRedux: true, useRouter: true });
|
||||
expect(screen.getAllByText(/missing dataset/i)).toHaveLength(2);
|
||||
@@ -554,10 +548,10 @@ test('should show missing dataset state', () => {
|
||||
});
|
||||
|
||||
test('should show forbidden dataset state', () => {
|
||||
// @ts-expect-error - overriding window.location for test
|
||||
delete window.location;
|
||||
// @ts-expect-error - overriding window.location for test
|
||||
window.location = { search: '?slice_id=152' };
|
||||
jest.spyOn(window, 'location', 'get').mockReturnValue({
|
||||
...window.location,
|
||||
search: '?slice_id=152',
|
||||
} as Location);
|
||||
const error = {
|
||||
error_type: 'TABLE_SECURITY_ACCESS_ERROR',
|
||||
statusText: 'FORBIDDEN',
|
||||
|
||||
@@ -21,10 +21,24 @@ import { VizType } from '@superset-ui/core';
|
||||
import { getParsedExploreURLParams } from './getParsedExploreURLParams';
|
||||
|
||||
const EXPLORE_BASE_URL = 'http://localhost:9000/explore/';
|
||||
|
||||
afterEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
const setupLocation = (newUrl: string) => {
|
||||
delete (window as any).location;
|
||||
// @ts-expect-error
|
||||
window.location = new URL(newUrl);
|
||||
const u = new URL(newUrl);
|
||||
jest.spyOn(window, 'location', 'get').mockReturnValue({
|
||||
href: u.href,
|
||||
pathname: u.pathname,
|
||||
search: u.search,
|
||||
hash: u.hash,
|
||||
origin: u.origin,
|
||||
host: u.host,
|
||||
hostname: u.hostname,
|
||||
port: u.port,
|
||||
protocol: u.protocol,
|
||||
} as Location);
|
||||
};
|
||||
|
||||
test('get form_data_key and slice_id from search params - url when moving from dashboard to explore', () => {
|
||||
|
||||
@@ -229,10 +229,10 @@ test('switches to Mine tab correctly', async () => {
|
||||
|
||||
test('handles create dashboard button click', async () => {
|
||||
const assignMock = jest.fn();
|
||||
Object.defineProperty(window, 'location', {
|
||||
value: { assign: assignMock },
|
||||
writable: true,
|
||||
});
|
||||
const locationSpy = jest.spyOn(window, 'location', 'get').mockReturnValue({
|
||||
...window.location,
|
||||
assign: assignMock,
|
||||
} as Location);
|
||||
|
||||
render(
|
||||
<Router history={history}>
|
||||
@@ -244,6 +244,7 @@ test('handles create dashboard button click', async () => {
|
||||
const createButton = screen.getByRole('button', { name: /dashboard$/i });
|
||||
await userEvent.click(createButton);
|
||||
expect(assignMock).toHaveBeenCalledWith('/dashboard/new');
|
||||
locationSpy.mockRestore();
|
||||
});
|
||||
|
||||
test('switches to Other tab when available', async () => {
|
||||
|
||||
@@ -99,11 +99,10 @@ describe('logger middleware', () => {
|
||||
|
||||
test('should include ts, start_offset, event_name, impression_id, source, and source_id in every event', () => {
|
||||
// Set window.location to include /dashboard/ so the middleware adds dashboard context
|
||||
const originalHref = window.location.href;
|
||||
Object.defineProperty(window, 'location', {
|
||||
value: { href: `http://localhost/dashboard/${dashboardId}/` },
|
||||
writable: true,
|
||||
});
|
||||
const locationSpy = jest.spyOn(window, 'location', 'get').mockReturnValue({
|
||||
...window.location,
|
||||
href: `http://localhost/dashboard/${dashboardId}/`,
|
||||
} as Location);
|
||||
|
||||
try {
|
||||
const fetchLog = (logger as Function)(mockStore)(next);
|
||||
@@ -134,11 +133,7 @@ describe('logger middleware', () => {
|
||||
expect(typeof events[0].ts).toBe('number');
|
||||
expect(typeof events[0].start_offset).toBe('number');
|
||||
} finally {
|
||||
// Restore original location
|
||||
Object.defineProperty(window, 'location', {
|
||||
value: { href: originalHref },
|
||||
writable: true,
|
||||
});
|
||||
locationSpy.mockRestore();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -251,23 +251,16 @@ test('handles special characters in dataset name from URL parameter', async () =
|
||||
status: 200,
|
||||
});
|
||||
|
||||
const originalLocation = window.location;
|
||||
Object.defineProperty(window, 'location', {
|
||||
value: {
|
||||
...originalLocation,
|
||||
search: '?dataset=flights%C3%86%20test',
|
||||
},
|
||||
writable: true,
|
||||
});
|
||||
const locationSpy = jest.spyOn(window, 'location', 'get').mockReturnValue({
|
||||
...window.location,
|
||||
search: '?dataset=flights%C3%86%20test',
|
||||
} as Location);
|
||||
|
||||
await renderComponent();
|
||||
|
||||
expect(await screen.findByText('flightsÆ test')).toBeInTheDocument();
|
||||
|
||||
Object.defineProperty(window, 'location', {
|
||||
value: originalLocation,
|
||||
writable: true,
|
||||
});
|
||||
locationSpy.mockRestore();
|
||||
});
|
||||
|
||||
test('pre-selects the dataset from URL parameter and shows it in dropdown', async () => {
|
||||
@@ -288,20 +281,16 @@ test('pre-selects the dataset from URL parameter and shows it in dropdown', asyn
|
||||
status: 200,
|
||||
});
|
||||
|
||||
const originalLocation = window.location;
|
||||
Object.defineProperty(window, 'location', {
|
||||
value: { ...originalLocation, search: '?dataset=flights' },
|
||||
writable: true,
|
||||
});
|
||||
const locationSpy = jest.spyOn(window, 'location', 'get').mockReturnValue({
|
||||
...window.location,
|
||||
search: '?dataset=flights',
|
||||
} as Location);
|
||||
|
||||
await renderComponent();
|
||||
|
||||
expect(await screen.findByText('flights')).toBeInTheDocument();
|
||||
|
||||
Object.defineProperty(window, 'location', {
|
||||
value: originalLocation,
|
||||
writable: true,
|
||||
});
|
||||
locationSpy.mockRestore();
|
||||
});
|
||||
|
||||
test('shows loading spinner when dataset parameter is present in URL', async () => {
|
||||
@@ -329,11 +318,10 @@ test('shows loading spinner when dataset parameter is present in URL', async ()
|
||||
})),
|
||||
);
|
||||
|
||||
const originalLocation = window.location;
|
||||
Object.defineProperty(window, 'location', {
|
||||
value: { ...originalLocation, search: '?dataset=flights' },
|
||||
writable: true,
|
||||
});
|
||||
const locationSpy = jest.spyOn(window, 'location', 'get').mockReturnValue({
|
||||
...window.location,
|
||||
search: '?dataset=flights',
|
||||
} as Location);
|
||||
|
||||
render(
|
||||
<ChartCreation
|
||||
@@ -356,10 +344,7 @@ test('shows loading spinner when dataset parameter is present in URL', async ()
|
||||
expect(screen.queryByRole('status')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
Object.defineProperty(window, 'location', {
|
||||
value: originalLocation,
|
||||
writable: true,
|
||||
});
|
||||
locationSpy.mockRestore();
|
||||
});
|
||||
|
||||
test('shows only exact match when loading dataset from URL, not partial matches', async () => {
|
||||
@@ -406,19 +391,15 @@ test('shows only exact match when loading dataset from URL, not partial matches'
|
||||
};
|
||||
});
|
||||
|
||||
const originalLocation = window.location;
|
||||
Object.defineProperty(window, 'location', {
|
||||
value: { ...originalLocation, search: '?dataset=flights' },
|
||||
writable: true,
|
||||
});
|
||||
const locationSpy = jest.spyOn(window, 'location', 'get').mockReturnValue({
|
||||
...window.location,
|
||||
search: '?dataset=flights',
|
||||
} as Location);
|
||||
|
||||
await renderComponent();
|
||||
|
||||
await screen.findByText('flights');
|
||||
expect(screen.queryByText('flights_delayed')).not.toBeInTheDocument();
|
||||
|
||||
Object.defineProperty(window, 'location', {
|
||||
value: originalLocation,
|
||||
writable: true,
|
||||
});
|
||||
locationSpy.mockRestore();
|
||||
});
|
||||
|
||||
@@ -57,28 +57,28 @@ test('isAllowedScheme allows relative URLs (unparseable as absolute)', () => {
|
||||
});
|
||||
|
||||
test('getTargetUrl reads the url query parameter', () => {
|
||||
Object.defineProperty(window, 'location', {
|
||||
value: { search: '?url=https%3A%2F%2Fexample.com%2Fpage' },
|
||||
writable: true,
|
||||
});
|
||||
const locationSpy = jest.spyOn(window, 'location', 'get').mockReturnValue({
|
||||
search: '?url=https%3A%2F%2Fexample.com%2Fpage',
|
||||
} as Location);
|
||||
expect(getTargetUrl()).toBe('https://example.com/page');
|
||||
locationSpy.mockRestore();
|
||||
});
|
||||
|
||||
test('getTargetUrl returns empty string when url param is missing', () => {
|
||||
Object.defineProperty(window, 'location', {
|
||||
value: { search: '' },
|
||||
writable: true,
|
||||
});
|
||||
const locationSpy = jest
|
||||
.spyOn(window, 'location', 'get')
|
||||
.mockReturnValue({ search: '' } as Location);
|
||||
expect(getTargetUrl()).toBe('');
|
||||
locationSpy.mockRestore();
|
||||
});
|
||||
|
||||
test('getTargetUrl does not double-decode percent-encoded values', () => {
|
||||
// %253A is the double-encoding of ":" — after one decode it should remain %3A
|
||||
Object.defineProperty(window, 'location', {
|
||||
value: { search: '?url=javascript%253Aalert(1)' },
|
||||
writable: true,
|
||||
});
|
||||
const locationSpy = jest
|
||||
.spyOn(window, 'location', 'get')
|
||||
.mockReturnValue({ search: '?url=javascript%253Aalert(1)' } as Location);
|
||||
expect(getTargetUrl()).toBe('javascript%3Aalert(1)');
|
||||
locationSpy.mockRestore();
|
||||
});
|
||||
|
||||
test('trustUrl stores and isUrlTrusted retrieves a URL', () => {
|
||||
|
||||
@@ -21,18 +21,21 @@ import { availableDomains, allowCrossDomain } from './hostNamesConfig';
|
||||
|
||||
// eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks
|
||||
describe('hostNamesConfig', () => {
|
||||
let locationSpy: jest.SpyInstance;
|
||||
|
||||
beforeEach(() => {
|
||||
// Reset DOM
|
||||
document.body.innerHTML = '';
|
||||
|
||||
// Mock window.location
|
||||
Object.defineProperty(window, 'location', {
|
||||
value: {
|
||||
hostname: 'localhost',
|
||||
search: '',
|
||||
},
|
||||
writable: true,
|
||||
});
|
||||
locationSpy = jest.spyOn(window, 'location', 'get').mockReturnValue({
|
||||
hostname: 'localhost',
|
||||
search: '',
|
||||
} as Location);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
locationSpy.mockRestore();
|
||||
});
|
||||
|
||||
test('should export availableDomains as array of strings', () => {
|
||||
|
||||
@@ -104,15 +104,10 @@ test('toQueryString should handle special characters in keys and values', () =>
|
||||
});
|
||||
|
||||
test('getDashboardUrlParams should exclude edit parameter by default', () => {
|
||||
// Mock window.location.search to include edit parameter
|
||||
const originalLocation = window.location;
|
||||
Object.defineProperty(window, 'location', {
|
||||
value: {
|
||||
...originalLocation,
|
||||
search: '?edit=true&standalone=false&expand_filters=1',
|
||||
},
|
||||
writable: true,
|
||||
});
|
||||
const locationSpy = jest.spyOn(window, 'location', 'get').mockReturnValue({
|
||||
...window.location,
|
||||
search: '?edit=true&standalone=false&expand_filters=1',
|
||||
} as Location);
|
||||
|
||||
const urlParams = getDashboardUrlParams(['edit']);
|
||||
const paramNames = urlParams.map(([key]) => key);
|
||||
@@ -121,20 +116,14 @@ test('getDashboardUrlParams should exclude edit parameter by default', () => {
|
||||
expect(paramNames).toContain('standalone');
|
||||
expect(paramNames).toContain('expand_filters');
|
||||
|
||||
// Restore original location
|
||||
window.location = originalLocation;
|
||||
locationSpy.mockRestore();
|
||||
});
|
||||
|
||||
test('getDashboardUrlParams should exclude multiple parameters when provided', () => {
|
||||
// Mock window.location.search with multiple parameters
|
||||
const originalLocation = window.location;
|
||||
Object.defineProperty(window, 'location', {
|
||||
value: {
|
||||
...originalLocation,
|
||||
search: '?edit=true&standalone=false&debug=true&test=value',
|
||||
},
|
||||
writable: true,
|
||||
});
|
||||
const locationSpy = jest.spyOn(window, 'location', 'get').mockReturnValue({
|
||||
...window.location,
|
||||
search: '?edit=true&standalone=false&debug=true&test=value',
|
||||
} as Location);
|
||||
|
||||
const urlParams = getDashboardUrlParams(['edit', 'debug']);
|
||||
const paramNames = urlParams.map(([key]) => key);
|
||||
@@ -144,36 +133,27 @@ test('getDashboardUrlParams should exclude multiple parameters when provided', (
|
||||
expect(paramNames).toContain('standalone');
|
||||
expect(paramNames).toContain('test');
|
||||
|
||||
// Restore original location
|
||||
window.location = originalLocation;
|
||||
locationSpy.mockRestore();
|
||||
});
|
||||
|
||||
test('getUrlParam reads from window.location.search by default', () => {
|
||||
const originalLocation = window.location;
|
||||
Object.defineProperty(window, 'location', {
|
||||
value: { ...originalLocation, search: '?dashboard_page_id=from-window' },
|
||||
writable: true,
|
||||
configurable: true,
|
||||
});
|
||||
const locationSpy = jest.spyOn(window, 'location', 'get').mockReturnValue({
|
||||
...window.location,
|
||||
search: '?dashboard_page_id=from-window',
|
||||
} as Location);
|
||||
|
||||
expect(getUrlParam(URL_PARAMS.dashboardPageId)).toBe('from-window');
|
||||
|
||||
Object.defineProperty(window, 'location', {
|
||||
value: originalLocation,
|
||||
writable: true,
|
||||
configurable: true,
|
||||
});
|
||||
locationSpy.mockRestore();
|
||||
});
|
||||
|
||||
test('getUrlParam uses provided search string instead of window.location.search (Safari race condition fix)', () => {
|
||||
// Simulate Safari race condition: window.location.search is stale (empty),
|
||||
// but the correct search string is passed in from React Router's useLocation()
|
||||
const originalLocation = window.location;
|
||||
Object.defineProperty(window, 'location', {
|
||||
value: { ...originalLocation, search: '' },
|
||||
writable: true,
|
||||
configurable: true,
|
||||
});
|
||||
const locationSpy = jest.spyOn(window, 'location', 'get').mockReturnValue({
|
||||
...window.location,
|
||||
search: '',
|
||||
} as Location);
|
||||
|
||||
// Without the search override, window.location.search is stale — returns null (the bug)
|
||||
expect(getUrlParam(URL_PARAMS.dashboardPageId)).toBeNull();
|
||||
@@ -183,9 +163,5 @@ test('getUrlParam uses provided search string instead of window.location.search
|
||||
getUrlParam(URL_PARAMS.dashboardPageId, '?dashboard_page_id=correct-id'),
|
||||
).toBe('correct-id');
|
||||
|
||||
Object.defineProperty(window, 'location', {
|
||||
value: originalLocation,
|
||||
writable: true,
|
||||
configurable: true,
|
||||
});
|
||||
locationSpy.mockRestore();
|
||||
});
|
||||
|
||||
@@ -1478,46 +1478,3 @@ def test_success_state_report_sends_and_logs_success(
|
||||
ReportState.SUCCESS,
|
||||
error_message=None,
|
||||
)
|
||||
|
||||
|
||||
def test_get_url_for_csv_uses_post_processed_type(
|
||||
app: SupersetApp,
|
||||
mocker: MockerFixture,
|
||||
) -> None:
|
||||
"""Regression for #25538: when an alert/report generates a CSV for a
|
||||
chart, the URL must request type=POST_PROCESSED so the chart's saved
|
||||
filters (including time-range filters) are applied. The original report
|
||||
described a chart with a "last 30 days" filter that returned only 14
|
||||
rows in the UI, but the alert CSV came back with 219 rows (the entire
|
||||
unfiltered table).
|
||||
|
||||
POST_PROCESSED is the marker that propagates the chart's query_context
|
||||
-- including its time filter -- through to the CSV renderer. A
|
||||
regression that switched to FULL or RAW would replicate the original
|
||||
bug.
|
||||
"""
|
||||
from datetime import datetime
|
||||
from uuid import UUID
|
||||
|
||||
from superset.commands.report.execute import BaseReportState
|
||||
from superset.common.chart_data import ChartDataResultFormat
|
||||
|
||||
app.config.update({"ALERT_REPORTS_EXECUTORS": {}})
|
||||
|
||||
report_schedule = create_report_schedule(mocker)
|
||||
report_schedule.force_screenshot = False
|
||||
report_schedule.chart_id = report_schedule.chart.id
|
||||
|
||||
state = BaseReportState(
|
||||
report_schedule=report_schedule,
|
||||
scheduled_dttm=datetime.now(),
|
||||
execution_id=UUID("084e7ee6-5557-4ecd-9632-b7f39c9ec524"),
|
||||
)
|
||||
|
||||
url = state._get_url(result_format=ChartDataResultFormat.CSV)
|
||||
|
||||
assert "format=csv" in url.lower(), f"expected csv format in URL: {url}"
|
||||
assert "type=post_processed" in url.lower(), (
|
||||
f"CSV report URL must use type=post_processed so chart filters "
|
||||
f"(incl. time filters) are applied; got: {url}; see issue #25538"
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user