1 use js_sys::*;
2 use wasm_bindgen::prelude::*;
3 use wasm_bindgen::JsCast;
4 use wasm_bindgen::JsValue;
5 use wasm_bindgen_test::*;
6 
7 #[wasm_bindgen(module = "tests/wasm/JsString.js")]
8 extern "C" {
new_string_object() -> JsValue9     fn new_string_object() -> JsValue;
get_replacer_function() -> Function10     fn get_replacer_function() -> Function;
11 }
12 
13 #[wasm_bindgen_test]
js_string_inheritance()14 fn js_string_inheritance() {
15     let string = new_string_object();
16     assert!(string.is_instance_of::<JsString>());
17     assert!(string.is_instance_of::<Object>());
18 }
19 
20 #[wasm_bindgen_test]
length()21 fn length() {
22     fn test(s: &str) {
23         assert_eq!(JsString::from(s).length(), s.len() as u32);
24     }
25     test("Mozilla");
26     test("");
27 }
28 
29 #[wasm_bindgen_test]
char_at()30 fn char_at() {
31     let s = JsString::from("Brave new world");
32     assert_eq!(JsValue::from(s.char_at(0)), "B");
33     assert_eq!(JsValue::from(s.char_at(999)), "");
34 }
35 
36 #[wasm_bindgen_test]
char_code_at()37 fn char_code_at() {
38     let s = "Brave new world";
39     let js = JsString::from(s);
40     for (i, b) in s.char_indices() {
41         assert_eq!(js.char_code_at(i as u32), b as u32 as f64);
42     }
43     assert!(js.char_code_at(s.len() as u32).is_nan());
44 }
45 
46 #[wasm_bindgen_test]
code_point_at()47 fn code_point_at() {
48     assert_eq!(JsString::from("ABC").code_point_at(1), b'B');
49     assert!(JsString::from("ABC").code_point_at(42).is_undefined());
50 }
51 
52 #[wasm_bindgen_test]
concat()53 fn concat() {
54     // TODO: Implement ability to receive multiple optional arguments
55     let s = JsString::from("Hello ").concat(&"World".into());
56     assert_eq!(JsValue::from(s), "Hello World");
57     let foo = JsString::from("foo");
58     assert_eq!(
59         JsValue::from(foo.concat(&Object::new().into())),
60         "foo[object Object]"
61     );
62     assert_eq!(JsValue::from(foo.concat(&Array::new().into())), "foo");
63     assert_eq!(JsValue::from(foo.concat(&JsValue::null())), "foonull");
64     assert_eq!(JsValue::from(foo.concat(&true.into())), "footrue");
65     assert_eq!(JsValue::from(foo.concat(&1234.into())), "foo1234");
66 }
67 
68 #[wasm_bindgen_test]
ends_with()69 fn ends_with() {
70     let s = "To be, or not to be, that is the question.";
71     let js = JsString::from(s);
72 
73     // TODO: remove third parameter once we have optional parameters
74     assert_eq!(js.ends_with("question.", s.len() as i32), true);
75     assert_eq!(js.ends_with("to be", s.len() as i32), false);
76     assert_eq!(js.ends_with("to be", 19), true);
77 }
78 
79 #[wasm_bindgen_test]
from_char_code()80 fn from_char_code() {
81     let s = "½+¾=";
82     let codes: Vec<u32> = s.chars().map(|char| char as u32).collect();
83 
84     assert_eq!(JsString::from_char_code1(codes[0]), "½");
85     assert_eq!(JsString::from_char_code2(codes[0], codes[1]), "½+");
86     assert_eq!(
87         JsString::from_char_code3(codes[0], codes[1], codes[2]),
88         "½+¾"
89     );
90     assert_eq!(
91         JsString::from_char_code4(codes[0], codes[1], codes[2], codes[3]),
92         "½+¾="
93     );
94 
95     let codes_u16: Vec<u16> = codes
96         .into_iter()
97         .map(|code| {
98             assert!(code <= u32::from(u16::max_value()));
99             code as u16
100         })
101         .collect();
102 
103     assert_eq!(JsString::from_char_code(&codes_u16), "½+¾=");
104 }
105 
106 #[wasm_bindgen_test]
from_code_point()107 fn from_code_point() {
108     let s = "☃★♲你";
109     let codes: Vec<u32> = s.chars().map(|char| char as u32).collect();
110 
111     assert_eq!(JsString::from_code_point1(codes[0]).unwrap(), "☃");
112     assert_eq!(
113         JsString::from_code_point2(codes[0], codes[1]).unwrap(),
114         "☃★"
115     );
116     assert_eq!(
117         JsString::from_code_point3(codes[0], codes[1], codes[2]).unwrap(),
118         "☃★♲"
119     );
120     assert_eq!(
121         JsString::from_code_point4(codes[0], codes[1], codes[2], codes[3]).unwrap(),
122         "☃★♲你"
123     );
124     assert_eq!(JsString::from_code_point(&codes).unwrap(), "☃★♲你");
125 
126     assert!(!JsString::from_code_point1(0x10FFFF).is_err());
127     assert!(JsString::from_code_point1(0x110000).is_err());
128     assert!(JsString::from_code_point1(u32::max_value()).is_err());
129 }
130 
131 #[wasm_bindgen_test]
includes()132 fn includes() {
133     let str = JsString::from("Blue Whale");
134 
135     // TODO: remove second parameter once we have optional parameters
136     assert_eq!(str.includes("Blue", 0), true);
137     assert_eq!(str.includes("Blute", 0), false);
138     assert_eq!(str.includes("Whale", 0), true);
139     assert_eq!(str.includes("Whale", 5), true);
140     assert_eq!(str.includes("Whale", 7), false);
141     assert_eq!(str.includes("", 0), true);
142     assert_eq!(str.includes("", 16), true);
143 }
144 
145 #[wasm_bindgen_test]
index_of()146 fn index_of() {
147     let str = JsString::from("Blue Whale");
148 
149     // TODO: remove second parameter once we have optional parameters
150     assert_eq!(str.index_of("Blue", 0), 0);
151     // TODO: remove second parameter once we have optional parameters
152     assert_eq!(str.index_of("Blute", 0), -1);
153     assert_eq!(str.index_of("Whale", 0), 5);
154     assert_eq!(str.index_of("Whale", 5), 5);
155     assert_eq!(str.index_of("Whale", 7), -1);
156     // TODO: remove second parameter once we have optional parameters
157     assert_eq!(str.index_of("", 0), 0);
158     assert_eq!(str.index_of("", 9), 9);
159     assert_eq!(str.index_of("", 10), 10);
160     assert_eq!(str.index_of("", 11), 10);
161 }
162 
163 #[wasm_bindgen_test]
last_index_of()164 fn last_index_of() {
165     let js = JsString::from("canal");
166     let len = js.length() as i32;
167 
168     // TODO: remove second parameter once we have optional parameters
169     assert_eq!(js.last_index_of("a", len), 3);
170     assert_eq!(js.last_index_of("a", 2), 1);
171     assert_eq!(js.last_index_of("a", 0), -1);
172     // TODO: remove second parameter once we have optional parameters
173     assert_eq!(js.last_index_of("x", len), -1);
174     assert_eq!(js.last_index_of("c", -5), 0);
175     assert_eq!(js.last_index_of("c", 0), 0);
176     // TODO: remove second parameter once we have optional parameters
177     assert_eq!(js.last_index_of("", len), 5);
178     assert_eq!(js.last_index_of("", 2), 2);
179 }
180 
181 #[wasm_bindgen_test]
match_()182 fn match_() {
183     let s = "The quick brown fox jumped over the lazy dog. It barked.";
184     let re = RegExp::new("[A-Z]", "g");
185     let result = JsString::from(s).match_(&re);
186     let obj = result.unwrap();
187 
188     assert_eq!(Reflect::get(obj.as_ref(), &"0".into()).unwrap(), "T");
189     assert_eq!(Reflect::get(obj.as_ref(), &"1".into()).unwrap(), "I");
190 
191     let result = JsString::from("foo").match_(&re);
192     assert!(result.is_none());
193 
194     let s = "For more information, see Chapter 3.4.5.1";
195     let re = RegExp::new("see (chapter \\d+(\\.\\d)*)", "i");
196     let result = JsString::from(s).match_(&re);
197     let obj = result.unwrap();
198 
199     assert_eq!(
200         Reflect::get(obj.as_ref(), &"0".into()).unwrap(),
201         "see Chapter 3.4.5.1"
202     );
203     assert_eq!(
204         Reflect::get(obj.as_ref(), &"1".into()).unwrap(),
205         "Chapter 3.4.5.1"
206     );
207     assert_eq!(Reflect::get(obj.as_ref(), &"2".into()).unwrap(), ".1");
208     assert_eq!(Reflect::get(obj.as_ref(), &"index".into()).unwrap(), 22);
209     assert_eq!(Reflect::get(obj.as_ref(), &"input".into()).unwrap(), s);
210 }
211 
212 #[wasm_bindgen_test]
normalize()213 fn normalize() {
214     let js = JsString::from("\u{1E9B}\u{0323}");
215 
216     // TODO: Handle undefined
217     assert_eq!(JsValue::from(js.normalize("NFC")), "\u{1E9B}\u{0323}");
218     assert_eq!(
219         JsValue::from(js.normalize("NFD")),
220         "\u{017F}\u{0323}\u{0307}"
221     );
222     assert_eq!(JsValue::from(js.normalize("NFKC")), "\u{1E69}");
223     assert_eq!(
224         JsValue::from(js.normalize("NFKD")),
225         "\u{0073}\u{0323}\u{0307}"
226     );
227 }
228 
229 #[wasm_bindgen_test]
pad_end()230 fn pad_end() {
231     let js = JsString::from("abc");
232 
233     // TODO: remove second parameter once we have optional parameters
234     assert_eq!(JsValue::from(js.pad_end(10, " ")), "abc       ");
235     // TODO: remove second parameter once we have optional parameters
236     assert_eq!(JsValue::from(js.pad_end(10, " ")), "abc       ");
237     assert_eq!(JsValue::from(js.pad_end(10, "foo")), "abcfoofoof");
238     assert_eq!(JsValue::from(js.pad_end(6, "123456")), "abc123");
239     // TODO: remove second parameter once we have optional parameters
240     assert_eq!(JsValue::from(js.pad_end(1, " ")), "abc");
241 }
242 
243 #[wasm_bindgen_test]
pad_start()244 fn pad_start() {
245     let js = JsString::from("abc");
246 
247     // TODO: remove second parameter once we have optional parameters
248     assert_eq!(js.pad_start(10, " "), "       abc");
249     assert_eq!(js.pad_start(10, "foo"), "foofoofabc");
250     assert_eq!(js.pad_start(6, "123465"), "123abc");
251     assert_eq!(js.pad_start(8, "0"), "00000abc");
252     // TODO: remove second parameter once we have optional parameters
253     assert_eq!(js.pad_start(1, " "), "abc");
254 }
255 
256 #[wasm_bindgen_test]
repeat()257 fn repeat() {
258     assert_eq!(JsString::from("test").repeat(3), "testtesttest");
259 }
260 
261 #[wasm_bindgen_test]
replace()262 fn replace() {
263     let js = JsString::from(
264         "The quick brown fox jumped over the lazy dog. If the dog reacted, was it really lazy?",
265     );
266     let result = js.replace("dog", "ferret");
267 
268     assert_eq!(
269         result,
270         "The quick brown fox jumped over the lazy ferret. If the dog reacted, was it really lazy?"
271     );
272 
273     let js = JsString::from("borderTop");
274     let result = js.replace_with_function("T", &get_replacer_function());
275 
276     assert_eq!(result, "border-top");
277 
278     let js = JsString::from(
279         "The quick brown fox jumped over the lazy dog. If the dog reacted, was it really lazy?",
280     );
281     let re = RegExp::new("dog", "g");
282     let result = js.replace_by_pattern(&re, "ferret");
283 
284     assert_eq!(result, "The quick brown fox jumped over the lazy ferret. If the ferret reacted, was it really lazy?");
285 
286     let js = JsString::from("borderTop");
287     let re = RegExp::new("[A-Z]", "g");
288     let result = js.replace_by_pattern_with_function(&re, &get_replacer_function());
289 
290     assert_eq!(result, "border-top");
291 }
292 
293 #[wasm_bindgen_test]
search()294 fn search() {
295     let js = JsString::from(
296         "The quick brown fox jumped over the lazy dog. If the dog reacted, was it really lazy?",
297     );
298     let re = RegExp::new("[^\\w\\s]", "g");
299 
300     assert_eq!(js.search(&re), 44);
301 
302     let js = JsString::from("hey JudE");
303     let re1 = RegExp::new("[A-Z]", "g");
304     let re2 = RegExp::new("[.]", "g");
305 
306     assert_eq!(js.search(&re1), 4);
307     assert_eq!(js.search(&re2), -1);
308 }
309 
310 #[wasm_bindgen_test]
slice()311 fn slice() {
312     let characters = JsString::from("acxn18");
313     assert_eq!(characters.slice(1, 3), "cx");
314 }
315 
316 #[wasm_bindgen_test]
split()317 fn split() {
318     let js = JsString::from("Oh brave new world");
319     let result = js.split(" ");
320 
321     let mut v = Vec::with_capacity(result.length() as usize);
322     result.for_each(&mut |x, _, _| v.push(x));
323 
324     assert_eq!(v[0], "Oh");
325     assert_eq!(v[1], "brave");
326     assert_eq!(v[2], "new");
327     assert_eq!(v[3], "world");
328 
329     let js = JsString::from("Oct,Nov,Dec");
330     let result = js.split(",");
331 
332     let mut v = Vec::with_capacity(result.length() as usize);
333     result.for_each(&mut |x, _, _| v.push(x));
334 
335     assert_eq!(v[0], "Oct");
336     assert_eq!(v[1], "Nov");
337     assert_eq!(v[2], "Dec");
338 
339     let result = js.split_limit(",", 2);
340 
341     let mut v = Vec::with_capacity(result.length() as usize);
342     result.for_each(&mut |x, _, _| v.push(x));
343 
344     assert_eq!(result.length(), 2);
345     assert_eq!(v[0], "Oct");
346     assert_eq!(v[1], "Nov");
347 
348     let js = JsString::from("Oh brave new world");
349     let re = RegExp::new("\\s", "g");
350     let result = js.split_by_pattern(&re);
351 
352     let mut v = Vec::with_capacity(result.length() as usize);
353     result.for_each(&mut |x, _, _| v.push(x));
354 
355     assert_eq!(v[0], "Oh");
356     assert_eq!(v[1], "brave");
357     assert_eq!(v[2], "new");
358     assert_eq!(v[3], "world");
359 
360     let result = js.split_by_pattern_limit(&re, 2);
361 
362     let mut v = Vec::with_capacity(result.length() as usize);
363     result.for_each(&mut |x, _, _| v.push(x));
364 
365     assert_eq!(result.length(), 2);
366     assert_eq!(v[0], "Oh");
367     assert_eq!(v[1], "brave");
368 }
369 
370 #[wasm_bindgen_test]
starts_with()371 fn starts_with() {
372     let js = JsString::from("To be, or not to be, that is the question.");
373 
374     // TODO: remove second parameter for both assertions once we have optional parameters
375     assert!(js.starts_with("To be", 0));
376     assert!(!js.starts_with("not to be", 0));
377     assert!(js.starts_with("not to be", 10));
378 }
379 
380 #[wasm_bindgen_test]
substring()381 fn substring() {
382     let js = JsString::from("Mozilla");
383 
384     assert_eq!(js.substring(0, 1), "M");
385     assert_eq!(js.substring(1, 0), "M");
386 
387     assert_eq!(js.substring(0, 6), "Mozill");
388 
389     // TODO: Add test once we have optional parameters
390     // assert_eq!(js.substring(4), "lla");
391     assert_eq!(js.substring(4, 7), "lla");
392     assert_eq!(js.substring(7, 4), "lla");
393 
394     assert_eq!(js.substring(0, 7), "Mozilla");
395     assert_eq!(js.substring(0, 10), "Mozilla");
396 }
397 
398 #[wasm_bindgen_test]
substr()399 fn substr() {
400     let js = JsString::from("Mozilla");
401 
402     assert_eq!(js.substr(0, 1), "M");
403     assert_eq!(js.substr(1, 0), "");
404     assert_eq!(js.substr(-1, 1), "a");
405     assert_eq!(js.substr(1, -1), "");
406     // TODO: Uncomment and test these assertions, once we have support for optional parameters
407     // assert_eq!(js.substr(-3), "lla");
408     // assert_eq!(js.substr(1), "ozilla");
409     assert_eq!(js.substr(-20, 2), "Mo");
410     assert_eq!(js.substr(20, 2), "");
411 }
412 
413 #[wasm_bindgen_test]
to_locale_lower_case()414 fn to_locale_lower_case() {
415     let js = JsString::from("Mozilla");
416     assert_eq!(js.to_locale_lower_case(None), "mozilla");
417     let s = JsString::from("\u{0130}");
418     assert_eq!(s.to_locale_lower_case(Some("tr".into())), "i");
419     assert_ne!(s.to_locale_lower_case(Some("en-US".into())), "i");
420 }
421 
422 #[wasm_bindgen_test]
to_locale_upper_case()423 fn to_locale_upper_case() {
424     let js = JsString::from("mozilla");
425     assert_eq!(js.to_locale_upper_case(None), "MOZILLA");
426     let s = JsString::from("i\u{0307}");
427     assert_eq!(s.to_locale_upper_case(Some("lt".into())), "I");
428     assert_ne!(s.to_locale_upper_case(Some("en-US".into())), "I");
429 }
430 
431 #[wasm_bindgen_test]
to_lower_case()432 fn to_lower_case() {
433     assert_eq!(JsString::from("Mozilla").to_lower_case(), "mozilla");
434 }
435 
436 #[wasm_bindgen_test]
to_string()437 fn to_string() {
438     assert_eq!(JsString::from("foo").to_string(), "foo");
439 }
440 
441 #[wasm_bindgen_test]
to_upper_case()442 fn to_upper_case() {
443     assert_eq!(JsString::from("Mozilla").to_upper_case(), "MOZILLA");
444 }
445 
446 #[wasm_bindgen_test]
trim()447 fn trim() {
448     assert_eq!(JsString::from("   foo  ").trim(), "foo");
449     // Another example of .trim() removing whitespace from just one side.
450     assert_eq!(JsString::from("foo   ").trim(), "foo");
451 }
452 
453 #[wasm_bindgen_test]
trim_end_and_trim_right()454 fn trim_end_and_trim_right() {
455     let greeting = JsString::from("   Hello world!   ");
456     let trimmed = "   Hello world!";
457     assert_eq!(greeting.trim_end(), trimmed);
458     assert_eq!(greeting.trim_right(), trimmed);
459 }
460 
461 #[wasm_bindgen_test]
trim_start_and_trim_left()462 fn trim_start_and_trim_left() {
463     let greeting = JsString::from("   Hello world!   ");
464     let trimmed = "Hello world!   ";
465     assert_eq!(greeting.trim_start(), trimmed);
466     assert_eq!(greeting.trim_left(), trimmed);
467 }
468 
469 #[wasm_bindgen_test]
value_of()470 fn value_of() {
471     let greeting = JsString::from("Hello world!");
472     assert_eq!(greeting.value_of(), "Hello world!");
473 }
474 
475 #[wasm_bindgen_test]
raw()476 fn raw() {
477     let call_site = Object::new();
478     let raw = Array::of3(&"foo".into(), &"bar".into(), &"123".into());
479     Reflect::set(&call_site.as_ref(), &"raw".into(), &raw.into()).unwrap();
480     assert_eq!(
481         JsString::raw_2(&call_site, "5", "JavaScript").unwrap(),
482         "foo5barJavaScript123"
483     );
484     let substitutions = Array::of2(&"5".into(), &"JavaScript".into());
485     assert_eq!(
486         JsString::raw(&call_site, &substitutions).unwrap(),
487         "foo5barJavaScript123"
488     );
489     assert!(JsString::raw_0(&JsValue::null().unchecked_into()).is_err());
490 }
491 
492 #[wasm_bindgen_test]
is_valid_utf16()493 fn is_valid_utf16() {
494     assert!(JsString::from("a").is_valid_utf16());
495     assert!(JsString::from("").is_valid_utf16());
496     assert!(JsString::from("��").is_valid_utf16());
497     assert!(JsString::from("Why hello there this, ��, is �� and is ��").is_valid_utf16());
498 
499     assert!(JsString::from_char_code1(0x00).is_valid_utf16());
500     assert!(!JsString::from_char_code1(0xd800).is_valid_utf16());
501     assert!(!JsString::from_char_code1(0xdc00).is_valid_utf16());
502 }
503 
504 #[wasm_bindgen_test]
as_char()505 fn as_char() {
506     assert_eq!(JsString::from('a').as_char(), Some('a'));
507     assert_eq!(JsString::from('��').as_char(), Some('��'));
508     assert_eq!(JsString::from("").as_char(), None);
509     assert_eq!(JsString::from("ab").as_char(), None);
510     assert_eq!(JsString::from_char_code1(0xd800).as_char(), None);
511     assert_eq!(JsString::from_char_code1(0xdc00).as_char(), None);
512     assert_eq!(JsString::from_char_code1(0xdfff).as_char(), None);
513 }
514