/ tests / integration_js_ts.rs
integration_js_ts.rs
  1  mod common;
  2  use common::TestFixture;
  3  use ecolog_lsp::server::handlers::{
  4      compute_diagnostics, handle_completion, handle_definition, handle_hover,
  5  };
  6  use tower_lsp::lsp_types::{
  7      CompletionContext, CompletionParams, CompletionTriggerKind, GotoDefinitionParams, HoverParams,
  8      Position, TextDocumentIdentifier, TextDocumentPositionParams,
  9  };
 10  
 11  #[tokio::test]
 12  async fn test_js_hover_direct() {
 13      let fixture = TestFixture::new().await;
 14      let uri = fixture.create_file("test.js", "const a = process.env.DB_URL;");
 15  
 16      fixture
 17          .state
 18          .document_manager
 19          .open(
 20              uri.clone(),
 21              "javascript".to_string(),
 22              "const a = process.env.DB_URL;".to_string(),
 23              0,
 24          )
 25          .await;
 26  
 27      let hover = handle_hover(
 28          HoverParams {
 29              text_document_position_params: TextDocumentPositionParams {
 30                  text_document: TextDocumentIdentifier { uri },
 31                  position: Position::new(0, 22),
 32              },
 33              work_done_progress_params: Default::default(),
 34          },
 35          &fixture.state,
 36      )
 37      .await;
 38  
 39      assert!(hover.is_some());
 40      assert!(format!("{:?}", hover.unwrap()).contains("postgres://"));
 41  }
 42  
 43  #[tokio::test]
 44  async fn test_js_hover_bracket() {
 45      let fixture = TestFixture::new().await;
 46      let uri = fixture.create_file("test.js", "const a = process.env['API_KEY'];");
 47  
 48      fixture
 49          .state
 50          .document_manager
 51          .open(
 52              uri.clone(),
 53              "javascript".to_string(),
 54              "const a = process.env['API_KEY'];".to_string(),
 55              0,
 56          )
 57          .await;
 58  
 59      let hover = handle_hover(
 60          HoverParams {
 61              text_document_position_params: TextDocumentPositionParams {
 62                  text_document: TextDocumentIdentifier { uri },
 63                  position: Position::new(0, 25),
 64              },
 65              work_done_progress_params: Default::default(),
 66          },
 67          &fixture.state,
 68      )
 69      .await;
 70  
 71      assert!(hover.is_some());
 72      assert!(format!("{:?}", hover.unwrap()).contains("secret_key"));
 73  }
 74  
 75  #[tokio::test]
 76  async fn test_js_hover_destructuring() {
 77      let fixture = TestFixture::new().await;
 78      let uri = fixture.create_file("test.js", "const { PORT } = process.env;");
 79  
 80      fixture
 81          .state
 82          .document_manager
 83          .open(
 84              uri.clone(),
 85              "javascript".to_string(),
 86              "const { PORT } = process.env;".to_string(),
 87              0,
 88          )
 89          .await;
 90  
 91      let hover = handle_hover(
 92          HoverParams {
 93              text_document_position_params: TextDocumentPositionParams {
 94                  text_document: TextDocumentIdentifier { uri },
 95                  position: Position::new(0, 9),
 96              },
 97              work_done_progress_params: Default::default(),
 98          },
 99          &fixture.state,
100      )
101      .await;
102  
103      assert!(hover.is_some());
104      assert!(format!("{:?}", hover.unwrap()).contains("8080"));
105  }
106  
107  #[tokio::test]
108  async fn test_js_completion_trigger() {
109      let fixture = TestFixture::new().await;
110      let uri = fixture.create_file("test.js", "process.env.");
111  
112      fixture
113          .state
114          .document_manager
115          .open(
116              uri.clone(),
117              "javascript".to_string(),
118              "process.env.".to_string(),
119              0,
120          )
121          .await;
122  
123      let completion = handle_completion(
124          CompletionParams {
125              text_document_position: TextDocumentPositionParams {
126                  text_document: TextDocumentIdentifier { uri },
127                  position: Position::new(0, 12),
128              },
129              work_done_progress_params: Default::default(),
130              partial_result_params: Default::default(),
131              context: Some(CompletionContext {
132                  trigger_kind: CompletionTriggerKind::TRIGGER_CHARACTER,
133                  trigger_character: Some(".".to_string()),
134              }),
135          },
136          &fixture.state,
137      )
138      .await;
139  
140      assert!(completion.is_some());
141      let items = completion.unwrap();
142      assert!(items.iter().any(|i| i.label == "DB_URL"));
143      assert!(items.iter().any(|i| i.label == "PORT"));
144  }
145  
146  #[tokio::test]
147  async fn test_js_definition_direct() {
148      let fixture = TestFixture::new().await;
149      let uri = fixture.create_file("test.js", "process.env.DB_URL");
150  
151      fixture
152          .state
153          .document_manager
154          .open(
155              uri.clone(),
156              "javascript".to_string(),
157              "process.env.DB_URL".to_string(),
158              0,
159          )
160          .await;
161  
162      let def = handle_definition(
163          GotoDefinitionParams {
164              text_document_position_params: TextDocumentPositionParams {
165                  text_document: TextDocumentIdentifier { uri },
166                  position: Position::new(0, 15),
167              },
168              work_done_progress_params: Default::default(),
169              partial_result_params: Default::default(),
170          },
171          &fixture.state,
172      )
173      .await;
174  
175      assert!(def.is_some());
176  }
177  
178  #[tokio::test]
179  async fn test_js_diagnostics_undefined() {
180      let fixture = TestFixture::new().await;
181      let uri = fixture.create_file("test.js", "process.env.MISSING_VAR");
182  
183      fixture
184          .state
185          .document_manager
186          .open(
187              uri.clone(),
188              "javascript".to_string(),
189              "process.env.MISSING_VAR".to_string(),
190              0,
191          )
192          .await;
193  
194      let diags = compute_diagnostics(&uri, &fixture.state).await;
195  
196      assert!(!diags.is_empty());
197      assert!(diags.iter().any(|d| d.message.contains("not defined")));
198  }
199  
200  #[tokio::test]
201  async fn test_js_object_alias_hover() {
202      let fixture = TestFixture::new().await;
203  
204      let content = "const e = process.env; e.PORT;";
205      let uri = fixture.create_file("test.js", content);
206  
207      fixture
208          .state
209          .document_manager
210          .open(
211              uri.clone(),
212              "javascript".to_string(),
213              content.to_string(),
214              0,
215          )
216          .await;
217  
218      let hover = handle_hover(
219          HoverParams {
220              text_document_position_params: TextDocumentPositionParams {
221                  text_document: TextDocumentIdentifier { uri },
222                  position: Position::new(0, 26),
223              },
224              work_done_progress_params: Default::default(),
225          },
226          &fixture.state,
227      )
228      .await;
229  
230      assert!(hover.is_some());
231      assert!(format!("{:?}", hover.unwrap()).contains("8080"));
232  }
233  
234  #[tokio::test]
235  async fn test_ts_hover_type_cast() {
236      let fixture = TestFixture::new().await;
237      let uri = fixture.create_file("test.ts", "const a = process.env.PORT as string;");
238  
239      fixture
240          .state
241          .document_manager
242          .open(
243              uri.clone(),
244              "typescript".to_string(),
245              "const a = process.env.PORT as string;".to_string(),
246              0,
247          )
248          .await;
249  
250      let hover = handle_hover(
251          HoverParams {
252              text_document_position_params: TextDocumentPositionParams {
253                  text_document: TextDocumentIdentifier { uri },
254                  position: Position::new(0, 24),
255              },
256              work_done_progress_params: Default::default(),
257          },
258          &fixture.state,
259      )
260      .await;
261  
262      assert!(hover.is_some());
263      assert!(format!("{:?}", hover.unwrap()).contains("8080"));
264  }
265  
266  #[tokio::test]
267  async fn test_ts_completion_on_alias() {
268      let fixture = TestFixture::new().await;
269      let uri = fixture.create_file("test.ts", "const env = process.env; env.");
270  
271      fixture
272          .state
273          .document_manager
274          .open(
275              uri.clone(),
276              "typescript".to_string(),
277              "const env = process.env; env.".to_string(),
278              0,
279          )
280          .await;
281  
282      let completion = handle_completion(
283          CompletionParams {
284              text_document_position: TextDocumentPositionParams {
285                  text_document: TextDocumentIdentifier { uri },
286                  position: Position::new(0, 29),
287              },
288              work_done_progress_params: Default::default(),
289              partial_result_params: Default::default(),
290              context: Some(CompletionContext {
291                  trigger_kind: CompletionTriggerKind::TRIGGER_CHARACTER,
292                  trigger_character: Some(".".to_string()),
293              }),
294          },
295          &fixture.state,
296      )
297      .await;
298  
299      if let Some(items) = completion {
300          assert!(items.iter().any(|i| i.label == "PORT"));
301      } else {
302      }
303  }
304  
305  #[tokio::test]
306  async fn test_js_hover_destructuring_rename() {
307      let fixture = TestFixture::new().await;
308      let uri = fixture.create_file("test.js", "const { API_KEY: apiKey } = process.env;");
309      fixture
310          .state
311          .document_manager
312          .open(
313              uri.clone(),
314              "javascript".to_string(),
315              "const { API_KEY: apiKey } = process.env;".to_string(),
316              0,
317          )
318          .await;
319  
320      let hover = handle_hover(
321          HoverParams {
322              text_document_position_params: TextDocumentPositionParams {
323                  text_document: TextDocumentIdentifier { uri },
324                  position: Position::new(0, 20),
325              },
326              work_done_progress_params: Default::default(),
327          },
328          &fixture.state,
329      )
330      .await;
331  
332      assert!(hover.is_some());
333      assert!(format!("{:?}", hover.unwrap()).contains("secret_key"));
334  }
335  
336  #[tokio::test]
337  async fn test_js_hover_destructuring_property_key() {
338      let fixture = TestFixture::new().await;
339      let uri = fixture.create_file("test.js", "const { API_KEY: apiKey } = process.env;");
340      fixture
341          .state
342          .document_manager
343          .open(
344              uri.clone(),
345              "javascript".to_string(),
346              "const { API_KEY: apiKey } = process.env;".to_string(),
347              0,
348          )
349          .await;
350  
351      let hover = handle_hover(
352          HoverParams {
353              text_document_position_params: TextDocumentPositionParams {
354                  text_document: TextDocumentIdentifier { uri },
355                  position: Position::new(0, 10),
356              },
357              work_done_progress_params: Default::default(),
358          },
359          &fixture.state,
360      )
361      .await;
362  
363      assert!(hover.is_some(), "Expected hover on API_KEY property key");
364      assert!(format!("{:?}", hover.unwrap()).contains("secret_key"));
365  }
366  
367  #[tokio::test]
368  async fn test_js_hover_destructuring_rename_bracket() {
369      let fixture = TestFixture::new().await;
370      let uri = fixture.create_file(
371          "test.js",
372          "const { API_KEY: apiKey } = process.env; console.log(apiKey);",
373      );
374      fixture
375          .state
376          .document_manager
377          .open(
378              uri.clone(),
379              "javascript".to_string(),
380              "const { API_KEY: apiKey } = process.env; console.log(apiKey);".to_string(),
381              0,
382          )
383          .await;
384  
385      let hover = handle_hover(
386          HoverParams {
387              text_document_position_params: TextDocumentPositionParams {
388                  text_document: TextDocumentIdentifier { uri },
389                  position: Position::new(0, 55),
390              },
391              work_done_progress_params: Default::default(),
392          },
393          &fixture.state,
394      )
395      .await;
396  
397      assert!(hover.is_some());
398      assert!(format!("{:?}", hover.unwrap()).contains("secret_key"));
399  }
400  
401  #[tokio::test]
402  async fn test_js_hover_destructuring_with_default() {
403      let fixture = TestFixture::new().await;
404      let uri = fixture.create_file("test.js", "const { PORT: port = 3000 } = process.env;");
405      fixture
406          .state
407          .document_manager
408          .open(
409              uri.clone(),
410              "javascript".to_string(),
411              "const { PORT: port = 3000 } = process.env;".to_string(),
412              0,
413          )
414          .await;
415  
416      let hover = handle_hover(
417          HoverParams {
418              text_document_position_params: TextDocumentPositionParams {
419                  text_document: TextDocumentIdentifier { uri },
420                  position: Position::new(0, 14),
421              },
422              work_done_progress_params: Default::default(),
423          },
424          &fixture.state,
425      )
426      .await;
427  
428      assert!(hover.is_some());
429      assert!(format!("{:?}", hover.unwrap()).contains("8080"));
430  }
431  
432  #[tokio::test]
433  async fn test_js_diagnostics_destructuring_rename_undefined() {
434      let fixture = TestFixture::new().await;
435      let uri = fixture.create_file(
436          "test.js",
437          "const { MISSING_VAR: missing } = process.env; console.log(missing);",
438      );
439      fixture
440          .state
441          .document_manager
442          .open(
443              uri.clone(),
444              "javascript".to_string(),
445              "const { MISSING_VAR: missing } = process.env; console.log(missing);".to_string(),
446              0,
447          )
448          .await;
449  
450      let diags = compute_diagnostics(&uri, &fixture.state).await;
451  
452      assert!(!diags.is_empty());
453      assert!(diags.iter().any(|d| d.message.contains("not defined")));
454  }
455  
456  #[tokio::test]
457  async fn test_js_diagnostics_destructuring_with_default_undefined() {
458      let fixture = TestFixture::new().await;
459      let uri = fixture.create_file(
460          "test.js",
461          "const { MISSING_VAR: missing = 'default' } = process.env; console.log(missing);",
462      );
463      fixture
464          .state
465          .document_manager
466          .open(
467              uri.clone(),
468              "javascript".to_string(),
469              "const { MISSING_VAR: missing = 'default' } = process.env; console.log(missing);"
470                  .to_string(),
471              0,
472          )
473          .await;
474  
475      let diags = compute_diagnostics(&uri, &fixture.state).await;
476  
477      assert!(!diags.is_empty());
478      assert!(diags.iter().any(|d| d.message.contains("not defined")));
479  }
480  
481  #[tokio::test]
482  async fn test_js_destructuring_multiple() {
483      let fixture = TestFixture::new().await;
484      let uri = fixture.create_file(
485          "test.js",
486          "const { API_KEY: apiKey, PORT: port, DB_URL: dbUrl } = process.env;",
487      );
488      fixture
489          .state
490          .document_manager
491          .open(
492              uri.clone(),
493              "javascript".to_string(),
494              "const { API_KEY: apiKey, PORT: port, DB_URL: dbUrl } = process.env;".to_string(),
495              0,
496          )
497          .await;
498  
499      let hover = handle_hover(
500          HoverParams {
501              text_document_position_params: TextDocumentPositionParams {
502                  text_document: TextDocumentIdentifier { uri: uri.clone() },
503                  position: Position::new(0, 17),
504              },
505              work_done_progress_params: Default::default(),
506          },
507          &fixture.state,
508      )
509      .await;
510  
511      assert!(hover.is_some(), "Expected hover on apiKey binding");
512      assert!(format!("{:?}", hover.unwrap()).contains("secret_key"));
513  
514      let hover = handle_hover(
515          HoverParams {
516              text_document_position_params: TextDocumentPositionParams {
517                  text_document: TextDocumentIdentifier { uri: uri.clone() },
518                  position: Position::new(0, 31),
519              },
520              work_done_progress_params: Default::default(),
521          },
522          &fixture.state,
523      )
524      .await;
525  
526      assert!(hover.is_some(), "Expected hover on port binding");
527      assert!(format!("{:?}", hover.unwrap()).contains("8080"));
528  
529      let hover = handle_hover(
530          HoverParams {
531              text_document_position_params: TextDocumentPositionParams {
532                  text_document: TextDocumentIdentifier { uri: uri.clone() },
533                  position: Position::new(0, 45),
534              },
535              work_done_progress_params: Default::default(),
536          },
537          &fixture.state,
538      )
539      .await;
540  
541      assert!(hover.is_some(), "Expected hover on dbUrl binding");
542      assert!(format!("{:?}", hover.unwrap()).contains("postgres://"));
543  }
544  
545  #[tokio::test]
546  async fn test_js_destructuring_undefined_and_defined_mix() {
547      let fixture = TestFixture::new().await;
548      let uri = fixture.create_file("test.js", "const { API_KEY: apiKey, MISSING_VAR: missing } = process.env; console.log(apiKey, missing);");
549      fixture.state.document_manager.open(uri.clone(), "javascript".to_string(), 
550          "const { API_KEY: apiKey, MISSING_VAR: missing } = process.env; console.log(apiKey, missing);".to_string(), 0).await;
551  
552      let diags = compute_diagnostics(&uri, &fixture.state).await;
553  
554      assert!(!diags.is_empty());
555      assert!(diags.iter().any(|d| d.message.contains("MISSING_VAR")));
556  }
557  
558  #[tokio::test]
559  async fn test_ts_hover_destructuring_rename() {
560      let fixture = TestFixture::new().await;
561      let uri = fixture.create_file("test.ts", "const { API_KEY: apiKey } = process.env;");
562      fixture
563          .state
564          .document_manager
565          .open(
566              uri.clone(),
567              "typescript".to_string(),
568              "const { API_KEY: apiKey } = process.env;".to_string(),
569              0,
570          )
571          .await;
572  
573      let hover = handle_hover(
574          HoverParams {
575              text_document_position_params: TextDocumentPositionParams {
576                  text_document: TextDocumentIdentifier { uri },
577                  position: Position::new(0, 10),
578              },
579              work_done_progress_params: Default::default(),
580          },
581          &fixture.state,
582      )
583      .await;
584  
585      assert!(hover.is_some(), "Expected hover on API_KEY property key");
586      assert!(format!("{:?}", hover.unwrap()).contains("secret_key"));
587  }
588  
589  #[tokio::test]
590  async fn test_ts_hover_destructuring_with_default() {
591      let fixture = TestFixture::new().await;
592      let uri = fixture.create_file("test.ts", "const { PORT: port = 3000 } = process.env;");
593      fixture
594          .state
595          .document_manager
596          .open(
597              uri.clone(),
598              "typescript".to_string(),
599              "const { PORT: port = 3000 } = process.env;".to_string(),
600              0,
601          )
602          .await;
603  
604      let hover = handle_hover(
605          HoverParams {
606              text_document_position_params: TextDocumentPositionParams {
607                  text_document: TextDocumentIdentifier { uri },
608                  position: Position::new(0, 14),
609              },
610              work_done_progress_params: Default::default(),
611          },
612          &fixture.state,
613      )
614      .await;
615  
616      assert!(hover.is_some());
617      assert!(format!("{:?}", hover.unwrap()).contains("8080"));
618  }
619  
620  #[tokio::test]
621  async fn test_ts_diagnostics_destructuring_rename_undefined() {
622      let fixture = TestFixture::new().await;
623      let uri = fixture.create_file(
624          "test.ts",
625          "const { MISSING_VAR: missing } = process.env; console.log(missing);",
626      );
627      fixture
628          .state
629          .document_manager
630          .open(
631              uri.clone(),
632              "typescript".to_string(),
633              "const { MISSING_VAR: missing } = process.env; console.log(missing);".to_string(),
634              0,
635          )
636          .await;
637  
638      let diags = compute_diagnostics(&uri, &fixture.state).await;
639  
640      assert!(!diags.is_empty());
641      assert!(diags.iter().any(|d| d.message.contains("not defined")));
642  }
643  
644  #[tokio::test]
645  async fn test_ts_destructuring_with_type_cast_and_rename() {
646      let fixture = TestFixture::new().await;
647      let uri = fixture.create_file(
648          "test.ts",
649          "const { API_KEY: apiKey } = process.env; const typed = apiKey as string;",
650      );
651      fixture
652          .state
653          .document_manager
654          .open(
655              uri.clone(),
656              "typescript".to_string(),
657              "const { API_KEY: apiKey } = process.env; const typed = apiKey as string;".to_string(),
658              0,
659          )
660          .await;
661  
662      let hover = handle_hover(
663          HoverParams {
664              text_document_position_params: TextDocumentPositionParams {
665                  text_document: TextDocumentIdentifier { uri },
666                  position: Position::new(0, 10),
667              },
668              work_done_progress_params: Default::default(),
669          },
670          &fixture.state,
671      )
672      .await;
673  
674      assert!(hover.is_some(), "Expected hover on API_KEY property key");
675      assert!(format!("{:?}", hover.unwrap()).contains("secret_key"));
676  }