11#include " plotly/plotly.hpp"
2+ #include < atomic>
3+ #include < condition_variable>
24#include < gtest/gtest.h>
5+ #include < mutex>
36
47namespace plotly {
58
@@ -14,6 +17,277 @@ TEST_F(FigureTest, Construction) {
1417 EXPECT_NO_THROW ({ Figure fig; });
1518}
1619
20+ // Test browser operations
21+ TEST_F (FigureTest, BrowserOperations) {
22+ Figure fig;
23+
24+ // Test initial state
25+ EXPECT_FALSE (fig.isOpen ());
26+
27+ // Test opening browser in headless mode
28+ bool opened = fig.openBrowser (true );
29+ if (opened) {
30+ EXPECT_TRUE (fig.isOpen ());
31+ }
32+ }
33+
34+ // Test plot creation and manipulation
35+ TEST_F (FigureTest, PlotCreationAndManipulation) {
36+ Figure fig;
37+
38+ bool browserOpened = fig.openBrowser (true );
39+ if (!browserOpened) {
40+ GTEST_SKIP () << " Browser could not be opened, skipping plot tests" ;
41+ }
42+
43+ // Create simple plot data
44+ Object trace = {{" x" , Object::array ({1 , 2 , 3 , 4 })},
45+ {" y" , Object::array ({10 , 11 , 12 , 13 })},
46+ {" type" , " scatter" },
47+ {" mode" , " lines+markers" }};
48+
49+ Object data = Object::array ({trace});
50+ Object layout = Object{{" title" , " Test Plot" }};
51+
52+ // Test newPlot
53+ EXPECT_TRUE (fig.newPlot (data, layout));
54+
55+ // Test update
56+ Object traceUpdate = {{" marker.color" , " red" }};
57+ EXPECT_TRUE (fig.update (traceUpdate));
58+
59+ // Test relayout
60+ Object layoutUpdate = {{" title" , " Updated Test Plot" }};
61+ EXPECT_TRUE (fig.relayout (layoutUpdate));
62+
63+ // Test redraw
64+ EXPECT_TRUE (fig.redraw ());
65+ }
66+
67+ // Test trace operations
68+ TEST_F (FigureTest, TraceOperations) {
69+ Figure fig;
70+
71+ bool browserOpened = fig.openBrowser (true );
72+ if (!browserOpened) {
73+ GTEST_SKIP () << " Browser could not be opened, skipping trace tests" ;
74+ }
75+
76+ // Create initial plot
77+ Object trace1 = {{" x" , Object::array ({1 , 2 , 3 })},
78+ {" y" , Object::array ({1 , 4 , 9 })},
79+ {" type" , " scatter" },
80+ {" name" , " trace1" }};
81+
82+ Object data = Object::array ({trace1});
83+ EXPECT_TRUE (fig.newPlot (data));
84+
85+ // Test addTraces
86+ Object trace2 = {{" x" , Object::array ({1 , 2 , 3 })},
87+ {" y" , Object::array ({2 , 5 , 10 })},
88+ {" type" , " scatter" },
89+ {" name" , " trace2" }};
90+
91+ EXPECT_TRUE (fig.addTraces (Object::array ({trace2})));
92+
93+ // Test restyle
94+ Object styleUpdate = {{" marker.color" , " blue" }};
95+ EXPECT_TRUE (fig.restyle (styleUpdate, Object::array ({0 })));
96+
97+ // Test extendTraces
98+ Object extendData = {{" x" , Object::array ({4 })}, {" y" , Object::array ({16 })}};
99+ EXPECT_TRUE (fig.extendTraces (extendData, Object::array ({0 })));
100+
101+ // Test moveTraces
102+ EXPECT_TRUE (fig.moveTraces (Object::array ({0 }), Object::array ({1 })));
103+
104+ // Test deleteTraces
105+ EXPECT_TRUE (fig.deleteTraces (Object::array ({1 })));
106+ }
107+
108+ // Test animation operations
109+ TEST_F (FigureTest, AnimationOperations) {
110+ Figure fig;
111+
112+ bool browserOpened = fig.openBrowser (true );
113+ if (!browserOpened) {
114+ GTEST_SKIP () << " Browser could not be opened, skipping animation tests" ;
115+ }
116+
117+ // Simple test data
118+ Object initialTrace = {{" x" , Object::array ({1 , 2 , 3 })},
119+ {" y" , Object::array ({1 , 2 , 3 })},
120+ {" type" , " scatter" }};
121+
122+ Object data = Object::array ({initialTrace});
123+ EXPECT_TRUE (fig.newPlot (data));
124+
125+ // Test addFrames with simple frame data
126+ std::vector<Object> frames;
127+ for (int i = 0 ; i < 3 ; i++) {
128+ Object frameObj = {{" name" , std::to_string (i)},
129+ {" data" , std::vector<Object>{Object{
130+ {" x" , Object::array ({1 , 2 , 3 })},
131+ {" y" , Object::array ({i + 1 , i + 2 , i + 3 })},
132+ {" type" , " scatter" }}}}};
133+ frames.push_back (frameObj);
134+ }
135+
136+ EXPECT_TRUE (fig.addFrames (frames));
137+
138+ // Test animate - basic functionality
139+ EXPECT_TRUE (fig.animate (Object (), Object{}));
140+ EXPECT_TRUE (fig.animate (Object::array ({" 0" , " 1" }), Object{}));
141+ EXPECT_TRUE (fig.animate (Object::array ({}), Object{})); // pause
142+
143+ // Test deleteFrames
144+ EXPECT_TRUE (fig.deleteFrames (Object::array ({" 0" , " 1" , " 2" })));
145+ }
146+
147+ // Test react operation
148+ TEST_F (FigureTest, ReactOperation) {
149+ Figure fig;
150+
151+ bool browserOpened = fig.openBrowser (true );
152+ if (!browserOpened) {
153+ GTEST_SKIP () << " Browser could not be opened, skipping react tests" ;
154+ }
155+
156+ // Test react with new data
157+ Object trace = {{" x" , Object::array ({1 , 2 , 3 })},
158+ {" y" , Object::array ({1 , 4 , 9 })},
159+ {" type" , " bar" }};
160+
161+ Object data = Object::array ({trace});
162+ Object layout = {{" title" , " React Test" }};
163+ Object config = {{" displayModeBar" , false }};
164+
165+ EXPECT_TRUE (fig.react (data, layout, config));
166+ }
167+
168+ // Test purge operation
169+ TEST_F (FigureTest, PurgeOperation) {
170+ Figure fig;
171+
172+ bool browserOpened = fig.openBrowser (true );
173+ if (!browserOpened) {
174+ GTEST_SKIP () << " Browser could not be opened, skipping purge tests" ;
175+ }
176+
177+ // Create a plot first
178+ Object trace = {{" x" , Object::array ({1 , 2 , 3 })},
179+ {" y" , Object::array ({1 , 4 , 9 })},
180+ {" type" , " scatter" }};
181+
182+ Object data = Object::array ({trace});
183+ EXPECT_TRUE (fig.newPlot (data));
184+
185+ // Test purge
186+ EXPECT_TRUE (fig.purge ());
187+ }
188+
189+ // Test event handling
190+ TEST_F (FigureTest, EventHandling) {
191+ Figure fig;
192+
193+ bool browserOpened = fig.openBrowser (true );
194+ if (!browserOpened) {
195+ GTEST_SKIP () << " Browser could not be opened, skipping event tests" ;
196+ }
197+
198+ // Create a plot to generate events
199+ Object trace = {{" x" , Object::array ({1 , 2 , 3 })},
200+ {" y" , Object::array ({1 , 4 , 9 })},
201+ {" type" , " scatter" }};
202+
203+ Object data = Object::array ({trace});
204+ EXPECT_TRUE (fig.newPlot (data));
205+
206+ // Test event listener registration
207+ std::atomic<bool > eventReceived{false };
208+ std::mutex eventMutex;
209+ std::condition_variable eventCv;
210+
211+ bool registered =
212+ fig.on (" plotly_click" , [&](const plotly::Object & /* eventData */ ) {
213+ std::lock_guard<std::mutex> lock (eventMutex);
214+ eventReceived = true ;
215+ eventCv.notify_all ();
216+ });
217+
218+ EXPECT_TRUE (registered);
219+
220+ // Test removing event listeners
221+ EXPECT_TRUE (fig.removeAllListeners (" plotly_click" ));
222+ }
223+
224+ // Test download directory setting
225+ TEST_F (FigureTest, DownloadDirectoryOperation) {
226+ Figure fig;
227+
228+ bool browserOpened = fig.openBrowser (true );
229+ if (!browserOpened) {
230+ GTEST_SKIP ()
231+ << " Browser could not be opened, skipping download directory tests" ;
232+ }
233+
234+ // Test setting download directory
235+ std::filesystem::path downloadPath = " /tmp" ;
236+ // Note: This might fail if Chrome DevTools is not available, which is okay
237+ // We're just testing that the method doesn't crash
238+ EXPECT_NO_THROW ({ fig.setDownloadDirectory (downloadPath); });
239+ }
240+
241+ // Test image download
242+ TEST_F (FigureTest, ImageDownload) {
243+ Figure fig;
244+
245+ bool browserOpened = fig.openBrowser (true );
246+ if (!browserOpened) {
247+ GTEST_SKIP () << " Browser could not be opened, skipping download tests" ;
248+ }
249+
250+ // Create a plot to download
251+ Object trace = {{" x" , Object::array ({1 , 2 , 3 , 4 })},
252+ {" y" , Object::array ({10 , 11 , 12 , 13 })},
253+ {" type" , " scatter" },
254+ {" mode" , " lines+markers" }};
255+
256+ Object data = Object::array ({trace});
257+ Object layout = Object{{" title" , " Download Test Plot" }};
258+
259+ EXPECT_TRUE (fig.newPlot (data, layout));
260+
261+ // Test different download options
262+ Object pngOpts = Object{{" format" , " png" },
263+ {" width" , 800 },
264+ {" height" , 600 },
265+ {" filename" , " test_plot_png" }};
266+
267+ EXPECT_NO_THROW ({ fig.downloadImage (pngOpts); });
268+
269+ Object svgOpts = Object{{" format" , " svg" },
270+ {" width" , 800 },
271+ {" height" , 600 },
272+ {" filename" , " test_plot_svg" }};
273+
274+ EXPECT_NO_THROW ({ fig.downloadImage (svgOpts); });
275+
276+ Object jpegOpts = Object{{" format" , " jpeg" },
277+ {" width" , 800 },
278+ {" height" , 600 },
279+ {" filename" , " test_plot_jpeg" }};
280+
281+ EXPECT_NO_THROW ({ fig.downloadImage (jpegOpts); });
282+
283+ Object pdfOpts = Object{{" format" , " pdf" },
284+ {" width" , 800 },
285+ {" height" , 600 },
286+ {" filename" , " test_plot_pdf" }};
287+
288+ EXPECT_NO_THROW ({ fig.downloadImage (pdfOpts); });
289+ }
290+
17291// Test basic plotting workflow with headless browser
18292TEST_F (FigureTest, BasicPlottingWorkflow) {
19293 Figure fig;
0 commit comments