From 866500f7293817ed5949dc422a99c9b1ea61e828 Mon Sep 17 00:00:00 2001 From: artem-zakharchenko Date: Tue, 8 Oct 2019 14:59:21 +0200 Subject: [PATCH 1/9] refactor: rewrites "isURL" to TypeScript --- packages/dredd/lib/{isURL.js => isURL.ts} | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) rename packages/dredd/lib/{isURL.js => isURL.ts} (52%) diff --git a/packages/dredd/lib/isURL.js b/packages/dredd/lib/isURL.ts similarity index 52% rename from packages/dredd/lib/isURL.js rename to packages/dredd/lib/isURL.ts index 2f27a30f9..12178f6c8 100644 --- a/packages/dredd/lib/isURL.js +++ b/packages/dredd/lib/isURL.ts @@ -1,8 +1,6 @@ /** * Decides whether given string is a URL or not - * @param {string} location - * @returns {boolean} */ -export default function isURL(location) { +export default function isURL(location: string): boolean { return /^http(s)?:\/\//.test(location); } From f2a044aaa03f75e31109de5373334d867d67186b Mon Sep 17 00:00:00 2001 From: artem-zakharchenko Date: Tue, 8 Oct 2019 14:59:40 +0200 Subject: [PATCH 2/9] refactor: adds basic types in "general.d.ts" --- packages/dredd/lib/general.ts | 82 +++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 packages/dredd/lib/general.ts diff --git a/packages/dredd/lib/general.ts b/packages/dredd/lib/general.ts new file mode 100644 index 000000000..f2acc2185 --- /dev/null +++ b/packages/dredd/lib/general.ts @@ -0,0 +1,82 @@ +export enum RESTMethod { + CONNECT = 'CONNECT', + OPTIONS = 'OPTIONS', + POST = 'POST', + GET = 'GET', + HEAD = 'HEAD', + PUT = 'PUT', + PATCH = 'PATCH', + DELETE = 'DELETE', + TRACE = 'TRACE', +} + +export enum BodyEncoding { + 'utf-8', + 'base64', +} + +export enum TransactionTestStatus { + 'pass', + 'fail', + 'skip', +} + +export interface Transaction { + id: string; + name: string; + origin: TransactionOrigin; + host: string; + port: number; + protocol: 'http:' | 'https:'; + fullPath: string; + request: TransactionRequest; + expected: { + statusCode: number; + headers: Record; + body: string; + bodySchema: Record; + }; + real: { + statusCode: string; + headers: Record; + body: string; + bodyEncoding: BodyEncoding; + }; + skip: boolean; + fail: boolean; + + test: TransactionTest; +} + +export interface TransactionRequest { + method: RESTMethod; + url: string; + body?: string; + bodyEncoding?: BodyEncoding; + headers?: Record; +} + +export interface TransactionOrigin { + filename: string; + apiName: string; + resourceGroupName: string; + resourceName: string; + actionName: string; + exampleName: string; +} + +export interface TransactionTest { + start: Date; + end: Date; + duration: number; + startedAt: number; + title: string; + request: TransactionRequest; + actual: any; + expected: any; + status: TransactionTestStatus; + message: string; + results: any; + valid: boolean; + origin: TransactionOrigin; +} From bf43084a7b8235c4adc9cc6019417743424fbd32 Mon Sep 17 00:00:00 2001 From: artem-zakharchenko Date: Tue, 8 Oct 2019 14:59:57 +0200 Subject: [PATCH 3/9] refactor: rewrites "resolveLocations" to TypeScript --- .../lib/{resolveLocations.js => resolveLocations.ts} | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) rename packages/dredd/lib/{resolveLocations.js => resolveLocations.ts} (73%) diff --git a/packages/dredd/lib/resolveLocations.js b/packages/dredd/lib/resolveLocations.ts similarity index 73% rename from packages/dredd/lib/resolveLocations.js rename to packages/dredd/lib/resolveLocations.ts index 4fffdafd2..bf0292b32 100644 --- a/packages/dredd/lib/resolveLocations.js +++ b/packages/dredd/lib/resolveLocations.ts @@ -7,19 +7,18 @@ import isURL from './isURL'; * * Keeps URLs intact. Keeps the original order. Throws in case there's a glob * pattern which doesn't resolve to any existing files. - * - * @param {string} workingDirectory - * @param {string[]} locations - * @returns {string[]} */ -export default function resolveLocations(workingDirectory, locations) { +export default function resolveLocations( + workingDirectory: string, + locations: string[], +): string[] { const resolvedLocations = locations // resolves paths to local files, produces an array of arrays .map((location) => isURL(location) ? [location] : resolvePaths(workingDirectory, [location]), ) // flattens the array of arrays - .reduce((flatArray, array) => flatArray.concat(array), []); + .reduce((flatArray, array) => flatArray.concat(array), []); return Array.from(new Set(resolvedLocations)); } From 57ef26458663b340b70306094bf37673c2416582 Mon Sep 17 00:00:00 2001 From: artem-zakharchenko Date: Tue, 8 Oct 2019 15:00:15 +0200 Subject: [PATCH 4/9] refactor: rewrites "resolveModule" to TypeScript --- packages/dredd/lib/{resolveModule.js => resolveModule.ts} | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) rename packages/dredd/lib/{resolveModule.js => resolveModule.ts} (69%) diff --git a/packages/dredd/lib/resolveModule.js b/packages/dredd/lib/resolveModule.ts similarity index 69% rename from packages/dredd/lib/resolveModule.js rename to packages/dredd/lib/resolveModule.ts index f521862ca..96a2b63dc 100644 --- a/packages/dredd/lib/resolveModule.js +++ b/packages/dredd/lib/resolveModule.ts @@ -1,7 +1,10 @@ import fs from 'fs'; import path from 'path'; -export default function resolveModule(workingDirectory, moduleName) { +export default function resolveModule( + workingDirectory: string, + moduleName: string, +): string { const absolutePath = path.resolve(workingDirectory, moduleName); return fs.existsSync(absolutePath) || fs.existsSync(`${absolutePath}.js`) ? absolutePath From 25e757a190f3bac9490e970f2b4ec2bef83ebe97 Mon Sep 17 00:00:00 2001 From: artem-zakharchenko Date: Tue, 8 Oct 2019 15:00:33 +0200 Subject: [PATCH 5/9] refactor: rewrites "resolvePaths" to TypeScript --- packages/dredd/lib/{resolvePaths.js => resolvePaths.ts} | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) rename packages/dredd/lib/{resolvePaths.js => resolvePaths.ts} (89%) diff --git a/packages/dredd/lib/resolvePaths.js b/packages/dredd/lib/resolvePaths.ts similarity index 89% rename from packages/dredd/lib/resolvePaths.js rename to packages/dredd/lib/resolvePaths.ts index c7e164a2c..1c8797d19 100644 --- a/packages/dredd/lib/resolvePaths.js +++ b/packages/dredd/lib/resolvePaths.ts @@ -6,7 +6,7 @@ import glob from 'glob'; const basename = process.platform === 'win32' ? path.win32.basename : path.basename; -function resolveGlob(workingDirectory, pattern) { +function resolveGlob(workingDirectory: string, pattern: string): string[] { // 'glob.sync()' does not resolve paths, only glob patterns if (glob.hasMagic(pattern)) { return glob @@ -23,7 +23,10 @@ function resolveGlob(workingDirectory, pattern) { * Resolves glob patterns and sorts the files alphabetically by their basename. * Throws in case there's a pattern which doesn't resolve to any existing files. */ -export default function resolvePaths(workingDirectory, patterns) { +export default function resolvePaths( + workingDirectory: string, + patterns: string[], +) { if (!patterns || patterns.length < 1) { return []; } From 5196ccbcc2f1a7b2a76d1c46e522c007e610e30c Mon Sep 17 00:00:00 2001 From: artem-zakharchenko Date: Tue, 8 Oct 2019 15:00:46 +0200 Subject: [PATCH 6/9] refactor: rewrites "sortTransactions" to TypeScript --- packages/dredd/lib/sortTransactions.js | 42 ------------------ packages/dredd/lib/sortTransactions.ts | 61 ++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 42 deletions(-) delete mode 100644 packages/dredd/lib/sortTransactions.js create mode 100644 packages/dredd/lib/sortTransactions.ts diff --git a/packages/dredd/lib/sortTransactions.js b/packages/dredd/lib/sortTransactions.js deleted file mode 100644 index 73a6bb814..000000000 --- a/packages/dredd/lib/sortTransactions.js +++ /dev/null @@ -1,42 +0,0 @@ -// Often, API description is arranged with a sequence of methods that lends -// itself to understanding by the human reading the documentation. -// -// However, the sequence of methods may not be appropriate for the machine -// reading the documentation in order to test the API. -// -// By sorting the transactions by their methods, it is possible to ensure that -// objects are created before they are read, updated, or deleted. -export default function sortTransactions(arr) { - arr.forEach((a, i) => { - a._index = i; - }); - - arr.sort((a, b) => { - const sortedMethods = [ - 'CONNECT', - 'OPTIONS', - 'POST', - 'GET', - 'HEAD', - 'PUT', - 'PATCH', - 'DELETE', - 'TRACE', - ]; - - const methodIndexA = sortedMethods.indexOf(a.request.method); - const methodIndexB = sortedMethods.indexOf(b.request.method); - - if (methodIndexA < methodIndexB) { - return -1; - } - if (methodIndexA > methodIndexB) { - return 1; - } - return a._index - b._index; - }); - - arr.forEach((a) => delete a._index); - - return arr; -} diff --git a/packages/dredd/lib/sortTransactions.ts b/packages/dredd/lib/sortTransactions.ts new file mode 100644 index 000000000..2f9f6ab1e --- /dev/null +++ b/packages/dredd/lib/sortTransactions.ts @@ -0,0 +1,61 @@ +import { RESTMethod, Transaction } from './general'; + +const sortedMethods: RESTMethod[] = [ + RESTMethod.CONNECT, + RESTMethod.OPTIONS, + RESTMethod.POST, + RESTMethod.GET, + RESTMethod.HEAD, + RESTMethod.PUT, + RESTMethod.PATCH, + RESTMethod.DELETE, + RESTMethod.TRACE, +]; + +// Often, API description is arranged with a sequence of methods that lends +// itself to understanding by the human reading the documentation. +// +// However, the sequence of methods may not be appropriate for the machine +// reading the documentation in order to test the API. +// +// By sorting the transactions by their methods, it is possible to ensure that +// objects are created before they are read, updated, or deleted. +export default function sortTransactions( + transactions: Transaction[], +): Transaction[] { + // Convert the list of transactions into a list of tuples + // that hold each trasnaction index and details. + const tempTransactions: Array<[number, Transaction]> = transactions.map( + (transaction, index) => [index, transaction], + ); + + tempTransactions.sort( + ([leftIndex, leftTransaction], [rightIndex, rightTransaction]) => { + const methodIndexA = sortedMethods.indexOf( + leftTransaction.request.method, + ); + const methodIndexB = sortedMethods.indexOf( + rightTransaction.request.method, + ); + + // Sort transactions according to the transaction's request method + if (methodIndexA < methodIndexB) { + return -1; + } + + if (methodIndexA > methodIndexB) { + return 1; + } + + // In case two transactions' request methods are the same, + // preserve the original order of those transactions + return leftIndex - rightIndex; + }, + ); + + const cleanTransactions = tempTransactions.map( + ([_, transaction]) => transaction, + ); + + return cleanTransactions; +} From a6071e3befa0b579206f6913888a327ef6fe9761 Mon Sep 17 00:00:00 2001 From: artem-zakharchenko Date: Wed, 9 Oct 2019 10:39:09 +0200 Subject: [PATCH 7/9] chore: sets rootDir of unit tests to the root dir of monorepo --- packages/dredd/test/tsconfig.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/dredd/test/tsconfig.json b/packages/dredd/test/tsconfig.json index 102f42c8d..70b87cd4a 100644 --- a/packages/dredd/test/tsconfig.json +++ b/packages/dredd/test/tsconfig.json @@ -5,7 +5,8 @@ "target": "esnext", "allowSyntheticDefaultImports": true, "allowJs": true, - "noEmit": true + "noEmit": true, + "rootDir": "../" }, "include": ["./**/*.ts"] } From bdd0c21eefb2d06148371153a631f7beed58c861 Mon Sep 17 00:00:00 2001 From: artem-zakharchenko Date: Wed, 9 Oct 2019 10:39:23 +0200 Subject: [PATCH 8/9] test: adds new "sortTransactions" unit test --- .../dredd/test/unit/sortTransactions-test.ts | 104 ++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 packages/dredd/test/unit/sortTransactions-test.ts diff --git a/packages/dredd/test/unit/sortTransactions-test.ts b/packages/dredd/test/unit/sortTransactions-test.ts new file mode 100644 index 000000000..62b418fde --- /dev/null +++ b/packages/dredd/test/unit/sortTransactions-test.ts @@ -0,0 +1,104 @@ +import R from 'ramda'; +import { expect } from 'chai'; +import sortTransactions from '../../lib/sortTransactions'; +import { Transaction, RESTMethod } from '../../lib/general'; + +const createTransaction = (transaction: Partial) => { + return R.mergeDeepRight>({ + protocol: 'http:', + host: 'localhost', + })(transaction); +}; + +const transactions = Object.keys(RESTMethod).reduce< + Record +>( + (acc, method: RESTMethod) => { + return R.assoc( + method, + createTransaction({ + request: { + url: '/endpoint', + method, + }, + }), + acc, + ); + }, + {} as any, +); + +describe('sortTransactions', () => { + describe('given transactions list in arbitrary order', () => { + const sorted = sortTransactions([ + transactions.GET, + transactions.TRACE, + transactions.OPTIONS, + transactions.HEAD, + transactions.DELETE, + transactions.POST, + transactions.PATCH, + transactions.PUT, + transactions.CONNECT, + ]); + + it('should return transactions list sorted', () => { + expect(sorted).to.deep.equal([ + transactions.CONNECT, + transactions.OPTIONS, + transactions.POST, + transactions.GET, + transactions.HEAD, + transactions.PUT, + transactions.PATCH, + transactions.DELETE, + transactions.TRACE, + ]); + }); + }); + + describe('given multiple transactions with the same method', () => { + const getOne = createTransaction({ + id: 'one', + request: { + method: RESTMethod.GET, + url: '/endpoint', + }, + }); + + const getTwo = createTransaction({ + id: 'two', + request: { + method: RESTMethod.GET, + url: '/endpoint', + }, + }); + + // This doesn't assert the identity of transactions. + const sorted = sortTransactions([getOne as any, getTwo]); + + it('should sort transactions by occurence (asc)', () => { + expect(sorted).to.deep.equal([getOne, getTwo]); + }); + }); + + describe('given transactions list sorted properly', () => { + const transactionsList = [ + transactions.CONNECT, + transactions.OPTIONS, + transactions.POST, + transactions.POST, + transactions.GET, + transactions.HEAD, + transactions.PUT, + transactions.PATCH, + transactions.DELETE, + transactions.TRACE, + ]; + const sorted = sortTransactions(transactionsList); + + it('should return transactions list as-is', () => { + expect(sorted).to.deep.equal(transactionsList); + }); + }); +}); From 875fc455f132ab4d648551b242d57fe14990e005 Mon Sep 17 00:00:00 2001 From: artem-zakharchenko Date: Tue, 22 Oct 2019 13:50:32 +0200 Subject: [PATCH 9/9] fix: uses "HTTPMethod" enum over "RESTMethod" --- packages/dredd/lib/general.ts | 4 ++-- packages/dredd/lib/sortTransactions.ts | 24 +++++++++---------- .../dredd/test/unit/sortTransactions-test.ts | 12 +++++----- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/packages/dredd/lib/general.ts b/packages/dredd/lib/general.ts index f2acc2185..07e669fe1 100644 --- a/packages/dredd/lib/general.ts +++ b/packages/dredd/lib/general.ts @@ -1,4 +1,4 @@ -export enum RESTMethod { +export enum HTTPMethod { CONNECT = 'CONNECT', OPTIONS = 'OPTIONS', POST = 'POST', @@ -49,7 +49,7 @@ export interface Transaction { } export interface TransactionRequest { - method: RESTMethod; + method: HTTPMethod; url: string; body?: string; bodyEncoding?: BodyEncoding; diff --git a/packages/dredd/lib/sortTransactions.ts b/packages/dredd/lib/sortTransactions.ts index 2f9f6ab1e..3abe6447a 100644 --- a/packages/dredd/lib/sortTransactions.ts +++ b/packages/dredd/lib/sortTransactions.ts @@ -1,15 +1,15 @@ -import { RESTMethod, Transaction } from './general'; - -const sortedMethods: RESTMethod[] = [ - RESTMethod.CONNECT, - RESTMethod.OPTIONS, - RESTMethod.POST, - RESTMethod.GET, - RESTMethod.HEAD, - RESTMethod.PUT, - RESTMethod.PATCH, - RESTMethod.DELETE, - RESTMethod.TRACE, +import { HTTPMethod, Transaction } from './general'; + +const sortedMethods: HTTPMethod[] = [ + HTTPMethod.CONNECT, + HTTPMethod.OPTIONS, + HTTPMethod.POST, + HTTPMethod.GET, + HTTPMethod.HEAD, + HTTPMethod.PUT, + HTTPMethod.PATCH, + HTTPMethod.DELETE, + HTTPMethod.TRACE, ]; // Often, API description is arranged with a sequence of methods that lends diff --git a/packages/dredd/test/unit/sortTransactions-test.ts b/packages/dredd/test/unit/sortTransactions-test.ts index 62b418fde..30db8cbd4 100644 --- a/packages/dredd/test/unit/sortTransactions-test.ts +++ b/packages/dredd/test/unit/sortTransactions-test.ts @@ -1,7 +1,7 @@ import R from 'ramda'; import { expect } from 'chai'; import sortTransactions from '../../lib/sortTransactions'; -import { Transaction, RESTMethod } from '../../lib/general'; +import { Transaction, HTTPMethod } from '../../lib/general'; const createTransaction = (transaction: Partial) => { return R.mergeDeepRight>({ @@ -10,10 +10,10 @@ const createTransaction = (transaction: Partial) => { })(transaction); }; -const transactions = Object.keys(RESTMethod).reduce< - Record +const transactions = Object.keys(HTTPMethod).reduce< + Record >( - (acc, method: RESTMethod) => { + (acc, method: HTTPMethod) => { return R.assoc( method, createTransaction({ @@ -61,7 +61,7 @@ describe('sortTransactions', () => { const getOne = createTransaction({ id: 'one', request: { - method: RESTMethod.GET, + method: HTTPMethod.GET, url: '/endpoint', }, }); @@ -69,7 +69,7 @@ describe('sortTransactions', () => { const getTwo = createTransaction({ id: 'two', request: { - method: RESTMethod.GET, + method: HTTPMethod.GET, url: '/endpoint', }, });