diff --git a/packages/insomnia-smoke-test/fixtures/graphql.yaml b/packages/insomnia-smoke-test/fixtures/graphql.yaml new file mode 100644 index 0000000000..b32b8970d4 --- /dev/null +++ b/packages/insomnia-smoke-test/fixtures/graphql.yaml @@ -0,0 +1,65 @@ +_type: export +__export_format: 4 +__export_date: 2022-06-15T10:44:33.319Z +__export_source: insomnia.desktop.app:v2022.4.0-beta.1 +resources: + - _id: req_63e5c8a8d1674bb39ba6df1d0eb452a0 + parentId: wrk_d8bfe72fd18b42daa55d2ccc08c9eecb + modified: 1655289849596 + created: 1655289826760 + url: localhost:4010/graphql + name: GraphQL request + description: "" + method: POST + body: + mimeType: application/graphql + text: '{"query":"query {\n\thello,\n\tbearer\n}"}' + parameters: [] + headers: + - name: Content-Type + value: application/json + id: pair_91da32f74847489ab06da669db0b425f + authentication: {} + metaSortKey: -1655289826760 + isPrivate: false + settingStoreCookies: true + settingSendCookies: true + settingDisableRenderRequestBody: false + settingEncodeUrl: true + settingRebuildPath: true + settingFollowRedirects: global + _type: request + - _id: wrk_d8bfe72fd18b42daa55d2ccc08c9eecb + parentId: null + modified: 1655289816535 + created: 1655289816535 + name: Smoke GraphQL + description: "" + scope: collection + _type: workspace + - _id: env_4da443f9b94cc634b24ccbeb89a3b99ad9a073c7 + parentId: wrk_d8bfe72fd18b42daa55d2ccc08c9eecb + modified: 1655289816558 + created: 1655289816558 + name: Base Environment + data: {} + dataPropertyOrder: null + color: null + isPrivate: false + metaSortKey: 1655289816558 + _type: environment + - _id: jar_4da443f9b94cc634b24ccbeb89a3b99ad9a073c7 + parentId: wrk_d8bfe72fd18b42daa55d2ccc08c9eecb + modified: 1655289816559 + created: 1655289816559 + name: Default Jar + cookies: [] + _type: cookie_jar + - _id: spc_93316b5a703d4f65a7e82328c0284835 + parentId: wrk_d8bfe72fd18b42daa55d2ccc08c9eecb + modified: 1655289816545 + created: 1655289816545 + fileName: Smoke GraphQL + contents: "" + contentType: yaml + _type: api_spec diff --git a/packages/insomnia-smoke-test/package-lock.json b/packages/insomnia-smoke-test/package-lock.json index b200f65b6c..1ea798148b 100644 --- a/packages/insomnia-smoke-test/package-lock.json +++ b/packages/insomnia-smoke-test/package-lock.json @@ -15,7 +15,9 @@ "@playwright/test": "^1.20.2", "@types/concurrently": "^6.0.1", "@types/express": "^4.17.11", + "@types/express-graphql": "^0.9.0", "@types/faker": "^5.5.5", + "@types/graphql": "^14.5.0", "@types/mkdirp": "^1.0.1", "@types/oidc-provider": "^7.8.1", "@types/ramda": "^0.27.45", @@ -25,8 +27,10 @@ "execa": "^5.0.0", "express": "^4.17.1", "express-basic-auth": "^1.2.0", + "express-graphql": "^0.12.0", "faker": "^5.1.0", "get-bin-path": "^5.1.0", + "graphql": "^15.8.0", "jest": "^28.1.0", "mkdirp": "^1.0.4", "oidc-provider": "^7.10.6", @@ -2669,6 +2673,16 @@ "@types/serve-static": "*" } }, + "node_modules/@types/express-graphql": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@types/express-graphql/-/express-graphql-0.9.0.tgz", + "integrity": "sha512-+n13emasFUtIn5kpJUTIUy4tAiJBRnHSChpA8OVmKvdKYbzzK6iI/zsnWEKP8qQJDpiUh48wrRKIXNC+5U/t+w==", + "deprecated": "This is a stub types definition. express-graphql provides its own type definitions, so you do not need this installed.", + "dev": true, + "dependencies": { + "express-graphql": "*" + } + }, "node_modules/@types/express-serve-static-core": { "version": "4.17.19", "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.19.tgz", @@ -2695,6 +2709,16 @@ "@types/node": "*" } }, + "node_modules/@types/graphql": { + "version": "14.5.0", + "resolved": "https://registry.npmjs.org/@types/graphql/-/graphql-14.5.0.tgz", + "integrity": "sha512-MOkzsEp1Jk5bXuAsHsUi6BVv0zCO+7/2PTiZMXWDSsMXvNU6w/PLMQT2vHn8hy2i0JqojPz1Sz6rsFjHtsU0lA==", + "deprecated": "This is a stub types definition. graphql provides its own type definitions, so you do not need this installed.", + "dev": true, + "dependencies": { + "graphql": "*" + } + }, "node_modules/@types/http-assert": { "version": "1.5.3", "resolved": "https://registry.npmjs.org/@types/http-assert/-/http-assert-1.5.3.tgz", @@ -4050,6 +4074,113 @@ "basic-auth": "^2.0.1" } }, + "node_modules/express-graphql": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/express-graphql/-/express-graphql-0.12.0.tgz", + "integrity": "sha512-DwYaJQy0amdy3pgNtiTDuGGM2BLdj+YO2SgbKoLliCfuHv3VVTt7vNG/ZqK2hRYjtYHE2t2KB705EU94mE64zg==", + "dev": true, + "dependencies": { + "accepts": "^1.3.7", + "content-type": "^1.0.4", + "http-errors": "1.8.0", + "raw-body": "^2.4.1" + }, + "engines": { + "node": ">= 10.x" + }, + "peerDependencies": { + "graphql": "^14.7.0 || ^15.3.0" + } + }, + "node_modules/express-graphql/node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/express-graphql/node_modules/http-errors": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.0.tgz", + "integrity": "sha512-4I8r0C5JDhT5VkvI47QktDW75rNlGVsUf/8hzjCC/wkWI/jdTRmBb9aI7erSG82r1bjKY3F6k28WnsVxB1C73A==", + "dev": true, + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express-graphql/node_modules/raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "dev": true, + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/express-graphql/node_modules/raw-body/node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/express-graphql/node_modules/raw-body/node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dev": true, + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/express-graphql/node_modules/raw-body/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/express-graphql/node_modules/raw-body/node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/express-graphql/node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true + }, "node_modules/express/node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -4373,6 +4504,15 @@ "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", "dev": true }, + "node_modules/graphql": { + "version": "15.8.0", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-15.8.0.tgz", + "integrity": "sha512-5gghUc24tP9HRznNpV2+FIoq3xKkj5dTQqf4v0CpdPbFVwFkWoxOM+o+2OC9ZSvjEMTjfmG9QT+gcvggTwW1zw==", + "dev": true, + "engines": { + "node": ">= 10.x" + } + }, "node_modules/has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -9573,6 +9713,15 @@ "@types/serve-static": "*" } }, + "@types/express-graphql": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@types/express-graphql/-/express-graphql-0.9.0.tgz", + "integrity": "sha512-+n13emasFUtIn5kpJUTIUy4tAiJBRnHSChpA8OVmKvdKYbzzK6iI/zsnWEKP8qQJDpiUh48wrRKIXNC+5U/t+w==", + "dev": true, + "requires": { + "express-graphql": "*" + } + }, "@types/express-serve-static-core": { "version": "4.17.19", "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.19.tgz", @@ -9599,6 +9748,15 @@ "@types/node": "*" } }, + "@types/graphql": { + "version": "14.5.0", + "resolved": "https://registry.npmjs.org/@types/graphql/-/graphql-14.5.0.tgz", + "integrity": "sha512-MOkzsEp1Jk5bXuAsHsUi6BVv0zCO+7/2PTiZMXWDSsMXvNU6w/PLMQT2vHn8hy2i0JqojPz1Sz6rsFjHtsU0lA==", + "dev": true, + "requires": { + "graphql": "*" + } + }, "@types/http-assert": { "version": "1.5.3", "resolved": "https://registry.npmjs.org/@types/http-assert/-/http-assert-1.5.3.tgz", @@ -10711,6 +10869,90 @@ "basic-auth": "^2.0.1" } }, + "express-graphql": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/express-graphql/-/express-graphql-0.12.0.tgz", + "integrity": "sha512-DwYaJQy0amdy3pgNtiTDuGGM2BLdj+YO2SgbKoLliCfuHv3VVTt7vNG/ZqK2hRYjtYHE2t2KB705EU94mE64zg==", + "dev": true, + "requires": { + "accepts": "^1.3.7", + "content-type": "^1.0.4", + "http-errors": "1.8.0", + "raw-body": "^2.4.1" + }, + "dependencies": { + "bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true + }, + "http-errors": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.0.tgz", + "integrity": "sha512-4I8r0C5JDhT5VkvI47QktDW75rNlGVsUf/8hzjCC/wkWI/jdTRmBb9aI7erSG82r1bjKY3F6k28WnsVxB1C73A==", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + } + }, + "raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "dev": true, + "requires": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "dependencies": { + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true + }, + "http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dev": true, + "requires": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + } + }, + "statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true + }, + "toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true + } + } + }, + "setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true + } + } + }, "extract-zip": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", @@ -10947,6 +11189,12 @@ "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", "dev": true }, + "graphql": { + "version": "15.8.0", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-15.8.0.tgz", + "integrity": "sha512-5gghUc24tP9HRznNpV2+FIoq3xKkj5dTQqf4v0CpdPbFVwFkWoxOM+o+2OC9ZSvjEMTjfmG9QT+gcvggTwW1zw==", + "dev": true + }, "has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", diff --git a/packages/insomnia-smoke-test/package.json b/packages/insomnia-smoke-test/package.json index f065662fa6..d827a3c9c6 100644 --- a/packages/insomnia-smoke-test/package.json +++ b/packages/insomnia-smoke-test/package.json @@ -34,7 +34,9 @@ "@playwright/test": "^1.20.2", "@types/concurrently": "^6.0.1", "@types/express": "^4.17.11", + "@types/express-graphql": "^0.9.0", "@types/faker": "^5.5.5", + "@types/graphql": "^14.5.0", "@types/mkdirp": "^1.0.1", "@types/oidc-provider": "^7.8.1", "@types/ramda": "^0.27.45", @@ -44,8 +46,10 @@ "execa": "^5.0.0", "express": "^4.17.1", "express-basic-auth": "^1.2.0", + "express-graphql": "^0.12.0", "faker": "^5.1.0", "get-bin-path": "^5.1.0", + "graphql": "^15.8.0", "jest": "^28.1.0", "mkdirp": "^1.0.4", "oidc-provider": "^7.10.6", diff --git a/packages/insomnia-smoke-test/server/graphql.ts b/packages/insomnia-smoke-test/server/graphql.ts new file mode 100644 index 0000000000..166285b208 --- /dev/null +++ b/packages/insomnia-smoke-test/server/graphql.ts @@ -0,0 +1,33 @@ +import { buildSchema } from 'graphql'; + +// Construct a schema, using GraphQL schema language +export const schema = buildSchema(` + """Characters who at any time bore a Ring of Power.""" + enum RingBearer { + Frodo + Bilbo + Thror + Gandalf + Galadriel + WitchKing + Nazgul + Elrond + GilGalad + Cirdan + Thrain + } + type Query { + hello: String, + bearer: RingBearer! + } +`); + +// The root provides a resolver function for each API endpoint +export const root = { + hello: () => { + return 'Hello world!'; + }, + bearer: () => { + return 'Gandalf'; + }, +}; diff --git a/packages/insomnia-smoke-test/server/index.ts b/packages/insomnia-smoke-test/server/index.ts index 026f251d62..4d6cadd29a 100644 --- a/packages/insomnia-smoke-test/server/index.ts +++ b/packages/insomnia-smoke-test/server/index.ts @@ -1,8 +1,10 @@ import express from 'express'; +import { graphqlHTTP } from 'express-graphql'; import { basicAuthRouter } from './basic-auth'; import githubApi from './github-api'; import gitlabApi from './gitlab-api'; +import { root, schema } from './graphql'; import { startGRPCServer } from './grpc'; import { oauthRoutes } from './oauth'; @@ -46,6 +48,12 @@ app.get('/', (_req, res) => { res.status(200).send(); }); +app.use('/graphql', graphqlHTTP({ + schema: schema, + rootValue: root, + graphiql: true, +})); + startGRPCServer(grpcPort).then(() => { app.listen(port, () => { console.log(`Listening at http://localhost:${port}`); diff --git a/packages/insomnia-smoke-test/tests/graphql.test.ts b/packages/insomnia-smoke-test/tests/graphql.test.ts new file mode 100644 index 0000000000..b13faa4d59 --- /dev/null +++ b/packages/insomnia-smoke-test/tests/graphql.test.ts @@ -0,0 +1,42 @@ +import { expect } from '@playwright/test'; + +import { loadFixture } from '../playwright/paths'; +import { test } from '../playwright/test'; + +test('can render schema and send GraphQL requests', async ({ app, page }) => { + test.slow(process.platform === 'darwin' || process.platform === 'win32', 'Slow app start on these platforms'); + + // Create a new the project + await page.click('[data-testid="project"]'); + await page.click('text=Create'); + + // Copy the collection with the graphql query to clipboard + const text = await loadFixture('graphql.yaml'); + await app.evaluate(async ({ clipboard }, text) => clipboard.writeText(text), text); + + // Import from clipboard + await page.click('button:has-text("Clipboard")'); + // Open the new collection workspace + await page.click('text=CollectionSmoke GraphQLjust now'); + // Open the graphql request + await page.click('button:has-text("POSTGQLGraphQL request")'); + + // Assert schema documentation stuff + await page.click('[data-testid="request-pane"] button:has-text("schema")'); + await page.click('button:has-text("Show Documentation")'); + await page.click('a:has-text("Query")'); + await page.locator('a:has-text("RingBearer")').click(); + const graphqlExplorer = page.locator('.graphql-explorer'); + await expect(graphqlExplorer).toContainText('Characters who at any time bore a Ring of Power.'); + await page.click('text=QueryRingBearer >> button'); + + // Send and assert GraphQL request + await page.click('[data-testid="request-pane"] >> text=Send'); + const statusTag = page.locator('[data-testid="response-status-tag"]:visible'); + await expect(statusTag).toContainText('200 OK'); + + const responseBody = page.locator('[data-testid="response-pane"] >> [data-testid="CodeEditor"]:visible', { + has: page.locator('.CodeMirror-activeline'), + }); + await expect(responseBody).toContainText('"bearer": "Gandalf"'); +});