diff --git a/packages/insomnia-smoke-test/fixtures/route_guide.bin b/packages/insomnia-smoke-test/fixtures/route_guide.bin index 489f4b3c5e8..565ddaae337 100644 Binary files a/packages/insomnia-smoke-test/fixtures/route_guide.bin and b/packages/insomnia-smoke-test/fixtures/route_guide.bin differ diff --git a/packages/insomnia-smoke-test/server/grpc.ts b/packages/insomnia-smoke-test/server/grpc.ts index 943d025ec69..9b0df85781a 100644 --- a/packages/insomnia-smoke-test/server/grpc.ts +++ b/packages/insomnia-smoke-test/server/grpc.ts @@ -59,6 +59,11 @@ const getFeature: HandleCall = (call: any, callback: any) => { callback(null, checkFeature(call.request)); }; +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const echoMetadata: HandleCall = (call: any, callback: any) => { + callback(null, { metadata: call.metadata.getMap() }); +}; + /** * listFeatures request handler. */ @@ -192,6 +197,12 @@ export const startGRPCServer = (port: number) => { const server = new grpc.Server(); // Enable reflection + // use this to generate .bin file: + // protoc \ + // --descriptor_set_out=packages/insomnia-smoke-test/fixtures/route_guide.bin \ + // --include_imports packages/insomnia/src/network/grpc/__fixtures__/library/route_guide.proto \ + // -I \ + // -I const descriptorSet = '../../packages/insomnia-smoke-test/fixtures/route_guide.bin'; addReflection(server, descriptorSet); @@ -201,6 +212,7 @@ export const startGRPCServer = (port: number) => { listFeatures: listFeatures, recordRoute: recordRoute, routeChat: routeChat, + echoMetadata, }); server.bindAsync(`localhost:${port}`, grpc.ServerCredentials.createInsecure(), error => { if (error) { diff --git a/packages/insomnia-smoke-test/tests/smoke/grpc.test.ts b/packages/insomnia-smoke-test/tests/smoke/grpc.test.ts index 326cae04820..cd854ddf91e 100644 --- a/packages/insomnia-smoke-test/tests/smoke/grpc.test.ts +++ b/packages/insomnia-smoke-test/tests/smoke/grpc.test.ts @@ -3,36 +3,112 @@ import { expect } from '@playwright/test'; import { loadFixture } from '../../playwright/paths'; import { test } from '../../playwright/test'; -test('can send gRPC requests with reflection', async ({ app, page }) => { - test.slow(process.platform === 'darwin' || process.platform === 'win32', 'Slow app start on these platforms'); - const statusTag = page.locator('[data-testid="response-status-tag"]:visible'); - const responseBody = page.locator('[data-testid="response-pane"] >> [data-testid="CodeEditor"]:visible', { - has: page.locator('.CodeMirror-activeline'), +test.describe('test grpc requests', async () => { + test('test unary request', async ({ app, page }) => { + test.slow(process.platform === 'darwin' || process.platform === 'win32', 'Slow app start on these platforms'); + + const statusTag = page.locator('[data-testid="response-status-tag"]:visible'); + const responseBody = page.locator('[data-testid="response-pane"] >> [data-testid="CodeEditor"]:visible', { + has: page.locator('.CodeMirror-activeline'), + }); + + await page.getByRole('button', { name: 'Create in project' }).click(); + + const text = await loadFixture('grpc.yaml'); + await app.evaluate(async ({ clipboard }, text) => clipboard.writeText(text), text); + + await page.getByRole('menuitemradio', { name: 'Import' }).click(); + await page.locator('[data-test-id="import-from-clipboard"]').click(); + await page.getByRole('button', { name: 'Scan' }).click(); + await page.getByRole('dialog').getByRole('button', { name: 'Import' }).click(); + await page.getByLabel('PreRelease gRPC').click(); + + // choose request + await page.getByLabel('Request Collection').getByTestId('UnaryWithOutProtoFile').press('Enter'); + await expect(page.getByRole('button', { name: 'Select Method' })).toBeDisabled(); + await page.getByTestId('button-server-reflection').click(); + + // choose method + await page.getByRole('button', { name: 'Select Method' }).click(); + await page.getByLabel('/RouteGuide/GetFeature', { exact: true }).click(); + + // start + await page.getByRole('button', { name: 'Send' }).click(); + + // verify + await page.getByRole('tab', { name: 'Response 1' }).click(); + await expect(statusTag).toContainText('0 OK'); + await expect(responseBody).toContainText('Berkshire Valley Management Area Trail'); }); - await page.getByRole('button', { name: 'Create in project' }).click(); + test('test client side stream', async ({ app, page }) => { + test.slow(process.platform === 'darwin' || process.platform === 'win32', 'Slow app start on these platforms'); - const text = await loadFixture('grpc.yaml'); - await app.evaluate(async ({ clipboard }, text) => clipboard.writeText(text), text); + const statusTag = page.locator('[data-testid="response-status-tag"]:visible'); + const responseBody = page.locator('[data-testid="response-pane"] >> [data-testid="CodeEditor"]:visible', { + has: page.locator('.CodeMirror-activeline'), + }); - await page.getByRole('menuitemradio', { name: 'Import' }).click(); - await page.locator('[data-test-id="import-from-clipboard"]').click(); - await page.getByRole('button', { name: 'Scan' }).click(); - await page.getByRole('dialog').getByRole('button', { name: 'Import' }).click(); - await page.getByLabel('PreRelease gRPC').click(); + await page.getByRole('button', { name: 'Create in project' }).click(); - await page.getByLabel('Request Collection').getByTestId('UnaryWithOutProtoFile').press('Enter'); - await expect(page.getByRole('button', { name: 'Select Method' })).toBeDisabled(); - await page.getByTestId('button-server-reflection').click(); + // import collection + const text = await loadFixture('grpc.yaml'); + await app.evaluate(async ({ clipboard }, text) => clipboard.writeText(text), text); + await page.getByRole('menuitemradio', { name: 'Import' }).click(); + await page.locator('[data-test-id="import-from-clipboard"]').click(); + await page.getByRole('button', { name: 'Scan' }).click(); + await page.getByRole('dialog').getByRole('button', { name: 'Import' }).click(); + await page.getByLabel('PreRelease gRPC').click(); - await page.getByRole('button', { name: 'Select Method' }).click(); - await page.getByRole('option', { name: 'RouteGuide/GetFeature' }).click(); + // choose request + await page.getByLabel('Request Collection').getByTestId('UnaryWithOutProtoFile').press('Enter'); + await expect(page.getByRole('button', { name: 'Select Method' })).toBeDisabled(); + await page.getByTestId('button-server-reflection').click(); - await page.getByRole('tab', { name: 'Unary' }).click(); - await page.getByRole('button', { name: 'Send' }).click(); + // choose method + await page.getByRole('button', { name: 'Select Method' }).click(); + await page.getByLabel('/RouteGuide/RecordRoute', { exact: true }).click(); - // Check for the single Unary response - await page.getByRole('tab', { name: 'Response 1' }).click(); - await expect(statusTag).toContainText('0 OK'); - await expect(responseBody).toContainText('Berkshire Valley Management Area Trail'); + // start + await page.getByRole('button', { name: 'Send' }).click(); + await page.getByRole('button', { name: 'Commit' }).click(); + + // verify + await page.getByRole('tab', { name: 'Response 1' }).click(); + await expect(statusTag).toContainText('0 OK'); + await expect(responseBody).toContainText('point_count": 0'); + }); + + test('test bidirectional stream', async ({ app, page }) => { + test.slow(process.platform === 'darwin' || process.platform === 'win32', 'Slow app start on these platforms'); + + const statusTag = page.locator('[data-testid="response-status-tag"]:visible'); + + await page.getByRole('button', { name: 'Create in project' }).click(); + + // import collection + const text = await loadFixture('grpc.yaml'); + await app.evaluate(async ({ clipboard }, text) => clipboard.writeText(text), text); + await page.getByRole('menuitemradio', { name: 'Import' }).click(); + await page.locator('[data-test-id="import-from-clipboard"]').click(); + await page.getByRole('button', { name: 'Scan' }).click(); + await page.getByRole('dialog').getByRole('button', { name: 'Import' }).click(); + await page.getByText('CollectionPreRelease gRPCjust now').click(); + + // choose request + await page.getByLabel('Request Collection').getByTestId('UnaryWithOutProtoFile').press('Enter'); + await expect(page.getByRole('button', { name: 'Select Method' })).toBeDisabled(); + await page.getByTestId('button-server-reflection').click(); + + // choose method + await page.getByRole('button', { name: 'Select Method' }).click(); + await page.getByLabel('/RouteGuide/RouteChat', { exact: true }).click(); + + // start + await page.getByRole('button', { name: 'Start' }).click(); + await page.getByRole('button', { name: 'Commit' }).click(); + + // verify + await expect(statusTag).toContainText('0 OK'); + }); }); diff --git a/packages/insomnia/src/network/grpc/__fixtures__/library/route_guide.proto b/packages/insomnia/src/network/grpc/__fixtures__/library/route_guide.proto index b519f558288..29a1ab65f81 100644 --- a/packages/insomnia/src/network/grpc/__fixtures__/library/route_guide.proto +++ b/packages/insomnia/src/network/grpc/__fixtures__/library/route_guide.proto @@ -21,6 +21,8 @@ option objc_class_prefix = "RTG"; package routeguide; +import "google/protobuf/empty.proto"; + // Interface exported by the server. service RouteGuide { // A simple RPC. @@ -50,6 +52,9 @@ service RouteGuide { // Accepts a stream of RouteNotes sent while a route is being traversed, // while receiving other RouteNotes (e.g. from other users). rpc RouteChat(stream RouteNote) returns (stream RouteNote) {} + + + rpc EchoMetadata(google.protobuf.Empty) returns (Metadata) {} } // Points are represented as latitude-longitude pairs in the E7 representation @@ -109,3 +114,7 @@ message RouteSummary { // The duration of the traversal in seconds. int32 elapsed_time = 4; } + +message Metadata { + map metadata = 1; +}