diff --git a/features/gherkin/request-options.feature b/features/gherkin/request-options.feature index f554a39..4ff2e22 100644 --- a/features/gherkin/request-options.feature +++ b/features/gherkin/request-options.feature @@ -37,6 +37,47 @@ Feature: Set request options When I send the request Then I expect last response to have status as "200" + Scenario: Set a form field + Given I request POST method at "https://httpbin.org/anything" url + And I set "field1" field with value "value1" + When I send the request + Then I expect last response to have status as "200" + + Scenario: Set multiple form fields using data table + Given I request POST method at "https://httpbin.org/anything" url + And I set fields as: + | field1 | value1 | + | field2 | value2 | + When I send the request + Then I expect last response to have status as "200" + + Scenario: Attach a file using local image + Given I request POST method at "https://httpbin.org/anything" url + And I attach a file at "./resources/blank.jpg" path with name "photo" + When I send the request + Then I expect last response to have status as "200" + + Scenario: Attach an image file using base64 + Given I request POST method at "https://httpbin.org/anything" url + And I add an attachment with name "image" and filename as "pixel.gif" with "R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs=" base64 data + When I send the request + Then I expect last response to have status as "200" + + Scenario: Attach an XML file using utf-8 encoding + Given I request POST method at "https://httpbin.org/anything" url + And I add an attachment with name "myxml" and filename as "test.xml" with "utf-8" encoding using content: + """ + + + Someone + Me + Test Message + This is a test message! + + """ + When I send the request + Then I expect last response to have status as "200" + Scenario: Use generator functions in inline js template to POST method to send data to a url Given I request GET method at "https://httpbin.org/anything" url And I set "header1" header as "value1" diff --git a/package.json b/package.json index a23f60a..0c56d61 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cucumber-steps-api", - "version": "0.1.3", + "version": "0.1.4", "description": "Cucumber step library for API testing and support functions", "main": "index.js", "scripts": { diff --git a/resources/blank.jpg b/resources/blank.jpg new file mode 100644 index 0000000..1cda9a5 Binary files /dev/null and b/resources/blank.jpg differ diff --git a/spec/support/http-client.spec.js b/spec/support/http-client.spec.js index 025c1cb..2112c96 100644 --- a/spec/support/http-client.spec.js +++ b/spec/support/http-client.spec.js @@ -155,6 +155,111 @@ describe('Http Client', () => { }); }); + it('should support sending field with name and value in request', () => { + const client = new Client(agent); + return client + .url('https://httpbin.org/anything') + .method('post') + .field('field-name', 'field-value') + .send() + .then(response => { + expect(response.status).toBe(200); + }) + .catch(err => { + expect(err).toBeFalsy(); + }); + }); + + it('should support sending field as an object in request', () => { + const client = new Client(agent); + return client + .url('https://httpbin.org/anything') + .method('post') + .field({ name: 'field-name', value: 'field-value' }) + .send() + .then(response => { + expect(response.status).toBe(200); + }) + .catch(err => { + expect(err).toBeFalsy(); + }); + }); + + it('should support sending multiple fields in request', () => { + const client = new Client(agent); + return client + .url('https://httpbin.org/anything') + .method('post') + .field({ name: 'field1', value: 'value1' }) + .field({ name: 'field2', value: 'value2' }) + .send() + .then(response => { + expect(response.status).toBe(200); + }) + .catch(err => { + expect(err).toBeFalsy(); + }); + }); + + it('should support sending an attachment as { name, path } in request', () => { + const client = new Client(agent); + return client + .url('https://httpbin.org/anything') + .method('post') + .attach({ + name: 'photo', + path: './resources/blank.jpg' + }) + .send() + .then(response => { + expect(response.status).toBe(200); + }) + .catch(err => { + expect(err).toBeFalsy(); + }); + }); + + it('should support sending an attachment as { name, filename, buffer } in request', () => { + const client = new Client(agent); + const base64Image = '/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCAABAAEDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD3+iiigD//2Q=='; + const buffer = new Buffer(base64Image, 'base64'); + return client + .url('https://httpbin.org/anything') + .method('post') + .attach({ + name: 'photo', + filename: 'photo.jpg', + buffer, + }) + .send() + .then(response => { + expect(response.status).toBe(200); + }) + .catch(err => { + expect(err).toBeFalsy(); + }); + }); + + it('should support sending multiple attachments in request', () => { + const client = new Client(agent); + const buffers = [ + new Buffer('R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs=', 'base64'), + new Buffer('R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7', 'base64'), + ]; + return client + .url('https://httpbin.org/anything') + .method('post') + .attach({ name: 'image1', filename: 'black.gif', buffer: buffers[0] }) + .attach({ name: 'image2', filename: 'transparent.gif', buffer: buffers[1] }) + .send() + .then(response => { + expect(response.status).toBe(200); + }) + .catch(err => { + expect(err).toBeFalsy(); + }); + }); + it('should support sending accept type using header in request', () => { const client = new Client(agent); return client diff --git a/step-definitions/request-options.steps.js b/step-definitions/request-options.steps.js index 5c07af9..5f7598c 100644 --- a/step-definitions/request-options.steps.js +++ b/step-definitions/request-options.steps.js @@ -30,6 +30,39 @@ Given(/I set headers(?: as):/, function (dataTable) { return this.client.headers(dataTable.rowsHash()); }); +Given(/I set "([^"]+)" field (?:with value|as) "([^"]+)"/, function (name, value) { + expect(this.client).to.exist; + return this.client.field(name, value); +}); +Given(/I set "([^"]+)" field (?:with content|as):/, function (name, content) { + expect(this.client).to.exist; + return this.client.field(name, content); +}); +Given(/I set fields(?: as):/, function (dataTable) { + expect(dataTable, 'data dataTable').to.exist; + expect(dataTable.rows().length > 0, 'data dataTable length > 0').to.be.true; + expect(this.client).to.exist; + const fields = dataTable.raw().map(row => ({ name: row[0], value: row[1] })); + return fields.forEach(field => this.client.field(field)); +}); + +Given(/I attach a|the file at "([^"]+)"(?: path) with name "([^"]+)"/, function (path, name) { + expect(this.client).to.exist; + return this.client.attach({ name, path }); +}); +Given(/I add an attachment with name "([^"]+)" (?:and|having) filename(?: as)? "([^"]+)" (?:with|using) "([^"]+)"(?:( base64)? data)/, function (name, filename, data, isBase64) { + expect(this.client).to.exist; + return this.client.attach({ name, filename, buffer: new Buffer(data, isBase64 ? 'base64' : 'utf8') }); +}); +Given(/I add an attachment with name "([^"]+)" (?:and|having) filename(?: as)? "([^"]+)" using "([^"]+)" buffer(?: with "([^"]+)" encoding)?/, function (name, filename, buffer, encoding) { + expect(this.client).to.exist; + return this.client.attach({ name, filename, buffer: new Buffer(buffer, encoding || 'base64') }); +}); +Given(/I add an attachment with name "([^"]+)" (?:and|having) filename(?: as)? "([^"]+)"( with "([^"]+)" encoding)? using content:/, function (name, filename, encoding, content) { + expect(this.client).to.exist; + return this.client.attach({ name, filename, buffer: new Buffer(content, encoding || 'utf8') }); +}); + Given(/I set "([^"]+)" query param as "([^"]+)"/, function (name, value) { expect(this.client, 'client').to.exist; return this.client.query(`${name}=${encodeURIComponent(value)}`); diff --git a/support/http-client.js b/support/http-client.js index 55281ee..e99d049 100644 --- a/support/http-client.js +++ b/support/http-client.js @@ -53,9 +53,9 @@ class RequestDefaults { const list = Array.isArray(headers) ? headers : Object.keys(headers).map(key => ({ - name: key, - value: headers[key], - })); + name: key, + value: headers[key], + })); list.forEach(header => this.header(header.name, header.value)); } return this; @@ -76,7 +76,7 @@ class RequestDefaults { queries(queries) { if (queries && queries.length) { this.$queries = this.$queries.concat( - queries.map(query => { + queries.map((query) => { if (typeof query === 'object' && Array.isArray(query)) { return [query[0], encodeURIComponent(query[1])].join('='); } else if (typeof query === 'object') { @@ -125,6 +125,8 @@ class RequestOptions extends RequestDefaults { this.$method = options.method; this.$url = options.url; this.$defaults = defaults || GlobalRequestDefaults; + this.$fields = []; + this.$attachments = []; } get defaults() { @@ -161,6 +163,20 @@ class RequestOptions extends RequestDefaults { return this; } + field(field) { + if (field) { + this.$fields.push(field); + } + return this; + } + + attach(attachment) { + if (attachment) { + this.$attachments.push(attachment); + } + return this; + } + dump(kind, wrap) { const lines = []; @@ -222,7 +238,7 @@ class RequestOptions extends RequestDefaults { `-d ${ typeof this.$body === 'object' ? JSON.stringify(this.$body) - : this.$body.toString() + : this.$body && this.$body.toString() }` ); } @@ -296,12 +312,22 @@ class HttpClient { this.$request.queries(queries); return this; } - body(body) { this.$request.body(body); return this; } - + field(name, value) { + if (typeof name === 'object') { + this.$request.field(name); + } else { + this.$request.field({ name, value }); + } + return this; + } + attach(attachment) { + this.$request.attach(attachment); + return this; + } key(key) { this.$request.key(key); return this; @@ -349,12 +375,12 @@ class HttpClient { this.$request.$defaults.$headers && this.$request.$defaults.$headers.length ) { - this.$request.$defaults.$headers.forEach(header => { + this.$request.$defaults.$headers.forEach((header) => { client = client.set(header.name, header.value); }); } if (this.$request.$headers && this.$request.$headers.length) { - this.$request.$headers.forEach(header => { + this.$request.$headers.forEach((header) => { client = client.set(header.name, header.value); }); } @@ -372,12 +398,12 @@ class HttpClient { this.$request.$defaults.$queries && this.$request.$defaults.$queries.length ) { - this.$request.$defaults.$queries.forEach(query => { + this.$request.$defaults.$queries.forEach((query) => { client = client.query(query); }); } if (this.$request.$queries && this.$request.$queries.length) { - this.$request.$queries.forEach(query => { + this.$request.$queries.forEach((query) => { client = client.query(query); }); } @@ -412,14 +438,39 @@ class HttpClient { ); } + if ( + this.$request.$attachments && + this.$request.$attachments.length && + ['post', 'put', 'patch'].includes(this.$request.$method) + ) { + this.$request.$attachments.forEach((attachment) => { + if (attachment.path) { + client = client.attach(attachment.name, attachment.path); + } else { + client = client.attach(attachment.name, attachment.buffer, attachment.filename); + } + }); + } + + if ( + this.$request.$fields && + this.$request.$fields.length && + ['post', 'put', 'patch'].includes(this.$request.$method) + ) { + this.$request.$fields.forEach((field) => { + client = client.field(field.name, field.value); + }); + } + if ( this.$request.$body && - !['get', 'head'].includes(this.$request.$method) + !['get', 'head'].includes(this.$request.$method) && + (this.$request.$fields.length === 0 || this.$request.$attachments.length === 0) ) { client = client.send(this.$request.$body); } - return client.ok(() => true).then(res => { + return client.ok(() => true).then((res) => { self.response = res; if (self.store) { self.store.put('last-response', res);