├── .babelrc ├── .coveralls.yml ├── .eslintrc ├── .gitignore ├── .npmignore ├── .travis.yml ├── LICENSE ├── README.md ├── lib └── @binpar │ ├── jest-gql.js │ ├── jest-gql.js.map │ └── jest-gql.min.js ├── package-lock.json ├── package.json ├── src ├── createApolloClient.js └── gqlTest.js ├── test ├── __tests__ │ ├── allWorks.js │ ├── me.js │ ├── orderedPrevious.js │ └── validateCode.js ├── login.js └── me.js └── webpack.config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "env", 5 | { 6 | "targets": { 7 | "node": "6.11" 8 | } 9 | } 10 | ] 11 | ], 12 | "plugins": [ 13 | [ 14 | "transform-object-rest-spread", 15 | { 16 | "useBuiltIns": true 17 | } 18 | ], 19 | [ 20 | "babel-plugin-inline-import", 21 | { 22 | "extensions": [".json", ".gql", ".graphql"] 23 | } 24 | ], 25 | "transform-exponentiation-operator" 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /.coveralls.yml: -------------------------------------------------------------------------------- 1 | repo_token: dWNDofdcK47k8b1vLDoQlbo8OX3RW6O2k -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "airbnb" 4 | ], 5 | "rules": { 6 | "no-underscore-dangle": [ 7 | "error", 8 | { 9 | "allow": [ 10 | "_id" 11 | ] 12 | } 13 | ], 14 | "import/no-extraneous-dependencies": 0 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (http://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # Typescript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | .coverage -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | test 2 | src 3 | .babelrc 4 | .eslintrc 5 | webpack.config.js 6 | /lib/@binpar/jest-gql.js 7 | /lib/@binpar/jest-gql.js.map -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "6" 4 | script: npm run testCI -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 BinPar 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 all 13 | 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 THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GraphQL based tests for Jest [![Build Status](https://travis-ci.org/BinPar/jest-gql.svg?branch=master)](https://travis-ci.org/BinPar/jest-gql) [![Coverage Status](https://coveralls.io/repos/github/BinPar/jest-gql/badge.svg?branch=master)](https://coveralls.io/github/BinPar/jest-gql?branch=master) 2 | 3 | **jest-gql** is a minimalistic framework for GraphQL tests using jest and Apollo Client 4 | 5 | - [How to use](#how-to-use) 6 | - [Setup](#setup) 7 | - [Creating the tests](#creating-the-tests) 8 | - [Composed test example](#composed-test-example) 9 | - [Oauth token authentication](#oauth-token-authentication) 10 | - [Remarks](#remarks) 11 | - [Executing the tests](#executing-the-tests) 12 | 13 | ## How to use 14 | ### Setup 15 | 16 | ```bash 17 | npm install --save-dev @binpar/jest-gql react react-apollo react-dom graphql-tag jest apollo-client 18 | ``` 19 | 20 | > If you are using ```"jest": "^21.2.1"``` please update to ```"jest": "^22.2.2"``` an error with the 21.1.1 version in the async test system makes it incompatible with **jest-gql**. 21 | 22 | If you have need to setup babel and eslint we recommend to add this packages too (otherwise jump to [executing the tests](#executing-the-tests): 23 | 24 | ```bash 25 | npm install --save-dev babel-cli babel-core babel-loader babel-plugin-inline-import babel-plugin-transform-exponentiation-operator babel-plugin-transform-object-rest-spread babel-preset-env babel-preset-es2015 babel-register eslint eslint-plugin-import eslint-plugin-jsx-a11y 26 | ``` 27 | 28 | And create the **.babelrc** and **.eslintrc** in the application directory: 29 | 30 | #### .babelrc 31 | ```json 32 | { 33 | "presets": [ 34 | [ 35 | "env", 36 | { 37 | "targets": { 38 | "node": "6.11" 39 | } 40 | } 41 | ] 42 | ], 43 | "plugins": [ 44 | [ 45 | "transform-object-rest-spread", 46 | { 47 | "useBuiltIns": true 48 | } 49 | ], 50 | [ 51 | "babel-plugin-inline-import", 52 | { 53 | "extensions": [".json", ".gql", ".graphql"] 54 | } 55 | ], 56 | "transform-exponentiation-operator" 57 | ] 58 | } 59 | ``` 60 | 61 | #### .eslintrc 62 | ```json 63 | { 64 | "extends": [ 65 | "airbnb" 66 | ], 67 | "rules": { 68 | "no-underscore-dangle": [ 69 | "error", 70 | { 71 | "allow": [ 72 | "_id" 73 | ] 74 | } 75 | ], 76 | "import/no-extraneous-dependencies": 0 77 | } 78 | } 79 | ``` 80 | 81 | ### Creating the tests 82 | 83 | It is recommended to create a **folder** in the root of the project called ```test``` and a ```__tests__``` folder inside it. 84 | 85 | On the ```__tests__``` folder we will place the tests that will be executed by the framework. 86 | 87 | On the ```test``` folder we will place the tests that are going to be required as pre-requisites by our tests (like a login GraphQL mutation used in several tests but not executed by it shelf). 88 | 89 | As an example, by adding a file called ```allWorks.js``` to the tests ```__tests__``` folder we will setup a test: 90 | 91 | ```javascript 92 | import gql from 'graphql-tag'; 93 | import runGQLTest from '@binpar/jest-gql'; 94 | 95 | const test = { 96 | name: 'Get Works List', 97 | gql: gql` 98 | query ($catalogPrefix: String!){ 99 | getWorks(catalogPrefix:$catalogPrefix, limit:20) { 100 | id 101 | title 102 | } 103 | } 104 | `, 105 | vars: () => ({ catalogPrefix: 'AM18' }), 106 | result: res => ({ workId: res.getWorks[0].id }), 107 | test: data => !!data.workId, 108 | repeat: 2, 109 | endPoint: process.env.GQL_API_URL, 110 | }; 111 | 112 | runGQLTest(test); 113 | export default test; 114 | ``` 115 | 116 | The properties of the tests are: 117 | 118 | **previous**: Array with the reference of the tests that need to be executed before the current test (typically stored in the parent folder). 119 | 120 | **name**: The name of the test 121 | 122 | **gql**: The gql request to execute 123 | 124 | **vars**: The variables that we will used by the gql. The function will receive a data object containing the data of the previous tests (all the tests setup in the previous property) 125 | 126 | **result**: A function that will be executed after the GraphQL execution that will allow us to setup what information do we retrieve from the server response. 127 | 128 | **test**: A function that will be executed with the data generated by the result (and the previous test) and will return a **boolean** indicating the test result (**true**: success, **false**: fail). 129 | 130 | **endPoint**: URL of the GraphQL end point. 131 | 132 | **repeat**: The number of times that we want the test to be runned 133 | 134 | ### Composed test example 135 | 136 | ```test/login.js```: 137 | ```javascript 138 | import gql from 'graphql-tag'; 139 | 140 | const test = { 141 | name: 'Login Test', 142 | gql: gql` 143 | mutation($email: String!, $password: String!) { 144 | login(email: $email, password: $password) { 145 | error 146 | token 147 | } 148 | } 149 | `, 150 | vars: () => ({ email: 'voceses@email.com', password: 'voceses' }), 151 | result: data => ({ error: data.login.error, oAuthToken: data.login.token }), 152 | test: data => !data.error, 153 | }; 154 | 155 | export default test; 156 | ``` 157 | 158 | ```test/__tests__/me.js```: 159 | ```javascript 160 | import gql from 'graphql-tag'; 161 | import runGQLTest from '@binpar/jest-gql'; 162 | import login from '../login'; 163 | 164 | const test = { 165 | previous: [login], 166 | name: 'Query authenticated user data', 167 | gql: gql` 168 | query { 169 | me { 170 | id 171 | name 172 | email 173 | } 174 | } 175 | `, 176 | result: data => ({ userEmail: data.me.email }), 177 | test: data => data.userEmail === 'voceses@email.com', 178 | endPoint: process.env.GQL_API_URL, 179 | }; 180 | runGQLTest(test); 181 | export default test; 182 | ``` 183 | 184 | Notice that the test that will run is ```me.js```, but it will execute ```login.js``` as a pre-requisite that will be shared by many tests. 185 | 186 | ### Oauth token authentication 187 | 188 | In the previous example, in the ```login.js``` the token returned by the query is stored in the data in the **oAuthToken** property. 189 | 190 | This is a special property that will be used for the subsequent requests of this test in order to authenticate the user using **Oauth token authentication**. 191 | 192 | ### Remarks 193 | 194 | All the tests stored in ```__tests__``` will run in parallel, but the execution plan of one test (resulting of the recursive evaluation of the **previous** property will be executed in serial). 195 | 196 | Every test will use an unique ApolloClient and NetworkInterface during the execution. 197 | 198 | ### Executing the tests 199 | 200 | It is recommended to add this two lines to the **scripts** section of the **package.json**: 201 | 202 | ```json 203 | { 204 | "scripts": { 205 | "test": "GQL_API_URL='[your endpoint URL]' jest", 206 | "testDev": "GQL_API_URL='[your endpoint URL]' jest --watch" 207 | } 208 | } 209 | ``` 210 | 211 | The first one will allow us to use **npm run test** to run all the tests. 212 | 213 | The second one will allow us to use **npm run testDev** to execute the tests in interactive mode during the development. 214 | -------------------------------------------------------------------------------- /lib/@binpar/jest-gql.js: -------------------------------------------------------------------------------- 1 | (function webpackUniversalModuleDefinition(root, factory) { 2 | if(typeof exports === 'object' && typeof module === 'object') 3 | module.exports = factory(require("react-apollo")); 4 | else if(typeof define === 'function' && define.amd) 5 | define("@binpar/jest-gql", ["react-apollo"], factory); 6 | else if(typeof exports === 'object') 7 | exports["@binpar/jest-gql"] = factory(require("react-apollo")); 8 | else 9 | root["@binpar/jest-gql"] = factory(root["react-apollo"]); 10 | })(typeof self !== 'undefined' ? self : this, function(__WEBPACK_EXTERNAL_MODULE_3__) { 11 | return /******/ (function(modules) { // webpackBootstrap 12 | /******/ // The module cache 13 | /******/ var installedModules = {}; 14 | /******/ 15 | /******/ // The require function 16 | /******/ function __webpack_require__(moduleId) { 17 | /******/ 18 | /******/ // Check if module is in cache 19 | /******/ if(installedModules[moduleId]) { 20 | /******/ return installedModules[moduleId].exports; 21 | /******/ } 22 | /******/ // Create a new module (and put it into the cache) 23 | /******/ var module = installedModules[moduleId] = { 24 | /******/ i: moduleId, 25 | /******/ l: false, 26 | /******/ exports: {} 27 | /******/ }; 28 | /******/ 29 | /******/ // Execute the module function 30 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 31 | /******/ 32 | /******/ // Flag the module as loaded 33 | /******/ module.l = true; 34 | /******/ 35 | /******/ // Return the exports of the module 36 | /******/ return module.exports; 37 | /******/ } 38 | /******/ 39 | /******/ 40 | /******/ // expose the modules object (__webpack_modules__) 41 | /******/ __webpack_require__.m = modules; 42 | /******/ 43 | /******/ // expose the module cache 44 | /******/ __webpack_require__.c = installedModules; 45 | /******/ 46 | /******/ // define getter function for harmony exports 47 | /******/ __webpack_require__.d = function(exports, name, getter) { 48 | /******/ if(!__webpack_require__.o(exports, name)) { 49 | /******/ Object.defineProperty(exports, name, { 50 | /******/ configurable: false, 51 | /******/ enumerable: true, 52 | /******/ get: getter 53 | /******/ }); 54 | /******/ } 55 | /******/ }; 56 | /******/ 57 | /******/ // getDefaultExport function for compatibility with non-harmony modules 58 | /******/ __webpack_require__.n = function(module) { 59 | /******/ var getter = module && module.__esModule ? 60 | /******/ function getDefault() { return module['default']; } : 61 | /******/ function getModuleExports() { return module; }; 62 | /******/ __webpack_require__.d(getter, 'a', getter); 63 | /******/ return getter; 64 | /******/ }; 65 | /******/ 66 | /******/ // Object.prototype.hasOwnProperty.call 67 | /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; 68 | /******/ 69 | /******/ // __webpack_public_path__ 70 | /******/ __webpack_require__.p = ""; 71 | /******/ 72 | /******/ // Load entry module and return exports 73 | /******/ return __webpack_require__(__webpack_require__.s = 0); 74 | /******/ }) 75 | /************************************************************************/ 76 | /******/ ([ 77 | /* 0 */ 78 | /***/ (function(module, exports, __webpack_require__) { 79 | 80 | "use strict"; 81 | /* WEBPACK VAR INJECTION */(function(global) { 82 | 83 | Object.defineProperty(exports, "__esModule", { 84 | value: true 85 | }); 86 | 87 | var _createApolloClient = __webpack_require__(2); 88 | 89 | var _createApolloClient2 = _interopRequireDefault(_createApolloClient); 90 | 91 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 92 | 93 | function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } 94 | 95 | const { describe, test, expect } = global; 96 | 97 | const runGQLTest = (() => { 98 | var _ref = _asyncToGenerator(function* (testToRun) { 99 | describe('GraphQL Tests', _asyncToGenerator(function* () { 100 | let data = {}; 101 | const executeGQLCommand = (() => { 102 | var _ref3 = _asyncToGenerator(function* (executionPlan) { 103 | const apolloClient = (0, _createApolloClient2.default)(data); 104 | const testInfo = executionPlan.pop(); 105 | 106 | if (testInfo.gql.definitions.length !== 1) { 107 | throw Error('Only one GQL operation is allowed in a runGQLTest.'); 108 | } 109 | const definition = testInfo.gql.definitions[0]; 110 | let result = null; 111 | switch (definition.operation) { 112 | case 'mutation': 113 | { 114 | const params = { mutation: testInfo.gql }; 115 | if (testInfo.vars) { 116 | params.variables = yield testInfo.vars(data); 117 | } 118 | result = yield apolloClient.mutate(params); 119 | 120 | if (testInfo.result) { 121 | data = Object.assign({}, data, (yield testInfo.result(result.data))); 122 | } 123 | expect(testInfo.test(data)).toBe(true); 124 | break; 125 | } 126 | case 'query': 127 | { 128 | const params = { query: testInfo.gql }; 129 | if (testInfo.vars) { 130 | params.variables = yield testInfo.vars(data); 131 | } 132 | result = yield apolloClient.query(params); 133 | 134 | if (testInfo.result) { 135 | data = Object.assign({}, data, (yield testInfo.result(result.data))); 136 | } 137 | expect(testInfo.test(data)).toBe(true); 138 | break; 139 | } 140 | default: 141 | throw Error(`Unknown GQL operation ${definition.operation}.`); 142 | } 143 | 144 | if (executionPlan.length) yield executeGQLCommand(executionPlan); 145 | }); 146 | 147 | return function executeGQLCommand(_x2) { 148 | return _ref3.apply(this, arguments); 149 | }; 150 | })(); 151 | 152 | const getExecutionPlan = function (testInfo, executionPlan) { 153 | executionPlan.push(testInfo); 154 | if (testInfo.previous) { 155 | const prev = [...testInfo.previous]; 156 | while (prev.length) { 157 | getExecutionPlan(prev.pop(), executionPlan); 158 | } 159 | } 160 | }; 161 | 162 | const executeTest = (() => { 163 | var _ref4 = _asyncToGenerator(function* (testInfo, num) { 164 | const executionPlan = []; 165 | getExecutionPlan(testInfo, executionPlan); 166 | test(`${testInfo.name}${num !== undefined ? ` ${num}` : ''}`, _asyncToGenerator(function* () { 167 | expect.assertions(executionPlan.length); 168 | yield executeGQLCommand(executionPlan); 169 | })); 170 | }); 171 | 172 | return function executeTest(_x3, _x4) { 173 | return _ref4.apply(this, arguments); 174 | }; 175 | })(); 176 | data.endPoint = testToRun.endPoint; 177 | if (testToRun.repeat) { 178 | yield Promise.all(new Array(testToRun.repeat).fill(0).map(function (_, i) { 179 | return executeTest(testToRun, i + 1); 180 | })); 181 | } else { 182 | yield executeTest(testToRun); 183 | } 184 | return data; 185 | })); 186 | }); 187 | 188 | return function runGQLTest(_x) { 189 | return _ref.apply(this, arguments); 190 | }; 191 | })(); 192 | exports.default = runGQLTest; 193 | /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(1))) 194 | 195 | /***/ }), 196 | /* 1 */ 197 | /***/ (function(module, exports) { 198 | 199 | var g; 200 | 201 | // This works in non-strict mode 202 | g = (function() { 203 | return this; 204 | })(); 205 | 206 | try { 207 | // This works if eval is allowed (see CSP) 208 | g = g || Function("return this")() || (1,eval)("this"); 209 | } catch(e) { 210 | // This works if the window reference is available 211 | if(typeof window === "object") 212 | g = window; 213 | } 214 | 215 | // g can still be undefined, but nothing to do about it... 216 | // We return undefined, instead of nothing here, so it's 217 | // easier to handle this case. if(!global) { ...} 218 | 219 | module.exports = g; 220 | 221 | 222 | /***/ }), 223 | /* 2 */ 224 | /***/ (function(module, exports, __webpack_require__) { 225 | 226 | "use strict"; 227 | 228 | 229 | Object.defineProperty(exports, "__esModule", { 230 | value: true 231 | }); 232 | 233 | var _reactApollo = __webpack_require__(3); 234 | 235 | function create(data) { 236 | const networkInterface = (0, _reactApollo.createNetworkInterface)({ 237 | uri: data.endPoint 238 | }); 239 | const middleWares = [{ 240 | applyMiddleware(req, next) { 241 | const token = data.oAuthToken; 242 | if (token) { 243 | req.options.headers = {}; 244 | req.options.headers.authorization = `Bearer ${token}`; 245 | } 246 | next(); 247 | } 248 | }]; 249 | networkInterface.use(middleWares); 250 | const severNetworkInterface = (0, _reactApollo.createNetworkInterface)({ uri: data.endPoint }); 251 | severNetworkInterface.use(middleWares); 252 | return new _reactApollo.ApolloClient({ 253 | ssrMode: true, 254 | networkInterface: severNetworkInterface 255 | }); 256 | } 257 | 258 | exports.default = create; 259 | 260 | /***/ }), 261 | /* 3 */ 262 | /***/ (function(module, exports) { 263 | 264 | module.exports = __WEBPACK_EXTERNAL_MODULE_3__; 265 | 266 | /***/ }) 267 | /******/ ]); 268 | }); 269 | //# sourceMappingURL=jest-gql.js.map -------------------------------------------------------------------------------- /lib/@binpar/jest-gql.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["webpack:///webpack/universalModuleDefinition","webpack:///webpack/bootstrap 921935aa089d2f9d6213","webpack:///./src/gqlTest.js","webpack:///(webpack)/buildin/global.js","webpack:///./src/createApolloClient.js","webpack:///external {\"root\":\"react-apollo\",\"commonjs2\":\"react-apollo\",\"commonjs\":\"react-apollo\",\"amd\":\"react-apollo\",\"umd\":\"react-apollo\"}"],"names":["describe","test","expect","global","runGQLTest","testToRun","data","executeGQLCommand","executionPlan","apolloClient","testInfo","pop","gql","definitions","length","Error","definition","result","operation","params","mutation","vars","variables","mutate","toBe","query","getExecutionPlan","push","previous","prev","executeTest","num","name","undefined","assertions","endPoint","repeat","Promise","all","Array","fill","map","_","i","create","networkInterface","uri","middleWares","applyMiddleware","req","next","token","oAuthToken","options","headers","authorization","use","severNetworkInterface","ssrMode"],"mappings":"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","file":"@binpar/jest-gql.js","sourcesContent":["(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory(require(\"react-apollo\"));\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine(\"@binpar/jest-gql\", [\"react-apollo\"], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"@binpar/jest-gql\"] = factory(require(\"react-apollo\"));\n\telse\n\t\troot[\"@binpar/jest-gql\"] = factory(root[\"react-apollo\"]);\n})(typeof self !== 'undefined' ? self : this, function(__WEBPACK_EXTERNAL_MODULE_3__) {\nreturn \n\n\n// WEBPACK FOOTER //\n// webpack/universalModuleDefinition"," \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, {\n \t\t\t\tconfigurable: false,\n \t\t\t\tenumerable: true,\n \t\t\t\tget: getter\n \t\t\t});\n \t\t}\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 0);\n\n\n\n// WEBPACK FOOTER //\n// webpack/bootstrap 921935aa089d2f9d6213","import createApolloClient from './createApolloClient';\n\nconst { describe, test, expect } = global;\n\nconst runGQLTest = async (testToRun) => {\n describe('GraphQL Tests', async () => {\n let data = {};\n const executeGQLCommand = async (executionPlan) => {\n const apolloClient = createApolloClient(data);\n const testInfo = executionPlan.pop();\n\n if (testInfo.gql.definitions.length !== 1) {\n throw Error('Only one GQL operation is allowed in a runGQLTest.');\n }\n const definition = testInfo.gql.definitions[0];\n let result = null;\n switch (definition.operation) {\n case 'mutation': {\n const params = { mutation: testInfo.gql };\n if (testInfo.vars) {\n params.variables = await testInfo.vars(data);\n }\n result = await apolloClient.mutate(params);\n\n if (testInfo.result) {\n data = { ...data, ...(await testInfo.result(result.data)) };\n }\n expect(testInfo.test(data)).toBe(true);\n break;\n }\n case 'query': {\n const params = { query: testInfo.gql };\n if (testInfo.vars) {\n params.variables = await testInfo.vars(data);\n }\n result = await apolloClient.query(params);\n\n if (testInfo.result) {\n data = { ...data, ...(await testInfo.result(result.data)) };\n }\n expect(testInfo.test(data)).toBe(true);\n break;\n }\n default:\n throw Error(`Unknown GQL operation ${definition.operation}.`);\n }\n\n if (executionPlan.length) await executeGQLCommand(executionPlan);\n };\n\n const getExecutionPlan = (testInfo, executionPlan) => {\n executionPlan.push(testInfo);\n if (testInfo.previous) {\n const prev = [...testInfo.previous];\n while (prev.length) {\n getExecutionPlan(prev.pop(), executionPlan);\n }\n }\n };\n\n const executeTest = async (testInfo, num) => {\n const executionPlan = [];\n getExecutionPlan(testInfo, executionPlan);\n test(`${testInfo.name}${num !== undefined ? ` ${num}` : ''}`, async () => {\n expect.assertions(executionPlan.length);\n await executeGQLCommand(executionPlan);\n });\n };\n data.endPoint = testToRun.endPoint;\n if (testToRun.repeat) {\n await Promise.all(\n new Array(testToRun.repeat).fill(0).map((_, i) => executeTest(testToRun, i + 1)),\n );\n } else {\n await executeTest(testToRun);\n }\n return data;\n });\n};\nexport default runGQLTest;\n\n\n\n// WEBPACK FOOTER //\n// ./src/gqlTest.js","var g;\r\n\r\n// This works in non-strict mode\r\ng = (function() {\r\n\treturn this;\r\n})();\r\n\r\ntry {\r\n\t// This works if eval is allowed (see CSP)\r\n\tg = g || Function(\"return this\")() || (1,eval)(\"this\");\r\n} catch(e) {\r\n\t// This works if the window reference is available\r\n\tif(typeof window === \"object\")\r\n\t\tg = window;\r\n}\r\n\r\n// g can still be undefined, but nothing to do about it...\r\n// We return undefined, instead of nothing here, so it's\r\n// easier to handle this case. if(!global) { ...}\r\n\r\nmodule.exports = g;\r\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// (webpack)/buildin/global.js\n// module id = 1\n// module chunks = 0","import { ApolloClient, createNetworkInterface } from 'react-apollo';\n\nfunction create(data) {\n const networkInterface = createNetworkInterface({\n uri: data.endPoint,\n });\n const middleWares = [\n {\n applyMiddleware(req, next) {\n const token = data.oAuthToken;\n if (token) {\n req.options.headers = {};\n req.options.headers.authorization = `Bearer ${token}`;\n }\n next();\n },\n },\n ];\n networkInterface.use(middleWares);\n const severNetworkInterface = createNetworkInterface({ uri: data.endPoint });\n severNetworkInterface.use(middleWares);\n return new ApolloClient({\n ssrMode: true,\n networkInterface: severNetworkInterface,\n });\n}\n\nexport default create;\n\n\n\n// WEBPACK FOOTER //\n// ./src/createApolloClient.js","module.exports = __WEBPACK_EXTERNAL_MODULE_3__;\n\n\n//////////////////\n// WEBPACK FOOTER\n// external {\"root\":\"react-apollo\",\"commonjs2\":\"react-apollo\",\"commonjs\":\"react-apollo\",\"amd\":\"react-apollo\",\"umd\":\"react-apollo\"}\n// module id = 3\n// module chunks = 0"],"sourceRoot":""} -------------------------------------------------------------------------------- /lib/@binpar/jest-gql.min.js: -------------------------------------------------------------------------------- 1 | !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("react-apollo")):"function"==typeof define&&define.amd?define("@binpar/jest-gql",["react-apollo"],t):"object"==typeof exports?exports["@binpar/jest-gql"]=t(require("react-apollo")):e["@binpar/jest-gql"]=t(e["react-apollo"])}("undefined"!=typeof self?self:this,function(e){return function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}return n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{configurable:!1,enumerable:!0,get:r})},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=0)}([function(e,t,n){"use strict";(function(e){Object.defineProperty(t,"__esModule",{value:!0});var r,o=n(2),i=(r=o)&&r.__esModule?r:{default:r};function u(e){return function(){var t=e.apply(this,arguments);return new Promise(function(e,n){return function r(o,i){try{var u=t[o](i),a=u.value}catch(e){return void n(e)}if(!u.done)return Promise.resolve(a).then(function(e){r("next",e)},function(e){r("throw",e)});e(a)}("next")})}}const{describe:a,test:s,expect:l}=e,c=(f=u(function*(e){a("GraphQL Tests",u(function*(){let t={};const n=(r=u(function*(e){const r=(0,i.default)(t),o=e.pop();if(1!==o.gql.definitions.length)throw Error("Only one GQL operation is allowed in a runGQLTest.");const u=o.gql.definitions[0];let a=null;switch(u.operation){case"mutation":{const e={mutation:o.gql};o.vars&&(e.variables=yield o.vars(t)),a=yield r.mutate(e),o.result&&(t=Object.assign({},t,yield o.result(a.data))),l(o.test(t)).toBe(!0);break}case"query":{const e={query:o.gql};o.vars&&(e.variables=yield o.vars(t)),a=yield r.query(e),o.result&&(t=Object.assign({},t,yield o.result(a.data))),l(o.test(t)).toBe(!0);break}default:throw Error(`Unknown GQL operation ${u.operation}.`)}e.length&&(yield n(e))}),function(e){return r.apply(this,arguments)});var r;const o=function(e,t){if(t.push(e),e.previous){const n=[...e.previous];for(;n.length;)o(n.pop(),t)}},a=(c=u(function*(e,t){const r=[];o(e,r),s(`${e.name}${void 0!==t?` ${t}`:""}`,u(function*(){l.assertions(r.length),yield n(r)}))}),function(e,t){return c.apply(this,arguments)});var c;return t.endPoint=e.endPoint,e.repeat?yield Promise.all(new Array(e.repeat).fill(0).map(function(t,n){return a(e,n+1)})):yield a(e),t}))}),function(e){return f.apply(this,arguments)});var f;t.default=c}).call(t,n(1))},function(e,t){var n;n=function(){return this}();try{n=n||Function("return this")()||(0,eval)("this")}catch(e){"object"==typeof window&&(n=window)}e.exports=n},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(3);t.default=function(e){const t=(0,r.createNetworkInterface)({uri:e.endPoint}),n=[{applyMiddleware(t,n){const r=e.oAuthToken;r&&(t.options.headers={},t.options.headers.authorization=`Bearer ${r}`),n()}}];t.use(n);const o=(0,r.createNetworkInterface)({uri:e.endPoint});return o.use(n),new r.ApolloClient({ssrMode:!0,networkInterface:o})}},function(t,n){t.exports=e}])}); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@binpar/jest-gql", 3 | "version": "1.1.35", 4 | "description": "GraphQL based tests for Jest", 5 | "keywords": [ 6 | "Jest", 7 | "GraphQL", 8 | "GQL", 9 | "Test", 10 | "Apollo", 11 | "BinPar" 12 | ], 13 | "engines": { 14 | "node": ">= 6.0" 15 | }, 16 | "main": "./lib/@binpar/jest-gql.min", 17 | "directories": { 18 | "lib": "src", 19 | "test": "__tests__" 20 | }, 21 | "jest": { 22 | "collectCoverage": true, 23 | "mapCoverage": true, 24 | "coverageDirectory": ".coverage" 25 | }, 26 | "scripts": { 27 | "test": "GQL_API_URL='https://api.artmadrid.market.binpar.com/graphql' jest", 28 | "testDev": "GQL_API_URL='https://api.artmadrid.market.binpar.com/graphql' jest --watch", 29 | "testCoverage": "GQL_API_URL='https://api.artmadrid.market.binpar.com/graphql' jest --coverage", 30 | "testCI": "npm run testCoverage && cat .coverage/lcov.info | coveralls", 31 | "build": "webpack --env dev && webpack --env build && npm run test", 32 | "openTestCoverageReport": "GQL_API_URL='https://api.artmadrid.market.binpar.com/graphql' jest --coverage; open ./.coverage/lcov-report/index.html", 33 | "openTestCoverageReportDev": "open ./.coverage/lcov-report/index.html;GQL_API_URL='https://api.artmadrid.market.binpar.com/graphql' jest --watch --coverage", 34 | "npmPublish": "webpack --env dev && webpack --env build && npm run test; npm version patch; npm publish --access public" 35 | }, 36 | "repository": { 37 | "type": "git", 38 | "url": "git+https://github.com/BinPar/jest-gql.git" 39 | }, 40 | "author": "Ignacio Ferro Picón", 41 | "license": "ISC", 42 | "bugs": { 43 | "url": "https://github.com/BinPar/jest-gql/issues" 44 | }, 45 | "homepage": "https://github.com/BinPar/jest-gql#readme", 46 | "dependencies": { 47 | "apollo-client": "^2.3.2", 48 | "graphql-tag": "^2.9.2", 49 | "process-env": "^1.1.0", 50 | "react": "^16.0.0", 51 | "react-apollo": "^1.4.16", 52 | "react-dom": "^16.0.0" 53 | }, 54 | "devDependencies": { 55 | "babel-cli": "^6.26.0", 56 | "babel-core": "^6.26.0", 57 | "babel-loader": "^7.1.2", 58 | "babel-plugin-inline-import": "^2.0.6", 59 | "babel-plugin-transform-exponentiation-operator": "^6.24.1", 60 | "babel-plugin-transform-object-rest-spread": "^6.26.0", 61 | "babel-preset-env": "^1.6.1", 62 | "babel-preset-es2015": "^6.24.1", 63 | "babel-register": "^6.26.0", 64 | "coveralls": "^3.0.0", 65 | "eslint": "^4.5.0", 66 | "eslint-config-airbnb": "^15.1.0", 67 | "eslint-import-resolver-meteor": "^0.4.0", 68 | "eslint-loader": "^1.9.0", 69 | "eslint-plugin-import": "^2.7.0", 70 | "eslint-plugin-jsx-a11y": "^5.1.1", 71 | "eslint-plugin-meteor": "^4.1.6", 72 | "eslint-plugin-react": "^7.3.0", 73 | "jest": "^22.2.2", 74 | "react": "^16.0.0", 75 | "react-apollo": "^1.4.16", 76 | "react-dom": "^16.0.0", 77 | "uglifyjs-webpack-plugin": "^1.1.8", 78 | "webpack": "^3.11.0" 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/createApolloClient.js: -------------------------------------------------------------------------------- 1 | import { ApolloClient, createNetworkInterface } from 'react-apollo'; 2 | 3 | function create(data) { 4 | const networkInterface = createNetworkInterface({ 5 | uri: data.endPoint, 6 | }); 7 | const middleWares = [ 8 | { 9 | applyMiddleware(req, next) { 10 | const token = data.oAuthToken; 11 | if (token) { 12 | req.options.headers = {}; 13 | req.options.headers.authorization = `Bearer ${token}`; 14 | } 15 | next(); 16 | }, 17 | }, 18 | ]; 19 | networkInterface.use(middleWares); 20 | const severNetworkInterface = createNetworkInterface({ uri: data.endPoint }); 21 | severNetworkInterface.use(middleWares); 22 | return new ApolloClient({ 23 | ssrMode: true, 24 | networkInterface: severNetworkInterface, 25 | }); 26 | } 27 | 28 | export default create; 29 | -------------------------------------------------------------------------------- /src/gqlTest.js: -------------------------------------------------------------------------------- 1 | import createApolloClient from './createApolloClient'; 2 | 3 | const { describe, test, expect } = global; 4 | 5 | const runGQLTest = async (testToRun) => { 6 | describe('GraphQL Tests', async () => { 7 | let data = {}; 8 | const executeGQLCommand = async (executionPlan) => { 9 | const apolloClient = createApolloClient(data); 10 | const testInfo = executionPlan.pop(); 11 | 12 | if (testInfo.gql.definitions.length !== 1) { 13 | throw Error('Only one GQL operation is allowed in a runGQLTest.'); 14 | } 15 | const definition = testInfo.gql.definitions[0]; 16 | let result = null; 17 | switch (definition.operation) { 18 | case 'mutation': { 19 | const params = { mutation: testInfo.gql }; 20 | if (testInfo.vars) { 21 | params.variables = await testInfo.vars(data); 22 | } 23 | result = await apolloClient.mutate(params); 24 | 25 | if (testInfo.result) { 26 | data = { ...data, ...(await testInfo.result(result.data)) }; 27 | } 28 | expect(testInfo.test(data)).toBe(true); 29 | break; 30 | } 31 | case 'query': { 32 | const params = { query: testInfo.gql }; 33 | if (testInfo.vars) { 34 | params.variables = await testInfo.vars(data); 35 | } 36 | result = await apolloClient.query(params); 37 | 38 | if (testInfo.result) { 39 | data = { ...data, ...(await testInfo.result(result.data)) }; 40 | } 41 | expect(testInfo.test(data)).toBe(true); 42 | break; 43 | } 44 | default: 45 | throw Error(`Unknown GQL operation ${definition.operation}.`); 46 | } 47 | 48 | if (executionPlan.length) await executeGQLCommand(executionPlan); 49 | }; 50 | 51 | const getExecutionPlan = (testInfo, executionPlan) => { 52 | executionPlan.push(testInfo); 53 | if (testInfo.previous) { 54 | const prev = [...testInfo.previous]; 55 | while (prev.length) { 56 | getExecutionPlan(prev.pop(), executionPlan); 57 | } 58 | } 59 | }; 60 | 61 | const executeTest = async (testInfo, num) => { 62 | const executionPlan = []; 63 | getExecutionPlan(testInfo, executionPlan); 64 | test(`${testInfo.name}${num !== undefined ? ` ${num}` : ''}`, async () => { 65 | expect.assertions(executionPlan.length); 66 | await executeGQLCommand(executionPlan); 67 | }); 68 | }; 69 | data.endPoint = testToRun.endPoint; 70 | if (testToRun.repeat) { 71 | await Promise.all( 72 | new Array(testToRun.repeat).fill(0).map((_, i) => executeTest(testToRun, i + 1)), 73 | ); 74 | } else { 75 | await executeTest(testToRun); 76 | } 77 | return data; 78 | }); 79 | }; 80 | export default runGQLTest; 81 | -------------------------------------------------------------------------------- /test/__tests__/allWorks.js: -------------------------------------------------------------------------------- 1 | import { gql } from 'react-apollo'; 2 | import runGQLTest from '../../src/gqlTest'; 3 | 4 | const test = { 5 | name: 'Get Works List', 6 | gql: gql` 7 | query ($catalogPrefix: String!){ 8 | getWorks(catalogPrefix:$catalogPrefix, limit:20) { 9 | id 10 | title 11 | } 12 | } 13 | `, 14 | vars: () => ({ catalogPrefix: 'AM18' }), 15 | result: data => ({ workId: data.getWorks[0].id }), 16 | test: data => !!data.workId, 17 | endPoint: process.env.GQL_API_URL, 18 | }; 19 | 20 | runGQLTest(test); 21 | export default test; 22 | -------------------------------------------------------------------------------- /test/__tests__/me.js: -------------------------------------------------------------------------------- 1 | import { gql } from 'react-apollo'; 2 | import runGQLTest from '../../src/gqlTest'; 3 | import login from '../login'; 4 | 5 | const test = { 6 | previous: [login], 7 | name: 'Query authenticated user data', 8 | gql: gql` 9 | query { 10 | me { 11 | id 12 | name 13 | email 14 | } 15 | } 16 | `, 17 | result: data => ({ userEmail: data.me.email }), 18 | test: data => data.userEmail === 'voceses@email.com', 19 | endPoint: process.env.GQL_API_URL, 20 | }; 21 | runGQLTest(test); 22 | export default test; 23 | -------------------------------------------------------------------------------- /test/__tests__/orderedPrevious.js: -------------------------------------------------------------------------------- 1 | import { gql } from 'react-apollo'; 2 | import runGQLTest from '../../src/gqlTest'; 3 | import login from '../login'; 4 | import me from '../me'; 5 | 6 | const test = { 7 | previous: [login, me], 8 | name: 'Get Works List', 9 | gql: gql` 10 | query($catalogPrefix: String!) { 11 | getWorks(catalogPrefix: $catalogPrefix, limit: 20) { 12 | id 13 | title 14 | } 15 | } 16 | `, 17 | vars: () => ({ catalogPrefix: 'AM18' }), 18 | result: data => ({ workId: data.getWorks[0].id }), 19 | test: data => !!data.workId, 20 | endPoint: process.env.GQL_API_URL, 21 | }; 22 | 23 | runGQLTest(test); 24 | export default test; 25 | -------------------------------------------------------------------------------- /test/__tests__/validateCode.js: -------------------------------------------------------------------------------- 1 | import { gql } from 'react-apollo'; 2 | import runGQLTest from '../../src/gqlTest'; 3 | import allWorks from './allWorks'; 4 | 5 | const test = { 6 | previous: [allWorks], 7 | name: 'Validate senseless code', 8 | gql: gql` 9 | mutation validateCode($longCode: String!, $lang: String!) { 10 | validateCode(longCode: $longCode, lang: $lang) { 11 | available 12 | } 13 | } 14 | `, 15 | vars: data => ({ longCode: data.workId, lang: 'es' }), 16 | result: data => ({ available: data.validateCode.available }), 17 | test: data => !data.available, 18 | repeat: 2, 19 | endPoint: process.env.GQL_API_URL, 20 | }; 21 | 22 | runGQLTest(test); 23 | export default test; 24 | -------------------------------------------------------------------------------- /test/login.js: -------------------------------------------------------------------------------- 1 | import { gql } from 'react-apollo'; 2 | 3 | const test = { 4 | name: 'Login Test', 5 | gql: gql` 6 | mutation($email: String!, $password: String!) { 7 | login(email: $email, password: $password) { 8 | error 9 | token 10 | } 11 | } 12 | `, 13 | vars: () => ({ email: 'voceses@email.com', password: 'voceses' }), 14 | result: data => ({ error: data.login.error, oAuthToken: data.login.token }), 15 | test: data => !data.error, 16 | }; 17 | 18 | export default test; 19 | -------------------------------------------------------------------------------- /test/me.js: -------------------------------------------------------------------------------- 1 | import { gql } from 'react-apollo'; 2 | 3 | const test = { 4 | name: 'Query authenticated user data', 5 | gql: gql` 6 | query { 7 | me { 8 | id 9 | name 10 | email 11 | } 12 | } 13 | `, 14 | result: data => ({ userEmail: data.me.email }), 15 | test: data => data.userEmail === 'voceses@email.com', 16 | }; 17 | 18 | export default test; 19 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | /* global __dirname, require, module */ 2 | const webpack = require('webpack'); 3 | const UglifyJSPlugin = require('uglifyjs-webpack-plugin'); 4 | const path = require('path'); 5 | const env = require('yargs').argv.env; // use --env with webpack 2 6 | const pkg = require('./package.json'); 7 | 8 | const libraryName = pkg.name; 9 | 10 | const plugins = []; 11 | 12 | let outputFile; 13 | 14 | if (env === 'build') { 15 | plugins.push(new UglifyJSPlugin()); 16 | outputFile = `${libraryName}.min.js`; 17 | } else { 18 | outputFile = `${libraryName}.js`; 19 | } 20 | 21 | const config = { 22 | entry: `${__dirname}/src/gqlTest.js`, 23 | devtool: 'source-map', 24 | output: { 25 | path: `${__dirname}/lib`, 26 | filename: outputFile, 27 | library: libraryName, 28 | libraryTarget: 'umd', 29 | umdNamedDefine: true, 30 | }, 31 | module: { 32 | rules: [ 33 | { 34 | test: /(\.jsx|\.js)$/, 35 | loader: 'babel-loader', 36 | exclude: /(node_modules|bower_components)/, 37 | }, 38 | { 39 | test: /(\.jsx|\.js)$/, 40 | loader: 'eslint-loader', 41 | exclude: /node_modules/, 42 | }, 43 | ], 44 | }, 45 | resolve: { 46 | modules: [path.resolve(__dirname, './node_modules'), path.resolve(__dirname, './src')], 47 | extensions: ['.json', '.js'], 48 | }, 49 | externals: [ 50 | { 51 | 'react-apollo': { 52 | root: 'react-apollo', 53 | commonjs2: 'react-apollo', 54 | commonjs: 'react-apollo', 55 | amd: 'react-apollo', 56 | umd: 'react-apollo', 57 | }, 58 | }, 59 | ], 60 | plugins, 61 | }; 62 | 63 | module.exports = config; 64 | --------------------------------------------------------------------------------