├── index.test.js ├── .eslintrc ├── .editorconfig ├── CHANGELOG.md ├── package.json ├── LICENSE ├── .gitignore ├── index.js ├── lib ├── queries.js └── client.js ├── CODE_OF_CONDUCT.md ├── README.md └── test └── client.test.js /index.test.js: -------------------------------------------------------------------------------- 1 | test("passes", () => { 2 | expect(true).toBe(true); 3 | }); 4 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "prettier", 3 | "parserOptions": { 4 | "ecmaVersion": 8 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | 10 | [*.md] 11 | trim_trailing_whitespace = false 12 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | This project adheres to [Semantic Versioning](https://semver.org/) 4 | 5 | ## [Unreleased] 6 | 7 | ## [0.1.2] - 2018-10-20 8 | ### Changed 9 | - Add Overview to README 10 | - Add FakerQL examples to README 11 | - Fix: minor code modernization 12 | 13 | ## [0.1.1] - 2018-10-19 14 | ### Changed 15 | - Fix: modernize code 16 | 17 | ## 0.1.0 - 2018-10-19 18 | ### Initial Release 19 | 20 | [Unreleased]: https://github.com/hoop33/gqall/compare/v0.1.2...HEAD 21 | [0.1.2]: https://github.com/hoop33/gqall/compare/v0.1.1...v0.1.2 22 | [0.1.1]: https://github.com/hoop33/gqall/compare/v0.1.0...v0.1.1 23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gqall", 3 | "version": "0.1.2", 4 | "description": "A CLI for running a GraphQL query and requesting all fields", 5 | "main": "index.js", 6 | "bin": { 7 | "gqall": "./index.js" 8 | }, 9 | "scripts": { 10 | "posttest": "eslint .", 11 | "test": "jest" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/hoop33/gqall.git" 16 | }, 17 | "keywords": [ 18 | "graphql" 19 | ], 20 | "author": "Rob Warner (https://grailbox.com)", 21 | "contributors": [ 22 | "Robert McGuinness ", 23 | "Tyson Warner " 24 | ], 25 | "license": "MIT", 26 | "bugs": { 27 | "url": "https://github.com/hoop33/gqall/issues" 28 | }, 29 | "homepage": "https://github.com/hoop33/gqall#readme", 30 | "dependencies": { 31 | "graphql-request": "^1.8.2", 32 | "yargs": "^12.0.2" 33 | }, 34 | "devDependencies": { 35 | "eslint": "^5.7.0", 36 | "eslint-config-prettier": "^3.1.0", 37 | "jest": "^23.6.0" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | The MIT License (MIT) 3 | Copyright © 2018 Rob Warner 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the “Software”), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/node 3 | 4 | ### Node ### 5 | # Logs 6 | logs 7 | *.log 8 | npm-debug.log* 9 | yarn-debug.log* 10 | yarn-error.log* 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | 24 | # nyc test coverage 25 | .nyc_output 26 | 27 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 28 | .grunt 29 | 30 | # Bower dependency directory (https://bower.io/) 31 | bower_components 32 | 33 | # node-waf configuration 34 | .lock-wscript 35 | 36 | # Compiled binary addons (https://nodejs.org/api/addons.html) 37 | build/Release 38 | 39 | # Dependency directories 40 | node_modules/ 41 | jspm_packages/ 42 | 43 | # TypeScript v1 declaration files 44 | typings/ 45 | 46 | # Optional npm cache directory 47 | .npm 48 | 49 | # Optional eslint cache 50 | .eslintcache 51 | 52 | # Optional REPL history 53 | .node_repl_history 54 | 55 | # Output of 'npm pack' 56 | *.tgz 57 | 58 | # Yarn Integrity file 59 | .yarn-integrity 60 | 61 | # dotenv environment variables file 62 | .env 63 | 64 | # parcel-bundler cache (https://parceljs.org/) 65 | .cache 66 | 67 | # next.js build output 68 | .next 69 | 70 | # nuxt.js build output 71 | .nuxt 72 | 73 | # vuepress build output 74 | .vuepress/dist 75 | 76 | # Serverless directories 77 | .serverless 78 | 79 | 80 | # End of https://www.gitignore.io/api/node 81 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const yargs = require("yargs"); 4 | const Client = require("./lib/client"); 5 | 6 | yargs 7 | .usage( 8 | "$0 ", 9 | "run a GraphQL query", 10 | yargs => { 11 | yargs.positional("url", { 12 | describe: "the GraphQL endpoint", 13 | type: "string" 14 | }); 15 | yargs.positional("query", { 16 | describe: "the GraphQL query", 17 | type: "string" 18 | }); 19 | }, 20 | yargs => { 21 | const c = new Client(yargs.url, yargs.query); 22 | c.setVerbose(yargs.verbose); 23 | yargs.header && 24 | yargs.header.forEach(header => { 25 | try { 26 | c.addHeader(header); 27 | } catch (err) { 28 | console.log(err); 29 | } 30 | }); 31 | c.run() 32 | .then(response => { 33 | console.log(JSON.stringify(response, undefined, 2)); 34 | }) 35 | .catch(err => { 36 | console.log(err); 37 | }); 38 | } 39 | ) 40 | .alias("h", "help") 41 | .alias("v", "version") 42 | .option("V", { 43 | alias: "verbose", 44 | default: false, 45 | type: "boolean", 46 | describe: "verbose mode" 47 | }) 48 | .option("H", { 49 | alias: "header", 50 | type: "array", 51 | describe: "HTTP header in key:value format" 52 | }) 53 | .wrap(yargs.terminalWidth()) 54 | .strict() 55 | .showHelpOnFail(true).argv; 56 | 57 | process.on("unhandledRejection", error => { 58 | console.log(error.stack); // eslint-disable-line 59 | }); 60 | -------------------------------------------------------------------------------- /lib/queries.js: -------------------------------------------------------------------------------- 1 | const queries = { 2 | schemaQuery: `query IntrospectionQuery { 3 | __schema { 4 | queryType { 5 | name 6 | } 7 | mutationType { 8 | name 9 | } 10 | subscriptionType { 11 | name 12 | } 13 | types { 14 | ...FullType 15 | } 16 | directives { 17 | name 18 | description 19 | locations 20 | args { 21 | ...InputValue 22 | } 23 | } 24 | } 25 | } 26 | 27 | fragment FullType on __Type { 28 | kind 29 | name 30 | description 31 | fields(includeDeprecated: true) { 32 | name 33 | description 34 | args { 35 | ...InputValue 36 | } 37 | type { 38 | ...TypeRef 39 | } 40 | isDeprecated 41 | deprecationReason 42 | } 43 | inputFields { 44 | ...InputValue 45 | } 46 | interfaces { 47 | ...TypeRef 48 | } 49 | enumValues(includeDeprecated: true) { 50 | name 51 | description 52 | isDeprecated 53 | deprecationReason 54 | } 55 | possibleTypes { 56 | ...TypeRef 57 | } 58 | } 59 | 60 | fragment InputValue on __InputValue { 61 | name 62 | description 63 | type { 64 | ...TypeRef 65 | } 66 | defaultValue 67 | } 68 | 69 | fragment TypeRef on __Type { 70 | kind 71 | name 72 | ofType { 73 | kind 74 | name 75 | ofType { 76 | kind 77 | name 78 | ofType { 79 | kind 80 | name 81 | ofType { 82 | kind 83 | name 84 | ofType { 85 | kind 86 | name 87 | ofType { 88 | kind 89 | name 90 | ofType { 91 | kind 92 | name 93 | } 94 | } 95 | } 96 | } 97 | } 98 | } 99 | } 100 | }` 101 | }; 102 | 103 | module.exports = queries; 104 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at hoop33@gmail.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at [http://contributor-covenant.org/version/1/4][version] 72 | 73 | [homepage]: http://contributor-covenant.org 74 | [version]: http://contributor-covenant.org/version/1/4/ 75 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gqall 2 | 3 | > A CLI for running a GraphQL query and requesting all fields 4 | 5 | [![License](https://img.shields.io/npm/l/gqall.svg)](https://github.com/hoop33/gqall/blob/master/package.json) 6 | 7 | ## Overview 8 | 9 | A common GraphQL question is how to easily request *all* fields to be returned -- something like: 10 | 11 | ``` 12 | query { 13 | hero { 14 | * 15 | } 16 | } 17 | ``` 18 | 19 | Here's an example of the question, and why no way to do that exists: 20 | 21 | During development and testing, however, such functionality would be useful. Imagine, for example, you have a GraphQL mutation to update a complex entity. To test that mutation, you want to grab some data, make some changes, and then call the mutation with the updated data. Even with the code completion of something like GraphiQL, this can be kind of painful to type in your query to get the entire record to update. 22 | 23 | That's why I've created `gqall`, a CLI that lets you specify the GraphQL endpoint and just the name of the query, and it will indeed return all fields. This should ease development and testing your GraphQL mutations! 24 | 25 | ## Installation 26 | 27 | ```sh 28 | $ npm install gqall 29 | ``` 30 | 31 | ## Usage 32 | 33 | `gqall` takes two parameters: 34 | 35 | 1. The URL of your GraphQL endpoint 36 | 2. The GraphQL query to run (just the name and the parameters -- *not* the fields to return!) 37 | 38 | It then prints to standard out the results of the query, including all the fields that you didn't have to specify. 39 | 40 | ### Examples 41 | 42 | These are some working examples, thanks to [FakerQL](https://medium.com/@notrab/fakerql-is-ultimate-graphql-endpoint-for-fake-data-bd83f4cd6ad1): 43 | 44 | ```sh 45 | $ gqall https://fakerql.com/graphql "allPosts(count:1)" 46 | { 47 | "allPosts": [ 48 | { 49 | "id": "cjngkb52m00ta28100t8qvier", 50 | "title": "Table", 51 | "body": "Et deleniti animi. Possimus natus vero quisquam omnis deleniti.", 52 | "published": false, 53 | "createdAt": "Tue Mar 13 2018 20:21:15 GMT+0000 (UTC)", 54 | "author": { 55 | "id": "cjngkb52o00tb28100b0rtxig", 56 | "firstName": "Anabel", 57 | "lastName": "Klocko", 58 | "email": "Jimmie78@yahoo.com", 59 | "avatar": "https://s3.amazonaws.com/uifaces/faces/twitter/mattlat/128.jpg" 60 | } 61 | } 62 | ] 63 | } 64 | ``` 65 | 66 | ```sh 67 | $ gqall https://fakerql.com/graphql 'User(id: "me")' 68 | { 69 | "User": { 70 | "id": "me", 71 | "firstName": "Jeffrey", 72 | "lastName": "Stroman", 73 | "email": "Alva46@hotmail.com", 74 | "avatar": "https://s3.amazonaws.com/uifaces/faces/twitter/salvafc/128.jpg" 75 | } 76 | } 77 | ``` 78 | 79 | You can also pass headers using the `-H/--header` flag. You can pass multiples. 80 | 81 | ```sh 82 | $ gqall -H "Authorization:Basic ..." https://example.com/graphql 'human(id: "1000")' 83 | { 84 | ... 85 | } 86 | ``` 87 | 88 | You can also get help by passing the `-h` flag. 89 | 90 | ## FAQ 91 | 92 | * How do you pronounce `gqall`? 93 | * Unless someone comes up with something better, "JEE - call" 94 | 95 | ## Contributing 96 | 97 | Please note that this project is released with a [Contributor Code of Conduct](http://contributor-covenant.org/). By participating in this project you agree to abide by its terms. See [CODE_OF_CONDUCT](CODE_OF_CONDUCT.md) file. 98 | 99 | Contributions are welcome! Please open pull requests. 100 | 101 | ## Credits 102 | 103 | `gqall` uses the following open source libraries -- thank you! 104 | 105 | * [yargs](http://yargs.js.org/) 106 | * [graphql-request](https://github.com/prisma/graphql-request) 107 | 108 | ## License 109 | 110 | Copyright © 2018 Rob Warner 111 | 112 | Licensed under the [MIT License](https://hoop33.mit-license.org/) 113 | -------------------------------------------------------------------------------- /lib/client.js: -------------------------------------------------------------------------------- 1 | const { GraphQLClient } = require("graphql-request"); 2 | const queries = require("./queries"); 3 | 4 | class Client { 5 | 6 | constructor(url, query) { 7 | this.url = url; 8 | this.query = query; 9 | } 10 | 11 | setVerbose(verbose) { 12 | this.verbose = verbose; 13 | }; 14 | 15 | addHeader(header) { 16 | if (header) { 17 | let parts = header.split(":", 2); 18 | if (parts.length !== 2) { 19 | throw "header not valid: " + header; 20 | } 21 | 22 | this.headers = this.headers || {}; 23 | this.headers[parts[0]] = parts[1]; 24 | } else { 25 | throw "header not valid"; 26 | } 27 | } 28 | 29 | async run() { 30 | this._log(`url: ${this.url}`); 31 | this._log(`query: ${this.query}`); 32 | 33 | const gqClient = new GraphQLClient(this.url, { 34 | headers: this.headers 35 | }); 36 | 37 | const data = await this._runQuery(gqClient, queries.schemaQuery); 38 | const schema = data ? data.__schema : undefined; 39 | if (!schema) throw "can't get schema"; 40 | 41 | const queryTypeName = schema.queryType.name; 42 | if (!queryTypeName) throw "can't get query type name"; 43 | this._log(`query type name: ${queryTypeName}`); 44 | 45 | const queryType = this._findTypeByName(schema.types, queryTypeName); 46 | if (!queryType) throw "can't get query type"; 47 | 48 | const rqQueryName = this._parseQueryName(); 49 | this._log(`query name: ${rqQueryName}`); 50 | 51 | const queryObject = this._findTypeByName(queryType.fields, rqQueryName); 52 | if (!queryObject) throw `can't find query ${rqQueryName}`; 53 | 54 | const responseType = this._findResponseType(queryObject.type); 55 | if (!responseType) throw `can't find response type for query ${rqQueryName}`; 56 | this._log(`response type: ${responseType}`); 57 | 58 | const responseObject = this._findTypeByName(schema.types, responseType); 59 | if (!responseObject) throw `can't find response for type ${responseType}`; 60 | this._log(`response description: ${responseObject.description}`); 61 | 62 | const q = this._buildQuery(schema, responseObject); 63 | if (!q) throw `can't build query for ${rqQueryName}`; 64 | this._log(`query: ${q}`); 65 | 66 | const response = await this._runQuery(gqClient, q); 67 | if (!response) throw `no response from query ${rqQueryName})`; 68 | return response; 69 | }; 70 | 71 | _findTypeByName(types, name) { 72 | if (types) { 73 | const found = types.filter(type => type.name === name); 74 | return found.length === 1 ? found[0] : undefined; 75 | } 76 | }; 77 | 78 | _findResponseType(type) { 79 | if (type) { 80 | if (type.name) return type.name; 81 | if (type.ofType) return this._findResponseType(type.ofType); 82 | } 83 | }; 84 | 85 | _findResponseKind(type) { 86 | if (type) { 87 | if (type.kind && type.kind !== "NON_NULL") return type.kind; 88 | if (type.ofType) return this._findResponseKind(type.ofType); 89 | } 90 | }; 91 | 92 | _buildQuery(schema, type) { 93 | return `query { ${this.query} ${this._buildFields(schema, type)}}`; 94 | }; 95 | 96 | _buildFields(schema, type) { 97 | let q = ""; 98 | if (type.fields) { 99 | q += "{ "; 100 | type.fields.forEach(field => { 101 | q += `${field.name} `; 102 | const kind = this._findResponseKind(field.type); 103 | switch (kind) { 104 | case "LIST": 105 | case "OBJECT": 106 | const lName = this._findResponseType(field.type); 107 | const lType = this._findTypeByName(schema.types, lName); 108 | q += this._buildFields(schema, lType); 109 | break; 110 | default: 111 | } 112 | }); 113 | q += "} "; 114 | } 115 | return q; 116 | }; 117 | 118 | async _runQuery(gqClient, query) { 119 | this._log("running query"); 120 | const data = await gqClient.request(query); 121 | return data; 122 | }; 123 | 124 | _parseQueryName() { 125 | return this.query.split("(", 1)[0]; 126 | }; 127 | 128 | _log(msg) { 129 | if (this.verbose) { 130 | console.log(msg); 131 | } 132 | }; 133 | } 134 | 135 | module.exports = Client; 136 | -------------------------------------------------------------------------------- /test/client.test.js: -------------------------------------------------------------------------------- 1 | const Client = require("../lib/client"); 2 | 3 | test("blank query should parse to blank name", () => { 4 | const c = new Client("", ""); 5 | expect(c._parseQueryName()).toBe(""); 6 | }); 7 | 8 | test("query with no parentheses should parse to itself", () => { 9 | const c = new Client("", "foo"); 10 | expect(c._parseQueryName()).toBe("foo"); 11 | }); 12 | 13 | test("query with parentheses should parse to part before paren", () => { 14 | const c = new Client("", 'foo(id: "bar")'); 15 | expect(c._parseQueryName()).toBe("foo"); 16 | }); 17 | 18 | test("verbose should default to false", () => { 19 | const c = new Client(); 20 | expect(c.verbose).toBeFalsy(); 21 | }); 22 | 23 | test("verbose should be false when set to false", () => { 24 | const c = new Client(); 25 | c.setVerbose(false); 26 | expect(c.verbose).toBeFalsy(); 27 | }); 28 | 29 | test("verbose should be true when set to true", () => { 30 | const c = new Client(); 31 | c.setVerbose(true); 32 | expect(c.verbose).toBeTruthy(); 33 | }); 34 | 35 | test("addHeader should throw when undefined", () => { 36 | const c = new Client(); 37 | expect(() => { 38 | c.addHeader(); 39 | }).toThrow("header not valid"); 40 | }); 41 | 42 | test("addHeader should throw when null", () => { 43 | const c = new Client(); 44 | expect(() => { 45 | c.addHeader(null); 46 | }).toThrow("header not valid"); 47 | }); 48 | 49 | test("addHeader should throw when empty", () => { 50 | const c = new Client(); 51 | expect(() => { 52 | c.addHeader(""); 53 | }).toThrow("header not valid"); 54 | }); 55 | 56 | test("addHeader should throw when no colons", () => { 57 | const c = new Client(); 58 | expect(() => { 59 | c.addHeader("foobar"); 60 | }).toThrow("header not valid"); 61 | }); 62 | 63 | test("addHeader should add header when properly formatted", () => { 64 | const c = new Client(); 65 | c.addHeader("foo:bar"); 66 | expect(c.headers["foo"]).toBe("bar"); 67 | }); 68 | 69 | test("findTypeByName should return undefined when types is undefined", () => { 70 | const c = new Client(); 71 | expect(c._findTypeByName(undefined, "foo")).toBeUndefined(); 72 | }); 73 | 74 | test("findTypeByName should return undefined when types is null", () => { 75 | const c = new Client(); 76 | expect(c._findTypeByName(null, "foo")).toBeUndefined(); 77 | }); 78 | 79 | test("findTypeByName should return undefined when types is empty", () => { 80 | const c = new Client(); 81 | expect(c._findTypeByName([], "foo")).toBeUndefined(); 82 | }); 83 | 84 | test("findTypeByName should return undefined when type has no name", () => { 85 | const c = new Client(); 86 | expect(c._findTypeByName([{}], "foo")).toBeUndefined(); 87 | }); 88 | 89 | test("findTypeByName should return undefined when type is not found", () => { 90 | const c = new Client(); 91 | expect( 92 | c._findTypeByName( 93 | [ 94 | { 95 | name: "bar" 96 | } 97 | ], 98 | "foo" 99 | ) 100 | ).toBeUndefined(); 101 | }); 102 | 103 | test("findTypeByName should return type when type is found", () => { 104 | const c = new Client(); 105 | expect( 106 | c._findTypeByName( 107 | [ 108 | { 109 | name: "foo" 110 | } 111 | ], 112 | "foo" 113 | ).name 114 | ).toBe("foo"); 115 | }); 116 | 117 | test("findTypeByName should return type and handle no-names when type is found", () => { 118 | const c = new Client(); 119 | expect( 120 | c._findTypeByName( 121 | [ 122 | {}, 123 | { 124 | name: "foo" 125 | } 126 | ], 127 | "foo" 128 | ).name 129 | ).toBe("foo"); 130 | }); 131 | 132 | test("findResponseType should return undefined when type is undefined", () => { 133 | const c = new Client(); 134 | expect(c._findResponseType()).toBeUndefined(); 135 | }); 136 | 137 | test("findResponseType should return undefined when type is null", () => { 138 | const c = new Client(); 139 | expect(c._findResponseType(null)).toBeUndefined(); 140 | }); 141 | 142 | test("findResponseType should return undefined when type is empty", () => { 143 | const c = new Client(); 144 | expect(c._findResponseType({})).toBeUndefined(); 145 | }); 146 | 147 | test("findResponseType should return undefined when type has no response type", () => { 148 | const c = new Client(); 149 | expect( 150 | c._findResponseType({ 151 | ofType: { 152 | ofType: { 153 | ofType: {} 154 | } 155 | } 156 | }) 157 | ).toBeUndefined(); 158 | }); 159 | 160 | test("findResponseType should return first name when type has name", () => { 161 | const c = new Client(); 162 | expect( 163 | c._findResponseType({ 164 | name: "foo", 165 | ofType: { 166 | ofType: { 167 | ofType: { 168 | name: "bar" 169 | } 170 | } 171 | } 172 | }) 173 | ).toBe("foo"); 174 | }); 175 | 176 | test("findResponseType should return name when type has name", () => { 177 | const c = new Client(); 178 | expect( 179 | c._findResponseType({ 180 | ofType: { 181 | ofType: { 182 | ofType: { 183 | name: "bar" 184 | } 185 | } 186 | } 187 | }) 188 | ).toBe("bar"); 189 | }); 190 | --------------------------------------------------------------------------------