Skip to main content

playwright_rs/protocol/
evaluate_conversion.rs

1//! Value conversion for Playwright's evaluate() method
2//!
3//! This module handles bidirectional conversion between Rust types and Playwright's
4//! JSON protocol format for the evaluate() expression method.
5//!
6//! # Functions
7//!
8//! - `serialize_argument<T>()` - Converts Rust arguments to protocol format
9//! - `serialize_null()` - Serializes None/null values
10//! - `parse_value()` - Deserializes protocol responses
11//! - `parse_result()` - Convenience wrapper for result deserialization
12//!
13//! # Protocol Format
14//!
15//! Playwright uses a type-tagged JSON format where each value includes type information:
16//! - `{"v": "null"}` - Null values
17//! - `{"v": "undefined"}` - Undefined values
18//! - `{"b": true}` - Boolean values
19//! - `{"n": 42}` - Number values (int or float)
20//! - `{"s": "hello"}` - String values
21//! - `{"d": "2025-12-25T00:00:00.000Z"}` - Date values (ISO 8601 format in UTC)
22//! - `{"bi": "12345678901234567890"}` - BigInt values (as strings)
23//! - `{"u": "https://example.com"}` - URL values (as strings)
24//! - `{"e": {"m": "msg", "n": "name", "s": "stack"}}` - Error objects
25//! - `{"ta": {"b": "base64...", "k": "ui8"}}` - TypedArray values (base64 encoded)
26//! - `{"a": [...], "id": 0}` - Arrays (with circular reference tracking)
27//! - `{"o": [...], "id": 1}` - Objects (with circular reference tracking)
28//! - `{"v": "Infinity"}`, `{"v": "NaN"}` - Special float values
29//!
30//! # Example
31//!
32//! ```ignore
33//! use playwright_rs::protocol::{serialize_argument, parse_result};
34//! use serde::Deserialize;
35//!
36//! #[derive(Deserialize)]
37//! struct Result {
38//!     sum: i32,
39//! }
40//!
41//! // Serialize argument for evaluate
42//! let arg = 5;
43//! let serialized = serialize_argument(&arg);
44//!
45//! // After sending to Playwright and getting response back...
46//! // Deserialize result from evaluate
47//! let response_value = serde_json::json!({"n": 10});
48//! let deserialized: i32 = serde_json::from_value(parse_result(&response_value))
49//!     .expect("Failed to deserialize result");
50//! ```
51//!
52//! # Implementation Notes
53//!
54//! Based on playwright-python's serialize_value and parse_value implementations:
55//! <https://github.com/microsoft/playwright-python/blob/main/playwright/_impl/_js_handle.py>
56
57use base64::{Engine as _, engine::general_purpose};
58use serde_json::{Value, json};
59use std::collections::HashMap;
60
61/// Serializes a value following Playwright's protocol specification.
62///
63/// Playwright expects values in specific formats:
64/// - `null`: `{"v": "null"}`
65/// - `undefined`: `{"v": "undefined"}`
66/// - Boolean: `{"b": true}` or `{"b": false}`
67/// - Number: `{"n": 42}` or `{"n": 3.14}`
68/// - String: `{"s": "hello"}`
69/// - Date: `{"d": "2025-12-25T00:00:00.000Z"}` (ISO 8601 UTC)
70/// - BigInt: `{"bi": "12345678901234567890"}` (as string)
71/// - URL: `{"u": "https://example.com"}` (as string)
72/// - Error: `{"e": {"m": "message", "n": "name", "s": "stack"}}`
73/// - TypedArray: `{"ta": {"b": "base64...", "k": "ui8"}}`
74/// - Array: `{"a": [...], "id": 0}`
75/// - Object: `{"o": [{"k": "name", "v": ...}], "id": 1}`
76/// - Special floats: `{"v": "Infinity"}`, `{"v": "-Infinity"}`, `{"v": "-0"}`, `{"v": "NaN"}`
77///
78/// The `id` field is used for circular reference tracking.
79fn serialize_value(value: &Value, visitor: &mut Visitor) -> Value {
80    // Handle null
81    if value.is_null() {
82        return json!({"v": "null"});
83    }
84
85    // Handle boolean
86    if let Some(b) = value.as_bool() {
87        return json!({"b": b});
88    }
89
90    // Handle number
91    if let Some(n) = value.as_f64() {
92        // Check for special float values
93        if n.is_infinite() {
94            if n.is_sign_positive() {
95                return json!({"v": "Infinity"});
96            } else {
97                return json!({"v": "-Infinity"});
98            }
99        }
100        if n.is_nan() {
101            return json!({"v": "NaN"});
102        }
103        // Check for negative zero
104        if n == 0.0 && n.is_sign_negative() {
105            return json!({"v": "-0"});
106        }
107        return json!({"n": n});
108    }
109
110    // Handle string
111    if let Some(s) = value.as_str() {
112        return json!({"s": s});
113    }
114
115    // Handle array
116    if let Some(arr) = value.as_array() {
117        // Check if already visited (circular reference)
118        let value_ptr = value as *const Value as usize;
119        if let Some(ref_id) = visitor.visited.get(&value_ptr) {
120            return json!({"ref": ref_id});
121        }
122
123        // Mark as visited
124        let id = visitor.next_id();
125        visitor.visited.insert(value_ptr, id);
126
127        // Serialize array elements
128        let serialized: Vec<Value> = arr
129            .iter()
130            .map(|item| serialize_value(item, visitor))
131            .collect();
132
133        return json!({"a": serialized, "id": id});
134    }
135
136    // Handle object
137    if let Some(obj) = value.as_object() {
138        // Check if already visited (circular reference)
139        let value_ptr = value as *const Value as usize;
140        if let Some(ref_id) = visitor.visited.get(&value_ptr) {
141            return json!({"ref": ref_id});
142        }
143
144        // Mark as visited
145        let id = visitor.next_id();
146        visitor.visited.insert(value_ptr, id);
147
148        // Serialize object properties
149        let serialized: Vec<Value> = obj
150            .iter()
151            .map(|(key, val)| {
152                json!({
153                    "k": key,
154                    "v": serialize_value(val, visitor)
155                })
156            })
157            .collect();
158
159        return json!({"o": serialized, "id": id});
160    }
161
162    // Default to undefined
163    json!({"v": "undefined"})
164}
165
166/// Tracks visited objects for circular reference detection.
167struct Visitor {
168    visited: HashMap<usize, usize>,
169    id_counter: usize,
170}
171
172impl Visitor {
173    fn new() -> Self {
174        Self {
175            visited: HashMap::new(),
176            id_counter: 0,
177        }
178    }
179
180    fn next_id(&mut self) -> usize {
181        let id = self.id_counter;
182        self.id_counter += 1;
183        id
184    }
185}
186
187/// Serializes an argument for Playwright's evaluateExpression method.
188///
189/// This is the main entry point for serializing arguments. It wraps the serialized
190/// value with the required "value" and "handles" fields.
191///
192/// # Arguments
193///
194/// * `arg` - A value that implements `serde::Serialize` or a `serde_json::Value`
195///
196/// # Returns
197///
198/// A JSON object with "value" and "handles" fields in Playwright's format:
199/// ```json
200/// {
201///   "value": { /* serialized value */ },
202///   "handles": []
203/// }
204/// ```
205///
206/// # Examples
207///
208/// ```
209/// use playwright_rs::protocol::serialize_argument;
210/// use serde_json::json;
211///
212/// // String argument
213/// let arg = serialize_argument(&json!("hello"));
214/// // Returns: {"value": {"s": "hello"}, "handles": []}
215///
216/// // Number argument
217/// let arg = serialize_argument(&json!(42));
218/// // Returns: {"value": {"n": 42}, "handles": []}
219///
220/// // Object argument
221/// let arg = serialize_argument(&json!({"name": "test"}));
222/// // Returns: {"value": {"o": [{"k": "name", "v": {"s": "test"}}], "id": 0}, "handles": []}
223/// ```
224pub fn serialize_argument<T: serde::Serialize>(arg: &T) -> Value {
225    let json_value = serde_json::to_value(arg).unwrap_or(Value::Null);
226    let mut visitor = Visitor::new();
227    let value = serialize_value(&json_value, &mut visitor);
228
229    json!({
230        "value": value,
231        "handles": []
232    })
233}
234
235/// Convenience function to serialize None/null as an argument.
236///
237/// # Returns
238///
239/// A JSON object representing null: `{"value": {"v": "null"}, "handles": []}`
240pub fn serialize_null() -> Value {
241    json!({
242        "value": {"v": "null"},
243        "handles": []
244    })
245}
246
247/// Parses a value returned by Playwright's evaluateExpression method.
248///
249/// This function deserializes values from Playwright's protocol format back
250/// into standard Rust/JSON values. It handles the same types as serialization:
251///
252/// - `{"v": "null"}` → `Value::Null`
253/// - `{"v": "undefined"}` → `Value::Null`
254/// - `{"b": true}` → `Value::Bool(true)`
255/// - `{"n": 42}` → `Value::Number(42)`
256/// - `{"s": "hello"}` → `Value::String("hello")`
257/// - `{"d": "2025-12-25T00:00:00.000Z"}` → `Value::String("2025-12-25T00:00:00.000Z")`
258/// - `{"bi": "12345678901234567890"}` → `Value::String("12345678901234567890")`
259/// - `{"u": "https://example.com"}` → `Value::String("https://example.com")`
260/// - `{"e": {...}}` → `Value::Object` with error details
261/// - `{"ta": {...}}` → `Value::Array` of decoded values
262/// - `{"a": [...]}` → `Value::Array([...])`
263/// - `{"o": [...]}` → `Value::Object({...})`
264/// - Special values: `"Infinity"`, `"-Infinity"`, `"NaN"`, `"-0"`
265///
266/// # Arguments
267///
268/// * `value` - The wrapped value from Playwright
269/// * `refs` - Optional map for tracking circular references
270///
271/// # Returns
272///
273/// The parsed value as a `serde_json::Value`
274///
275/// # Examples
276///
277/// ```
278/// use playwright_rs::protocol::parse_value;
279/// use serde_json::json;
280///
281/// // Parse a string
282/// let result = parse_value(&json!({"s": "hello"}), None);
283/// assert_eq!(result, json!("hello"));
284///
285/// // Parse a number
286/// let result = parse_value(&json!({"n": 42}), None);
287/// assert_eq!(result, json!(42));
288///
289/// // Parse a boolean
290/// let result = parse_value(&json!({"b": true}), None);
291/// assert_eq!(result, json!(true));
292/// ```
293pub fn parse_value(value: &Value, refs: Option<&mut HashMap<usize, Value>>) -> Value {
294    let mut local_refs = HashMap::new();
295    let refs = match refs {
296        Some(r) => r,
297        None => &mut local_refs,
298    };
299
300    // Handle null input
301    if value.is_null() {
302        return Value::Null;
303    }
304
305    // Must be an object with type indicators
306    if let Some(obj) = value.as_object() {
307        // Handle circular reference
308        if let Some(ref_id) = obj.get("ref").and_then(|v| v.as_u64()) {
309            return refs.get(&(ref_id as usize)).cloned().unwrap_or(Value::Null);
310        }
311
312        // Handle special "v" values (null, undefined, special floats)
313        if let Some(v) = obj.get("v").and_then(|v| v.as_str()) {
314            return match v {
315                "null" | "undefined" => Value::Null,
316                "Infinity" => {
317                    // Return as number if possible, otherwise as null
318                    serde_json::Number::from_f64(f64::INFINITY)
319                        .map(Value::Number)
320                        .unwrap_or(Value::Null)
321                }
322                "-Infinity" => serde_json::Number::from_f64(f64::NEG_INFINITY)
323                    .map(Value::Number)
324                    .unwrap_or(Value::Null),
325                "NaN" => serde_json::Number::from_f64(f64::NAN)
326                    .map(Value::Number)
327                    .unwrap_or(Value::Null),
328                "-0" => serde_json::Number::from_f64(-0.0)
329                    .map(Value::Number)
330                    .unwrap_or(json!(0.0)),
331                _ => Value::Null,
332            };
333        }
334
335        // Handle boolean
336        if let Some(b) = obj.get("b").and_then(|v| v.as_bool()) {
337            return json!(b);
338        }
339
340        // Handle number
341        if let Some(n) = obj.get("n") {
342            return n.clone();
343        }
344
345        // Handle string
346        if let Some(s) = obj.get("s").and_then(|v| v.as_str()) {
347            return json!(s);
348        }
349
350        // Handle date (ISO 8601 UTC string)
351        if let Some(d) = obj.get("d").and_then(|v| v.as_str()) {
352            // Node.js Date objects are always in UTC, store as ISO 8601 string
353            return json!(d);
354        }
355
356        // Handle BigInt (stored as string to preserve precision)
357        if let Some(bi) = obj.get("bi").and_then(|v| v.as_str()) {
358            return json!(bi);
359        }
360
361        // Handle URL (stored as string)
362        if let Some(u) = obj.get("u").and_then(|v| v.as_str()) {
363            return json!(u);
364        }
365
366        // Handle error objects
367        if let Some(error_obj) = obj.get("e").and_then(|v| v.as_object()) {
368            let mut result = serde_json::Map::new();
369            if let Some(message) = error_obj.get("m").and_then(|v| v.as_str()) {
370                result.insert("m".to_string(), json!(message));
371            }
372            if let Some(name) = error_obj.get("n").and_then(|v| v.as_str()) {
373                result.insert("n".to_string(), json!(name));
374            }
375            if let Some(stack) = error_obj.get("s").and_then(|v| v.as_str()) {
376                result.insert("s".to_string(), json!(stack));
377            }
378            return Value::Object(result);
379        }
380
381        // Handle TypedArray (base64 encoded)
382        if let Some(ta_obj) = obj.get("ta").and_then(|v| v.as_object()) {
383            let (Some(encoded), Some(kind)) = (
384                ta_obj.get("b").and_then(|v| v.as_str()),
385                ta_obj.get("k").and_then(|v| v.as_str()),
386            ) else {
387                return Value::Null;
388            };
389
390            let Ok(decoded) = general_purpose::STANDARD.decode(encoded) else {
391                return Value::Null;
392            };
393            // Return as array of decoded values
394            let mut result_array = Vec::new();
395            match kind {
396                "ui8" | "ui8c" => {
397                    // Unsigned 8-bit
398                    for byte in decoded {
399                        result_array.push(json!(byte as u32));
400                    }
401                }
402                "i8" => {
403                    // Signed 8-bit
404                    for byte in decoded {
405                        result_array.push(json!(byte as i8 as i32));
406                    }
407                }
408
409                "ui16" => {
410                    // Unsigned 16-bit
411                    for chunk in decoded.chunks(2) {
412                        if chunk.len() == 2 {
413                            let value = u16::from_le_bytes([chunk[0], chunk[1]]);
414                            result_array.push(json!(value as u32));
415                        }
416                    }
417                }
418                "i16" => {
419                    // Signed 16-bit
420                    for chunk in decoded.chunks(2) {
421                        if chunk.len() == 2 {
422                            let value = i16::from_le_bytes([chunk[0], chunk[1]]);
423                            result_array.push(json!(value as i32));
424                        }
425                    }
426                }
427                "i32" => {
428                    // Signed 32-bit
429                    for chunk in decoded.chunks(4) {
430                        if chunk.len() == 4 {
431                            let value =
432                                i32::from_le_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]);
433                            result_array.push(json!(value as i64));
434                        }
435                    }
436                }
437                "ui32" => {
438                    // Unsigned 32-bit
439                    for chunk in decoded.chunks(4) {
440                        if chunk.len() == 4 {
441                            let value =
442                                u32::from_le_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]);
443                            result_array.push(json!(value as u64));
444                        }
445                    }
446                }
447                "f32" => {
448                    // 32-bit floating point
449                    for chunk in decoded.chunks(4) {
450                        if chunk.len() == 4 {
451                            let value =
452                                f32::from_le_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]);
453                            result_array.push(json!(value));
454                        }
455                    }
456                }
457                "f64" => {
458                    // 64-bit floating point
459                    for chunk in decoded.chunks(8) {
460                        if chunk.len() == 8 {
461                            let value = f64::from_le_bytes([
462                                chunk[0], chunk[1], chunk[2], chunk[3], chunk[4], chunk[5],
463                                chunk[6], chunk[7],
464                            ]);
465                            result_array.push(json!(value));
466                        }
467                    }
468                }
469                _ => {
470                    // For other types, return as array of bytes
471                    for byte in decoded {
472                        result_array.push(json!(byte));
473                    }
474                }
475            }
476            return json!(result_array);
477        }
478
479        // Handle array
480        if let Some(arr) = obj.get("a").and_then(|v| v.as_array()) {
481            // Store reference if has id
482            let result_arr: Vec<Value> = arr
483                .iter()
484                .map(|item| parse_value(item, Some(refs)))
485                .collect();
486
487            let result = json!(result_arr);
488
489            if let Some(id) = obj.get("id").and_then(|v| v.as_u64()) {
490                refs.insert(id as usize, result.clone());
491            }
492
493            return result;
494        }
495
496        // Handle object
497        if let Some(props) = obj.get("o").and_then(|v| v.as_array()) {
498            let mut result_obj = serde_json::Map::new();
499
500            for prop in props {
501                if let Some(prop_obj) = prop.as_object() {
502                    if let (Some(key), Some(val)) = (
503                        prop_obj.get("k").and_then(|v| v.as_str()),
504                        prop_obj.get("v"),
505                    ) {
506                        result_obj.insert(key.to_string(), parse_value(val, Some(refs)));
507                    }
508                }
509            }
510
511            let result = Value::Object(result_obj);
512
513            if let Some(id) = obj.get("id").and_then(|v| v.as_u64()) {
514                refs.insert(id as usize, result.clone());
515            }
516
517            return result;
518        }
519    }
520
521    // Default to null for unrecognized formats
522    Value::Null
523}
524
525/// Parses a result from Playwright's evaluate methods.
526///
527/// This is a convenience wrapper around `parse_value` for parsing
528/// evaluation results. It handles the common case where you receive
529/// a result from `evaluateExpression` or similar methods.
530///
531/// # Arguments
532///
533/// * `result` - The result value from Playwright
534///
535/// # Returns
536///
537/// The parsed value as a `serde_json::Value`
538///
539/// # Examples
540///
541/// ```
542/// use playwright_rs::protocol::parse_result;
543/// use serde_json::json;
544///
545/// // Parse evaluation result
546/// let result = parse_result(&json!({"s": "hello"}));
547/// assert_eq!(result, json!("hello"));
548/// ```
549pub fn parse_result(result: &Value) -> Value {
550    parse_value(result, None)
551}
552
553#[cfg(test)]
554mod tests {
555    use super::*;
556    use serde_json::json;
557
558    const PI: f64 = std::f64::consts::PI;
559
560    #[test]
561    fn test_serialize_null() {
562        let result = serialize_argument(&json!(null));
563        assert_eq!(
564            result,
565            json!({
566                "value": {"v": "null"},
567                "handles": []
568            })
569        );
570    }
571
572    #[test]
573    fn test_serialize_boolean() {
574        let result = serialize_argument(&json!(true));
575        assert_eq!(
576            result,
577            json!({
578                "value": {"b": true},
579                "handles": []
580            })
581        );
582
583        let result = serialize_argument(&json!(false));
584        assert_eq!(
585            result,
586            json!({
587                "value": {"b": false},
588                "handles": []
589            })
590        );
591    }
592
593    #[test]
594    fn test_serialize_number() {
595        let result = serialize_argument(&json!(42));
596        let value = &result["value"];
597        assert_eq!(value["n"].as_f64().unwrap(), 42.0);
598        assert_eq!(result["handles"], json!([]));
599
600        let result = serialize_argument(&json!(PI));
601        let value = &result["value"];
602        assert_eq!(value["n"].as_f64().unwrap(), PI);
603        assert_eq!(result["handles"], json!([]));
604    }
605
606    #[test]
607    fn test_serialize_special_floats() {
608        // Note: serde_json serializes special floats as null by default
609        // This test documents the behavior - in practice, special floats
610        // would need to be handled before serialization or passed as strings
611
612        // Test that regular floats work
613        let result = serialize_argument(&json!(1.5));
614        let value = &result["value"];
615        assert_eq!(value["n"].as_f64().unwrap(), 1.5);
616
617        // Test zero
618        let result = serialize_argument(&json!(0.0));
619        let value = &result["value"];
620        assert_eq!(value["n"].as_f64().unwrap(), 0.0);
621    }
622
623    #[test]
624    fn test_serialize_string() {
625        let result = serialize_argument(&json!("hello"));
626        assert_eq!(
627            result,
628            json!({
629                "value": {"s": "hello"},
630                "handles": []
631            })
632        );
633    }
634
635    #[test]
636    fn test_serialize_array() {
637        let result = serialize_argument(&json!([1, 2, 3]));
638
639        assert_eq!(result["handles"], json!([]));
640        let value = &result["value"];
641        assert!(value["a"].is_array());
642        assert_eq!(value["id"], 0);
643
644        let items = value["a"].as_array().unwrap();
645        assert_eq!(items.len(), 3);
646        assert_eq!(items[0]["n"].as_f64().unwrap(), 1.0);
647        assert_eq!(items[1]["n"].as_f64().unwrap(), 2.0);
648        assert_eq!(items[2]["n"].as_f64().unwrap(), 3.0);
649    }
650
651    #[test]
652    fn test_serialize_object() {
653        let result = serialize_argument(&json!({
654            "name": "test",
655            "value": 42
656        }));
657
658        assert_eq!(result["handles"], json!([]));
659        let value = &result["value"];
660        assert!(value["o"].is_array());
661        assert_eq!(value["id"], 0);
662
663        let props = value["o"].as_array().unwrap();
664        assert_eq!(props.len(), 2);
665
666        // Properties can be in any order
667        let mut found_name = false;
668        let mut found_value = false;
669
670        for prop in props {
671            if prop["k"] == "name" {
672                assert_eq!(prop["v"], json!({"s": "test"}));
673                found_name = true;
674            }
675            if prop["k"] == "value" {
676                assert_eq!(prop["v"]["n"].as_f64().unwrap(), 42.0);
677                found_value = true;
678            }
679        }
680
681        assert!(found_name);
682        assert!(found_value);
683    }
684
685    #[test]
686    fn test_serialize_nested_array() {
687        let result = serialize_argument(&json!([1, "test", true, [2, 3]]));
688
689        let value = &result["value"];
690        let items = value["a"].as_array().unwrap();
691        assert_eq!(items.len(), 4);
692
693        // First three items
694        assert_eq!(items[0]["n"].as_f64().unwrap(), 1.0);
695        assert_eq!(items[1], json!({"s": "test"}));
696        assert_eq!(items[2], json!({"b": true}));
697
698        // Fourth item is nested array
699        assert_eq!(items[3]["id"], 1); // Second object gets id=1
700        let nested = items[3]["a"].as_array().unwrap();
701        assert_eq!(nested.len(), 2);
702        assert_eq!(nested[0]["n"].as_f64().unwrap(), 2.0);
703        assert_eq!(nested[1]["n"].as_f64().unwrap(), 3.0);
704    }
705
706    #[test]
707    fn test_serialize_nested_object() {
708        let result = serialize_argument(&json!({
709            "outer": {
710                "inner": "value"
711            }
712        }));
713
714        let value = &result["value"];
715        assert_eq!(value["id"], 0);
716
717        let props = value["o"].as_array().unwrap();
718        assert_eq!(props.len(), 1);
719        assert_eq!(props[0]["k"], "outer");
720
721        let inner_obj = &props[0]["v"];
722        assert_eq!(inner_obj["id"], 1);
723
724        let inner_props = inner_obj["o"].as_array().unwrap();
725        assert_eq!(inner_props.len(), 1);
726        assert_eq!(inner_props[0]["k"], "inner");
727        assert_eq!(inner_props[0]["v"], json!({"s": "value"}));
728    }
729
730    #[test]
731    fn test_serialize_mixed_types() {
732        let result = serialize_argument(&json!({
733            "string": "hello",
734            "number": 42,
735            "boolean": true,
736            "null": null,
737            "array": [1, 2, 3],
738            "object": {"nested": "value"}
739        }));
740
741        let value = &result["value"];
742        assert!(value["o"].is_array());
743
744        let props = value["o"].as_array().unwrap();
745        assert_eq!(props.len(), 6);
746    }
747
748    #[test]
749    fn test_serialize_null_helper() {
750        let result = serialize_null();
751        assert_eq!(
752            result,
753            json!({
754                "value": {"v": "null"},
755                "handles": []
756            })
757        );
758    }
759
760    // ===== Deserialization Tests =====
761
762    #[test]
763    fn test_parse_null() {
764        let result = parse_value(&json!({"v": "null"}), None);
765        assert_eq!(result, Value::Null);
766    }
767
768    #[test]
769    fn test_parse_undefined() {
770        let result = parse_value(&json!({"v": "undefined"}), None);
771        assert_eq!(result, Value::Null);
772    }
773
774    #[test]
775    fn test_parse_boolean() {
776        let result = parse_value(&json!({"b": true}), None);
777        assert_eq!(result, json!(true));
778
779        let result = parse_value(&json!({"b": false}), None);
780        assert_eq!(result, json!(false));
781    }
782
783    #[test]
784    fn test_parse_number() {
785        let result = parse_value(&json!({"n": 42}), None);
786        assert_eq!(result.as_f64().unwrap(), 42.0);
787
788        let result = parse_value(&json!({"n": PI}), None);
789        assert_eq!(result.as_f64().unwrap(), PI);
790    }
791
792    #[test]
793    fn test_parse_special_floats() {
794        // Note: serde_json cannot represent special float values in JSON
795        // They get converted to null. This documents the behavior.
796
797        // Infinity - serde_json will return null for special floats
798        let result = parse_value(&json!({"v": "Infinity"}), None);
799        // serde_json::Number::from_f64 returns None for special floats
800        assert!(result.is_null());
801
802        // -Infinity
803        let result = parse_value(&json!({"v": "-Infinity"}), None);
804        assert!(result.is_null());
805
806        // NaN
807        let result = parse_value(&json!({"v": "NaN"}), None);
808        assert!(result.is_null());
809
810        // -0 can be represented
811        let result = parse_value(&json!({"v": "-0"}), None);
812        assert!(result.is_number());
813    }
814
815    #[test]
816    fn test_parse_string() {
817        let result = parse_value(&json!({"s": "hello"}), None);
818        assert_eq!(result, json!("hello"));
819
820        let result = parse_value(&json!({"s": "world"}), None);
821        assert_eq!(result, json!("world"));
822    }
823
824    #[test]
825    fn test_parse_date() {
826        let result = parse_value(&json!({"d": "2025-12-25T00:00:00.000Z"}), None);
827        assert_eq!(result, json!("2025-12-25T00:00:00.000Z"));
828
829        let result = parse_value(&json!({"d": "2025-12-25T10:30:45.123Z"}), None);
830        assert_eq!(result, json!("2025-12-25T10:30:45.123Z"));
831    }
832
833    #[test]
834    fn test_parse_bigint() {
835        let result = parse_value(&json!({"bi": "12345678901234567890"}), None);
836        assert_eq!(result, json!("12345678901234567890"));
837
838        let result = parse_value(&json!({"bi": "9007199254740991"}), None);
839        assert_eq!(result, json!("9007199254740991"));
840    }
841
842    #[test]
843    fn test_parse_url() {
844        let result = parse_value(&json!({"u": "https://example.com"}), None);
845        assert_eq!(result, json!("https://example.com"));
846
847        let result = parse_value(&json!({"u": "https://example.com/path?query=1"}), None);
848        assert_eq!(result, json!("https://example.com/path?query=1"));
849    }
850
851    #[test]
852    fn test_parse_error() {
853        let result = parse_value(
854            &json!({
855                "e": {
856                    "m": "Something went wrong",
857                    "n": "TypeError",
858                    "s": "Error: at line 1"
859                }
860            }),
861            None,
862        );
863
864        let obj = result.as_object().unwrap();
865        assert_eq!(
866            obj.get("m").and_then(|v| v.as_str()),
867            Some("Something went wrong")
868        );
869        assert_eq!(obj.get("n").and_then(|v| v.as_str()), Some("TypeError"));
870        assert_eq!(
871            obj.get("s").and_then(|v| v.as_str()),
872            Some("Error: at line 1")
873        );
874    }
875
876    #[test]
877    fn test_parse_typed_array_ui8() {
878        // Array [1, 2, 3, 4, 5]
879        let values: Vec<u8> = vec![1, 2, 3, 4, 5];
880        let base64_encoded = general_purpose::STANDARD.encode(&values);
881
882        let result = parse_value(&json!({"ta": {"b": base64_encoded, "k": "ui8"}}), None);
883        let arr = result.as_array().unwrap();
884        assert_eq!(arr.len(), values.len());
885        for (i, &expected) in values.iter().enumerate() {
886            assert_eq!(arr[i].as_u64().unwrap(), expected as u64);
887        }
888    }
889
890    #[test]
891    fn test_parse_typed_array_ui8c() {
892        // ui8c (Uint8ClampedArray) should behave same as ui8
893        let values: Vec<u8> = vec![1, 2, 3, 4, 5];
894        let base64_encoded = general_purpose::STANDARD.encode(&values);
895
896        let result = parse_value(&json!({"ta": {"b": base64_encoded, "k": "ui8c"}}), None);
897        let arr = result.as_array().unwrap();
898        assert_eq!(arr.len(), values.len());
899        assert_eq!(arr[0].as_u64().unwrap(), 1);
900    }
901
902    #[test]
903    fn test_parse_typed_array_i8() {
904        // Array [-1, 127, -128, 0, 1]
905        let values: Vec<i8> = vec![-1, 127, -128, 0, 1];
906        let bytes: Vec<u8> = values.iter().map(|&v| v as u8).collect();
907        let base64_encoded = general_purpose::STANDARD.encode(&bytes);
908
909        let result = parse_value(&json!({"ta": {"b": base64_encoded, "k": "i8"}}), None);
910        let arr = result.as_array().unwrap();
911        assert_eq!(arr.len(), values.len());
912        for (i, &expected) in values.iter().enumerate() {
913            assert_eq!(arr[i].as_i64().unwrap(), expected as i64);
914        }
915    }
916
917    #[test]
918    fn test_parse_typed_array_ui16() {
919        // Uint16Array [1, 256, 65535]
920        let values: Vec<u16> = vec![1, 256, 65535];
921        let mut bytes = Vec::new();
922        for &v in &values {
923            bytes.extend_from_slice(&v.to_le_bytes());
924        }
925        let base64_encoded = general_purpose::STANDARD.encode(&bytes);
926
927        let result = parse_value(&json!({"ta": {"b": base64_encoded, "k": "ui16"}}), None);
928        let arr = result.as_array().unwrap();
929        assert_eq!(arr.len(), values.len());
930        for (i, &expected) in values.iter().enumerate() {
931            assert_eq!(arr[i].as_u64().unwrap(), expected as u64);
932        }
933    }
934
935    #[test]
936    fn test_parse_typed_array_i16() {
937        // Int16Array [1, -1, 32767, -32768]
938        let values: Vec<i16> = vec![1, -1, 32767, -32768];
939        let mut bytes = Vec::new();
940        for &v in &values {
941            bytes.extend_from_slice(&v.to_le_bytes());
942        }
943        let base64_encoded = general_purpose::STANDARD.encode(&bytes);
944
945        let result = parse_value(&json!({"ta": {"b": base64_encoded, "k": "i16"}}), None);
946        let arr = result.as_array().unwrap();
947        assert_eq!(arr.len(), values.len());
948        for (i, &expected) in values.iter().enumerate() {
949            assert_eq!(arr[i].as_i64().unwrap(), expected as i64);
950        }
951    }
952
953    #[test]
954    fn test_parse_typed_array_ui32() {
955        // Uint32Array [1, 256, 4294967295]
956        let values: Vec<u32> = vec![1, 256, 4294967295];
957        let mut bytes = Vec::new();
958        for &v in &values {
959            bytes.extend_from_slice(&v.to_le_bytes());
960        }
961        let base64_encoded = general_purpose::STANDARD.encode(&bytes);
962
963        let result = parse_value(&json!({"ta": {"b": base64_encoded, "k": "ui32"}}), None);
964        let arr = result.as_array().unwrap();
965        assert_eq!(arr.len(), values.len());
966        for (i, &expected) in values.iter().enumerate() {
967            assert_eq!(arr[i].as_u64().unwrap(), expected as u64);
968        }
969    }
970
971    #[test]
972    fn test_parse_typed_array_i32() {
973        // Int32Array [1, -1, 2147483647, -2147483648]
974        let values: Vec<i32> = vec![1, -1, 2147483647, -2147483648];
975        let mut bytes = Vec::new();
976        for &v in &values {
977            bytes.extend_from_slice(&v.to_le_bytes());
978        }
979        let base64_encoded = general_purpose::STANDARD.encode(&bytes);
980
981        let result = parse_value(&json!({"ta": {"b": base64_encoded, "k": "i32"}}), None);
982        let arr = result.as_array().unwrap();
983        assert_eq!(arr.len(), values.len());
984        for (i, &expected) in values.iter().enumerate() {
985            assert_eq!(arr[i].as_i64().unwrap(), expected as i64);
986        }
987    }
988
989    #[test]
990    fn test_parse_typed_array_f32() {
991        // Float32Array [1.0, -1.0, 3.14]
992        let values: Vec<f32> = vec![1.0, -1.0, PI as f32];
993        let mut bytes = Vec::new();
994        for &v in &values {
995            bytes.extend_from_slice(&v.to_le_bytes());
996        }
997        let base64_encoded = general_purpose::STANDARD.encode(&bytes);
998
999        let result = parse_value(&json!({"ta": {"b": base64_encoded, "k": "f32"}}), None);
1000        let arr = result.as_array().unwrap();
1001        assert_eq!(arr.len(), values.len());
1002        for (i, &expected) in values.iter().enumerate() {
1003            assert!((arr[i].as_f64().unwrap() - expected as f64).abs() < 0.01);
1004        }
1005    }
1006
1007    #[test]
1008    fn test_parse_typed_array_f64() {
1009        // Float64Array [1.0, -1.0, 3.141592653589793]
1010        let values: Vec<f64> = vec![1.0, -1.0, PI];
1011        let mut bytes = Vec::new();
1012        for &v in &values {
1013            bytes.extend_from_slice(&v.to_le_bytes());
1014        }
1015        let base64_encoded = general_purpose::STANDARD.encode(&bytes);
1016
1017        let result = parse_value(&json!({"ta": {"b": base64_encoded, "k": "f64"}}), None);
1018        let arr = result.as_array().unwrap();
1019        assert_eq!(arr.len(), values.len());
1020        for (i, &expected) in values.iter().enumerate() {
1021            assert!((arr[i].as_f64().unwrap() - expected).abs() < 0.0000001);
1022        }
1023    }
1024
1025    #[test]
1026    fn test_parse_typed_array_empty() {
1027        // Base64 encoded empty array
1028        let result = parse_value(&json!({"ta": {"b": "", "k": "ui8"}}), None);
1029        let arr = result.as_array().unwrap();
1030        assert_eq!(arr.len(), 0);
1031    }
1032
1033    #[test]
1034    fn test_parse_typed_array_invalid_base64() {
1035        // Invalid base64 should return null
1036        let result = parse_value(&json!({"ta": {"b": "not-valid-base64!", "k": "ui8"}}), None);
1037        assert!(result.is_null());
1038    }
1039
1040    #[test]
1041    fn test_parse_typed_array_unknown_kind() {
1042        // Unknown kind should default to byte array
1043        let result = parse_value(&json!({"ta": {"b": "AQIDBAU=", "k": "unknown"}}), None);
1044        let arr = result.as_array().unwrap();
1045        assert_eq!(arr.len(), 5);
1046        assert_eq!(arr[0].as_u64().unwrap(), 1);
1047    }
1048
1049    #[test]
1050    fn test_parse_typed_array_missing_fields() {
1051        // Missing "b" field
1052        let result = parse_value(&json!({"ta": {"k": "ui8"}}), None);
1053        assert!(result.is_null());
1054
1055        // Missing "k" field
1056        let result = parse_value(&json!({"ta": {"b": "AQIDBAU="}}), None);
1057        assert!(result.is_null());
1058    }
1059
1060    #[test]
1061    fn test_parse_circular_reference() {
1062        // Test array with circular reference
1063        let mut refs = HashMap::new();
1064        refs.insert(5, json!([1, 2, 3]));
1065
1066        let result = parse_value(&json!({"ref": 5}), Some(&mut refs));
1067        let arr = result.as_array().unwrap();
1068        assert_eq!(arr.len(), 3);
1069        assert_eq!(arr[0].as_i64().unwrap(), 1);
1070    }
1071
1072    #[test]
1073    fn test_parse_array() {
1074        let input = json!({
1075            "a": [
1076                {"n": 1},
1077                {"n": 2},
1078                {"n": 3}
1079            ],
1080            "id": 0
1081        });
1082
1083        let result = parse_value(&input, None);
1084        assert!(result.is_array());
1085
1086        let arr = result.as_array().unwrap();
1087        assert_eq!(arr.len(), 3);
1088        assert_eq!(arr[0].as_f64().unwrap(), 1.0);
1089        assert_eq!(arr[1].as_f64().unwrap(), 2.0);
1090        assert_eq!(arr[2].as_f64().unwrap(), 3.0);
1091    }
1092
1093    #[test]
1094    fn test_parse_object() {
1095        let input = json!({
1096            "o": [
1097                {"k": "name", "v": {"s": "John"}},
1098                {"k": "age", "v": {"n": 30}}
1099            ],
1100            "id": 0
1101        });
1102
1103        let result = parse_value(&input, None);
1104        assert!(result.is_object());
1105
1106        let obj = result.as_object().unwrap();
1107        assert_eq!(obj.get("name").and_then(|v| v.as_str()), Some("John"));
1108        assert_eq!(obj.get("age").and_then(|v| v.as_f64()), Some(30.0));
1109    }
1110
1111    #[test]
1112    fn test_parse_nested_array() {
1113        let input = json!({
1114            "a": [
1115                {"n": 1},
1116                {"s": "test"},
1117                {"b": true},
1118                {
1119                    "a": [
1120                        {"n": 2},
1121                        {"n": 3}
1122                    ],
1123                    "id": 1
1124                }
1125            ],
1126            "id": 0
1127        });
1128
1129        let result = parse_value(&input, None);
1130        let arr = result.as_array().unwrap();
1131        assert_eq!(arr.len(), 4);
1132
1133        assert_eq!(arr[0].as_f64().unwrap(), 1.0);
1134        assert_eq!(arr[1].as_str().unwrap(), "test");
1135        assert!(arr[2].as_bool().unwrap());
1136
1137        let nested = arr[3].as_array().unwrap();
1138        assert_eq!(nested.len(), 2);
1139        assert_eq!(nested[0].as_f64().unwrap(), 2.0);
1140        assert_eq!(nested[1].as_f64().unwrap(), 3.0);
1141    }
1142
1143    #[test]
1144    fn test_parse_nested_object() {
1145        let input = json!({
1146            "o": [
1147                {
1148                    "k": "outer",
1149                    "v": {
1150                        "o": [
1151                            {"k": "inner", "v": {"s": "value"}}
1152                        ],
1153                        "id": 1
1154                    }
1155                }
1156            ],
1157            "id": 0
1158        });
1159
1160        let result = parse_value(&input, None);
1161        let obj = result.as_object().unwrap();
1162        let outer = obj.get("outer").unwrap().as_object().unwrap();
1163        assert_eq!(outer.get("inner").and_then(|v| v.as_str()), Some("value"));
1164    }
1165
1166    #[test]
1167    fn test_parse_result() {
1168        // Test the convenience wrapper
1169        let result = parse_result(&json!({"s": "hello"}));
1170        assert_eq!(result, json!("hello"));
1171
1172        let result = parse_result(&json!({"n": 42}));
1173        assert_eq!(result.as_f64().unwrap(), 42.0);
1174    }
1175
1176    #[test]
1177    fn test_roundtrip_serialization() {
1178        // Test that we can serialize and deserialize values
1179        let original = json!({
1180            "name": "test",
1181            "value": 42,
1182            "active": true,
1183            "items": [1, 2, 3]
1184        });
1185
1186        // Serialize
1187        let serialized = serialize_argument(&original);
1188        let serialized_value = &serialized["value"];
1189
1190        // Deserialize
1191        let deserialized = parse_value(serialized_value, None);
1192
1193        // Compare (note: object property order may differ)
1194        assert!(deserialized.is_object());
1195        let obj = deserialized.as_object().unwrap();
1196        assert_eq!(obj.get("name").and_then(|v| v.as_str()), Some("test"));
1197        assert_eq!(obj.get("value").and_then(|v| v.as_f64()), Some(42.0));
1198        assert_eq!(obj.get("active").and_then(|v| v.as_bool()), Some(true));
1199
1200        let items = obj.get("items").and_then(|v| v.as_array()).unwrap();
1201        assert_eq!(items.len(), 3);
1202    }
1203}