forked from phcode-dev/staging.phcode.dev
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathunittests.js
More file actions
1 lines (1 loc) · 12.1 KB
/
Copy pathunittests.js
File metadata and controls
1 lines (1 loc) · 12.1 KB
1
define(function(require,exports,module){const DocCommentHints=require("./main");describe("unit:DocCommentHints",function(){describe("_splitParams - top-level comma split",function(){const split=DocCommentHints._splitParams;it("splits simple params",function(){expect(split("a, b, c").map(s=>s.trim())).toEqual(["a","b","c"])}),it("ignores commas inside braces/brackets/parens/angles",function(){expect(split("a, {x, y}, b").map(s=>s.trim())).toEqual(["a","{x, y}","b"]),expect(split("a, f(b, c), d").map(s=>s.trim())).toEqual(["a","f(b, c)","d"]),expect(split("a: Map<string, number>, b").map(s=>s.trim())).toEqual(["a: Map<string, number>","b"])}),it("returns nothing for an empty list",function(){expect(split("")).toEqual([]),expect(split(" ")).toEqual([])})}),describe("_parseParam - name, type and optional",function(){const parse=DocCommentHints._parseParam;function nt(token,conv){return parse(token,conv||"first")}it("name-first: plain / typed / default / PHP / rest",function(){expect(nt("name")).toEqual({name:"name",type:null,optional:!1}),expect(nt("name: string")).toEqual({name:"name",type:"string",optional:!1}),expect(nt("count = 5")).toEqual({name:"count",type:null,optional:!1}),expect(nt("count: number = 1")).toEqual({name:"count",type:"number",optional:!1}),expect(nt("$user")).toEqual({name:"$user",type:null,optional:!1}),expect(nt("...rest: number[]")).toEqual({name:"rest",type:"number[]",optional:!1})}),it("marks optional params (`?`)",function(){expect(nt("a?: string")).toEqual({name:"a",type:"string",optional:!0})}),it("extracts complex types without breaking on , : => or strings",function(){expect(nt("items: Array<Map<string, number>>").type).toBe("Array<Map<string, number>>"),expect(nt("cb: (x: number, y: string) => void").type).toBe("(x: number, y: string) => void"),expect(nt("opts: { a: number; b: string }").type).toBe("{ a: number; b: string }"),expect(nt("t: [number, string]").type).toBe("[number, string]"),expect(nt('mode: "on" | "off"').type).toBe('"on" | "off"'),expect(nt("x: T extends U ? A : B").type).toBe("T extends U ? A : B"),expect(nt("cb: () => void = () => {}").type).toBe("() => void")}),it("falls back to a null type on an unbalanced/garbled annotation",function(){expect(nt("a: Array<string").type).toBeNull()}),it("type-first (C/Java): trailing identifier is the name, no signature type",function(){expect(nt("int x","last")).toEqual({name:"x",type:null,optional:!1}),expect(nt("const char *ptr","last")).toEqual({name:"ptr",type:null,optional:!1})})}),describe("_validType",function(){const valid=DocCommentHints._validType;it("accepts balanced complex types",function(){["number","Array<Map<string, number>>","(x: T) => void","{ a: number }","[a, b]",'"on" | "off"'].forEach(t=>expect(valid(t)).toBe(!0))}),it("rejects empty or unbalanced types",function(){[""," ","Array<string","(x => void","}{"].forEach(t=>expect(valid(t)).toBe(!1))})}),describe("_parseSignature",function(){const parse=DocCommentHints._parseSignature,names=sig=>sig.params.map(p=>p.name),types=sig=>sig.params.map(p=>p.type);it("parses a plain JS function (name-first, returns, untyped)",function(){const sig=parse("function add(a, b) {","first");expect(names(sig)).toEqual(["a","b"]),expect(types(sig)).toEqual([null,null]),expect(sig.isClass).toBe(!1),expect(sig.hasReturn).toBe(!0)}),it("extracts param + return types from a typed TS function",function(){const sig=parse("function f(name: string, count: number = 1): boolean {","first");expect(names(sig)).toEqual(["name","count"]),expect(types(sig)).toEqual(["string","number"]),expect(sig.returnType).toBe("boolean"),expect(sig.hasReturn).toBe(!0)}),it("handles a complex TS arrow (generic, function-type param, generic return)",function(){const sig=parse("const f = (items: T[], cb: (x: T) => void): Promise<number> => {","first");expect(names(sig)).toEqual(["items","cb"]),expect(types(sig)).toEqual(["T[]","(x: T) => void"]),expect(sig.returnType).toBe("Promise<number>")}),it("treats an explicit void return as no @returns",function(){expect(parse("function log(msg: string): void {","first").hasReturn).toBe(!1)}),it("skips self in a Python method",function(){expect(names(parse("def greet(self, name):","first"))).toEqual(["name"])}),it("parses a Java method type-first (names only, no signature type)",function(){const sig=parse("public int sum(int a, String b) {","last");expect(names(sig)).toEqual(["a","b"]),expect(types(sig)).toEqual([null,null]),expect(sig.hasReturn).toBe(!0)}),it("parses an arrow function",function(){expect(names(parse("const mul = (a, b) => {","first"))).toEqual(["a","b"])}),it("handles a rest parameter",function(){expect(names(parse("function f(a, ...rest) {","first"))).toEqual(["a","rest"])}),it("reports a class with no params/return",function(){const sig=parse("export class Bar extends Base {","first");expect(sig.isClass).toBe(!0),expect(names(sig)).toEqual([]),expect(sig.hasReturn).toBe(!1)}),it("treats a constructor as returning nothing",function(){const sig=parse("constructor(x, y) {","first");expect(names(sig)).toEqual(["x","y"]),expect(sig.hasReturn).toBe(!1)}),it("handles an empty parameter list",function(){expect(names(parse("function noop() {","first"))).toEqual([])}),it("flags isDeclaration only for real declarations (gates the partial /, /* triggers)",function(){expect(parse("function add(a, b) {","first").isDeclaration).toBe(!0),expect(parse("class Bar {","first").isDeclaration).toBe(!0),expect(parse("const x = 5;","first").isDeclaration).toBe(!1),expect(parse("return total;","first").isDeclaration).toBe(!1)})}),describe("_buildSnippet",function(){const build=DocCommentHints._buildSnippet,P=(...names)=>names.map(n=>({name:n}));function expectNoTrailingWhitespace(snippet){snippet.split("\n").forEach(function(line){expect(line).toBe(line.replace(/\s+$/,""))})}it("builds a JSDoc skeleton with {*} for untyped params and no trailing whitespace",function(){const snip=build("jsdoc",{params:P("a","b"),isClass:!1,hasReturn:!0},"");expect(snip.indexOf("/**")).toBe(0),expect(snip).toContain("${1:"),expect(snip).toContain("@param {${2:*}} a"),expect(snip).toContain("@param {${3:*}} b"),expect(snip).toContain("@returns {${4:*}}"),expect(snip.trim().slice(-2)).toBe("*/"),expectNoTrailingWhitespace(snip)}),it("fills the real type as the {type} tabstop, [name] for optional, typed @returns",function(){const sig={params:[{name:"a",type:"number"},{name:"b",type:"Array<string>"},{name:"c",type:"T",optional:!0}],returnType:"boolean",isClass:!1,hasReturn:!0},snip=build("jsdoc",sig,"");expect(snip).toContain("@param {${2:number}} a"),expect(snip).toContain("@param {${3:Array<string>}} b"),expect(snip).toContain("@param {${4:T}} [c]"),expect(snip).toContain("@returns {${5:boolean}}"),expectNoTrailingWhitespace(snip)}),it("omits @param/@returns for a class",function(){const snip=build("jsdoc",{params:[],isClass:!0,hasReturn:!1},"");expect(snip).toContain("${1:"),expect(snip).not.toContain("@param"),expect(snip).not.toContain("@returns"),expectNoTrailingWhitespace(snip)}),it("indents continuation lines",function(){const snip=build("jsdoc",{params:P("a"),isClass:!1,hasReturn:!1}," ");expect(snip).toContain("\n * @param")}),it("escapes $ in a PHP-style parameter name",function(){const snip=build("jsdoc",{params:P("$user"),isClass:!1,hasReturn:!1},"");expect(snip).toContain("\\$user")}),it("builds a PHPDoc skeleton (type before the $name, @return, no braces)",function(){const snip=build("phpdoc",{params:P("$a","$b"),isClass:!1,hasReturn:!0},"");expect(snip).toContain("@param ${2:mixed} \\$a"),expect(snip).toContain("@param ${3:mixed} \\$b"),expect(snip).toContain("@return ${4:mixed}"),expect(snip).not.toContain("@param {"),expectNoTrailingWhitespace(snip)}),it("builds a Javadoc/Doxygen skeleton (no {type} braces, singular @return)",function(){const snip=build("tagdoc",{params:P("a","b"),isClass:!1,hasReturn:!0},"");expect(snip).toContain("@param a"),expect(snip).toContain("@param b"),expect(snip).toContain("@return"),expect(snip).not.toContain("@returns"),expect(snip).not.toContain("@param {"),expectNoTrailingWhitespace(snip)}),it("builds a TypeScript (tsdoc) skeleton - @param name / @returns, NO {type} braces",function(){const snip=build("tsdoc",{params:P("a","b"),isClass:!1,hasReturn:!0},"");expect(snip).toContain("@param a"),expect(snip).toContain("@param b"),expect(snip).toContain("@returns"),expect(snip).not.toContain("@param {"),expect(snip).not.toContain("{*}"),expectNoTrailingWhitespace(snip)}),it("builds a TypeScript skeleton (no {type} braces, @returns) - types live in the signature",function(){const snip=build("tsdoc",{params:P("a","b"),isClass:!1,hasReturn:!0},"");expect(snip).toContain("@param a"),expect(snip).toContain("@param b"),expect(snip).toContain("@returns"),expect(snip).not.toContain("@param {"),expect(snip).not.toContain("{*}"),expectNoTrailingWhitespace(snip)}),it("builds a Python docstring with Args/Returns and no trailing whitespace",function(){const snip=build("pydoc",{params:P("name"),isClass:!1,hasReturnType:!0}," ");expect(snip.indexOf('"""')).toBe(0),expect(snip).toContain("Args:"),expect(snip).toContain("name: ${2:"),expect(snip).toContain("Returns:"),expect(snip).not.toContain("@param"),expectNoTrailingWhitespace(snip)}),it("Python omits Returns without an explicit return annotation",function(){const snip=build("pydoc",{params:P("name"),isClass:!1,hasReturnType:!1}," ");expect(snip).toContain("Args:"),expect(snip).not.toContain("Returns:")})}),describe("provider, per language",function(){const SpecRunnerUtils=brackets.getModule("spec/SpecRunnerUtils"),Strings=brackets.getModule("strings"),TabstopManager=brackets.getModule("editor/TabstopManager");let mockDoc=null;function run(langId,content,line,ch){const mock=SpecRunnerUtils.createMockEditor(content,langId);mockDoc=mock.doc,mock.editor.setCursorPos(line,ch);const provider=new DocCommentHints._Provider,has=provider.hasHints(mock.editor,null),hints=has?provider.getHints():null,label=hints&&hints.hints[0].text();return has&&provider.insertHint(),{has:has,label:label,text:mock.doc.getText()}}afterEach(function(){TabstopManager.hasActiveSession&&TabstopManager.hasActiveSession()&&TabstopManager.endSession(),mockDoc&&(SpecRunnerUtils.destroyMockEditor(mockDoc),mockDoc=null)});const CASES=[{id:"javascript",content:"/**\nfunction f(a, b) {\n}\n",line:0,ch:3,label:"DOC_COMMENT_ADD_JSDOC",has:["@param {*} a","@param {*} b","@returns {*}"],absent:[]},{id:"typescript",content:"/**\nfunction f(a: number, b: string): boolean {\n}\n",line:0,ch:3,label:"DOC_COMMENT_ADD_JSDOC",has:["@param a","@param b","@returns"],absent:["@param {","{number}","{*}"]},{id:"php",content:"<?php\n/**\nfunction f($a, $b) {\n}\n",line:1,ch:3,label:"DOC_COMMENT_ADD_PHPDOC",has:["@param mixed $a","@return mixed"],absent:["{*}"]},{id:"java",content:"class T {\n /**\n int f(int a, int b) {\n }\n}\n",line:1,ch:7,label:"DOC_COMMENT_ADD_JAVADOC",has:["@param a","@param b","@return"],absent:["{*}","@returns"]},{id:"c",content:"/**\nint f(int a) {\n}\n",line:0,ch:3,label:"DOC_COMMENT_ADD_DOXYGEN",has:["@param a","@return"],absent:["{*}","@returns"]},{id:"cpp",content:"/**\nint f(int a) {\n}\n",line:0,ch:3,label:"DOC_COMMENT_ADD_DOXYGEN",has:["@param a","@return"],absent:["{*}","@returns"]},{id:"python",content:'def f(a, b) -> int:\n """\n',line:1,ch:7,label:"DOC_COMMENT_ADD_DOCSTRING",has:["Args:","a:","Returns:"],absent:["@param","{*}"]}];CASES.forEach(function(tc){it("offers and inserts the right doc comment for "+tc.id,function(){const r=run(tc.id,tc.content,tc.line,tc.ch);expect(r.has).toBe(!0),expect(r.label).toContain(Strings[tc.label]),tc.has.forEach(function(frag){expect(r.text).toContain(frag)}),tc.absent.forEach(function(frag){expect(r.text).not.toContain(frag)})})}),it("does NOT fire for an unsupported language (css)",function(){expect(run("css","/**\n.x { color: red; }\n",0,3).has).toBe(!1)}),it("Python adds no Returns when the def has no return annotation",function(){const r=run("python",'def f(a):\n """\n',1,7);expect(r.has).toBe(!0),expect(r.text).toContain("Args:"),expect(r.text).not.toContain("Returns:")}),it('Python fires on the auto-closed "" state (caret between the quotes)',function(){const r=run("python",'def f(a):\n ""\n',1,5);expect(r.has).toBe(!0),expect(r.text).toContain('"""')})})}),require("./integration-tests")});