@marimo-team/islands 0.23.15-dev49 → 0.23.15-dev50
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{ConnectedDataExplorerComponent-7p7rt9iw.js → ConnectedDataExplorerComponent-Cmq_9LVS.js} +4 -4
- package/dist/{ErrorBoundary-B_CAG7_e.js → ErrorBoundary-CnMJ8jR9.js} +1 -1
- package/dist/{chat-ui-C0z13gJB.js → chat-ui-Dy0arY8E.js} +6 -6
- package/dist/{code-visibility-CoTgpfnd.js → code-visibility-B5xe1Xp2.js} +9 -9
- package/dist/{formats-CitsMtUm.js → formats-CdvkxTS6.js} +1 -1
- package/dist/{glide-data-editor-CwZz71BD.js → glide-data-editor-D5w23DvC.js} +3 -3
- package/dist/{html-to-image-CEo5pRYw.js → html-to-image-f-kPEnn0.js} +5 -5
- package/dist/{input-BGPrFH3g.js → input-CF5Sgjba.js} +1 -1
- package/dist/main.js +19 -18
- package/dist/{mermaid-UdmxG2PZ.js → mermaid-BaZJ2mds.js} +2 -2
- package/dist/{process-output-BOvvilRG.js → process-output-O9FeFAVM.js} +1 -1
- package/dist/{reveal-component-Bvigsc8L.js → reveal-component-BkZp_qsV.js} +5 -5
- package/dist/{spec-DSs9v0xx.js → spec-DxIove3q.js} +1 -1
- package/dist/style.css +1 -1
- package/dist/{toDate-BRJgtOGm.js → toDate-CEOdz9nC.js} +1 -1
- package/dist/{useAsyncData-BSOyAbac.js → useAsyncData-Cs0mpJTy.js} +1 -1
- package/dist/{useDeepCompareMemoize-BTLeWzuR.js → useDeepCompareMemoize-DoOZicGb.js} +1 -1
- package/dist/{useLifecycle-C6wHjkhW.js → useLifecycle-DR_F1d7F.js} +1 -1
- package/dist/{useTheme-Df_vGflw.js → useTheme-BA-8MM7N.js} +1 -0
- package/dist/{vega-component-BAvmvTjO.js → vega-component-BN0fz-QA.js} +5 -5
- package/package.json +1 -1
- package/src/__mocks__/requests.ts +3 -0
- package/src/components/editor/actions/useNotebookActions.tsx +8 -0
- package/src/components/editor/chrome/components/__tests__/feedback-button.test.tsx +221 -0
- package/src/components/editor/chrome/components/feedback-button.tsx +308 -77
- package/src/core/ai/context/providers/error.ts +6 -158
- package/src/core/constants.ts +2 -0
- package/src/core/diagnostics/__tests__/issue-details.test.ts +173 -0
- package/src/core/diagnostics/issue-details.ts +139 -0
- package/src/core/errors/__tests__/error-entries.test.ts +25 -0
- package/src/core/errors/error-entries.ts +176 -0
- package/src/core/islands/bridge.ts +1 -0
- package/src/core/network/__tests__/requests-lazy.test.ts +18 -0
- package/src/core/network/__tests__/requests-network.test.ts +7 -0
- package/src/core/network/requests-lazy.ts +13 -1
- package/src/core/network/requests-network.ts +3 -0
- package/src/core/network/requests-static.ts +1 -0
- package/src/core/network/requests-toasting.tsx +1 -0
- package/src/core/network/types.ts +3 -0
- package/src/core/wasm/bridge.ts +8 -0
- package/src/core/wasm/worker/types.ts +2 -0
|
@@ -5,7 +5,7 @@ import { t as __commonJSMin } from "./chunk-BNovOVIE.js";
|
|
|
5
5
|
import { _ as Logger } from "./button-BacYv-bE.js";
|
|
6
6
|
import { t as require_compiler_runtime } from "./compiler-runtime-CEbnTgxf.js";
|
|
7
7
|
import { u as createLucideIcon } from "./dist--2Bqjvs0.js";
|
|
8
|
-
import { T as atom, a as KnownQueryParams, g as store, v as waitFor, y as isIslands } from "./useTheme-
|
|
8
|
+
import { T as atom, a as KnownQueryParams, g as store, v as waitFor, y as isIslands } from "./useTheme-BA-8MM7N.js";
|
|
9
9
|
import { t as invariant } from "./invariant-wRzNXIsJ.js";
|
|
10
10
|
var CircleQuestionMark = createLucideIcon("circle-question-mark", [
|
|
11
11
|
["circle", {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { s as __toESM } from "./chunk-BNovOVIE.js";
|
|
2
2
|
import { t as require_react } from "./react-DA-nE2FX.js";
|
|
3
3
|
import { t as require_compiler_runtime } from "./compiler-runtime-CEbnTgxf.js";
|
|
4
|
-
import { A as useEvent_default } from "./useTheme-
|
|
4
|
+
import { A as useEvent_default } from "./useTheme-BA-8MM7N.js";
|
|
5
5
|
import { t as invariant } from "./invariant-wRzNXIsJ.js";
|
|
6
6
|
var import_compiler_runtime = require_compiler_runtime(), import_react = /* @__PURE__ */ __toESM(require_react(), 1), Result = {
|
|
7
7
|
error(e, s) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { s as __toESM } from "./chunk-BNovOVIE.js";
|
|
2
2
|
import { t as require_react } from "./react-DA-nE2FX.js";
|
|
3
|
-
import { k as dequal } from "./useTheme-
|
|
3
|
+
import { k as dequal } from "./useTheme-BA-8MM7N.js";
|
|
4
4
|
var import_react = /* @__PURE__ */ __toESM(require_react(), 1);
|
|
5
5
|
function useDeepCompareMemoize(e) {
|
|
6
6
|
let i = import_react.useRef(e);
|
|
@@ -4,7 +4,7 @@ import { t as require_react } from "./react-DA-nE2FX.js";
|
|
|
4
4
|
import { t as require_compiler_runtime } from "./compiler-runtime-CEbnTgxf.js";
|
|
5
5
|
import { u as createLucideIcon } from "./dist--2Bqjvs0.js";
|
|
6
6
|
import { t as require_jsx_runtime } from "./jsx-runtime-DebpN0FN.js";
|
|
7
|
-
import { C as useSetAtom, T as atom } from "./useTheme-
|
|
7
|
+
import { C as useSetAtom, T as atom } from "./useTheme-BA-8MM7N.js";
|
|
8
8
|
var Calendar = createLucideIcon("calendar", [
|
|
9
9
|
["path", {
|
|
10
10
|
d: "M8 2v4",
|
|
@@ -740,6 +740,7 @@ const Constants = {
|
|
|
740
740
|
githubPage: "https://github.com/marimo-team/marimo",
|
|
741
741
|
releasesPage: "https://github.com/marimo-team/marimo/releases",
|
|
742
742
|
issuesPage: "https://github.com/marimo-team/marimo/issues",
|
|
743
|
+
bugReportUrl: "https://github.com/marimo-team/marimo/issues/new?template=bug_report.yaml",
|
|
743
744
|
feedbackForm: "https://marimo.io/feedback",
|
|
744
745
|
discordLink: "https://marimo.io/discord?ref=notebook",
|
|
745
746
|
docsPage: "https://docs.marimo.io",
|
|
@@ -2,23 +2,23 @@ import { s as __toESM } from "./chunk-BNovOVIE.js";
|
|
|
2
2
|
import { _ as Logger, c as Objects, g as cn, h as Events } from "./button-BacYv-bE.js";
|
|
3
3
|
import { t as require_react } from "./react-DA-nE2FX.js";
|
|
4
4
|
import { t as require_compiler_runtime } from "./compiler-runtime-CEbnTgxf.js";
|
|
5
|
-
import { c as asRemoteURL, v as CircleQuestionMark } from "./toDate-
|
|
5
|
+
import { c as asRemoteURL, v as CircleQuestionMark } from "./toDate-CEOdz9nC.js";
|
|
6
6
|
import "./react-dom-BTJzcVJ9.js";
|
|
7
7
|
import { t as require_jsx_runtime } from "./jsx-runtime-DebpN0FN.js";
|
|
8
8
|
import "./zod-CijjQh4u.js";
|
|
9
9
|
import { n as ErrorBanner } from "./error-banner-DFPfz_Qf.js";
|
|
10
10
|
import { t as Tooltip } from "./tooltip-Czds6Qr8.js";
|
|
11
11
|
import { t as debounce_default } from "./debounce-BOD3DbfP.js";
|
|
12
|
-
import { A as useEvent_default, n as useTheme } from "./useTheme-
|
|
12
|
+
import { A as useEvent_default, n as useTheme } from "./useTheme-BA-8MM7N.js";
|
|
13
13
|
import { s as uniq } from "./arrays-sEtDRoG4.js";
|
|
14
|
-
import { a as isValid, i as AlertTitle, n as Alert, t as arrow } from "./formats-
|
|
14
|
+
import { a as isValid, i as AlertTitle, n as Alert, t as arrow } from "./formats-CdvkxTS6.js";
|
|
15
15
|
import { n as formats } from "./vega-loader.browser-CZ-J8Py3.js";
|
|
16
16
|
import { a as getContainerWidth, n as vegaLoadData, s as tooltipHandler } from "./loader-Boph2xIS.js";
|
|
17
17
|
import { t as j } from "./react-vega-B0sAlDTL.js";
|
|
18
18
|
import "./defaultLocale-u-3osm0P.js";
|
|
19
19
|
import "./defaultLocale-BoHTsDG6.js";
|
|
20
|
-
import { t as useAsyncData } from "./useAsyncData-
|
|
21
|
-
import { t as useDeepCompareMemoize } from "./useDeepCompareMemoize-
|
|
20
|
+
import { t as useAsyncData } from "./useAsyncData-Cs0mpJTy.js";
|
|
21
|
+
import { t as useDeepCompareMemoize } from "./useDeepCompareMemoize-DoOZicGb.js";
|
|
22
22
|
import { t as Semaphore } from "./semaphore-CNDGTzkX.js";
|
|
23
23
|
var import_compiler_runtime = require_compiler_runtime(), import_react = /* @__PURE__ */ __toESM(require_react(), 1);
|
|
24
24
|
function fixRelativeUrl(e) {
|
package/package.json
CHANGED
|
@@ -44,6 +44,9 @@ export const MockRequestClient = {
|
|
|
44
44
|
validateSQL: vi.fn().mockResolvedValue({}),
|
|
45
45
|
openFile: vi.fn().mockResolvedValue({}),
|
|
46
46
|
getUsageStats: vi.fn().mockResolvedValue({}),
|
|
47
|
+
getEnvironmentInfo: vi
|
|
48
|
+
.fn()
|
|
49
|
+
.mockRejectedValue(new Error("Environment information unavailable")),
|
|
47
50
|
sendPdb: vi.fn().mockResolvedValue({}),
|
|
48
51
|
sendSetBreakpoints: vi.fn().mockResolvedValue({}),
|
|
49
52
|
sendListFiles: vi.fn().mockResolvedValue({ files: [] }),
|
|
@@ -28,6 +28,7 @@ import {
|
|
|
28
28
|
KeyboardIcon,
|
|
29
29
|
LayoutTemplateIcon,
|
|
30
30
|
LinkIcon,
|
|
31
|
+
MessageCircleQuestionIcon,
|
|
31
32
|
MessagesSquareIcon,
|
|
32
33
|
NotebookIcon,
|
|
33
34
|
PanelLeftIcon,
|
|
@@ -44,6 +45,7 @@ import {
|
|
|
44
45
|
settingDialogAtom,
|
|
45
46
|
useOpenSettingsToTab,
|
|
46
47
|
} from "@/components/app-config/state";
|
|
48
|
+
import { FeedbackModal } from "@/components/editor/chrome/components/feedback-button";
|
|
47
49
|
import { MarkdownIcon } from "@/components/editor/cell/code/icons";
|
|
48
50
|
import { GitHubIcon } from "@/components/icons/github";
|
|
49
51
|
import { MarimoPlusIcon } from "@/components/icons/marimo-icons";
|
|
@@ -642,6 +644,12 @@ export function useNotebookActions() {
|
|
|
642
644
|
redundant: true,
|
|
643
645
|
additionalKeywords: ["preferences", "options", "configuration"],
|
|
644
646
|
},
|
|
647
|
+
{
|
|
648
|
+
icon: <MessageCircleQuestionIcon size={14} strokeWidth={1.5} />,
|
|
649
|
+
label: "Report an issue",
|
|
650
|
+
additionalKeywords: ["feedback", "bug", "issue", "report", "diagnostics"],
|
|
651
|
+
handle: () => openModal(<FeedbackModal onClose={closeModal} />),
|
|
652
|
+
},
|
|
645
653
|
{
|
|
646
654
|
icon: <ExternalLinkIcon size={14} strokeWidth={1.5} />,
|
|
647
655
|
label: "Resources",
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
|
|
4
|
+
import { Provider } from "jotai";
|
|
5
|
+
import type React from "react";
|
|
6
|
+
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
7
|
+
import { MockRequestClient } from "@/__mocks__/requests";
|
|
8
|
+
import { Dialog } from "@/components/ui/dialog";
|
|
9
|
+
import { Constants } from "@/core/constants";
|
|
10
|
+
import { TooltipProvider } from "@/components/ui/tooltip";
|
|
11
|
+
import { viewStateAtom } from "@/core/mode";
|
|
12
|
+
import { connectionAtom } from "@/core/network/connection";
|
|
13
|
+
import { requestClientAtom } from "@/core/network/requests";
|
|
14
|
+
import type { EnvironmentInfo } from "@/core/network/types";
|
|
15
|
+
import { filenameAtom } from "@/core/saving/file-state";
|
|
16
|
+
import { store } from "@/core/state/jotai";
|
|
17
|
+
import { WebSocketState } from "@/core/websocket/types";
|
|
18
|
+
import * as copyModule from "@/utils/copy";
|
|
19
|
+
import { FeedbackModal } from "../feedback-button";
|
|
20
|
+
|
|
21
|
+
vi.mock("@/utils/copy", () => ({
|
|
22
|
+
copyToClipboard: vi.fn().mockResolvedValue(undefined),
|
|
23
|
+
}));
|
|
24
|
+
|
|
25
|
+
const environment: EnvironmentInfo = {
|
|
26
|
+
marimo: "1.2.3",
|
|
27
|
+
editable: false,
|
|
28
|
+
location: "~/.venv/site-packages/marimo",
|
|
29
|
+
OS: "Darwin",
|
|
30
|
+
"OS Version": "25.0",
|
|
31
|
+
Processor: "arm",
|
|
32
|
+
"Python Version": "3.12.9",
|
|
33
|
+
Locale: "en_US",
|
|
34
|
+
Binaries: { Browser: "chrome 140", Node: "v22", uv: "0.11" },
|
|
35
|
+
Dependencies: { click: "8.4.2" },
|
|
36
|
+
"Optional Dependencies": { pandas: "3.0.0" },
|
|
37
|
+
"Experimental Flags": {},
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
function wrapper({ children }: { children: React.ReactNode }) {
|
|
41
|
+
return (
|
|
42
|
+
<Provider store={store}>
|
|
43
|
+
<TooltipProvider>
|
|
44
|
+
<Dialog open={true}>{children}</Dialog>
|
|
45
|
+
</TooltipProvider>
|
|
46
|
+
</Provider>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
describe("FeedbackModal issue reporting", () => {
|
|
51
|
+
beforeEach(() => {
|
|
52
|
+
vi.clearAllMocks();
|
|
53
|
+
store.set(requestClientAtom, MockRequestClient.create());
|
|
54
|
+
store.set(viewStateAtom, { mode: "edit", cellAnchor: null });
|
|
55
|
+
store.set(connectionAtom, { state: WebSocketState.OPEN });
|
|
56
|
+
store.set(filenameAtom, "/project/example.py");
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("loads and previews issue environment details", async () => {
|
|
60
|
+
store.set(
|
|
61
|
+
requestClientAtom,
|
|
62
|
+
MockRequestClient.create({
|
|
63
|
+
getEnvironmentInfo: vi.fn().mockResolvedValue(environment),
|
|
64
|
+
}),
|
|
65
|
+
);
|
|
66
|
+
render(<FeedbackModal onClose={vi.fn()} />, { wrapper });
|
|
67
|
+
|
|
68
|
+
expect(screen.getByText("Loading environment details…")).toBeVisible();
|
|
69
|
+
await screen.findByText("Environment details");
|
|
70
|
+
expect(screen.getByText(/"marimo": "1.2.3"/)).toBeInTheDocument();
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it("toggles the environment preview between show more and show less", async () => {
|
|
74
|
+
store.set(
|
|
75
|
+
requestClientAtom,
|
|
76
|
+
MockRequestClient.create({
|
|
77
|
+
getEnvironmentInfo: vi.fn().mockResolvedValue(environment),
|
|
78
|
+
}),
|
|
79
|
+
);
|
|
80
|
+
render(<FeedbackModal onClose={vi.fn()} />, { wrapper });
|
|
81
|
+
await screen.findByText("Environment details");
|
|
82
|
+
|
|
83
|
+
const toggle = screen.getByRole("button", { name: "Show more" });
|
|
84
|
+
fireEvent.click(toggle);
|
|
85
|
+
expect(
|
|
86
|
+
screen.getByRole("button", { name: "Show less" }),
|
|
87
|
+
).toBeInTheDocument();
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it("copies partial details when the environment request fails", async () => {
|
|
91
|
+
store.set(
|
|
92
|
+
requestClientAtom,
|
|
93
|
+
MockRequestClient.create({
|
|
94
|
+
getEnvironmentInfo: vi.fn().mockRejectedValue(new Error("offline")),
|
|
95
|
+
}),
|
|
96
|
+
);
|
|
97
|
+
render(<FeedbackModal onClose={vi.fn()} />, { wrapper });
|
|
98
|
+
|
|
99
|
+
await screen.findByText("Server environment information unavailable");
|
|
100
|
+
fireEvent.click(screen.getByRole("button", { name: "Copy issue details" }));
|
|
101
|
+
await waitFor(() =>
|
|
102
|
+
expect(copyModule.copyToClipboard).toHaveBeenCalledWith(
|
|
103
|
+
expect.stringContaining("Environment Collection Error"),
|
|
104
|
+
),
|
|
105
|
+
);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it("copies raw environment JSON", async () => {
|
|
109
|
+
store.set(
|
|
110
|
+
requestClientAtom,
|
|
111
|
+
MockRequestClient.create({
|
|
112
|
+
getEnvironmentInfo: vi.fn().mockResolvedValue(environment),
|
|
113
|
+
}),
|
|
114
|
+
);
|
|
115
|
+
render(<FeedbackModal onClose={vi.fn()} />, { wrapper });
|
|
116
|
+
await screen.findByText("Environment details");
|
|
117
|
+
|
|
118
|
+
fireEvent.click(
|
|
119
|
+
screen.getByRole("button", { name: "Copy environment JSON" }),
|
|
120
|
+
);
|
|
121
|
+
await waitFor(() =>
|
|
122
|
+
expect(copyModule.copyToClipboard).toHaveBeenCalledWith(
|
|
123
|
+
expect.stringContaining('"marimo": "1.2.3"'),
|
|
124
|
+
),
|
|
125
|
+
);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it("includes notebook source only after explicit opt-in", async () => {
|
|
129
|
+
const readCode = vi.fn().mockResolvedValue({
|
|
130
|
+
contents: "secret = 'review before posting'",
|
|
131
|
+
});
|
|
132
|
+
store.set(
|
|
133
|
+
requestClientAtom,
|
|
134
|
+
MockRequestClient.create({
|
|
135
|
+
getEnvironmentInfo: vi.fn().mockResolvedValue(environment),
|
|
136
|
+
readCode,
|
|
137
|
+
}),
|
|
138
|
+
);
|
|
139
|
+
render(<FeedbackModal onClose={vi.fn()} />, { wrapper });
|
|
140
|
+
await screen.findByText("Environment details");
|
|
141
|
+
|
|
142
|
+
expect(readCode).not.toHaveBeenCalled();
|
|
143
|
+
fireEvent.click(
|
|
144
|
+
screen.getByRole("checkbox", { name: "Include full notebook source" }),
|
|
145
|
+
);
|
|
146
|
+
await waitFor(() => expect(readCode).toHaveBeenCalledOnce());
|
|
147
|
+
|
|
148
|
+
fireEvent.click(screen.getByRole("button", { name: "Copy issue details" }));
|
|
149
|
+
await waitFor(() =>
|
|
150
|
+
expect(copyModule.copyToClipboard).toHaveBeenCalledWith(
|
|
151
|
+
expect.stringContaining("Notebook source: example.py"),
|
|
152
|
+
),
|
|
153
|
+
);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it("discards notebook source after unchecking", async () => {
|
|
157
|
+
const readCode = vi.fn().mockResolvedValue({ contents: "x = 1" });
|
|
158
|
+
store.set(
|
|
159
|
+
requestClientAtom,
|
|
160
|
+
MockRequestClient.create({
|
|
161
|
+
getEnvironmentInfo: vi.fn().mockResolvedValue(environment),
|
|
162
|
+
readCode,
|
|
163
|
+
}),
|
|
164
|
+
);
|
|
165
|
+
render(<FeedbackModal onClose={vi.fn()} />, { wrapper });
|
|
166
|
+
await screen.findByText("Environment details");
|
|
167
|
+
|
|
168
|
+
const checkbox = screen.getByRole("checkbox", {
|
|
169
|
+
name: "Include full notebook source",
|
|
170
|
+
});
|
|
171
|
+
fireEvent.click(checkbox);
|
|
172
|
+
await waitFor(() => expect(readCode).toHaveBeenCalledOnce());
|
|
173
|
+
fireEvent.click(checkbox);
|
|
174
|
+
|
|
175
|
+
fireEvent.click(screen.getByRole("button", { name: "Copy issue details" }));
|
|
176
|
+
await waitFor(() =>
|
|
177
|
+
expect(copyModule.copyToClipboard).toHaveBeenCalledWith(
|
|
178
|
+
expect.not.stringContaining("Notebook source"),
|
|
179
|
+
),
|
|
180
|
+
);
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it("disables the notebook option and explains why when disconnected", async () => {
|
|
184
|
+
store.set(connectionAtom, { state: WebSocketState.CONNECTING });
|
|
185
|
+
store.set(
|
|
186
|
+
requestClientAtom,
|
|
187
|
+
MockRequestClient.create({
|
|
188
|
+
getEnvironmentInfo: vi.fn().mockResolvedValue(environment),
|
|
189
|
+
}),
|
|
190
|
+
);
|
|
191
|
+
render(<FeedbackModal onClose={vi.fn()} />, { wrapper });
|
|
192
|
+
await screen.findByText("Environment details");
|
|
193
|
+
|
|
194
|
+
expect(
|
|
195
|
+
screen.getByRole("checkbox", { name: "Include full notebook source" }),
|
|
196
|
+
).toBeDisabled();
|
|
197
|
+
expect(
|
|
198
|
+
screen.getByText("Connect the notebook to include its source."),
|
|
199
|
+
).toBeInTheDocument();
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
it("prefills the GitHub issue link with the environment once loaded", async () => {
|
|
203
|
+
store.set(
|
|
204
|
+
requestClientAtom,
|
|
205
|
+
MockRequestClient.create({
|
|
206
|
+
getEnvironmentInfo: vi.fn().mockResolvedValue(environment),
|
|
207
|
+
}),
|
|
208
|
+
);
|
|
209
|
+
render(<FeedbackModal onClose={vi.fn()} />, { wrapper });
|
|
210
|
+
|
|
211
|
+
const link = screen.getByRole("link", { name: "Open GitHub issue" });
|
|
212
|
+
expect(link).toHaveAttribute("href", Constants.bugReportUrl);
|
|
213
|
+
|
|
214
|
+
await screen.findByText("Environment details");
|
|
215
|
+
await waitFor(() => {
|
|
216
|
+
const href = link.getAttribute("href") ?? "";
|
|
217
|
+
expect(href).toContain("&env=");
|
|
218
|
+
expect(href).toContain(encodeURIComponent('"marimo": "1.2.3"'));
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
});
|