summaryrefslogtreecommitdiff
path: root/cli/doc/tests.rs
diff options
context:
space:
mode:
Diffstat (limited to 'cli/doc/tests.rs')
-rw-r--r--cli/doc/tests.rs2377
1 files changed, 1413 insertions, 964 deletions
diff --git a/cli/doc/tests.rs b/cli/doc/tests.rs
index 7efc3857b..e46fff621 100644
--- a/cli/doc/tests.rs
+++ b/cli/doc/tests.rs
@@ -1,5 +1,6 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
use super::DocParser;
+use super::DocPrinter;
use crate::colors;
use serde_json::json;
@@ -41,360 +42,334 @@ impl DocFileLoader for TestLoader {
}
}
+macro_rules! doc_test {
+ ( $name:ident, $source:expr; $block:block ) => {
+ doc_test!($name, $source, false, false; $block);
+ };
+
+ ( $name:ident, $source:expr, details; $block:block ) => {
+ doc_test!($name, $source, true, false; $block);
+ };
+
+ ( $name:ident, $source:expr, private; $block:block ) => {
+ doc_test!($name, $source, false, true; $block);
+ };
+
+ ( $name:ident, $source:expr, details, private; $block:block ) => {
+ doc_test!($name, $source, true, true; $block);
+ };
+
+ ( $name:ident, $source:expr, $details:expr, $private:expr; $block:block ) => {
+ #[tokio::test]
+ async fn $name() {
+ let source_code = $source;
+ let details = $details;
+ let private = $private;
+
+ let loader =
+ TestLoader::new(vec![("test.ts".to_string(), source_code.to_string())]);
+ let entries = DocParser::new(loader, private)
+ .parse("test.ts")
+ .await
+ .unwrap();
+
+ let doc = DocPrinter::new(&entries, details, private).to_string();
+ #[allow(unused_variables)]
+ let doc = colors::strip_ansi_codes(&doc);
+
+ $block
+ }
+ };
+}
+
+macro_rules! contains_test {
+ ( $name:ident, $source:expr;
+ $( $contains:expr ),* $( ; $( $notcontains:expr ),* )? ) => {
+ contains_test!($name, $source, false, false; $($contains),* $(;$($notcontains),*)?);
+ };
+
+ ( $name:ident, $source:expr, details;
+ $( $contains:expr ),* $( ; $( $notcontains:expr ),* )? ) => {
+ contains_test!($name, $source, true, false; $($contains),* $(;$($notcontains),*)?);
+ };
+
+ ( $name:ident, $source:expr, private;
+ $( $contains:expr ),* $( ; $( $notcontains:expr ),* )? ) => {
+ contains_test!($name, $source, false, true; $($contains),* $(;$($notcontains),*)?);
+ };
+
+ ( $name:ident, $source:expr, details, private;
+ $( $contains:expr ),* $( ; $( $notcontains:expr ),* )? ) => {
+ contains_test!($name, $source, true, true; $($contains),* $(;$($notcontains),*)?);
+ };
+
+ ( $name:ident, $source:expr, $details:expr, $private:expr;
+ $( $contains:expr ),* $( ; $( $notcontains:expr ),* )? ) => {
+ doc_test!($name, $source, $details, $private; {
+ $(
+ assert!(doc.contains($contains));
+ )*
+ $(
+ $(
+ assert!(!doc.contains($notcontains));
+ )*
+ )?
+ });
+ };
+}
+
+macro_rules! json_test {
+ ( $name:ident, $source:expr; $json:tt ) => {
+ json_test!($name, $source, false; $json);
+ };
+
+ ( $name:ident, $source:expr, private; $json:tt ) => {
+ json_test!($name, $source, true; $json);
+ };
+
+ ( $name:ident, $source:expr, $private:expr; $json:tt ) => {
+ doc_test!($name, $source, false, $private; {
+ let actual = serde_json::to_value(&entries).unwrap();
+ let expected_json = json!($json);
+ assert_eq!(actual, expected_json);
+ });
+ };
+}
+
#[tokio::test]
-async fn export_fn() {
- let source_code = r#"/**
-* @module foo
-*/
+async fn reexports() {
+ let nested_reexport_source_code = r#"
+/**
+ * JSDoc for bar
+ */
+export const bar = "bar";
+
+export default 42;
+"#;
+ let reexport_source_code = r#"
+import { bar } from "./nested_reexport.ts";
/**
-* Hello there, this is a multiline JSdoc.
-*
-* It has many lines
-*
-* Or not that many?
-*/
-export function foo(a: string, b?: number, cb: (...cbArgs: unknown[]) => void, ...args: unknown[]): void {
- /**
- * @todo document all the things.
- */
- console.log("Hello world");
+ * JSDoc for const
+ */
+export const foo = "foo";
+"#;
+ let test_source_code = r#"
+export { default, foo as fooConst } from "./reexport.ts";
+
+/** JSDoc for function */
+export function fooFn(a: number) {
+ return a;
}
"#;
- let loader =
- TestLoader::new(vec![("test.ts".to_string(), source_code.to_string())]);
- let entries = DocParser::new(loader).parse("test.ts").await.unwrap();
- assert_eq!(entries.len(), 1);
- let entry = &entries[0];
- let expected_json = json!({
- "functionDef": {
- "isAsync": false,
- "isGenerator": false,
- "typeParams": [],
- "params": [
- {
- "name": "a",
- "kind": "identifier",
- "optional": false,
- "tsType": {
- "keyword": "string",
- "kind": "keyword",
- "repr": "string",
- },
- },
- {
- "name": "b",
- "kind": "identifier",
- "optional": true,
- "tsType": {
- "keyword": "number",
- "kind": "keyword",
- "repr": "number",
- },
- },
- {
- "name": "cb",
- "kind": "identifier",
- "optional": false,
- "tsType": {
- "repr": "",
- "kind": "fnOrConstructor",
- "fnOrConstructor": {
- "constructor": false,
- "tsType": {
- "keyword": "void",
- "kind": "keyword",
- "repr": "void"
- },
- "typeParams": [],
- "params": [{
- "kind": "rest",
- "name": "cbArgs",
- "optional": false,
- "tsType": {
- "repr": "",
- "kind": "array",
- "array": {
- "repr": "unknown",
- "kind": "keyword",
- "keyword": "unknown"
- }
- },
- }]
- }
- },
- },
- {
- "name": "args",
- "kind": "rest",
- "optional": false,
- "tsType": {
- "repr": "",
- "kind": "array",
- "array": {
- "repr": "unknown",
- "kind": "keyword",
- "keyword": "unknown"
- }
- }
- }
- ],
- "returnType": {
- "keyword": "void",
- "kind": "keyword",
- "repr": "void",
+ let loader = TestLoader::new(vec![
+ ("file:///test.ts".to_string(), test_source_code.to_string()),
+ (
+ "file:///reexport.ts".to_string(),
+ reexport_source_code.to_string(),
+ ),
+ (
+ "file:///nested_reexport.ts".to_string(),
+ nested_reexport_source_code.to_string(),
+ ),
+ ]);
+ let entries = DocParser::new(loader, false)
+ .parse_with_reexports("file:///test.ts")
+ .await
+ .unwrap();
+ assert_eq!(entries.len(), 2);
+
+ let expected_json = json!([
+ {
+ "kind": "variable",
+ "name": "fooConst",
+ "location": {
+ "filename": "file:///reexport.ts",
+ "line": 7,
+ "col": 0
},
+ "jsDoc": "JSDoc for const",
+ "variableDef": {
+ "tsType": null,
+ "kind": "const"
+ }
},
- "jsDoc": "Hello there, this is a multiline JSdoc.\n\nIt has many lines\n\nOr not that many?",
- "kind": "function",
- "location": {
- "col": 0,
- "filename": "test.ts",
- "line": 12,
- },
- "name": "foo",
- });
-
- let actual = serde_json::to_value(entry).unwrap();
+ {
+ "kind": "function",
+ "name": "fooFn",
+ "location": {
+ "filename": "file:///test.ts",
+ "line": 5,
+ "col": 0
+ },
+ "jsDoc": "JSDoc for function",
+ "functionDef": {
+ "params": [
+ {
+ "name": "a",
+ "kind": "identifier",
+ "optional": false,
+ "tsType": {
+ "keyword": "number",
+ "kind": "keyword",
+ "repr": "number",
+ },
+ }
+ ],
+ "typeParams": [],
+ "returnType": null,
+ "isAsync": false,
+ "isGenerator": false
+ }
+ }
+ ]);
+ let actual = serde_json::to_value(&entries).unwrap();
assert_eq!(actual, expected_json);
assert!(colors::strip_ansi_codes(
- super::printer::format(entries.clone()).as_str()
+ DocPrinter::new(&entries, false, false).to_string().as_str()
)
- .contains("Hello there"));
- assert!(
- colors::strip_ansi_codes(super::printer::format(entries).as_str())
- .contains("b?: number")
- );
-}
-
-#[tokio::test]
-async fn format_type_predicate() {
- let source_code = r#"
-export function isFish(pet: Fish | Bird): pet is Fish {
- return (pet as Fish).swim !== undefined;
-}
-"#;
- let loader =
- TestLoader::new(vec![("test.ts".to_string(), source_code.to_string())]);
- let entries = DocParser::new(loader).parse("test.ts").await.unwrap();
- super::printer::format(entries);
+ .contains("function fooFn(a: number)"));
}
#[tokio::test]
-async fn export_fn2() {
+async fn filter_nodes_by_name() {
+ use super::find_nodes_by_name_recursively;
let source_code = r#"
-interface AssignOpts {
- a: string;
- b: number;
+export namespace Deno {
+ export class Buffer {}
+ export function test(options: object): void;
+ export function test(name: string, fn: Function): void;
+ export function test(name: string | object, fn?: Function): void {}
}
-export function foo([e,,f, ...g]: number[], { c, d: asdf, i = "asdf", ...rest}, ops: AssignOpts = {}): void {
- console.log("Hello world");
+export namespace Deno {
+ export namespace Inner {
+ export function a(): void {}
+ export const b = 100;
+ }
}
"#;
let loader =
TestLoader::new(vec![("test.ts".to_string(), source_code.to_string())]);
- let entries = DocParser::new(loader).parse("test.ts").await.unwrap();
- assert_eq!(entries.len(), 1);
- let entry = &entries[0];
- let expected_json = json!({
- "functionDef": {
- "isAsync": false,
- "isGenerator": false,
- "typeParams": [],
- "params": [
- {
- "name": "",
- "kind": "array",
- "optional": false,
- "tsType": {
- "repr": "",
- "kind": "array",
- "array": {
- "repr": "number",
- "kind": "keyword",
- "keyword": "number"
- }
- }
- },
- {
- "name": "",
- "kind": "object",
- "optional": false,
- "tsType": null
- },
- {
- "name": "ops",
- "kind": "identifier",
- "optional": false,
- "tsType": {
- "repr": "AssignOpts",
- "kind": "typeRef",
- "typeRef": {
- "typeName": "AssignOpts",
- "typeParams": null,
- }
- }
- },
- ],
- "returnType": {
- "keyword": "void",
- "kind": "keyword",
- "repr": "void",
- },
- },
- "jsDoc": null,
- "kind": "function",
- "location": {
- "col": 0,
- "filename": "test.ts",
- "line": 7,
- },
- "name": "foo",
- });
+ let entries = DocParser::new(loader, false)
+ .parse("test.ts")
+ .await
+ .unwrap();
- let actual = serde_json::to_value(entry).unwrap();
- assert_eq!(actual, expected_json);
+ let found =
+ find_nodes_by_name_recursively(entries.clone(), "Deno".to_string());
+ assert_eq!(found.len(), 2);
+ assert_eq!(found[0].name, "Deno".to_string());
+ assert_eq!(found[1].name, "Deno".to_string());
- assert!(
- colors::strip_ansi_codes(super::printer::format(entries).as_str())
- .contains("foo")
- );
-}
+ let found =
+ find_nodes_by_name_recursively(entries.clone(), "Deno.test".to_string());
+ assert_eq!(found.len(), 3);
+ assert_eq!(found[0].name, "test".to_string());
+ assert_eq!(found[1].name, "test".to_string());
+ assert_eq!(found[2].name, "test".to_string());
-#[tokio::test]
-async fn export_const() {
- let source_code = r#"
-/** Something about fizzBuzz */
-export const fizzBuzz = "fizzBuzz";
+ let found =
+ find_nodes_by_name_recursively(entries.clone(), "Deno.Inner.a".to_string());
+ assert_eq!(found.len(), 1);
+ assert_eq!(found[0].name, "a".to_string());
-export const env: {
- /** get doc */
- get(key: string): string | undefined;
+ let found =
+ find_nodes_by_name_recursively(entries.clone(), "Deno.test.a".to_string());
+ assert_eq!(found.len(), 0);
- /** set doc */
- set(key: string, value: string): void;
+ let found = find_nodes_by_name_recursively(entries, "a.b.c".to_string());
+ assert_eq!(found.len(), 0);
}
-"#;
- let loader =
- TestLoader::new(vec![("test.ts".to_string(), source_code.to_string())]);
- let entries = DocParser::new(loader).parse("test.ts").await.unwrap();
- assert_eq!(entries.len(), 2);
- let expected_json = json!([
- {
- "kind":"variable",
- "name":"fizzBuzz",
- "location":{
- "filename":"test.ts",
- "line":3,
- "col":0
- },
- "jsDoc":"Something about fizzBuzz",
- "variableDef":{
- "tsType":null,
- "kind":"const"
+
+mod serialization {
+ use super::*;
+
+ json_test!(declare_namespace,
+ r#"
+/** Namespace JSdoc */
+declare namespace RootNs {
+ declare const a = "a";
+
+ /** Nested namespace JSDoc */
+ declare namespace NestedNs {
+ declare enum Foo {
+ a = 1,
+ b = 2,
+ c = 3,
+ }
}
- },
- {
- "kind":"variable",
- "name":"env",
- "location":{
- "filename":"test.ts",
- "line":5,
- "col":0
+}
+ "#;
+ [{
+ "kind": "namespace",
+ "name": "RootNs",
+ "location": {
+ "filename": "test.ts",
+ "line": 3,
+ "col": 0
},
- "jsDoc":null,
- "variableDef":{
- "tsType":{
- "repr":"",
- "kind":"typeLiteral",
- "typeLiteral":{
- "methods":[{
- "name":"get",
- "params":[
+ "jsDoc": "Namespace JSdoc",
+ "namespaceDef": {
+ "elements": [
+ {
+ "kind": "variable",
+ "name": "a",
+ "location": {
+ "filename": "test.ts",
+ "line": 4,
+ "col": 12
+ },
+ "jsDoc": null,
+ "variableDef": {
+ "tsType": null,
+ "kind": "const"
+ }
+ },
+ {
+ "kind": "namespace",
+ "name": "NestedNs",
+ "location": {
+ "filename": "test.ts",
+ "line": 7,
+ "col": 4
+ },
+ "jsDoc": "Nested namespace JSDoc",
+ "namespaceDef": {
+ "elements": [
{
- "name":"key",
- "kind":"identifier",
- "optional":false,
- "tsType":{
- "repr":"string",
- "kind":"keyword",
- "keyword":"string"
- }
- }
- ],
- "returnType":{
- "repr":"",
- "kind":"union",
- "union":[
- {
- "repr":"string",
- "kind":"keyword",
- "keyword":"string"
+ "kind": "enum",
+ "name": "Foo",
+ "location": {
+ "filename": "test.ts",
+ "line": 8,
+ "col": 6
},
- {
- "repr":"undefined",
- "kind":"keyword",
- "keyword":"undefined"
- }
- ]
- },
- "typeParams":[]
- }, {
- "name":"set",
- "params":[
- {
- "name":"key",
- "kind":"identifier",
- "optional":false,
- "tsType":{
- "repr":"string",
- "kind":"keyword",
- "keyword":"string"
- }
- },
- {
- "name":"value",
- "kind":"identifier",
- "optional":false,
- "tsType":{
- "repr":"string",
- "kind":"keyword",
- "keyword":"string"
+ "jsDoc": null,
+ "enumDef": {
+ "members": [
+ {
+ "name": "a"
+ },
+ {
+ "name": "b"
+ },
+ {
+ "name": "c"
+ }
+ ]
}
}
- ],
- "returnType":{
- "repr":"void",
- "kind":"keyword",
- "keyword":"void"
- },
- "typeParams":[]
- }
- ],
- "properties":[],
- "callSignatures":[]
+ ]
}
- },
- "kind":"const"
- }
+ }
+ ]
}
- ]
- );
-
- let actual = serde_json::to_value(entries.clone()).unwrap();
- assert_eq!(actual, expected_json);
-
- assert!(
- colors::strip_ansi_codes(super::printer::format(entries).as_str())
- .contains("Something about fizzBuzz")
- );
-}
+ }]);
-#[tokio::test]
-async fn export_class() {
- let source_code = r#"
+ json_test!(export_class,
+ r#"
/** Class doc */
export class Foobar extends Fizz implements Buzz, Aldrin {
private private1?: boolean;
@@ -415,12 +390,8 @@ export class Foobar extends Fizz implements Buzz, Aldrin {
//
}
}
-"#;
- let loader =
- TestLoader::new(vec![("test.ts".to_string(), source_code.to_string())]);
- let entries = DocParser::new(loader).parse("test.ts").await.unwrap();
- assert_eq!(entries.len(), 1);
- let expected_json = json!({
+ "#;
+ [{
"kind": "class",
"name": "Foobar",
"location": {
@@ -432,8 +403,26 @@ export class Foobar extends Fizz implements Buzz, Aldrin {
"classDef": {
"isAbstract": false,
"extends": "Fizz",
- "implements": ["Buzz", "Aldrin"],
+ "implements": [
+ {
+ "repr": "Buzz",
+ "kind": "typeRef",
+ "typeRef": {
+ "typeParams": null,
+ "typeName": "Buzz"
+ }
+ },
+ {
+ "repr": "Aldrin",
+ "kind": "typeRef",
+ "typeRef": {
+ "typeParams": null,
+ "typeName": "Aldrin"
+ }
+ }
+ ],
"typeParams": [],
+ "superTypeParams": [],
"constructors": [
{
"jsDoc": "Constructor js doc",
@@ -556,6 +545,7 @@ export class Foobar extends Fizz implements Buzz, Aldrin {
}
}
],
+ "indexSignatures": [],
"methods": [
{
"jsDoc": "Async foo method",
@@ -618,25 +608,591 @@ export class Foobar extends Fizz implements Buzz, Aldrin {
}
]
}
- });
- let entry = &entries[0];
- let actual = serde_json::to_value(entry).unwrap();
- assert_eq!(actual, expected_json);
+ }]);
- assert!(colors::strip_ansi_codes(
- super::printer::format_details(entry.clone()).as_str()
- )
- .contains("bar?(): void"));
+ json_test!(export_const,
+ r#"
+/** Something about fizzBuzz */
+export const fizzBuzz = "fizzBuzz";
+
+export const env: {
+ /** get doc */
+ get(key: string): string | undefined;
- assert!(
- colors::strip_ansi_codes(super::printer::format(entries).as_str())
- .contains("class Foobar extends Fizz implements Buzz, Aldrin")
+ /** set doc */
+ set(key: string, value: string): void;
+}
+ "#;
+ [
+ {
+ "kind":"variable",
+ "name":"fizzBuzz",
+ "location":{
+ "filename":"test.ts",
+ "line":3,
+ "col":0
+ },
+ "jsDoc":"Something about fizzBuzz",
+ "variableDef":{
+ "tsType":null,
+ "kind":"const"
+ }
+ },
+ {
+ "kind":"variable",
+ "name":"env",
+ "location":{
+ "filename":"test.ts",
+ "line":5,
+ "col":0
+ },
+ "jsDoc":null,
+ "variableDef":{
+ "tsType":{
+ "repr":"",
+ "kind":"typeLiteral",
+ "typeLiteral":{
+ "methods":[{
+ "name":"get",
+ "params":[
+ {
+ "name":"key",
+ "kind":"identifier",
+ "optional":false,
+ "tsType":{
+ "repr":"string",
+ "kind":"keyword",
+ "keyword":"string"
+ }
+ }
+ ],
+ "returnType":{
+ "repr":"",
+ "kind":"union",
+ "union":[
+ {
+ "repr":"string",
+ "kind":"keyword",
+ "keyword":"string"
+ },
+ {
+ "repr":"undefined",
+ "kind":"keyword",
+ "keyword":"undefined"
+ }
+ ]
+ },
+ "typeParams":[]
+ }, {
+ "name":"set",
+ "params":[
+ {
+ "name":"key",
+ "kind":"identifier",
+ "optional":false,
+ "tsType":{
+ "repr":"string",
+ "kind":"keyword",
+ "keyword":"string"
+ }
+ },
+ {
+ "name":"value",
+ "kind":"identifier",
+ "optional":false,
+ "tsType":{
+ "repr":"string",
+ "kind":"keyword",
+ "keyword":"string"
+ }
+ }
+ ],
+ "returnType":{
+ "repr":"void",
+ "kind":"keyword",
+ "keyword":"void"
+ },
+ "typeParams":[]
+ }
+ ],
+ "properties":[],
+ "callSignatures":[],
+ "indexSignatures": []
+ }
+ },
+ "kind":"const"
+ }
+ }
+ ]
);
+
+ json_test!(export_default_class,
+ r#"
+/** Class doc */
+export default class Foobar {
+ /** Constructor js doc */
+ constructor(name: string, private private2: number, protected protected2: number) {}
+}
+ "#;
+ [{
+ "kind": "class",
+ "name": "default",
+ "location": {
+ "filename": "test.ts",
+ "line": 3,
+ "col": 0
+ },
+ "jsDoc": "Class doc",
+ "classDef": {
+ "isAbstract": false,
+ "extends": null,
+ "implements": [],
+ "typeParams": [],
+ "superTypeParams": [],
+ "constructors": [
+ {
+ "jsDoc": "Constructor js doc",
+ "accessibility": null,
+ "name": "constructor",
+ "params": [
+ {
+ "name": "name",
+ "kind": "identifier",
+ "optional": false,
+ "tsType": {
+ "repr": "string",
+ "kind": "keyword",
+ "keyword": "string"
+ }
+ },
+ {
+ "name": "private2",
+ "kind": "identifier",
+ "optional": false,
+ "tsType": {
+ "repr": "number",
+ "kind": "keyword",
+ "keyword": "number"
+ }
+ },
+ {
+ "name": "protected2",
+ "kind": "identifier",
+ "optional": false,
+ "tsType": {
+ "repr": "number",
+ "kind": "keyword",
+ "keyword": "number"
+ }
+ }
+ ],
+ "location": {
+ "filename": "test.ts",
+ "line": 5,
+ "col": 4
+ }
+ }
+ ],
+ "properties": [],
+ "indexSignatures": [],
+ "methods": []
+ }
+ }]);
+
+ json_test!(export_default_fn,
+ r#"
+export default function foo(a: number) {
+ return a;
}
+ "#;
+ [{
+ "kind": "function",
+ "name": "default",
+ "location": {
+ "filename": "test.ts",
+ "line": 2,
+ "col": 15
+ },
+ "jsDoc": null,
+ "functionDef": {
+ "params": [
+ {
+ "name": "a",
+ "kind": "identifier",
+ "optional": false,
+ "tsType": {
+ "keyword": "number",
+ "kind": "keyword",
+ "repr": "number",
+ },
+ }
+ ],
+ "typeParams": [],
+ "returnType": null,
+ "isAsync": false,
+ "isGenerator": false
+ }
+ }]);
-#[tokio::test]
-async fn export_interface() {
- let source_code = r#"
+ json_test!(export_default_interface,
+ r#"
+/**
+ * Interface js doc
+ */
+export default interface Reader {
+ /** Read n bytes */
+ read?(buf: Uint8Array, something: unknown): Promise<number>
+}
+ "#;
+ [{
+ "kind": "interface",
+ "name": "default",
+ "location": {
+ "filename": "test.ts",
+ "line": 5,
+ "col": 0
+ },
+ "jsDoc": "Interface js doc",
+ "interfaceDef": {
+ "extends": [],
+ "methods": [
+ {
+ "name": "read",
+ "location": {
+ "filename": "test.ts",
+ "line": 7,
+ "col": 4
+ },
+ "optional": true,
+ "jsDoc": "Read n bytes",
+ "params": [
+ {
+ "name": "buf",
+ "kind": "identifier",
+ "optional": false,
+ "tsType": {
+ "repr": "Uint8Array",
+ "kind": "typeRef",
+ "typeRef": {
+ "typeParams": null,
+ "typeName": "Uint8Array"
+ }
+ }
+ },
+ {
+ "name": "something",
+ "kind": "identifier",
+ "optional": false,
+ "tsType": {
+ "repr": "unknown",
+ "kind": "keyword",
+ "keyword": "unknown"
+ }
+ }
+ ],
+ "typeParams": [],
+ "returnType": {
+ "repr": "Promise",
+ "kind": "typeRef",
+ "typeRef": {
+ "typeParams": [
+ {
+ "repr": "number",
+ "kind": "keyword",
+ "keyword": "number"
+ }
+ ],
+ "typeName": "Promise"
+ }
+ }
+ }
+ ],
+ "properties": [],
+ "callSignatures": [],
+ "indexSignatures": [],
+ "typeParams": []
+ }
+ }]);
+
+ json_test!(export_enum,
+ r#"
+/**
+ * Some enum for good measure
+ */
+export enum Hello {
+ World = "world",
+ Fizz = "fizz",
+ Buzz = "buzz",
+}
+ "#;
+ [{
+ "kind": "enum",
+ "name": "Hello",
+ "location": {
+ "filename": "test.ts",
+ "line": 5,
+ "col": 0
+ },
+ "jsDoc": "Some enum for good measure",
+ "enumDef": {
+ "members": [
+ {
+ "name": "World"
+ },
+ {
+ "name": "Fizz"
+ },
+ {
+ "name": "Buzz"
+ }
+ ]
+ }
+ }]);
+
+ json_test!(export_fn,
+ r#"/**
+* @module foo
+*/
+
+/**
+* Hello there, this is a multiline JSdoc.
+*
+* It has many lines
+*
+* Or not that many?
+*/
+export function foo(a: string, b?: number, cb: (...cbArgs: unknown[]) => void, ...args: unknown[]): void {
+ /**
+ * @todo document all the things.
+ */
+ console.log("Hello world");
+}
+ "#;
+ [{
+ "functionDef": {
+ "isAsync": false,
+ "isGenerator": false,
+ "typeParams": [],
+ "params": [
+ {
+ "name": "a",
+ "kind": "identifier",
+ "optional": false,
+ "tsType": {
+ "keyword": "string",
+ "kind": "keyword",
+ "repr": "string",
+ },
+ },
+ {
+ "name": "b",
+ "kind": "identifier",
+ "optional": true,
+ "tsType": {
+ "keyword": "number",
+ "kind": "keyword",
+ "repr": "number",
+ },
+ },
+ {
+ "name": "cb",
+ "kind": "identifier",
+ "optional": false,
+ "tsType": {
+ "repr": "",
+ "kind": "fnOrConstructor",
+ "fnOrConstructor": {
+ "constructor": false,
+ "tsType": {
+ "keyword": "void",
+ "kind": "keyword",
+ "repr": "void"
+ },
+ "typeParams": [],
+ "params": [{
+ "arg": {
+ "name": "cbArgs",
+ "kind": "identifier",
+ "optional": false,
+ "tsType": null
+ },
+ "kind": "rest",
+ "tsType": {
+ "repr": "",
+ "kind": "array",
+ "array": {
+ "repr": "unknown",
+ "kind": "keyword",
+ "keyword": "unknown"
+ }
+ },
+ }]
+ }
+ },
+ },
+ {
+ "arg": {
+ "name": "args",
+ "kind": "identifier",
+ "optional": false,
+ "tsType": null
+ },
+ "kind": "rest",
+ "tsType": {
+ "array": {
+ "keyword": "unknown",
+ "kind": "keyword",
+ "repr": "unknown"
+ },
+ "kind": "array",
+ "repr": ""
+ }
+ }
+ ],
+ "returnType": {
+ "keyword": "void",
+ "kind": "keyword",
+ "repr": "void",
+ },
+ },
+ "jsDoc": "Hello there, this is a multiline JSdoc.\n\nIt has many lines\n\nOr not that many?",
+ "kind": "function",
+ "location": {
+ "col": 0,
+ "filename": "test.ts",
+ "line": 12,
+ },
+ "name": "foo",
+ }]);
+
+ json_test!(export_fn2,
+ r#"
+interface AssignOpts {
+ a: string;
+ b: number;
+}
+
+export function foo([e,,f, ...g]: number[], { c, d: asdf, i = "asdf", ...rest}, ops: AssignOpts = {}): void {
+ console.log("Hello world");
+}
+ "#;
+ [{
+ "functionDef": {
+ "isAsync": false,
+ "isGenerator": false,
+ "typeParams": [],
+ "params": [
+ {
+ "elements": [
+ {
+ "name": "e",
+ "kind": "identifier",
+ "optional": false,
+ "tsType": null
+ },
+ null,
+ {
+ "name": "f",
+ "kind": "identifier",
+ "optional": false,
+ "tsType": null
+ },
+ {
+ "arg": {
+ "name": "g",
+ "kind": "identifier",
+ "optional": false,
+ "tsType": null
+ },
+ "kind": "rest",
+ "tsType": null
+ }
+ ],
+ "kind": "array",
+ "optional": false,
+ "tsType": {
+ "repr": "",
+ "kind": "array",
+ "array": {
+ "repr": "number",
+ "kind": "keyword",
+ "keyword": "number"
+ }
+ }
+ },
+ {
+ "kind": "object",
+ "optional": false,
+ "props": [
+ {
+ "kind": "assign",
+ "key": "c",
+ "value": null
+ },
+ {
+ "kind": "keyValue",
+ "key": "d",
+ "value": {
+ "name": "asdf",
+ "kind": "identifier",
+ "optional": false,
+ "tsType": null
+ }
+ },
+ {
+ "kind": "assign",
+ "key": "i",
+ "value": "<UNIMPLEMENTED>"
+ },
+ {
+ "arg": {
+ "name": "rest",
+ "kind": "identifier",
+ "optional": false,
+ "tsType": null
+ },
+ "kind": "rest"
+ }
+ ],
+ "tsType": null
+ },
+ {
+ "kind": "assign",
+ "left": {
+ "name": "ops",
+ "kind": "identifier",
+ "optional": false,
+ "tsType": {
+ "repr": "AssignOpts",
+ "kind": "typeRef",
+ "typeRef": {
+ "typeName": "AssignOpts",
+ "typeParams": null,
+ }
+ }
+ },
+ "right": "<UNIMPLEMENTED>",
+ "tsType": null
+ }
+ ],
+ "returnType": {
+ "keyword": "void",
+ "kind": "keyword",
+ "repr": "void",
+ },
+ },
+ "jsDoc": null,
+ "kind": "function",
+ "location": {
+ "col": 0,
+ "filename": "test.ts",
+ "line": 7,
+ },
+ "name": "foo",
+ }]);
+
+ json_test!(export_interface,
+ r#"
interface Foo {
foo(): void;
}
@@ -651,12 +1207,7 @@ export interface Reader extends Foo, Bar {
read?(buf: Uint8Array, something: unknown): Promise<number>
}
"#;
- let loader =
- TestLoader::new(vec![("test.ts".to_string(), source_code.to_string())]);
- let entries = DocParser::new(loader).parse("test.ts").await.unwrap();
- assert_eq!(entries.len(), 1);
- let entry = &entries[0];
- let expected_json = json!({
+ [{
"kind": "interface",
"name": "Reader",
"location": {
@@ -666,7 +1217,24 @@ export interface Reader extends Foo, Bar {
},
"jsDoc": "Interface js doc",
"interfaceDef": {
- "extends": ["Foo", "Bar"],
+ "extends": [
+ {
+ "repr": "Foo",
+ "kind": "typeRef",
+ "typeRef": {
+ "typeParams": null,
+ "typeName": "Foo"
+ }
+ },
+ {
+ "repr": "Bar",
+ "kind": "typeRef",
+ "typeRef": {
+ "typeParams": null,
+ "typeName": "Bar"
+ }
+ }
+ ],
"methods": [
{
"name": "read",
@@ -721,31 +1289,18 @@ export interface Reader extends Foo, Bar {
],
"properties": [],
"callSignatures": [],
+ "indexSignatures": [],
"typeParams": [],
}
- });
- let actual = serde_json::to_value(entry).unwrap();
- assert_eq!(actual, expected_json);
-
- assert!(
- colors::strip_ansi_codes(super::printer::format(entries).as_str())
- .contains("interface Reader extends Foo, Bar")
- );
-}
+ }]);
-#[tokio::test]
-async fn export_interface2() {
- let source_code = r#"
+ json_test!(export_interface2,
+ r#"
export interface TypedIface<T> {
something(): T
}
"#;
- let loader =
- TestLoader::new(vec![("test.ts".to_string(), source_code.to_string())]);
- let entries = DocParser::new(loader).parse("test.ts").await.unwrap();
- assert_eq!(entries.len(), 1);
- let entry = &entries[0];
- let expected_json = json!({
+ [{
"kind": "interface",
"name": "TypedIface",
"location": {
@@ -780,32 +1335,19 @@ export interface TypedIface<T> {
],
"properties": [],
"callSignatures": [],
+ "indexSignatures": [],
"typeParams": [
{ "name": "T" }
],
}
- });
- let actual = serde_json::to_value(entry).unwrap();
- assert_eq!(actual, expected_json);
-
- assert!(
- colors::strip_ansi_codes(super::printer::format(entries).as_str())
- .contains("interface TypedIface")
- );
-}
+ }]);
-#[tokio::test]
-async fn export_type_alias() {
- let source_code = r#"
+ json_test!(export_type_alias,
+ r#"
/** Array holding numbers */
export type NumberArray = Array<number>;
"#;
- let loader =
- TestLoader::new(vec![("test.ts".to_string(), source_code.to_string())]);
- let entries = DocParser::new(loader).parse("test.ts").await.unwrap();
- assert_eq!(entries.len(), 1);
- let entry = &entries[0];
- let expected_json = json!({
+ [{
"kind": "typeAlias",
"name": "NumberArray",
"location": {
@@ -831,76 +1373,10 @@ export type NumberArray = Array<number>;
}
}
}
- });
- let actual = serde_json::to_value(entry).unwrap();
- assert_eq!(actual, expected_json);
-
- assert!(
- colors::strip_ansi_codes(super::printer::format(entries).as_str())
- .contains("Array holding numbers")
- );
-}
-
-#[tokio::test]
-async fn export_enum() {
- let source_code = r#"
-/**
- * Some enum for good measure
- */
-export enum Hello {
- World = "world",
- Fizz = "fizz",
- Buzz = "buzz",
-}
- "#;
- let loader =
- TestLoader::new(vec![("test.ts".to_string(), source_code.to_string())]);
- let entries = DocParser::new(loader).parse("test.ts").await.unwrap();
- assert_eq!(entries.len(), 1);
- let entry = &entries[0];
- let expected_json = json!({
- "kind": "enum",
- "name": "Hello",
- "location": {
- "filename": "test.ts",
- "line": 5,
- "col": 0
- },
- "jsDoc": "Some enum for good measure",
- "enumDef": {
- "members": [
- {
- "name": "World"
- },
- {
- "name": "Fizz"
- },
- {
- "name": "Buzz"
- }
- ]
- }
- });
- let actual = serde_json::to_value(entry).unwrap();
- assert_eq!(actual, expected_json);
+ }]);
- assert!(colors::strip_ansi_codes(
- super::printer::format_details(entry.clone()).as_str()
- )
- .contains("World"));
- assert!(colors::strip_ansi_codes(
- super::printer::format(entries.clone()).as_str()
- )
- .contains("Some enum for good measure"));
- assert!(
- colors::strip_ansi_codes(super::printer::format(entries).as_str())
- .contains("enum Hello")
- );
-}
-
-#[tokio::test]
-async fn export_namespace() {
- let source_code = r#"
+ json_test!(export_namespace,
+ r#"
/** Namespace JSdoc */
export namespace RootNs {
export const a = "a";
@@ -915,12 +1391,7 @@ export namespace RootNs {
}
}
"#;
- let loader =
- TestLoader::new(vec![("test.ts".to_string(), source_code.to_string())]);
- let entries = DocParser::new(loader).parse("test.ts").await.unwrap();
- assert_eq!(entries.len(), 1);
- let entry = &entries[0];
- let expected_json = json!({
+ [{
"kind": "namespace",
"name": "RootNs",
"location": {
@@ -984,453 +1455,23 @@ export namespace RootNs {
}
]
}
- });
- let actual = serde_json::to_value(entry).unwrap();
- assert_eq!(actual, expected_json);
- assert!(
- colors::strip_ansi_codes(super::printer::format(entries).as_str())
- .contains("namespace RootNs")
- );
-}
-
-#[tokio::test]
-async fn declare_namespace() {
- let source_code = r#"
-/** Namespace JSdoc */
-declare namespace RootNs {
- declare const a = "a";
-
- /** Nested namespace JSDoc */
- declare namespace NestedNs {
- declare enum Foo {
- a = 1,
- b = 2,
- c = 3,
- }
- }
-}
- "#;
- let loader =
- TestLoader::new(vec![("test.ts".to_string(), source_code.to_string())]);
- let entries = DocParser::new(loader).parse("test.ts").await.unwrap();
- assert_eq!(entries.len(), 1);
- let entry = &entries[0];
- let expected_json = json!({
- "kind": "namespace",
- "name": "RootNs",
- "location": {
- "filename": "test.ts",
- "line": 3,
- "col": 0
- },
- "jsDoc": "Namespace JSdoc",
- "namespaceDef": {
- "elements": [
- {
- "kind": "variable",
- "name": "a",
- "location": {
- "filename": "test.ts",
- "line": 4,
- "col": 12
- },
- "jsDoc": null,
- "variableDef": {
- "tsType": null,
- "kind": "const"
- }
- },
- {
- "kind": "namespace",
- "name": "NestedNs",
- "location": {
- "filename": "test.ts",
- "line": 7,
- "col": 4
- },
- "jsDoc": "Nested namespace JSDoc",
- "namespaceDef": {
- "elements": [
- {
- "kind": "enum",
- "name": "Foo",
- "location": {
- "filename": "test.ts",
- "line": 8,
- "col": 6
- },
- "jsDoc": null,
- "enumDef": {
- "members": [
- {
- "name": "a"
- },
- {
- "name": "b"
- },
- {
- "name": "c"
- }
- ]
- }
- }
- ]
- }
- }
- ]
- }
- });
- let actual = serde_json::to_value(entry).unwrap();
- assert_eq!(actual, expected_json);
- assert!(
- colors::strip_ansi_codes(super::printer::format(entries).as_str())
- .contains("namespace RootNs")
- );
-}
-
-#[tokio::test]
-async fn export_default_fn() {
- let source_code = r#"
-export default function foo(a: number) {
- return a;
-}
- "#;
- let loader =
- TestLoader::new(vec![("test.ts".to_string(), source_code.to_string())]);
- let entries = DocParser::new(loader).parse("test.ts").await.unwrap();
- assert_eq!(entries.len(), 1);
- let entry = &entries[0];
- let expected_json = json!({
- "kind": "function",
- "name": "default",
- "location": {
- "filename": "test.ts",
- "line": 2,
- "col": 15
- },
- "jsDoc": null,
- "functionDef": {
- "params": [
- {
- "name": "a",
- "kind": "identifier",
- "optional": false,
- "tsType": {
- "keyword": "number",
- "kind": "keyword",
- "repr": "number",
- },
- }
- ],
- "typeParams": [],
- "returnType": null,
- "isAsync": false,
- "isGenerator": false
- }
- });
- let actual = serde_json::to_value(entry).unwrap();
- assert_eq!(actual, expected_json);
-
- assert!(
- colors::strip_ansi_codes(super::printer::format(entries).as_str())
- .contains("function default(a: number)")
- );
-}
-
-#[tokio::test]
-async fn export_default_class() {
- let source_code = r#"
-/** Class doc */
-export default class Foobar {
- /** Constructor js doc */
- constructor(name: string, private private2: number, protected protected2: number) {}
-}
-"#;
- let loader =
- TestLoader::new(vec![("test.ts".to_string(), source_code.to_string())]);
- let entries = DocParser::new(loader).parse("test.ts").await.unwrap();
- assert_eq!(entries.len(), 1);
- let expected_json = json!({
- "kind": "class",
- "name": "default",
- "location": {
- "filename": "test.ts",
- "line": 3,
- "col": 0
- },
- "jsDoc": "Class doc",
- "classDef": {
- "isAbstract": false,
- "extends": null,
- "implements": [],
- "typeParams": [],
- "constructors": [
- {
- "jsDoc": "Constructor js doc",
- "accessibility": null,
- "name": "constructor",
- "params": [
- {
- "name": "name",
- "kind": "identifier",
- "optional": false,
- "tsType": {
- "repr": "string",
- "kind": "keyword",
- "keyword": "string"
- }
- },
- {
- "name": "private2",
- "kind": "identifier",
- "optional": false,
- "tsType": {
- "repr": "number",
- "kind": "keyword",
- "keyword": "number"
- }
- },
- {
- "name": "protected2",
- "kind": "identifier",
- "optional": false,
- "tsType": {
- "repr": "number",
- "kind": "keyword",
- "keyword": "number"
- }
- }
- ],
- "location": {
- "filename": "test.ts",
- "line": 5,
- "col": 4
- }
- }
- ],
- "properties": [],
- "methods": []
- }
- });
- let entry = &entries[0];
- let actual = serde_json::to_value(entry).unwrap();
- assert_eq!(actual, expected_json);
-
- assert!(
- colors::strip_ansi_codes(super::printer::format(entries).as_str())
- .contains("class default")
- );
-}
-
-#[tokio::test]
-async fn export_default_interface() {
- let source_code = r#"
-/**
- * Interface js doc
- */
-export default interface Reader {
- /** Read n bytes */
- read?(buf: Uint8Array, something: unknown): Promise<number>
-}
- "#;
- let loader =
- TestLoader::new(vec![("test.ts".to_string(), source_code.to_string())]);
- let entries = DocParser::new(loader).parse("test.ts").await.unwrap();
- assert_eq!(entries.len(), 1);
- let entry = &entries[0];
- let expected_json = json!({
- "kind": "interface",
- "name": "default",
- "location": {
- "filename": "test.ts",
- "line": 5,
- "col": 0
- },
- "jsDoc": "Interface js doc",
- "interfaceDef": {
- "extends": [],
- "methods": [
- {
- "name": "read",
- "location": {
- "filename": "test.ts",
- "line": 7,
- "col": 4
- },
- "optional": true,
- "jsDoc": "Read n bytes",
- "params": [
- {
- "name": "buf",
- "kind": "identifier",
- "optional": false,
- "tsType": {
- "repr": "Uint8Array",
- "kind": "typeRef",
- "typeRef": {
- "typeParams": null,
- "typeName": "Uint8Array"
- }
- }
- },
- {
- "name": "something",
- "kind": "identifier",
- "optional": false,
- "tsType": {
- "repr": "unknown",
- "kind": "keyword",
- "keyword": "unknown"
- }
- }
- ],
- "typeParams": [],
- "returnType": {
- "repr": "Promise",
- "kind": "typeRef",
- "typeRef": {
- "typeParams": [
- {
- "repr": "number",
- "kind": "keyword",
- "keyword": "number"
- }
- ],
- "typeName": "Promise"
- }
- }
- }
- ],
- "properties": [],
- "callSignatures": [],
- "typeParams": [],
- }
- });
- let actual = serde_json::to_value(entry).unwrap();
- assert_eq!(actual, expected_json);
-
- assert!(
- colors::strip_ansi_codes(super::printer::format(entries).as_str())
- .contains("interface default")
- );
-}
+ }]);
-#[tokio::test]
-async fn optional_return_type() {
- let source_code = r#"
+ json_test!(optional_return_type,
+ r#"
export function foo(a: number) {
return a;
}
"#;
- let loader =
- TestLoader::new(vec![("test.ts".to_string(), source_code.to_string())]);
- let entries = DocParser::new(loader).parse("test.ts").await.unwrap();
- assert_eq!(entries.len(), 1);
- let entry = &entries[0];
- let expected_json = json!({
- "kind": "function",
- "name": "foo",
- "location": {
- "filename": "test.ts",
- "line": 2,
- "col": 2
- },
- "jsDoc": null,
- "functionDef": {
- "params": [
- {
- "name": "a",
- "kind": "identifier",
- "optional": false,
- "tsType": {
- "keyword": "number",
- "kind": "keyword",
- "repr": "number",
- },
- }
- ],
- "typeParams": [],
- "returnType": null,
- "isAsync": false,
- "isGenerator": false
- }
- });
- let actual = serde_json::to_value(entry).unwrap();
- assert_eq!(actual, expected_json);
-
- assert!(
- colors::strip_ansi_codes(super::printer::format(entries).as_str())
- .contains("function foo(a: number)")
- );
-}
-
-#[tokio::test]
-async fn reexports() {
- let nested_reexport_source_code = r#"
-/**
- * JSDoc for bar
- */
-export const bar = "bar";
-
-export default 42;
-"#;
- let reexport_source_code = r#"
-import { bar } from "./nested_reexport.ts";
-
-/**
- * JSDoc for const
- */
-export const foo = "foo";
-"#;
- let test_source_code = r#"
-export { default, foo as fooConst } from "./reexport.ts";
-
-/** JSDoc for function */
-export function fooFn(a: number) {
- return a;
-}
-"#;
- let loader = TestLoader::new(vec![
- ("file:///test.ts".to_string(), test_source_code.to_string()),
- (
- "file:///reexport.ts".to_string(),
- reexport_source_code.to_string(),
- ),
- (
- "file:///nested_reexport.ts".to_string(),
- nested_reexport_source_code.to_string(),
- ),
- ]);
- let entries = DocParser::new(loader)
- .parse_with_reexports("file:///test.ts")
- .await
- .unwrap();
- assert_eq!(entries.len(), 2);
-
- let expected_json = json!([
- {
- "kind": "variable",
- "name": "fooConst",
- "location": {
- "filename": "file:///reexport.ts",
- "line": 7,
- "col": 0
- },
- "jsDoc": "JSDoc for const",
- "variableDef": {
- "tsType": null,
- "kind": "const"
- }
- },
- {
+ [{
"kind": "function",
- "name": "fooFn",
+ "name": "foo",
"location": {
- "filename": "file:///test.ts",
- "line": 5,
- "col": 0
+ "filename": "test.ts",
+ "line": 2,
+ "col": 2
},
- "jsDoc": "JSDoc for function",
+ "jsDoc": null,
"functionDef": {
"params": [
{
@@ -1449,30 +1490,17 @@ export function fooFn(a: number) {
"isAsync": false,
"isGenerator": false
}
- }
- ]);
- let actual = serde_json::to_value(entries.clone()).unwrap();
- assert_eq!(actual, expected_json);
-
- assert!(
- colors::strip_ansi_codes(super::printer::format(entries).as_str())
- .contains("function fooFn(a: number)")
+ }]
);
-}
-#[tokio::test]
-async fn ts_lit_types() {
- let source_code = r#"
+ json_test!(ts_lit_types,
+ r#"
export type boolLit = false;
export type strLit = "text";
export type tplLit = `text`;
export type numLit = 5;
-"#;
- let loader =
- TestLoader::new(vec![("test.ts".to_string(), source_code.to_string())]);
- let entries = DocParser::new(loader).parse("test.ts").await.unwrap();
- let actual = serde_json::to_value(entries).unwrap();
- let expected_json = json!([
+ "#;
+ [
{
"kind": "typeAlias",
"name": "boolLit",
@@ -1555,70 +1583,491 @@ export type numLit = 5;
}
}
]);
- assert_eq!(actual, expected_json);
}
-#[tokio::test]
-async fn filter_nodes_by_name() {
- use super::find_nodes_by_name_recursively;
- let source_code = r#"
-export namespace Deno {
- export class Buffer {}
- export function test(options: object): void;
- export function test(name: string, fn: Function): void;
- export function test(name: string | object, fn?: Function): void {}
+mod printer {
+ use super::*;
+
+ contains_test!(abstract_class,
+ "export abstract class Class {}",
+ details;
+ "abstract class Class"
+ );
+
+ contains_test!(abstract_class_abstract_method,
+ r#"
+export abstract class Class {
+ abstract method() {}
}
+ "#,
+ details;
+ "abstract method()"
+ );
-export namespace Deno {
- export namespace Inner {
- export function a(): void {}
- export const b = 100;
- }
+ contains_test!(class_async_method,
+ r#"
+export class Class {
+ async amethod(v) {}
}
-"#;
- let loader =
- TestLoader::new(vec![("test.ts".to_string(), source_code.to_string())]);
- let entries = DocParser::new(loader).parse("test.ts").await.unwrap();
+ "#,
+ details;
+ "async amethod(v)"
+ );
- let found =
- find_nodes_by_name_recursively(entries.clone(), "Deno".to_string());
- assert_eq!(found.len(), 2);
- assert_eq!(found[0].name, "Deno".to_string());
- assert_eq!(found[1].name, "Deno".to_string());
+ contains_test!(class_constructor,
+ r#"
+export class Class {
+ constructor(a, b) {}
+}
+ "#,
+ details;
+ "constructor(a, b)"
+ );
- let found =
- find_nodes_by_name_recursively(entries.clone(), "Deno.test".to_string());
- assert_eq!(found.len(), 3);
- assert_eq!(found[0].name, "test".to_string());
- assert_eq!(found[1].name, "test".to_string());
- assert_eq!(found[2].name, "test".to_string());
+ const CLASS_SOURCE: &str = r#"
+export class C {
+ /** a doc */
+ a() {}
+ f: number;
+}
+ "#;
- let found =
- find_nodes_by_name_recursively(entries.clone(), "Deno.Inner.a".to_string());
- assert_eq!(found.len(), 1);
- assert_eq!(found[0].name, "a".to_string());
+ contains_test!(class_details,
+ CLASS_SOURCE,
+ details;
+ "class C",
+ "a()",
+ "f: number"
+ );
- let found =
- find_nodes_by_name_recursively(entries.clone(), "Deno.test.a".to_string());
- assert_eq!(found.len(), 0);
+ contains_test!(class_details_all_with_private,
+ r#"
+export class Class {
+ private pri() {}
+ protected pro() {}
+ public pub() {}
+}
+ "#,
+ details,
+ private;
+ "private pri()",
+ "protected pro()",
+ "pub()"
+ );
- let found = find_nodes_by_name_recursively(entries, "a.b.c".to_string());
- assert_eq!(found.len(), 0);
+ contains_test!(class_details_only_non_private_without_private,
+ r#"
+export class Class {
+ private pri() {}
+ protected pro() {}
+ public pub() {}
}
+ "#,
+ details;
+ "protected pro()",
+ "pub()"
+ );
-#[tokio::test]
-async fn generic_instantiated_with_tuple_type() {
- let source_code = r#"
+ contains_test!(class_declaration,
+ "export class Class {}";
+ "class Class"
+ );
+
+ contains_test!(class_extends,
+ "export class Class extends Object {}";
+ "class Class extends Object"
+ );
+
+ contains_test!(class_extends_implements,
+ "export class Class extends Object implements Iterator, Iterable {}";
+ "class Class extends Object implements Iterator, Iterable"
+ );
+
+ contains_test!(class_generic_extends_implements,
+ "export class Class<A, B> extends Map<A, B> implements Iterator<A>, Iterable<B> {}";
+ "class Class<A, B> extends Map<A, B> implements Iterator<A>, Iterable<B>"
+ );
+
+ contains_test!(class_getter_and_setter,
+ r#"
+export class Class {
+ get a(): void {}
+ set b(_v: void) {}
+}
+ "#,
+ details;
+ "get a(): void",
+ "set b(_v: void)"
+ );
+
+ contains_test!(class_index_signature,
+ r#"
+export class C {
+ [key: string]: number;
+}
+ "#,
+ details;
+ "[key: string]: number"
+ );
+
+ contains_test!(class_implements,
+ "export class Class implements Iterator {}";
+ "class Class implements Iterator"
+ );
+
+ contains_test!(class_implements2,
+ "export class Class implements Iterator, Iterable {}";
+ "class Class implements Iterator, Iterable"
+ );
+
+ contains_test!(class_method,
+ r#"
+export class Class {
+ method(v) {}
+}
+ "#,
+ details;
+ "method(v)"
+ );
+
+ contains_test!(class_property,
+ r#"
+export class Class {
+ someproperty: bool;
+ optproperty: bigint;
+}
+ "#,
+ details;
+ "someproperty: bool",
+ "optproperty: bigint"
+ );
+
+ contains_test!(class_readonly_index_signature,
+ r#"
+export class C {
+ readonly [key: string]: number;
+}
+ "#,
+ details;
+ "readonly [key: string]: number"
+ );
+
+ contains_test!(class_static_property,
+ r#"
+export class Class {
+ static property = "";
+}
+ "#,
+ details;
+ "static property"
+ );
+
+ contains_test!(class_summary,
+ CLASS_SOURCE;
+ "class C";
+ "a()",
+ "f: number"
+ );
+
+ contains_test!(class_readonly_property,
+ r#"
+export class Class {
+ readonly property = "";
+}
+ "#,
+ details;
+ "readonly property"
+ );
+
+ contains_test!(class_private_property,
+ r#"
+export class Class {
+ private property = "";
+}
+ "#,
+ details,
+ private;
+ "private property"
+ );
+
+ contains_test!(const_declaration,
+ "export const Const = 0;";
+ "const Const"
+ );
+
+ contains_test!(enum_declaration,
+ "export enum Enum {}";
+ "enum Enum"
+ );
+
+ const EXPORT_SOURCE: &str = r#"
+export function a() {}
+function b() {}
+export class C {}
+class D {}
+export interface E {}
+interface F {}
+export namespace G {}
+namespace H {}
+ "#;
+
+ contains_test!(exports_all_with_private,
+ EXPORT_SOURCE,
+ private;
+ "function a()",
+ "class C",
+ "interface E",
+ "namespace G",
+ "function b()",
+ "class D",
+ "interface F",
+ "namespace H"
+ );
+
+ contains_test!(exports_only_exports_without_private,
+ EXPORT_SOURCE;
+ "function a()",
+ "class C",
+ "interface E",
+ "namespace G";
+ "function b()",
+ "class D",
+ "interface F",
+ "namespace H"
+ );
+
+ contains_test!(function_async,
+ "export async function a() {}";
+ "async function a()"
+ );
+
+ contains_test!(function_array_deconstruction,
+ "export function f([a, b, ...c]) {}";
+ "function f([a, b, ...c])"
+ );
+
+ contains_test!(function_async_generator,
+ "export async function* ag() {}";
+ "async function* ag()"
+ );
+
+ contains_test!(function_declaration,
+ "export function fun() {}";
+ "function fun()"
+ );
+
+ contains_test!(function_generator,
+ "export function* g() {}";
+ "function* g()"
+ );
+
+ contains_test!(function_generic,
+ "export function add<T>(a: T, b: T) { return a + b; }";
+ "function add<T>(a: T, b: T)"
+ );
+
+ contains_test!(function_object_deconstruction,
+ "export function f({ a, b, ...c }) {}";
+ "function f({a, b, ...c})"
+ );
+
+ /* TODO(SyrupThinker) NYI
+ contains_test!(function_type_predicate,
+ r#"
+ export function isFish(pet: Fish | Bird): pet is Fish {
+ return (pet as Fish).swim !== undefined;
+ }
+ "#;
+ "pet is Fish"
+ );
+ */
+
+ contains_test!(generic_instantiated_with_tuple_type,
+ r#"
interface Generic<T> {}
export function f(): Generic<[string, number]> { return {}; }
- "#;
+ "#;
+ "Generic<[string, number]>"
+ );
- let loader =
- TestLoader::new(vec![("test.ts".to_string(), source_code.to_string())]);
- let entries = DocParser::new(loader).parse("test.ts").await.unwrap();
+ contains_test!(type_literal_declaration,
+ "export type T = {}";
+ "{ }"
+ );
- assert!(colors::strip_ansi_codes(
- crate::doc::printer::format(entries).as_str()
- )
- .contains("Generic<[string, number]>"))
+ contains_test!(type_literal_index_signature,
+ "export type T = { [key: string]: number; }";
+ "[key: string]: number"
+ );
+
+ contains_test!(type_literal_readonly_index_signature,
+ "export type T = { readonly [key: string]: number; }";
+ "readonly [key: string]: number"
+ );
+
+ contains_test!(interface_declaration,
+ "export interface Interface {}";
+ "interface Interface"
+ );
+
+ contains_test!(interface_extends,
+ "export interface Interface extends Iterator {}";
+ "interface Interface extends Iterator"
+ );
+
+ contains_test!(interface_extends2,
+ "export interface Interface extends Iterator, Iterable {}";
+ "interface Interface extends Iterator, Iterable"
+ );
+
+ contains_test!(interface_generic,
+ "export interface Interface<T> {}";
+ "interface Interface<T>"
+ );
+
+ contains_test!(interface_generic_extends,
+ "export interface Interface<V> extends Iterable<V> {}";
+ "interface Interface<V> extends Iterable<V>"
+ );
+
+ contains_test!(interface_index_signature,
+ r#"
+export interface Interface {
+ [index: number]: Interface;
+}
+ "#,
+ details;
+ "[index: number]: Interface"
+ );
+
+ contains_test!(interface_method,
+ r#"
+export interface I {
+ m(a, b);
+ mo?(c);
+}
+ "#,
+ details;
+ "m(a, b)",
+ "mo?(c)"
+ );
+
+ contains_test!(interface_property,
+ r#"
+export interface I {
+ p: string;
+ po?: number;
+}
+ "#,
+ details;
+ "p: string",
+ "po?: number"
+ );
+
+ contains_test!(interface_readonly_index_signature,
+ r#"
+export interface Interface {
+ readonly [index: number]: Interface;
+}
+ "#,
+ details;
+ "readonly [index: number]: Interface"
+ );
+
+ const JSDOC_SOURCE: &str = r#"
+/**
+ * A is a class
+ *
+ * Nothing more
+ */
+export class A {}
+/**
+ * B is an interface
+ *
+ * Should be
+ */
+export interface B {}
+/**
+ * C is a function
+ *
+ * Summarised
+ */
+export function C() {}
+ "#;
+
+ contains_test!(jsdoc_details,
+ JSDOC_SOURCE,
+ details;
+ "A is a class",
+ "B is an interface",
+ "C is a function",
+ "Nothing more",
+ "Should be",
+ "Summarised"
+ );
+
+ contains_test!(jsdoc_summary,
+ JSDOC_SOURCE;
+ "A is a class",
+ "B is an interface",
+ "C is a function";
+ "Nothing more",
+ "Should be",
+ "Summarised"
+ );
+
+ contains_test!(namespace_declaration,
+ "export namespace Namespace {}";
+ "namespace Namespace"
+ );
+
+ const NAMESPACE_SOURCE: &str = r#"
+export namespace Namespace {
+ /**
+ * Doc comment 1
+ *
+ * Details 1
+ */
+ export function a() {}
+ /**
+ * Doc comment 2
+ *
+ * Details 2
+ */
+ export class B {}
+}
+ "#;
+
+ contains_test!(namespace_details,
+ NAMESPACE_SOURCE,
+ details;
+ "namespace Namespace",
+ "function a()",
+ "class B",
+ "Doc comment 1",
+ "Doc comment 2";
+ "Details 1",
+ "Details 2"
+ );
+
+ contains_test!(namespace_summary,
+ NAMESPACE_SOURCE;
+ "namespace Namespace",
+ "function a()",
+ "class B",
+ "Doc comment 1",
+ "Doc comment 2";
+ "Details 1",
+ "Details 2"
+ );
+
+ contains_test!(type_alias,
+ "export type A = number";
+ "type A = number"
+ );
+
+ contains_test!(type_generic_alias,
+ "export type A<T> = T";
+ "type A<T> = T"
+ );
}