diff --git a/.nvmrc b/.nvmrc
new file mode 100644
index 00000000..368fe859
--- /dev/null
+++ b/.nvmrc
@@ -0,0 +1 @@
+v8.12.0
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 00000000..62af5cf7
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,23 @@
+# Contributing
+
+Thank you for deciding to contribute to Gavel.js. Please read the guidelines below to ensure the smoothest developer's experience during the involvement.
+
+## Workflow
+
+1. Clone the repository.
+2. Install dependencies:
+
+```bash
+npm install
+```
+
+3. Use the correct NodeJS version:
+
+```bash
+nvm use
+```
+
+4. Create a branch for a feature or a bugfix.
+5. Follow the [Conventional Commit Messages](https://github.com/angular/angular.js/blob/master/DEVELOPERS.md#user-content--git-commit-guidelines) for commit messages.
+6. Issue a pull request and undergo a code review.
+7. Upon approval, merge the pull request.
diff --git a/README.md b/README.md
index cc02e6cf..da5d41cf 100644
--- a/README.md
+++ b/README.md
@@ -1,24 +1,249 @@
-# Gavel.js — Validator of HTTP Transactions
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-[![npm version](https://badge.fury.io/js/gavel.svg)](https://badge.fury.io/js/gavel)
-[![Build Status](https://travis-ci.org/apiaryio/gavel.js.svg?branch=master)](https://travis-ci.org/apiaryio/gavel.js)
-[![Build status](https://ci.appveyor.com/api/projects/status/0cpnaoakhs8q58tn/branch/master?svg=true)](https://ci.appveyor.com/project/Apiary/gavel-js/branch/master)
-[![Coverage Status](https://coveralls.io/repos/apiaryio/gavel.js/badge.svg?branch=master)](https://coveralls.io/r/apiaryio/gavel.js?branch=master)
-[![Known Vulnerabilities](https://snyk.io/test/npm/gavel/badge.svg)](https://snyk.io/test/npm/gavel)
+
-![Gavel.js - Validator of HTTP Transactions](https://raw.github.com/apiaryio/gavel/master/img/gavel.png?v=1)
+
+
+
-Gavel detects important differences between actual and expected HTTP transactions (HTTP request and response pairs). Gavel also decides whether the actual HTTP transaction is valid or not.
+Gavel
-## Installation
+Gavel tells you whether an actual HTTP message is valid against an expected HTTP message.
-```sh
-$ npm install gavel
+## Install
+
+```bash
+npm install gavel
+```
+
+## Usage
+
+### CLI
+
+```bash
+# (Optional) Record HTTP messages
+curl -s --trace - http://httpbin.org/ip | curl-trace-parser > expected
+curl -s --trace - http://httpbin.org/ip | curl-trace-parser > actual
+
+# Perform the validation
+cat actual | gavel expected
+```
+
+> **Gavel CLI is not supported on Windows**. Example above uses [`curl-trace-parser`](https://github.com/apiaryio/curl-trace-parser).
+
+### NodeJS
+
+```js
+const gavel = require('gavel');
+
+// Define HTTP messages
+const expected = {
+ statusCode: 200,
+ headers: {
+ 'Content-Type': 'application/json'
+ }
+};
+
+const actual = {
+ statusCode: 404,
+ headers: {
+ 'Content-Type': 'application/json'
+ }
+};
+
+// Perform the validation
+const result = gavel.validate(expected, actual);
+```
+
+The code above would return the following validation `result`:
+
+```js
+{
+ valid: false,
+ fields: {
+ statusCode: {
+ valid: false,
+ kind: 'text',
+ values: {
+ expected: '200',
+ actual: '404'
+ },
+ errors: [
+ {
+ message: `Expected status code '200', but got '404'.`,
+ values: {
+ expected: '200',
+ actual: '404'
+ }
+ }
+ ]
+ },
+ headers: {
+ valid: true,
+ kind: 'json',
+ values: {
+ expected: {
+ 'Content-Type': 'application/json'
+ },
+ actual: {
+ 'Content-Type': 'application/json'
+ }
+ },
+ errors: []
+ }
+ }
+}
+```
+
+### Usage with JSON Schema
+
+> When a parsable JSON body is expected without an explicit schema the [default schema](https://github.com/apiaryio/gavel-spec/blob/master/features/expectations/bodyJsonExample.feature) is inferred.
+
+You can describe the body expectations using [JSON Schema](https://json-schema.org/) by providing a valid schema to the `bodySchema` property of the expected HTTP message:
+
+```js
+const gavel = require('gavel');
+
+const expected = {
+ bodySchema: {
+ type: 'object',
+ properties: {
+ fruits: {
+ type: 'array',
+ items: {
+ type: 'string'
+ }
+ }
+ }
+ }
+};
+
+const actual = {
+ body: JSON.stringify({
+ fruits: ['apple', 'banana', 2]
+ })
+};
+
+const result = gavel.validate(expected, actual);
+```
+
+The validation `result` against the given JSON Schema will look as follows:
+
+```js
+{
+ valid: false,
+ fields: {
+ body: {
+ valid: false,
+ kind: 'json',
+ values: {
+ actual: "{\"fruits\":[\"apple\",\"banana\",2]}"
+ },
+ errors: [
+ {
+ message: `At '/fruits/2' Invalid type: number (expected string)`,
+ location: {
+ pointer: '/fruits/2'
+ }
+ }
+ ]
+ }
+ }
+}
+```
+
+> Note that JSON schema Draft-05+ are not currently supported. [Follow the support progress](https://github.com/apiaryio/gavel.js/issues/90).
+
+## Examples
+
+Take a look at the [Gherkin](https://cucumber.io/docs/gherkin/) specification, which describes on examples how validation of each field behaves:
+
+- [`method`](https://github.com/apiaryio/gavel-spec/blob/master/features/javascript/fields/method)
+- [`statusCode`](https://github.com/apiaryio/gavel-spec/blob/master/features/javascript/fields/statusCode)
+- [`headers`](https://github.com/apiaryio/gavel-spec/blob/master/features/javascript/fields/headers)
+- [`body`](https://github.com/apiaryio/gavel-spec/blob/master/features/javascript/fields/body)
+- [`bodySchema`](https://github.com/apiaryio/gavel-spec/blob/master/features/javascript/fields/bodySchema)
+
+## Type definitions
+
+Type definitions below are described using [TypeScript](https://www.typescriptlang.org/) syntax.
+
+### Input
+
+> Gavel makes no assumptions over the validity of a given HTTP message according to the HTTP specification (RFCs [2616](https://www.ietf.org/rfc/rfc2616.txt), [7540](https://httpwg.org/specs/rfc7540.html)) and will accept any input matching the input type definition. Gavel will throw an exception when given malformed input data.
+
+Both expected and actual HTTP messages (no matter request or response) inherit from a single `HttpMessage` interface:
+
+```ts
+interface HttpMessage {
+ method?: string;
+ statusCode?: number;
+ headers?: Record | string;
+ body?: string;
+ bodySchema?: Object | string;
+}
```
-## Documentation
+### Output
+
+```ts
+// Field kind describes the type of a field's values
+// subjected to the end comparison.
+enum ValidationKind {
+ null // non-comparable data (validation didn't happen)
+ text // compared as text
+ json // compared as JSON
+}
+
+interface ValidationResult {
+ valid: boolean // validity of the actual message
+ fields: {
+ [fieldName: string]: {
+ valid: boolean // validity of a single field
+ kind: ValidationKind
+ values: { // end compared values (coerced, normalized)
+ actual: any
+ expected: any
+ }
+ errors: FieldError[]
+ }
+ }
+}
+
+interface FieldError {
+ message: string
+ location?: { // kind-specific additional information
+ // kind: json
+ pointer?: string
+ property?: string[]
+ }
+ values?: {
+ expected: any
+ actual: any
+ }
+}
+```
+
+## API
+
+- `validate(expected: HttpMessage, actual: HttpMessage): ValidationResult`
-Gavel.js is a JavaScript implementation of the [Gavel behavior specification](https://www.relishapp.com/apiary/gavel/) ([repository](https://github.com/apiaryio/gavel-spec)):
+## License
-- [Gavel.js-specific documentation](https://www.relishapp.com/apiary/gavel/docs/javascript/)
-- [CLI documentation](https://www.relishapp.com/apiary/gavel/docs/command-line-interface/)
+[MIT](LICENSE)
diff --git a/bin/gavel b/bin/gavel
index d7f8e779..be54be16 100755
--- a/bin/gavel
+++ b/bin/gavel
@@ -33,7 +33,7 @@ process.stdin.on('end', function() {
const requestResult = gavel.validate(expectedRequest, realRequest);
const responseResult = gavel.validate(expectedResponse, realResponse);
- if (requestResult.isValid && responseResult.isValid) {
+ if (requestResult.valid && responseResult.valid) {
process.exit(0);
} else {
process.exit(1);
diff --git a/lib/units/isValid.js b/lib/units/isValid.js
index 6e427cd1..d5a84fab 100644
--- a/lib/units/isValid.js
+++ b/lib/units/isValid.js
@@ -10,11 +10,11 @@ function isValidField({ errors }) {
/**
* Returns a boolean indicating the given validation result as valid.
- * @param {Object} validationResult
+ * @param {Object} fields
* @returns {boolean}
*/
-function isValidResult(validationResult) {
- return Object.values(validationResult.fields).every(isValidField);
+function isValidResult(fields) {
+ return Object.values(fields).every(isValidField);
}
module.exports = {
diff --git a/lib/units/validateBody.js b/lib/units/validateBody.js
index 58ed19de..e7c3b687 100644
--- a/lib/units/validateBody.js
+++ b/lib/units/validateBody.js
@@ -3,6 +3,7 @@ const mediaTyper = require('media-typer');
const contentTypeUtils = require('content-type');
const { TextDiff, JsonExample, JsonSchema } = require('../validators');
+const isset = require('../utils/isset');
const { isValidField } = require('./isValid');
function isPlainText(mediaType) {
@@ -126,16 +127,17 @@ function getBodyValidator(realType, expectedType) {
};
const validators = [
- [TextDiff, both(isPlainText)],
+ [TextDiff, both(isPlainText), 'text'],
// List JsonSchema first, because weak predicate of JsonExample
// would resolve on "application/schema+json" media type too.
[
JsonSchema,
(real, expected) => {
return isJson(real) && isJsonSchema(expected);
- }
+ },
+ 'json'
],
- [JsonExample, both(isJson)]
+ [JsonExample, both(isJson), 'json']
];
const validator = validators.find(([_name, predicate]) => {
@@ -143,13 +145,13 @@ function getBodyValidator(realType, expectedType) {
});
if (!validator) {
- const error = `Can't validate real media type '${mediaTyper.format(
+ const error = `Can't validate actual media type '${mediaTyper.format(
realType
- )}' against expected media type '${mediaTyper.format(expectedType)}'.`;
- return [error, null];
+ )}' against the expected media type '${mediaTyper.format(expectedType)}'.`;
+ return [error, null, null];
}
- return [null, validator[0]];
+ return [null, validator[0], validator[2]];
}
/**
@@ -157,10 +159,20 @@ function getBodyValidator(realType, expectedType) {
* @param {Object} expected
* @param {Object} real
*/
-function validateBody(expected, real) {
+function validateBody(expected, actual) {
+ const values = {
+ actual: actual.body
+ };
+
+ // Prevent assigning { expected: undefined }.
+ // Also ignore "bodySchema" as the expected value.
+ if (isset(expected.body)) {
+ values.expected = expected.body;
+ }
+
const errors = [];
- const realBodyType = typeof real.body;
- const hasEmptyRealBody = real.body === '';
+ const realBodyType = typeof actual.body;
+ const hasEmptyRealBody = actual.body === '';
// Throw when user input for real body is not a string.
if (realBodyType !== 'string') {
@@ -170,8 +182,8 @@ function validateBody(expected, real) {
}
const [realTypeError, realType] = getBodyType(
- real.body,
- real.headers && real.headers['content-type'],
+ actual.body,
+ actual.headers && actual.headers['content-type'],
'real'
);
@@ -185,13 +197,15 @@ function validateBody(expected, real) {
if (realTypeError) {
errors.push({
- message: realTypeError
+ message: realTypeError,
+ values
});
}
if (expectedTypeError) {
errors.push({
- message: expectedTypeError
+ message: expectedTypeError,
+ values
});
}
@@ -199,8 +213,8 @@ function validateBody(expected, real) {
// Skipping body validation in case errors during
// real/expected body type definition.
- const [validatorError, ValidatorClass] = hasErrors
- ? [null, null]
+ const [validatorError, ValidatorClass, kind] = hasErrors
+ ? [null, null, null]
: getBodyValidator(realType, expectedType);
if (validatorError) {
@@ -213,11 +227,13 @@ function validateBody(expected, real) {
errors.push({
message: `Expected "body" of "${mediaTyper.format(
expectedType
- )}" media type, but actual "body" is missing.`
+ )}" media type, but actual "body" is missing.`,
+ values
});
} else {
errors.push({
- message: validatorError
+ message: validatorError,
+ values
});
}
}
@@ -226,19 +242,22 @@ function validateBody(expected, real) {
const validator =
ValidatorClass &&
new ValidatorClass(
- real.body,
- usesJsonSchema ? expected.bodySchema : expected.body
+ usesJsonSchema ? expected.bodySchema : expected.body,
+ actual.body
);
- const rawData = validator && validator.validate();
+
+ // Calling "validate()" often updates an internal state of a validator.
+ // That state is later used to output the gavel-compliant results.
+ // Cannot remove until validators are refactored into simple functions.
+ // @see https://github.com/apiaryio/gavel.js/issues/150
+ validator && validator.validate();
const validationErrors = validator ? validator.evaluateOutputToResults() : [];
errors.push(...validationErrors);
return {
- isValid: isValidField({ errors }),
- validator: ValidatorClass && ValidatorClass.name,
- realType: mediaTyper.format(realType),
- expectedType: mediaTyper.format(expectedType),
- rawData,
+ valid: isValidField({ errors }),
+ kind,
+ values,
errors
};
}
diff --git a/lib/units/validateHeaders.js b/lib/units/validateHeaders.js
index bcfed620..5d5aaef2 100644
--- a/lib/units/validateHeaders.js
+++ b/lib/units/validateHeaders.js
@@ -14,19 +14,25 @@ function getHeadersType(headers) {
* @param {Object} expected
* @param {Object} real
*/
-function validateHeaders(expected, real) {
- const expectedType = getHeadersType(expected.headers);
- const realType = getHeadersType(real.headers);
+function validateHeaders(expected, actual) {
+ const values = {
+ expected: expected.headers,
+ actual: actual.headers
+ };
+ const expectedType = getHeadersType(values.expected);
+ const actualType = getHeadersType(values.actual);
const errors = [];
const hasJsonHeaders =
- realType === APIARY_JSON_HEADER_TYPE &&
+ actualType === APIARY_JSON_HEADER_TYPE &&
expectedType === APIARY_JSON_HEADER_TYPE;
const validator = hasJsonHeaders
- ? new HeadersJsonExample(real.headers, expected.headers)
+ ? new HeadersJsonExample(values.expected, values.actual)
: null;
- const rawData = validator && validator.validate();
+
+ // if you don't call ".validate()", it never evaluates any results.
+ validator && validator.validate();
if (validator) {
errors.push(...validator.evaluateOutputToResults());
@@ -34,19 +40,18 @@ function validateHeaders(expected, real) {
errors.push({
message: `\
No validator found for real data media type
-"${realType}"
+"${actualType}"
and expected data media type
"${expectedType}".\
-`
+`,
+ values
});
}
return {
- isValid: isValidField({ errors }),
- validator: validator && 'HeadersJsonExample',
- realType,
- expectedType,
- rawData,
+ valid: isValidField({ errors }),
+ kind: hasJsonHeaders ? 'json' : 'text',
+ values,
errors
};
}
diff --git a/lib/units/validateMethod.js b/lib/units/validateMethod.js
index ef14f144..ba30ef09 100644
--- a/lib/units/validateMethod.js
+++ b/lib/units/validateMethod.js
@@ -1,22 +1,22 @@
-const APIARY_METHOD_TYPE = 'text/vnd.apiary.method';
-
-function validateMethod(expected, real) {
- const { method: expectedMethod } = expected;
- const { method: realMethod } = real;
- const isValid = realMethod === expectedMethod;
+function validateMethod(expected, actual) {
+ const values = {
+ expected: expected.method,
+ actual: actual.method
+ };
+ const valid = values.actual === values.expected;
const errors = [];
- if (!isValid) {
+ if (!valid) {
errors.push({
- message: `Expected "method" field to equal "${expectedMethod}", but got "${realMethod}".`
+ message: `Expected method '${values.expected}', but got '${values.actual}'.`,
+ values
});
}
return {
- isValid,
- validator: null,
- realType: APIARY_METHOD_TYPE,
- expectedType: APIARY_METHOD_TYPE,
+ valid,
+ kind: 'text',
+ values,
errors
};
}
diff --git a/lib/units/validateStatusCode.js b/lib/units/validateStatusCode.js
index 5c757514..9d867dd9 100644
--- a/lib/units/validateStatusCode.js
+++ b/lib/units/validateStatusCode.js
@@ -1,28 +1,27 @@
-const APIARY_STATUS_CODE_TYPE = 'text/vnd.apiary.status-code';
-
/**
* Validates given real and expected status codes.
* @param {Object} real
* @param {number} expected
*/
-function validateStatusCode(expected, real) {
- const isValid = real.statusCode === expected.statusCode;
+function validateStatusCode(expected, actual) {
+ const values = {
+ expected: expected.statusCode,
+ actual: actual.statusCode
+ };
+ const valid = values.actual === values.expected;
const errors = [];
- if (!isValid) {
+ if (!valid) {
errors.push({
- message: `Status code is '${real.statusCode}' instead of '${
- expected.statusCode
- }'`
+ message: `Expected status code '${values.expected}', but got '${values.actual}'.`,
+ values
});
}
return {
- isValid,
- validator: 'TextDiff',
- realType: APIARY_STATUS_CODE_TYPE,
- expectedType: APIARY_STATUS_CODE_TYPE,
- rawData: '',
+ valid,
+ kind: 'text',
+ values,
errors
};
}
diff --git a/lib/units/validateURI.js b/lib/units/validateURI.js
index 7f769c11..8c5a6cb9 100644
--- a/lib/units/validateURI.js
+++ b/lib/units/validateURI.js
@@ -1,8 +1,6 @@
const url = require('url');
const deepEqual = require('deep-equal');
-const APIARY_URI_TYPE = 'text/vnd.apiary.uri';
-
/**
* Parses the given URI and returns the properties
* elligible for comparison. Leaves out raw properties like "path"
@@ -20,32 +18,34 @@ const parseURI = (uri) => {
};
};
-const validateURI = (expected, real) => {
- const { uri: expectedURI } = expected;
- const { uri: realURI } = real;
+const validateURI = (expected, actual) => {
+ const values = {
+ expected: expected.uri,
+ actual: actual.uri
+ };
// Parses URI to perform a correct comparison:
// - literal comparison of pathname
// - order-insensitive comparison of query parameters
- const parsedExpected = parseURI(expectedURI, true);
- const parsedReal = parseURI(realURI, true);
+ const parsedExpected = parseURI(values.expected);
+ const parsedActual = parseURI(values.actual);
// Note the different order of arguments between
// "validateURI" and "deepEqual".
- const isValid = deepEqual(parsedReal, parsedExpected);
+ const valid = deepEqual(parsedActual, parsedExpected);
const errors = [];
- if (!isValid) {
+ if (!valid) {
errors.push({
- message: `Expected "uri" field to equal "${expectedURI}", but got: "${realURI}".`
+ message: `Expected URI '${values.expected}', but got '${values.actual}'.`,
+ values
});
}
return {
- isValid,
- validator: null,
- expectedType: APIARY_URI_TYPE,
- realType: APIARY_URI_TYPE,
+ valid,
+ kind: 'text',
+ values,
errors
};
};
diff --git a/lib/validate.js b/lib/validate.js
index ba456b72..5272c311 100644
--- a/lib/validate.js
+++ b/lib/validate.js
@@ -8,16 +8,15 @@ const { validateStatusCode } = require('./units/validateStatusCode');
const { validateHeaders } = require('./units/validateHeaders');
const { validateBody } = require('./units/validateBody');
-function validate(expectedMessage, realMessage) {
- const result = {
- fields: {}
- };
+function validate(expectedMessage, actualMessage) {
+ const result = {};
+ const fields = {};
// Uses strict coercion on real message.
// Strict coercion ensures that real message always has properties
// illegible for validation with the expected message, even if they
// are not present in the real message.
- const real = normalize(coerce(realMessage));
+ const actual = normalize(coerce(actualMessage));
// Use weak coercion on expected message.
// Weak coercion will transform only the properties present in the
@@ -27,27 +26,28 @@ function validate(expectedMessage, realMessage) {
const expected = normalize(coerceWeak(expectedMessage));
if (expected.method) {
- result.fields.method = validateMethod(expected, real);
+ fields.method = validateMethod(expected, actual);
}
if (expected.uri) {
- result.fields.uri = validateURI(expected, real);
+ fields.uri = validateURI(expected, actual);
}
if (expected.statusCode) {
- result.fields.statusCode = validateStatusCode(expected, real);
+ fields.statusCode = validateStatusCode(expected, actual);
}
if (expected.headers) {
- result.fields.headers = validateHeaders(expected, real);
+ fields.headers = validateHeaders(expected, actual);
}
if (isset(expected.body) || isset(expected.bodySchema)) {
- result.fields.body = validateBody(expected, real);
+ fields.body = validateBody(expected, actual);
}
- // Indicates the validity of the real message
- result.isValid = isValidResult(result);
+ // Indicates the validity of the actual message
+ result.valid = isValidResult(fields);
+ result.fields = fields;
return result;
}
diff --git a/lib/validators/headers-json-example.js b/lib/validators/headers-json-example.js
index af4b4074..659d72ad 100644
--- a/lib/validators/headers-json-example.js
+++ b/lib/validators/headers-json-example.js
@@ -38,9 +38,9 @@ const getSchema = (json) => {
};
class HeadersJsonExample extends JsonSchema {
- constructor(real, expected) {
- if (typeof real !== 'object') {
- throw new errors.MalformedDataError('Real is not an Object');
+ constructor(expected, actual) {
+ if (typeof actual !== 'object') {
+ throw new errors.MalformedDataError('Actual is not an Object');
}
if (typeof expected !== 'object') {
@@ -48,7 +48,7 @@ class HeadersJsonExample extends JsonSchema {
}
const preparedExpected = prepareHeaders(expected);
- const preparedReal = prepareHeaders(real);
+ const preparedActual = prepareHeaders(actual);
const preparedSchema = getSchema(preparedExpected);
if (preparedSchema && preparedSchema.properties) {
@@ -60,10 +60,10 @@ class HeadersJsonExample extends JsonSchema {
});
}
- super(preparedReal, preparedSchema);
+ super(preparedSchema, preparedActual);
this.expected = preparedExpected;
- this.real = preparedReal;
+ this.actual = preparedActual;
this.schema = preparedSchema;
}
diff --git a/lib/validators/json-example.js b/lib/validators/json-example.js
index 84b8d323..0e2b2573 100644
--- a/lib/validators/json-example.js
+++ b/lib/validators/json-example.js
@@ -26,12 +26,12 @@ class JsonExample extends JsonSchema {
* @throw {SchemaNotJsonParsableError} when given schema is not a json parsable string or valid json
* @throw {NotEnoughDataError} when at least one of expected data and json schema is not given
*/
- constructor(real, expected) {
- if (typeof real !== 'string') {
+ constructor(expected, actual) {
+ if (typeof actual !== 'string') {
const outError = new errors.MalformedDataError(
- 'JsonExample validator: provided real data is not string'
+ 'JsonExample validator: provided actual data is not string'
);
- outError.data = real;
+ outError.data = actual;
throw outError;
}
@@ -44,7 +44,7 @@ class JsonExample extends JsonSchema {
}
const schema = getSchema(expected);
- super(real, schema);
+ super(schema, actual);
}
}
diff --git a/lib/validators/json-schema.js b/lib/validators/json-schema.js
index c43d716e..14b533a2 100644
--- a/lib/validators/json-schema.js
+++ b/lib/validators/json-schema.js
@@ -41,17 +41,11 @@ const jsonSchemaOptions = {
singleError: false,
messages: {
minLength: (prop, val, validator) =>
- `The ${prop} property must be at least ${validator} characters long (currently ${
- val.length
- } characters long).`,
+ `The ${prop} property must be at least ${validator} characters long (currently ${val.length} characters long).`,
maxLength: (prop, val, validator) =>
- `The ${prop} property must not exceed ${validator} characters (currently${
- val.length
- } characters long).`,
+ `The ${prop} property must not exceed ${validator} characters (currently${val.length} characters long).`,
length: (prop, val, validator) =>
- `The ${prop} property must be exactly ${validator} characters long (currently ${
- val.length
- } characters long).`,
+ `The ${prop} property must be exactly ${validator} characters long (currently ${val.length} characters long).`,
format: (prop, val, validator) =>
`The ${prop} property must be ${getArticle(
validator[0]
@@ -72,13 +66,9 @@ const jsonSchemaOptions = {
pattern: (prop, val, validator) =>
`The ${prop} value (${val}) does not match the ${validator} pattern.`,
maxItems: (prop, val, validator) =>
- `The ${prop} property must not contain more than ${validator} items (currently contains ${
- val.length
- } items).`,
+ `The ${prop} property must not contain more than ${validator} items (currently contains ${val.length} items).`,
minItems: (prop, val, validator) =>
- `The ${prop} property must contain at least ${validator} items (currently contains ${
- val.length
- } items).`,
+ `The ${prop} property must contain at least ${validator} items (currently contains ${val.length} items).`,
divisibleBy: (prop, val, validator) =>
`The ${prop} property is not divisible by ${validator} (current value is ${JSON.stringify(
val
@@ -90,12 +80,12 @@ const jsonSchemaOptions = {
class JsonSchema {
/**
* Constructs a JsonValidator and validates given data.
- * @param {Object | string} data
* @param {Object | string} schema
+ * @param {Object | string} data
*/
- constructor(data, schema) {
- this.data = data;
+ constructor(schema, data) {
this.schema = schema;
+ this.data = data;
if (typeof this.data === 'string') {
try {
@@ -138,9 +128,7 @@ class JsonSchema {
const validationResult = tv4.validateResult(this.schema, metaSchema);
if (!validationResult.valid) {
throw new errors.JsonSchemaNotValid(
- `JSON schema is not valid draft ${this.jsonSchemaVersion}! ${
- validationResult.error.message
- } at path "${validationResult.error.dataPath}"`
+ `JSON schema is not valid draft ${this.jsonSchemaVersion}! ${validationResult.error.message} at path "${validationResult.error.dataPath}"`
);
}
}
@@ -230,23 +218,27 @@ class JsonSchema {
const results = Array.from({ length: data.length }, (_, index) => {
const item = data[index];
+ const { message, property } = item;
let pathArray = [];
- if (item.property === null) {
+ if (property === null) {
pathArray = [];
} else if (
- Array.isArray(item.property) &&
- item.property.length === 1 &&
- [null, undefined].includes(item.property[0])
+ Array.isArray(property) &&
+ property.length === 1 &&
+ [null, undefined].includes(property[0])
) {
pathArray = [];
} else {
- pathArray = item.property;
+ pathArray = property;
}
return {
- pointer: jsonPointer.compile(pathArray),
- message: item.message
+ message,
+ location: {
+ pointer: jsonPointer.compile(pathArray),
+ property
+ }
};
});
@@ -314,9 +306,9 @@ class JsonSchema {
const pointer = jsonPointer.compile(pathArray);
amandaCompatibleError[index] = {
+ message: `At '${pointer}' ${error.message}`,
property: pathArray,
attributeValue: true,
- message: `At '${pointer}' ${error.message}`,
validatorName: 'error'
};
}
diff --git a/lib/validators/text-diff.js b/lib/validators/text-diff.js
index 0fc73f92..b5923dba 100644
--- a/lib/validators/text-diff.js
+++ b/lib/validators/text-diff.js
@@ -1,13 +1,12 @@
-const DiffMatchPatch = require('googlediff');
const errors = require('../errors');
class TextDiff {
- constructor(real, expected) {
- if (typeof real !== 'string') {
+ constructor(expected, actual) {
+ if (typeof actual !== 'string') {
const outError = new errors.DataNotStringError(
- 'String validator real: input data is not string'
+ 'String validator actual: input data is not string'
);
- outError.data = real;
+ outError.data = actual;
throw outError;
}
@@ -19,51 +18,27 @@ class TextDiff {
throw outError;
}
- this.real = real;
this.expected = expected;
+ this.actual = actual;
}
validate() {
- const sanitizeSurrogatePairs = (data) => {
- return data
- .replace(/[\uD800-\uDBFF]/g, '')
- .replace(/[\uDC00-\uDFFF]/g, '');
- };
-
- this.output = null;
- const dmp = new DiffMatchPatch();
-
- try {
- const patch = dmp.patch_make(this.real, this.expected);
- this.output = dmp.patch_toText(patch);
- return this.output;
- } catch (error) {
- if (error instanceof URIError) {
- const patch = dmp.patch_make(
- sanitizeSurrogatePairs(this.real),
- sanitizeSurrogatePairs(this.expected)
- );
- this.output = dmp.patch_toText(patch);
- return this.output;
- }
-
- throw error;
- }
+ this.valid = this.actual === this.expected;
+ return this.valid;
}
- evaluateOutputToResults(data) {
- if (!data) {
- data = this.output;
- }
-
- if (!data) {
+ evaluateOutputToResults() {
+ if (this.valid) {
return [];
}
return [
{
- // TODO Improve the message to contain real and expected data
- message: 'Real and expected data does not match.'
+ message: 'Actual and expected data do not match.',
+ values: {
+ expected: this.expected,
+ actual: this.actual
+ }
}
];
}
diff --git a/package-lock.json b/package-lock.json
index 7011e32c..d1f20d4e 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -3483,8 +3483,7 @@
"ansi-regex": {
"version": "2.1.1",
"bundled": true,
- "dev": true,
- "optional": true
+ "dev": true
},
"aproba": {
"version": "1.2.0",
@@ -3505,14 +3504,12 @@
"balanced-match": {
"version": "1.0.0",
"bundled": true,
- "dev": true,
- "optional": true
+ "dev": true
},
"brace-expansion": {
"version": "1.1.11",
"bundled": true,
"dev": true,
- "optional": true,
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@@ -3527,20 +3524,17 @@
"code-point-at": {
"version": "1.1.0",
"bundled": true,
- "dev": true,
- "optional": true
+ "dev": true
},
"concat-map": {
"version": "0.0.1",
"bundled": true,
- "dev": true,
- "optional": true
+ "dev": true
},
"console-control-strings": {
"version": "1.1.0",
"bundled": true,
- "dev": true,
- "optional": true
+ "dev": true
},
"core-util-is": {
"version": "1.0.2",
@@ -3657,8 +3651,7 @@
"inherits": {
"version": "2.0.3",
"bundled": true,
- "dev": true,
- "optional": true
+ "dev": true
},
"ini": {
"version": "1.3.5",
@@ -3670,7 +3663,6 @@
"version": "1.0.0",
"bundled": true,
"dev": true,
- "optional": true,
"requires": {
"number-is-nan": "^1.0.0"
}
@@ -3685,7 +3677,6 @@
"version": "3.0.4",
"bundled": true,
"dev": true,
- "optional": true,
"requires": {
"brace-expansion": "^1.1.7"
}
@@ -3693,14 +3684,12 @@
"minimist": {
"version": "0.0.8",
"bundled": true,
- "dev": true,
- "optional": true
+ "dev": true
},
"minipass": {
"version": "2.3.5",
"bundled": true,
"dev": true,
- "optional": true,
"requires": {
"safe-buffer": "^5.1.2",
"yallist": "^3.0.0"
@@ -3719,7 +3708,6 @@
"version": "0.5.1",
"bundled": true,
"dev": true,
- "optional": true,
"requires": {
"minimist": "0.0.8"
}
@@ -3800,8 +3788,7 @@
"number-is-nan": {
"version": "1.0.1",
"bundled": true,
- "dev": true,
- "optional": true
+ "dev": true
},
"object-assign": {
"version": "4.1.1",
@@ -3813,7 +3800,6 @@
"version": "1.4.0",
"bundled": true,
"dev": true,
- "optional": true,
"requires": {
"wrappy": "1"
}
@@ -3899,8 +3885,7 @@
"safe-buffer": {
"version": "5.1.2",
"bundled": true,
- "dev": true,
- "optional": true
+ "dev": true
},
"safer-buffer": {
"version": "2.1.2",
@@ -3936,7 +3921,6 @@
"version": "1.0.2",
"bundled": true,
"dev": true,
- "optional": true,
"requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",
@@ -3956,7 +3940,6 @@
"version": "3.0.1",
"bundled": true,
"dev": true,
- "optional": true,
"requires": {
"ansi-regex": "^2.0.0"
}
@@ -4000,14 +3983,12 @@
"wrappy": {
"version": "1.0.2",
"bundled": true,
- "dev": true,
- "optional": true
+ "dev": true
},
"yallist": {
"version": "3.0.3",
"bundled": true,
- "dev": true,
- "optional": true
+ "dev": true
}
}
},
@@ -4024,9 +4005,9 @@
"dev": true
},
"gavel-spec": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/gavel-spec/-/gavel-spec-3.0.2.tgz",
- "integrity": "sha512-r8EZvdety8qc80Vf/zJbKCPHYMPBs2Wucg1+qbhTyw20I4doeZZXaB01XQhP8M9qgBnjP7oSJEbNfLputSJ4Dg==",
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/gavel-spec/-/gavel-spec-4.0.0.tgz",
+ "integrity": "sha512-+8hBF2qyrCpos47y9+PjL0FL5ZwIwvfW7W/RQKLq6X1GJ8C5/KHPLrjOcuRdpwMrz1C7HYPGgYCBuaE3a4iWew==",
"dev": true
},
"get-assigned-identifiers": {
@@ -4174,11 +4155,6 @@
"pinkie-promise": "^2.0.0"
}
},
- "googlediff": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/googlediff/-/googlediff-0.1.0.tgz",
- "integrity": "sha1-mazwXMBiI+tmwpAI2B+bLRjCRT0="
- },
"graceful-fs": {
"version": "4.1.15",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz",
diff --git a/package.json b/package.json
index 06977618..14ab9c39 100644
--- a/package.json
+++ b/package.json
@@ -19,8 +19,12 @@
"coveralls": "nyc --reporter=text-lcov npm run test:server | coveralls",
"ci:lint": "npm run lint",
"ci:test": "npm run coveralls && npm run test:browser && npm run test:features",
- "ci:release": "semantic-release",
- "precommit": "lint-staged"
+ "ci:release": "semantic-release"
+ },
+ "husky": {
+ "hooks": {
+ "pre-commit": "lint-staged"
+ }
},
"lint-staged": {
"*.js": [
@@ -37,7 +41,6 @@
"content-type": "1.0.4",
"curl-trace-parser": "0.0.10",
"deep-equal": "1.0.1",
- "googlediff": "0.1.0",
"http-string-parser": "0.0.6",
"json-parse-helpfulerror": "1.0.3",
"json-pointer": "0.6.0",
@@ -54,7 +57,7 @@
"eslint-config-airbnb-base": "13.2.0",
"eslint-config-prettier": "6.0.0",
"eslint-plugin-import": "2.18.0",
- "gavel-spec": "3.0.2",
+ "gavel-spec": "4.0.0",
"husky": "3.0.0",
"lint-staged": "9.0.2",
"mocha": "6.1.4",
diff --git a/scripts/cucumber.js b/scripts/cucumber.js
index 4109a69d..c986f967 100644
--- a/scripts/cucumber.js
+++ b/scripts/cucumber.js
@@ -1,33 +1,25 @@
+/* eslint-disable import/no-extraneous-dependencies */
const spawn = require('cross-spawn');
const isWindows = process.platform.match(/^win/);
-// Removing '@cli' behavior from tests due to
+// Excludes Cucumber features marked with the "@cli" tag.
+// CLI does not work on Windows:
// https://github.com/apiaryio/gavel-spec/issues/24
-const tags = [
- '@javascript',
- '~@proposal',
- '~@draft',
- '~@javascript-pending',
- isWindows && '~@cli'
-].filter(Boolean);
+const tags = [isWindows && '-t ~@cli'].filter(Boolean);
-const args = tags.reduce((acc, tag) => {
- return acc.concat('-t').concat(tag);
-}, []);
+const args = [
+ ...tags,
+ '-r',
+ 'test/cucumber/support/',
+ '-r',
+ 'test/cucumber/steps/',
+ '-f',
+ 'pretty',
+ 'node_modules/gavel-spec/features/'
+];
-const cucumber = spawn(
- 'node_modules/.bin/cucumber-js',
- args.concat([
- '-r',
- 'test/cucumber/support/',
- '-r',
- 'test/cucumber/step_definitions/',
- '-f',
- 'pretty',
- 'node_modules/gavel-spec/features/'
- ])
-);
+const cucumber = spawn('node_modules/.bin/cucumber-js', args);
cucumber.stdout.on('data', (data) => process.stdout.write(data));
cucumber.stderr.on('data', (data) => process.stderr.write(data));
diff --git a/test/chai.js b/test/chai.js
index 43b6ea6f..afdfca48 100644
--- a/test/chai.js
+++ b/test/chai.js
@@ -1,5 +1,6 @@
/* eslint-disable no-underscore-dangle */
const chai = require('chai');
+const deepEqual = require('deep-equal');
const stringify = (obj) => {
return JSON.stringify(obj, null, 2);
@@ -18,51 +19,80 @@ chai.use(({ Assertion }, utils) => {
new Assertion(error).to.have.property(propName);
this.assert(
- isRegExp ? expectedValue.test(target) : target === expectedValue,
+ isRegExp
+ ? expectedValue.test(target)
+ : deepEqual(target, expectedValue),
`
- Expected the next HTTP message field:
-
- ${stringifiedObj}
-
- to have ${propName} at index ${currentErrorIndex} that ${matchWord}:
-
- ${expectedValue.toString()}
-
- but got:
-
- ${target.toString()}
+Expected the next HTTP message field:
+
+${stringifiedObj}
+
+to have an error at index ${currentErrorIndex} that includes property "${propName}" that ${matchWord}:
+
+${JSON.stringify(expectedValue)}
+
+but got:
+
+${JSON.stringify(target)}
`,
`
- Expected the next HTTP message field:
-
- ${stringifiedObj}
-
- not to have ${propName} at index ${currentErrorIndex}, but got:
-
- ${target.toString()}
+Expected the next HTTP message field:
+
+${stringifiedObj}
+
+to have an error at index ${currentErrorIndex} that includes property "${propName}" that not ${matchWord}:
+
+${JSON.stringify(target)}
`,
- expectedValue.toString(),
- target.toString(),
+ JSON.stringify(target),
+ JSON.stringify(expectedValue),
true
);
});
};
createErrorPropertyAssertion('message', 'withMessage');
- createErrorPropertyAssertion('pointer', 'withPointer');
+ createErrorPropertyAssertion('location', 'withLocation');
+ createErrorPropertyAssertion('values', 'withValues');
+
+ Assertion.addMethod('kind', function(expectedValue) {
+ const { kind } = this._obj;
+ const stringifiedObj = stringify(this._obj);
+
+ this.assert(
+ kind === expectedValue,
+ `
+Expected the following HTTP message field:
+
+${stringifiedObj}
+
+to have "kind" property equal "${expectedValue}", but got ${kind}.
+ `,
+ `
+Expected the following HTTP message field:
+
+${stringifiedObj}
+
+to not have "kind" property equal "${expectedValue}".
+ `,
+ kind,
+ expectedValue,
+ true
+ );
+ });
utils.addProperty(Assertion.prototype, 'valid', function() {
- const { isValid } = this._obj;
+ const { valid } = this._obj;
const stringifiedObj = stringify(this._obj);
this.assert(
- isValid === true,
+ valid === true,
`
Expected the following HTTP message field:
${stringifiedObj}
-to have "isValid" equal #{exp}, but got #{act}'.
+to have "valid" equal #{exp}, but got #{act}'.
`,
`
Expected the following HTTP message field:
@@ -70,8 +100,8 @@ Expected the following HTTP message field:
${stringifiedObj}
to be invalid, but it is actually valid.`,
- { isValid },
- { isValid: true },
+ { valid },
+ { valid: true },
true
);
});
@@ -120,84 +150,6 @@ to have no errors, but got ${errors.length} error(s).
utils.flag(this, 'currentError', errors[index]);
utils.flag(this, 'currentErrorIndex', index);
});
-
- Assertion.addMethod('validator', function(expectedValue) {
- const { validator: actualValue } = this._obj;
- const stringifiedObj = stringify(this._obj);
-
- this.assert(
- actualValue === expectedValue,
- `
-Expected the following HTTP message field:
-
-${stringifiedObj}
-
-to have "${expectedValue}" validator, but got "${actualValue}".
- `,
- `
-Expected the following HTTP message field:
-
-${stringifiedObj}
-
-to not have validator equal to "${expectedValue}".
-`,
- expectedValue,
- actualValue,
- true
- );
- });
-
- Assertion.addMethod('expectedType', function(expectedValue) {
- const { expectedType: actualValue } = this._obj;
- const stringifiedObj = stringify(this._obj);
-
- this.assert(
- actualValue === expectedValue,
- `
-Expected the following HTTP message field:
-
-${stringifiedObj}
-
-to have an "expectedType" equal to "${expectedValue}", but got "${actualValue}".
- `,
- `
-Expected the following HTTP message field:
-
-${stringifiedObj}
-
-to not have an "expectedType" of "${expectedValue}".
- `,
- expectedValue,
- actualValue,
- true
- );
- });
-
- Assertion.addMethod('realType', function(expectedValue) {
- const { realType: actualValue } = this._obj;
- const stringifiedObj = stringify(this._obj);
-
- this.assert(
- actualValue === expectedValue,
- `
-Expected the following HTTP message field:
-
-${stringifiedObj}
-
-to have an "realType" equal to "${expectedValue}", but got "${actualValue}".
-`,
- `
-Expected the following HTTP message field:
-
-${stringifiedObj}
-
-to not have an "realType" of "${expectedValue}".
- `,
- expectedValue,
- actualValue,
- true
- );
- });
});
module.exports = chai;
diff --git a/test/cucumber/step_definitions/body_steps.js b/test/cucumber/step_definitions/body_steps.js
deleted file mode 100644
index db3b21d0..00000000
--- a/test/cucumber/step_definitions/body_steps.js
+++ /dev/null
@@ -1,21 +0,0 @@
-module.exports = function() {
- this.Given(
- /^you define expected HTTP body using the following "([^"]*)":$/,
- function(type, body, callback) {
- if (type === 'textual example') {
- this.expected.body = body;
- } else if (type === 'JSON example') {
- this.expected.body = body;
- } else if (type === 'JSON schema') {
- this.expected.bodySchema = JSON.parse(body);
- }
-
- return callback();
- }
- );
-
- return this.When(/^real HTTP body is following:$/, function(body, callback) {
- this.real.body = body;
- return callback();
- });
-};
diff --git a/test/cucumber/step_definitions/cli_stepdefs.js b/test/cucumber/step_definitions/cli_stepdefs.js
deleted file mode 100644
index 1feec0ec..00000000
--- a/test/cucumber/step_definitions/cli_stepdefs.js
+++ /dev/null
@@ -1,68 +0,0 @@
-const { exec } = require('child_process');
-
-module.exports = function() {
- this.Given(/^you record expected raw HTTP messages:$/, function(
- cmd,
- callback
- ) {
- this.commandBuffer += `;${cmd}`;
- return callback();
- });
-
- this.Given(/^you record real raw HTTP messages:$/, function(cmd, callback) {
- this.commandBuffer += `;${cmd}`;
- return callback();
- });
-
- this.When(
- /^you validate the message using the following Gavel command:$/,
- function(cmd, callback) {
- this.commandBuffer += `;${cmd}`;
- return callback();
- }
- );
-
- this.When(/^a header is missing in real messages:$/, function(cmd, callback) {
- this.commandBuffer += `;${cmd}`;
- return callback();
- });
-
- return this.Then(/^exit status is (\d+)$/, function(
- expectedExitStatus,
- callback
- ) {
- const cmd = `PATH=$PATH:${process.cwd()}/bin:${process.cwd()}/node_modules/.bin; cd /tmp/gavel-* ${
- this.commandBuffer
- }`;
- const child = exec(cmd, function(error, stdout, stderr) {
- if (error) {
- if (parseInt(error.code) !== parseInt(expectedExitStatus)) {
- return callback(
- new Error(
- `Expected exit status ${expectedExitStatus} but got ${
- error.code
- }.` +
- 'STDERR: ' +
- stderr +
- 'STDOUT: ' +
- stdout
- )
- );
- }
-
- return callback();
- }
- });
-
- return child.on('exit', function(code) {
- if (parseInt(code) !== parseInt(expectedExitStatus)) {
- callback(
- new Error(
- `Expected exit status ${expectedExitStatus} but got ${code}.`
- )
- );
- }
- return callback();
- });
- });
-};
diff --git a/test/cucumber/step_definitions/headers_steps.js b/test/cucumber/step_definitions/headers_steps.js
deleted file mode 100644
index 952f1a3b..00000000
--- a/test/cucumber/step_definitions/headers_steps.js
+++ /dev/null
@@ -1,17 +0,0 @@
-module.exports = function() {
- this.Given(/^you expect the following HTTP headers:$/, function(
- string,
- callback
- ) {
- this.expected.headers = this.parseHeaders(string);
- return callback();
- });
-
- return this.When(/^real HTTP headers are following:$/, function(
- string,
- callback
- ) {
- this.real.headers = this.parseHeaders(string);
- return callback();
- });
-};
diff --git a/test/cucumber/step_definitions/javascript_steps.js b/test/cucumber/step_definitions/javascript_steps.js
deleted file mode 100644
index 7fb8314f..00000000
--- a/test/cucumber/step_definitions/javascript_steps.js
+++ /dev/null
@@ -1,54 +0,0 @@
-/* eslint-disable */
-module.exports = function() {
- this.Given(
- /^you define following( expected)? HTTP (request|response) object:/,
- function(isExpected, messageType, string, callback) {
- this.codeBuffer += `${string}\n`;
- return callback();
- }
- );
-
- //
- this.Given(/^you define the following "([^"]*)" variable:$/, function(
- arg1,
- string,
- callback
- ) {
- this.codeBuffer += string + '\n';
- return callback();
- });
-
- this.Given(/^you add expected "([^"]*)" to real "([^"]*)":$/, function(
- arg1,
- arg2,
- string,
- callback
- ) {
- this.codeBuffer += string + '\n';
- return callback();
- });
-
- this.Given(/^prepare result variable:$/, function(string, callback) {
- this.codeBuffer += string + '\n';
- return callback();
- });
-
- this.Then(/^"([^"]*)" variable will contain:$/, function(
- varName,
- string,
- callback
- ) {
- this.codeBuffer += varName + '\n';
- const expected = string;
- return this.expectBlockEval(this.codeBuffer, expected, callback);
- });
-
- this.When(/^you call:$/, function(string, callback) {
- this.codeBuffer += string + '\n';
- return callback();
- });
-
- return this.Then(/^it will return:$/, function(expected, callback) {
- return this.expectBlockEval(this.codeBuffer, expected, callback);
- });
-};
diff --git a/test/cucumber/step_definitions/method_steps.js b/test/cucumber/step_definitions/method_steps.js
deleted file mode 100644
index 3d6a6cfd..00000000
--- a/test/cucumber/step_definitions/method_steps.js
+++ /dev/null
@@ -1,17 +0,0 @@
-module.exports = function() {
- this.Given(/^you expect HTTP message method "([^"]*)"$/, function(
- method,
- callback
- ) {
- this.expected.method = method;
- return callback();
- });
-
- return this.When(/^real HTTP message method is "([^"]*)"$/, function(
- method,
- callback
- ) {
- this.real.method = method;
- return callback();
- });
-};
diff --git a/test/cucumber/step_definitions/model_steps.js b/test/cucumber/step_definitions/model_steps.js
deleted file mode 100644
index e21f6dac..00000000
--- a/test/cucumber/step_definitions/model_steps.js
+++ /dev/null
@@ -1,57 +0,0 @@
-/* eslint-disable */
-const deepEqual = require('deep-equal');
-const gavel = require('../../../lib');
-
-module.exports = function() {
- // TODO consider refactoring for for better acceptace testing to separated steps
- // i.e. do not use http parsing, use separate steps for body, headers, code, etc...
- this.When(/^you have the following real HTTP request:$/, function(
- requestString,
- callback
- ) {
- this.model.request = this.parseHttp('request', requestString);
- return callback();
- });
-
- this.When(/^you have the following real HTTP response:$/, function(
- responseString,
- callback
- ) {
- this.model.response = this.parseHttp('response', responseString);
- return callback();
- });
-
- return this.Then(
- /^"([^"]*)" JSON representation will look like this:$/,
- function(objectTypeString, string, callback) {
- let data;
- const expectedObject = JSON.parse(string);
-
- if (objectTypeString === 'HTTP Request') {
- data = this.model.request;
- } else if (objectTypeString === 'HTTP Response') {
- data = this.model.response;
- } else if (objectTypeString === 'Expected HTTP Request') {
- data = this.expected;
- } else if (objectTypeString === 'Expected HTTP Response') {
- data = this.expected;
- }
-
- const jsonizedInstance = JSON.parse(JSON.stringify(data));
-
- if (!deepEqual(expectedObject, jsonizedInstance, { strict: true })) {
- callback(
- new Error(
- 'Objects are not equal: ' +
- '\nexpected: \n' +
- JSON.stringify(expectedObject, null, 2) +
- '\njsonized instance: \n' +
- JSON.stringify(jsonizedInstance, null, 2)
- )
- );
- }
-
- return callback();
- }
- );
-};
diff --git a/test/cucumber/step_definitions/status_code_steps.js b/test/cucumber/step_definitions/status_code_steps.js
deleted file mode 100644
index d57ed23a..00000000
--- a/test/cucumber/step_definitions/status_code_steps.js
+++ /dev/null
@@ -1,14 +0,0 @@
-module.exports = function() {
- this.Given(/^you expect HTTP status code "([^"]*)"$/, function(
- code,
- callback
- ) {
- this.expected.statusCode = code;
- return callback();
- });
-
- return this.When(/^real status code is "([^"]*)"$/, function(code, callback) {
- this.real.statusCode = code;
- return callback();
- });
-};
diff --git a/test/cucumber/step_definitions/uri_steps.js b/test/cucumber/step_definitions/uri_steps.js
deleted file mode 100644
index ffb7c1e6..00000000
--- a/test/cucumber/step_definitions/uri_steps.js
+++ /dev/null
@@ -1,17 +0,0 @@
-module.exports = function() {
- this.Given(/^you expect HTTP message URI "([^"]*)"$/, function(
- uri,
- callback
- ) {
- this.expected.uri = uri;
- return callback();
- });
-
- return this.When(/^real HTTP message URI is "([^"]*)"$/, function(
- uri,
- callback
- ) {
- this.real.uri = uri;
- return callback();
- });
-};
diff --git a/test/cucumber/step_definitions/validation_errors_thens.js b/test/cucumber/step_definitions/validation_errors_thens.js
deleted file mode 100644
index 4758af25..00000000
--- a/test/cucumber/step_definitions/validation_errors_thens.js
+++ /dev/null
@@ -1,46 +0,0 @@
-const { assert } = require('chai');
-
-module.exports = function() {
- this.Then(/^field "([^"]*)" is( NOT)? valid$/, function(
- fieldName,
- isNotValid,
- callback
- ) {
- const result = this.validate();
-
- assert.property(
- result.fields,
- fieldName,
- `Expected to have "${fieldName}" field in the validation result, but got none.`
- );
-
- assert.propertyVal(
- result.fields[fieldName],
- 'isValid',
- !isNotValid,
- `Expected "result.fields.${fieldName}" to be valid, but it's not.`
- );
-
- return callback();
- });
-
- this.Then(/^Request or Response is NOT valid$/, function(callback) {
- const result = this.validate();
- if (result.isValid) {
- callback(
- new Error('Request or Response is valid and should NOT be valid.')
- );
- }
- return callback();
- });
-
- return this.Then(/^Request or Response is valid$/, function(callback) {
- const result = this.validate();
- if (!result.isValid) {
- callback(
- new Error('Request or Response is NOT valid and should be valid.')
- );
- }
- return callback();
- });
-};
diff --git a/test/cucumber/step_definitions/validators_steps.js b/test/cucumber/step_definitions/validators_steps.js
deleted file mode 100644
index 7eb10a60..00000000
--- a/test/cucumber/step_definitions/validators_steps.js
+++ /dev/null
@@ -1,235 +0,0 @@
-/* eslint-disable */
-const tv4 = require('tv4');
-const { assert } = require('chai');
-const deepEqual = require('deep-equal');
-
-module.exports = function() {
- this.When(
- /^you perform a failing validation on any validatable HTTP component$/,
- function(callback) {
- const json1 = '{"a": "b"}';
- const json2 = '{"c": "d"}';
-
- this.component = 'body';
-
- this.real = {
- headers: {
- 'content-type': 'application/json'
- },
- body: json1
- };
-
- this.expected = {
- headers: {
- 'content-type': 'application/json'
- },
- body: json2
- };
-
- try {
- const result = this.validate();
- this.results = JSON.parse(JSON.stringify(result));
- this.booleanResult = result.isValid;
- return callback();
- } catch (error) {
- callback(new Error(`Got error during validation:\n${error}`));
- }
- }
- );
-
- this.Then(
- /^the validator output for the HTTP component looks like the following JSON:$/,
- function(expectedJson, callback) {
- const expected = JSON.parse(expectedJson);
- const real = this.results.fields[this.component];
- if (!deepEqual(real, expected, { strict: true })) {
- return callback(
- new Error(
- 'Not matched! Expected:\n' +
- JSON.stringify(expected, null, 2) +
- '\n' +
- 'But got:' +
- '\n' +
- JSON.stringify(real, null, 2)
- )
- );
- } else {
- return callback();
- }
- }
- );
-
- this.Then(/^validated HTTP component is considered invalid$/, function(
- callback
- ) {
- assert.isFalse(this.booleanResult);
- return callback();
- });
-
- this.Then(
- /^the validator output for the HTTP component is valid against "([^"]*)" model JSON schema:$/,
- function(model, schema, callback) {
- const valid = tv4.validate(
- this.results.fields[this.component],
- JSON.parse(schema)
- );
- if (!valid) {
- return callback(
- new Error(
- 'Expected no validation errors on schema but got:\n' +
- JSON.stringify(tv4.error, null, 2)
- )
- );
- } else {
- return callback();
- }
- }
- );
-
- this.Then(
- /^each result entry under "([^"]*)" key must contain "([^"]*)" key$/,
- function(key1, key2, callback) {
- const error = this.results.fields[this.component];
- if (error === undefined) {
- callback(
- new Error(
- 'Validation result for "' +
- this.component +
- '" is undefined. Validations: ' +
- JSON.stringify(this.results, null, 2)
- )
- );
- }
-
- error[key1].forEach((error) => assert.include(Object.keys(error), key2));
- return callback();
- }
- );
-
- this.Then(
- /^the output JSON contains key "([^"]*)" with one of the following values:$/,
- function(key, table, callback) {
- const error = this.results.fields[this.component];
-
- const validators = [].concat.apply([], table.raw());
-
- assert.include(validators, error[key]);
- return callback();
- }
- );
-
- this.Given(/^you want validate "([^"]*)" HTTP component$/, function(
- component,
- callback
- ) {
- this.component = component;
- return callback();
- });
-
- this.Given(
- /^you express expected data by the following "([^"]*)" example:$/,
- function(type, data, callback) {
- if (type === 'application/schema+json') {
- this.expected['bodySchema'] = data;
- } else if (type === 'application/vnd.apiary.http-headers+json') {
- this.expected[this.component] = JSON.parse(data);
- } else {
- this.expected[this.component] = data;
- }
-
- this.expectedType = type;
- return callback();
- }
- );
-
- this.Given(/^you have the following "([^"]*)" real data:$/, function(
- type,
- data,
- callback
- ) {
- if (type === 'application/vnd.apiary.http-headers+json') {
- this.real[this.component] = JSON.parse(data);
- } else {
- this.real[this.component] = data;
- }
-
- this.realType = type;
- return callback();
- });
-
- this.When(/^you perform validation on the HTTP component$/, function(
- callback
- ) {
- try {
- const result = this.validate();
- this.results = result;
- this.componentResults = this.results.fields[this.component];
- return callback();
- } catch (error) {
- callback(new Error(`Error during validation: ${error}`));
- }
- });
-
- this.Then(/^validator "([^"]*)" is used for validation$/, function(
- validator,
- callback
- ) {
- const usedValidator = this.componentResults.validator;
- if (validator !== usedValidator) {
- callback(
- new Error(
- `Used validator '${usedValidator}'` +
- " instead of '" +
- validator +
- "'. Got validation results: " +
- JSON.stringify(this.results, null, 2)
- )
- );
- }
- return callback();
- });
-
- this.Then(
- /^validation key "([^"]*)" looks like the following "([^"]*)":$/,
- function(key, type, expected, callback) {
- const real = this.componentResults[key];
- if (type === 'JSON') {
- expected = JSON.parse(expected);
- } else if (type === 'text') {
- // FIXME investigate how does cucumber docstrings handle
- // newlines and remove trim and remove this hack
- expected = expected + '\n';
- }
-
- if (type === 'JSON') {
- if (!deepEqual(expected, real, { strict: true })) {
- callback(
- new Error(
- 'Not matched! Expected:\n' +
- this.inspect(expected) +
- '\n' +
- 'But got:' +
- '\n' +
- this.inspect(real) +
- '\n' +
- 'End'
- )
- );
- }
- } else if (type === 'text') {
- assert.equal(expected, real);
- }
- return callback();
- }
- );
-
- return this.Then(/^each result entry must contain "([^"]*)" key$/, function(
- key,
- callback
- ) {
- this.componentResults.errors.forEach((error) =>
- assert.include(Object.keys(error), key)
- );
- return callback();
- });
-};
diff --git a/test/cucumber/steps/cli.js b/test/cucumber/steps/cli.js
new file mode 100644
index 00000000..e5453d7d
--- /dev/null
+++ b/test/cucumber/steps/cli.js
@@ -0,0 +1,26 @@
+const { assert } = require('chai');
+
+module.exports = function() {
+ this.Given(
+ /^(I record (expected|actual) raw HTTP message:)|(a header is missing in actual message:)$/,
+ function(_1, _2, _3, command) {
+ this.commands.push(command);
+ }
+ );
+
+ this.When(
+ 'I validate the message using the following Gavel command:',
+ async function(command) {
+ this.commands.push(command);
+ this.exitCode = await this.executeCommands(this.commands);
+ }
+ );
+
+ this.Then(/^exit status is (\d+)$/, function(expectedExitCode) {
+ assert.equal(
+ this.exitCode,
+ expectedExitCode,
+ `Expected process to exit with code ${expectedExitCode}, but got ${this.exitCode}.`
+ );
+ });
+};
diff --git a/test/cucumber/steps/fields.js b/test/cucumber/steps/fields.js
new file mode 100644
index 00000000..3433e4d4
--- /dev/null
+++ b/test/cucumber/steps/fields.js
@@ -0,0 +1,15 @@
+const chai = require('chai');
+const jhp = require('json-parse-helpfulerror');
+
+chai.config.truncateThreshold = 0;
+const { expect } = chai;
+
+module.exports = function() {
+ this.Then(/^the result field "([^"]*)" equals:$/, function(
+ fieldName,
+ expectedJson
+ ) {
+ const expected = jhp.parse(expectedJson);
+ expect(this.result.fields[fieldName]).to.deep.equal(expected);
+ });
+};
diff --git a/test/cucumber/steps/general.js b/test/cucumber/steps/general.js
new file mode 100644
index 00000000..efa804d8
--- /dev/null
+++ b/test/cucumber/steps/general.js
@@ -0,0 +1,94 @@
+const { expect } = require('chai');
+const jhp = require('json-parse-helpfulerror');
+
+module.exports = function() {
+ this.Given(
+ /^I expect the following HTTP (message|request|response):$/i,
+ function(_, expectedMessage) {
+ this.expected = jhp.parse(expectedMessage);
+ }
+ );
+
+ this.Given(/^the actual HTTP (message|request|response) equals:$/i, function(
+ _,
+ actualMessage
+ ) {
+ this.actual = jhp.parse(actualMessage);
+ });
+
+ // Inline value assertion.
+ this.Given(/^the actual "([^"]*)" is "([^"]*)"/, function(fieldName, value) {
+ this.actual[fieldName] = value;
+ });
+
+ this.Given(/^I expect "([^"]*)" to be "([^"]*)"$/, function(
+ fieldName,
+ expectedValue
+ ) {
+ this.expected[fieldName] = expectedValue;
+ });
+
+ this.Given(/^I expect "([^"]*)" to equal:$/, function(fieldName, codeBlock) {
+ // Perform conditional code block parsing (if headers, etc.)
+ this.expected[fieldName] = this.transformCodeBlock(fieldName, codeBlock);
+ });
+
+ this.Given(/^I expect "body" to match the following "([^"]*)":$/, function(
+ bodyType,
+ value
+ ) {
+ switch (bodyType.toLowerCase()) {
+ case 'json schema':
+ this.expected.bodySchema = value;
+ break;
+ default:
+ this.expected.body = value;
+ break;
+ }
+ });
+
+ // Block value assertion.
+ this.Given(/^the actual "([^"]*)" equals:$/, function(fieldName, codeBlock) {
+ // Also perform conditional code parsing
+ this.actual[fieldName] = this.transformCodeBlock(fieldName, codeBlock);
+ });
+
+ // Actions
+ this.When('Gavel validates the HTTP message', function() {
+ this.validate();
+ });
+
+ // Vocabulary proxy over the previous action for better scenarios readability.
+ this.When(/^I call "gavel.validate(([^"]*))"$/, function(_, _command) {
+ this.validate();
+ });
+
+ // Assertions
+ this.Then(/^the actual HTTP message is( NOT)? valid$/i, function(isInvalid) {
+ expect(this.result).to.have.property('valid', !isInvalid);
+ });
+
+ this.Then('the validation result is:', function(expectedResult) {
+ const stringifiedActual = JSON.stringify(this.result, null, 2);
+
+ expect(this.result).to.deep.equal(
+ jhp.parse(expectedResult),
+ `\
+Expected the following result:
+
+${stringifiedActual}
+
+to equal:
+
+${expectedResult}
+`
+ );
+ });
+
+ this.Then(/^the "(\w+)" is( NOT)? valid$/i, function(fieldName, isInvalid) {
+ expect(this.result).to.have.nested.property(
+ `fields.${fieldName}.valid`,
+ !isInvalid
+ );
+ });
+};
diff --git a/test/cucumber/support/world.js b/test/cucumber/support/world.js
index 9e70eb4e..d9916598 100644
--- a/test/cucumber/support/world.js
+++ b/test/cucumber/support/world.js
@@ -5,137 +5,98 @@
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/
/* eslint-disable */
-const gavel = require('../../../lib');
const vm = require('vm');
const util = require('util');
const { assert } = require('chai');
+const { exec } = require('child_process');
+const gavel = require('../../../lib');
const HTTP_LINE_DELIMITER = '\n';
class World {
constructor() {
- this.codeBuffer = '';
- this.commandBuffer = '';
-
- // Data for creation of:
- //
- // - ExpecterHttpResponse
- // - ExpectedHttpRequest
- // - ExpectedHttpMessage
this.expected = {};
+ this.actual = {};
- // Data for creation of:
- //
- // - HttpResponse
- // - HttpRequest
- // - HttpMessage
- this.real = {};
+ // Gavel validation result
+ this.results = {};
- // Parsed HTTP objects for model valdiation
- this.model = {};
+ // CLI
+ this.commands = [];
+ this.exitCode = null;
+ }
- // Results of validators
- this.results = {};
+ executeCommands(commands) {
+ const commandsBuffer = commands.join(';');
+ const cmd =
+ `PATH=$PATH:${process.cwd()}/bin:${process.cwd()}/node_modules/.bin; cd /tmp/gavel-* ;` +
+ commandsBuffer;
- // Validation verdict for the whole HTTP Message
- this.booleanResult = false;
+ return new Promise((resolve) => {
+ const child = exec(cmd, function(error, stdout, stderr) {
+ if (error) {
+ resolve(error.code);
+ }
+ });
- // Component relevant to the expectation, e.g. 'body'
- this.component = null;
- this.componentResults = null;
+ child.on('exit', function(code) {
+ resolve(code);
+ });
+ });
+ }
- this.expectedType = null;
- this.realType = null;
+ validate() {
+ this.result = gavel.validate(this.expected, this.actual);
}
- expectBlockEval(block, expectedReturn, callback) {
- const realOutput = this.safeEval(block, callback);
-
- // I'm terribly sorry, but curly braces not asigned to any
- // variable in evaled string are interpreted as code block
- // not an Object literal, so I'm wrapping expected code output
- // with brackets.
- // see: http://stackoverflow.com/questions/8949274/javascript-calling-eval-on-an-object-literal-with-functions
-
- const expectedOutput = this.safeEval(`(${expectedReturn})`, callback);
-
- const realOutputInspect = util.inspect(realOutput);
- const expectedOutputInspect = util.inspect(expectedOutput);
-
- try {
- assert.deepEqual(realOutput, expectedOutput);
- } catch (error) {
- callback(
- new Error(
- 'Output of code buffer does not equal. Expected output:\n' +
- expectedOutputInspect +
- '\nbut got: \n' +
- realOutputInspect +
- '\n' +
- 'Evaled code block:' +
- '\n' +
- '- - - \n' +
- block +
- '\n' +
- '- - - '
- )
- );
+ transformCodeBlock(fieldName, value) {
+ switch (fieldName) {
+ case 'headers':
+ return this.parseHeaders(value);
+ default:
+ return value;
}
- return callback();
}
- safeEval(code, callback) {
- // I'm terribly sorry, it's no longer possible to manipulate module require/load
- // path inside node's process. So I'm prefixing require path by hard
- // substitution in code to pretend to 'hit' is packaged module.
- //
- // further reading on node.js load paths:
- // http://nodejs.org/docs/v0.8.23/api/all.html#all_all_together
-
- const formattedCode = code.replace(
- "require('gavel",
- "require('../../../lib"
- );
+ parseHeaders(headersString) {
+ const lines = headersString.split(HTTP_LINE_DELIMITER);
- try {
- return eval(formattedCode);
- } catch (error) {
- return callback(
- new Error(
- 'Eval failed. Code buffer: \n\n' +
- formattedCode +
- '\nWith error: ' +
- error
- )
+ const headers = lines.reduce((acc, line) => {
+ // Using RegExp to parse a header line.
+ // Splitting by semicolon (:) would split
+ // Date header's time delimiter:
+ // > Date: Fri, 13 Dec 3000 23:59:59 GMT
+ const match = line.match(/^(\S+):\s+(.+)$/);
+
+ assert.isNotNull(
+ match,
+ `\
+Failed to parse a header line:
+${line}
+
+Make sure it's in the "Header-Name: value" format.
+`
);
- }
- }
- validate() {
- return gavel.validate(this.expected, this.real);
- }
+ const [_, key, value] = match;
- parseHeaders(headersString) {
- const lines = headersString.split(HTTP_LINE_DELIMITER);
- const headers = {};
- for (let line of Array.from(lines)) {
- const parts = line.split(':');
- const key = parts.shift();
- headers[key.toLowerCase()] = parts.join(':').trim();
- }
+ return {
+ ...acc,
+ [key.toLowerCase()]: value.trim()
+ };
+ }, {});
return headers;
}
parseRequestLine(parsed, firstLine) {
- firstLine = firstLine.split(' ');
- parsed.method = firstLine[0];
- parsed.uri = firstLine[1];
+ const [method, uri] = firstLine.split(' ');
+ parsed.method = method;
+ parsed.uri = uri;
}
parseResponseLine(parsed, firstLine) {
- firstLine = firstLine.split(' ');
- parsed.statusCode = firstLine[1];
- parsed.statusMessage = firstLine[2];
+ const [statusCode] = firstLine.split(' ');
+ parsed.statusCode = statusCode;
}
parseHttp(type, string) {
@@ -144,7 +105,6 @@ class World {
}
const parsed = {};
-
const lines = string.split(HTTP_LINE_DELIMITER);
if (type === 'request') {
@@ -157,6 +117,7 @@ class World {
const bodyLines = [];
const headersLines = [];
let bodyEntered = false;
+
for (let line of Array.from(lines)) {
if (line === '') {
bodyEntered = true;
@@ -177,21 +138,24 @@ class World {
// Hacky coercion function to parse expcected Boolean values
// from Gherkin feature suites.
+ //
+ // TODO Replace with the {boolean} placeholder from the
+ // next version of Cucumber.
toBoolean(string) {
if (string === 'true') return true;
if (string === 'false') return false;
return !!string;
}
- toCamelCase(input) {
- const result = input.replace(/\s([a-z])/g, (strings) =>
+ toCamelCase(string) {
+ const result = string.replace(/\s([a-z])/g, (strings) =>
strings[1].toUpperCase()
);
return result;
}
- toPascalCase(input) {
- let result = input.replace(
+ toPascalCase(string) {
+ let result = string.replace(
/(\w)(\w*)/g,
(g0, g1, g2) => g1.toUpperCase() + g2.toLowerCase()
);
diff --git a/test/integration/validate.test.js b/test/integration/validate.test.js
index a6e2baaf..f39e1f4f 100644
--- a/test/integration/validate.test.js
+++ b/test/integration/validate.test.js
@@ -22,31 +22,19 @@ describe('validate', () => {
describe('method', () => {
expect(result.fields.method).to.be.valid;
- expect(result.fields.method).to.have.validator(null);
- expect(result.fields.method).to.have.expectedType(
- 'text/vnd.apiary.method'
- );
- expect(result.fields.method).to.have.realType('text/vnd.apiary.method');
+ expect(result.fields.method).to.have.kind('text');
expect(result.fields.method).to.not.have.errors;
});
describe('headers', () => {
expect(result.fields.headers).to.be.valid;
- expect(result.fields.headers).to.have.validator('HeadersJsonExample');
- expect(result.fields.headers).to.have.expectedType(
- 'application/vnd.apiary.http-headers+json'
- );
- expect(result.fields.headers).to.have.realType(
- 'application/vnd.apiary.http-headers+json'
- );
+ expect(result.fields.headers).to.have.kind('json');
expect(result.fields.headers).to.not.have.errors;
});
describe('body', () => {
expect(result.fields.body).to.be.valid;
- expect(result.fields.body).to.have.validator('JsonExample');
- expect(result.fields.body).to.have.expectedType('application/json');
- expect(result.fields.body).to.have.realType('application/json');
+ expect(result.fields.body).to.have.kind('json');
expect(result.fields.body).to.not.have.errors;
});
});
@@ -77,12 +65,7 @@ describe('validate', () => {
describe('method', () => {
expect(result.fields.method).to.not.be.valid;
- expect(result.fields.method).to.have.validator(null);
- expect(result.fields.method).to.have.expectedType(
- 'text/vnd.apiary.method'
- );
- expect(result.fields.method).to.have.realType('text/vnd.apiary.method');
-
+ expect(result.fields.method).to.have.kind('text');
describe('produces one error', () => {
it('exactly one error', () => {
expect(result.fields.method).to.have.errors.lengthOf(1);
@@ -91,30 +74,20 @@ describe('validate', () => {
it('has explanatory message', () => {
expect(result.fields.method)
.to.have.errorAtIndex(0)
- .withMessage(
- 'Expected "method" field to equal "PUT", but got "POST".'
- );
+ .withMessage(`Expected method 'PUT', but got 'POST'.`);
});
});
});
describe('headers', () => {
expect(result.fields.headers).to.be.valid;
- expect(result.fields.headers).to.have.validator('HeadersJsonExample');
- expect(result.fields.headers).to.have.expectedType(
- 'application/vnd.apiary.http-headers+json'
- );
- expect(result.fields.headers).to.have.realType(
- 'application/vnd.apiary.http-headers+json'
- );
+ expect(result.fields.headers).to.have.kind('json');
expect(result.fields.headers).to.not.have.errors;
});
describe('body', () => {
expect(result.fields.body).to.not.be.valid;
- expect(result.fields.body).to.have.validator('JsonExample');
- expect(result.fields.body).to.have.expectedType('application/json');
- expect(result.fields.body).to.have.realType('application/json');
+ expect(result.fields.body).to.have.kind('json');
describe('produces an error', () => {
it('exactly one error', () => {
@@ -150,33 +123,19 @@ describe('validate', () => {
describe('statusCode', () => {
expect(result.fields.statusCode).to.be.valid;
- expect(result.fields.statusCode).to.have.validator('TextDiff');
- expect(result.fields.statusCode).to.have.expectedType(
- 'text/vnd.apiary.status-code'
- );
- expect(result.fields.statusCode).to.have.realType(
- 'text/vnd.apiary.status-code'
- );
+ expect(result.fields.statusCode).to.have.kind('text');
expect(result.fields.statusCode).to.not.have.errors;
});
describe('headers', () => {
expect(result.fields.headers).to.be.valid;
- expect(result.fields.headers).to.have.validator('HeadersJsonExample');
- expect(result.fields.headers).to.have.expectedType(
- 'application/vnd.apiary.http-headers+json'
- );
- expect(result.fields.headers).to.have.realType(
- 'application/vnd.apiary.http-headers+json'
- );
+ expect(result.fields.headers).to.have.kind('json');
expect(result.fields.headers).to.not.have.errors;
});
describe('body', () => {
expect(result.fields.body).to.be.valid;
- expect(result.fields.body).to.have.validator('JsonExample');
- expect(result.fields.body).to.have.expectedType('application/json');
- expect(result.fields.body).to.have.realType('application/json');
+ expect(result.fields.body).to.have.kind('json');
expect(result.fields.body).to.not.have.errors;
});
});
@@ -206,14 +165,7 @@ describe('validate', () => {
describe('statusCode', () => {
expect(result.fields.statusCode).to.not.be.valid;
- expect(result.fields.statusCode).to.have.validator('TextDiff');
- expect(result.fields.statusCode).to.have.expectedType(
- 'text/vnd.apiary.status-code'
- );
- expect(result.fields.statusCode).to.have.realType(
- 'text/vnd.apiary.status-code'
- );
-
+ expect(result.fields.statusCode).to.have.kind('text');
describe('produces an error', () => {
it('exactly one error', () => {
expect(result.fields.statusCode).to.have.errors.lengthOf(1);
@@ -222,20 +174,14 @@ describe('validate', () => {
it('has explanatory message', () => {
expect(result.fields.statusCode)
.to.have.errorAtIndex(0)
- .withMessage(`Status code is '400' instead of '200'`);
+ .withMessage(`Expected status code '200', but got '400'.`);
});
});
});
describe('headers', () => {
expect(result.fields.headers).to.not.be.valid;
- expect(result.fields.headers).to.have.validator('HeadersJsonExample');
- expect(result.fields.headers).to.have.expectedType(
- 'application/vnd.apiary.http-headers+json'
- );
- expect(result.fields.headers).to.have.realType(
- 'application/vnd.apiary.http-headers+json'
- );
+ expect(result.fields.headers).to.have.kind('json');
describe('produces an error', () => {
it('exactly one error', () => {
@@ -276,26 +222,13 @@ describe('validate', () => {
describe('statusCode', () => {
expect(result.fields.statusCode).to.be.valid;
- expect(result.fields.statusCode).to.have.validator('TextDiff');
- expect(result.fields.statusCode).to.have.expectedType(
- 'text/vnd.apiary.status-code'
- );
- expect(result.fields.statusCode).to.have.realType(
- 'text/vnd.apiary.status-code'
- );
+ expect(result.fields.statusCode).to.have.kind('text');
expect(result.fields.statusCode).to.not.have.errors;
});
describe('headers', () => {
expect(result.fields.headers).to.not.be.valid;
- expect(result.fields.headers).to.have.validator('HeadersJsonExample');
- expect(result.fields.headers).to.have.expectedType(
- 'application/vnd.apiary.http-headers+json'
- );
- expect(result.fields.headers).to.have.realType(
- 'application/vnd.apiary.http-headers+json'
- );
-
+ expect(result.fields.headers).to.have.kind('json');
describe('produces an error', () => {
it('exactly one error', () => {
expect(result.fields.headers).to.have.errors.lengthOf(1);
@@ -304,7 +237,10 @@ describe('validate', () => {
it('has pointer to missing "Content-Type"', () => {
expect(result.fields.headers)
.to.have.errorAtIndex(0)
- .withPointer('/content-type');
+ .withLocation({
+ pointer: '/content-type',
+ property: ['content-type']
+ });
});
it('has explanatory message', () => {
@@ -349,11 +285,7 @@ describe('validate', () => {
describe('for properties present in both expected and real', () => {
describe('method', () => {
expect(result.fields.method).to.not.be.valid;
- expect(result.fields.method).to.have.validator(null);
- expect(result.fields.method).to.have.expectedType(
- 'text/vnd.apiary.method'
- );
- expect(result.fields.method).to.have.realType('text/vnd.apiary.method');
+ expect(result.fields.method).to.have.kind('text');
describe('produces an error', () => {
it('exactly one error', () => {
@@ -363,9 +295,16 @@ describe('validate', () => {
it('has explanatory message', () => {
expect(result.fields.method)
.to.have.errorAtIndex(0)
- .withMessage(
- 'Expected "method" field to equal "POST", but got "PUT".'
- );
+ .withMessage(`Expected method 'POST', but got 'PUT'.`);
+ });
+
+ it('includes values', () => {
+ expect(result.fields.method)
+ .to.have.errorAtIndex(0)
+ .withValues({
+ expected: 'POST',
+ actual: 'PUT'
+ });
});
});
});
@@ -374,14 +313,7 @@ describe('validate', () => {
describe('for properties present in expected, but not in real', () => {
describe('statusCode', () => {
expect(result.fields.statusCode).to.not.be.valid;
- expect(result.fields.statusCode).to.have.validator('TextDiff');
- expect(result.fields.statusCode).to.have.expectedType(
- 'text/vnd.apiary.status-code'
- );
- expect(result.fields.statusCode).to.have.realType(
- 'text/vnd.apiary.status-code'
- );
-
+ expect(result.fields.statusCode).to.have.kind('text');
describe('produces an error', () => {
it('exactly one error', () => {
expect(result.fields.statusCode).to.have.errors.lengthOf(1);
@@ -390,21 +322,14 @@ describe('validate', () => {
it('has explanatory message', () => {
expect(result.fields.statusCode)
.to.have.errorAtIndex(0)
- .withMessage(`Status code is 'undefined' instead of '200'`);
+ .withMessage(`Expected status code '200', but got 'undefined'.`);
});
});
});
describe('headers', () => {
expect(result.fields.headers).to.not.be.valid;
- expect(result.fields.headers).to.have.validator('HeadersJsonExample');
- expect(result.fields.headers).to.have.expectedType(
- 'application/vnd.apiary.http-headers+json'
- );
- expect(result.fields.headers).to.have.realType(
- 'application/vnd.apiary.http-headers+json'
- );
-
+ expect(result.fields.headers).to.have.kind('json');
describe('produces one error', () => {
it('exactly one error', () => {
expect(result.fields.headers).to.have.errors.lengthOf(1);
@@ -422,10 +347,7 @@ describe('validate', () => {
describe('body', () => {
expect(result.fields.body).to.not.be.valid;
- expect(result.fields.body).to.have.validator(null);
- expect(result.fields.body).to.have.expectedType('application/json');
- expect(result.fields.body).to.have.realType('text/plain');
-
+ expect(result.fields.body).to.have.kind(null);
describe('produces an error', () => {
it('exactly one error', () => {
expect(result.fields.body).to.have.errors.lengthOf(1);
diff --git a/test/unit/support/amanda-to-gavel-shared.js b/test/unit/support/amanda-to-gavel-shared.js
index 1a233c18..9c76ae01 100644
--- a/test/unit/support/amanda-to-gavel-shared.js
+++ b/test/unit/support/amanda-to-gavel-shared.js
@@ -45,7 +45,7 @@ exports.shouldBehaveLikeAmandaToGavel = (instance) => {
assert.isObject(item);
});
- const props = ['message', 'pointer'];
+ const props = ['message', 'location'];
props.forEach((key) => {
it('should have "' + key + '"', () => {
assert.include(Object.keys(item), key);
@@ -55,7 +55,7 @@ exports.shouldBehaveLikeAmandaToGavel = (instance) => {
describe('pointer key value', () => {
value = null;
before(() => {
- value = item['pointer'];
+ value = item.location.pointer;
});
it('should be a string', () => {
@@ -63,9 +63,8 @@ exports.shouldBehaveLikeAmandaToGavel = (instance) => {
});
it('should be a parseable JSON poitner', () => {
- parsed = jsonPointer.parse(value);
-
- assert.isArray(parsed);
+ const parsedPointer = jsonPointer.parse(value);
+ assert.isArray(parsedPointer);
});
});
});
diff --git a/test/unit/units/validateBody.test.js b/test/unit/units/validateBody.test.js
index 846de9b6..480daac9 100644
--- a/test/unit/units/validateBody.test.js
+++ b/test/unit/units/validateBody.test.js
@@ -35,16 +35,8 @@ describe('validateBody', () => {
expect(result).to.not.be.valid;
});
- it('has no validator', () => {
- expect(result).to.have.validator(null);
- });
-
- it('has "application/json" real type', () => {
- expect(result).to.have.realType('application/json');
- });
-
- it('has "text/plain" expected type', () => {
- expect(result).to.have.expectedType('text/plain');
+ it('has "null" kind', () => {
+ expect(result).to.have.kind(null);
});
describe('produces validation error', () => {
@@ -56,9 +48,18 @@ describe('validateBody', () => {
expect(result)
.to.have.errorAtIndex(0)
.withMessage(
- `Can't validate real media type 'application/json' against expected media type 'text/plain'.`
+ `Can't validate actual media type 'application/json' against the expected media type 'text/plain'.`
);
});
+
+ it('includes values', () => {
+ expect(result)
+ .to.have.errorAtIndex(0)
+ .withValues({
+ expected: '',
+ actual: '{ "foo": "bar" }'
+ });
+ });
});
});
@@ -79,16 +80,8 @@ describe('validateBody', () => {
expect(result).to.be.valid;
});
- it('has "JsonExample" validator', () => {
- expect(result).to.have.validator('JsonExample');
- });
-
- it('has "application/json" real type', () => {
- expect(result).to.have.realType('application/json');
- });
-
- it('has "application/json" expected type', () => {
- expect(result).to.have.expectedType('application/json');
+ it('has "json" kind', () => {
+ expect(result).to.have.kind('json');
});
it('has no errors', () => {
@@ -111,16 +104,8 @@ describe('validateBody', () => {
expect(result).to.not.be.valid;
});
- it('has no validator', () => {
- expect(result).to.have.validator(null);
- });
-
- it('fallbacks to "text/plain" real type', () => {
- expect(result).to.have.realType('text/plain');
- });
-
- it('has "application/json" expected type', () => {
- expect(result).to.have.expectedType('application/json');
+ it('has "null" kind', () => {
+ expect(result).to.have.kind(null);
});
describe('produces content-type error', () => {
@@ -157,16 +142,8 @@ describe('validateBody', () => {
expect(result).to.be.valid;
});
- it('has "JsonExample" validator', () => {
- expect(result).to.have.validator('JsonExample');
- });
-
- it('has "application/hal+json" real type', () => {
- expect(result).to.have.realType('application/hal+json');
- });
-
- it('has "application/json" expected type', () => {
- expect(result).to.have.expectedType('application/json');
+ it('has "json" kind', () => {
+ expect(result).to.have.kind('json');
});
it('has no errors', () => {
@@ -191,16 +168,8 @@ describe('validateBody', () => {
expect(result).to.not.be.valid;
});
- it('has no validator', () => {
- expect(result).to.have.validator(null);
- });
-
- it('fallbacks to "text/plain" real type', () => {
- expect(result).to.have.realType('text/plain');
- });
-
- it('has "text/plain" expected type', () => {
- expect(result).to.have.expectedType('text/plain');
+ it('has "null" kind', () => {
+ expect(result).to.have.kind(null);
});
describe('produces error', () => {
@@ -236,16 +205,8 @@ describe('validateBody', () => {
expect(result).to.be.valid;
});
- it('has "TextDiff" validator', () => {
- expect(result).to.have.validator('TextDiff');
- });
-
- it('has text/plain real type', () => {
- expect(result).to.have.realType('text/plain');
- });
-
- it('has "text/plain" expected type', () => {
- expect(result).to.have.expectedType('text/plain');
+ it('has "text" kind', () => {
+ expect(result).to.have.kind('text');
});
it('has no errors', () => {
@@ -267,16 +228,8 @@ describe('validateBody', () => {
expect(result).to.not.be.valid;
});
- it('has "TextDiff" validator', () => {
- expect(result).to.have.validator('TextDiff');
- });
-
- it('has "text/plain" real type', () => {
- expect(result).to.have.realType('text/plain');
- });
-
- it('has "text/plain" expected type', () => {
- expect(result).to.have.expectedType('text/plain');
+ it('has "text" kind', () => {
+ expect(result).to.have.kind('text');
});
describe('produces validation error', () => {
@@ -287,7 +240,7 @@ describe('validateBody', () => {
it('with explanatory message', () => {
expect(result)
.to.have.errorAtIndex(0)
- .withMessage('Real and expected data does not match.');
+ .withMessage('Actual and expected data do not match.');
});
});
});
@@ -308,16 +261,8 @@ describe('validateBody', () => {
expect(result).to.be.valid;
});
- it('has "JsonExample" validator', () => {
- expect(result).to.have.validator('JsonExample');
- });
-
- it('has "application/json" real type', () => {
- expect(result).to.have.realType('application/json');
- });
-
- it('has "application/json" expected type', () => {
- expect(result).to.have.expectedType('application/json');
+ it('has "json" kind', () => {
+ expect(result).to.have.kind('json');
});
it('has no errors', () => {
@@ -339,16 +284,8 @@ describe('validateBody', () => {
expect(result).to.not.be.valid;
});
- it('has "JsonExample" validator', () => {
- expect(result).to.have.validator('JsonExample');
- });
-
- it('has "application/json" real type', () => {
- expect(result).to.have.realType('application/json');
- });
-
- it('has "application/json" expected type', () => {
- expect(result).to.have.expectedType('application/json');
+ it('has "json" kind', () => {
+ expect(result).to.have.kind('json');
});
describe('produces validation errors', () => {
@@ -382,16 +319,8 @@ describe('validateBody', () => {
expect(result).to.be.valid;
});
- it('has "JsonSchema" validator', () => {
- expect(result).to.have.validator('JsonSchema');
- });
-
- it('has "application/json" real type', () => {
- expect(result).to.have.realType('application/json');
- });
-
- it('has "application/schema+json" expected type', () => {
- expect(result).to.have.expectedType('application/schema+json');
+ it('has "json" kind', () => {
+ expect(result).to.have.kind('json');
});
it('has no errors', () => {
@@ -415,16 +344,8 @@ describe('validateBody', () => {
expect(result).to.not.be.valid;
});
- it('has "JsonSchema" validator', () => {
- expect(result).to.have.validator('JsonSchema');
- });
-
- it('has "application/json" real type', () => {
- expect(result).to.have.realType('application/json');
- });
-
- it('has "application/schema+json" expected type', () => {
- expect(result).to.have.expectedType('application/schema+json');
+ it('has "json" kind', () => {
+ expect(result).to.have.kind('json');
});
describe('produces an error', () => {
diff --git a/test/unit/units/validateBody/getBodyValidator.test.js b/test/unit/units/validateBody/getBodyValidator.test.js
index 8406eb90..9a977444 100644
--- a/test/unit/units/validateBody/getBodyValidator.test.js
+++ b/test/unit/units/validateBody/getBodyValidator.test.js
@@ -43,24 +43,4 @@ describe('getBodyValidator', () => {
});
});
});
-
- // describe('when given unknown media type', () => {
- // const unknownContentTypes = [['text/html', 'text/xml']];
-
- // unknownContentTypes.forEach((contentTypes) => {
- // const [realContentType, expectedContentType] = contentTypes;
- // const [real, expected] = getMediaTypes(
- // realContentType,
- // expectedContentType
- // );
-
- // describe(`${realContentType} + ${expectedContentType}`, () => {
- // const [error, validator] = getBodyValidator(real, expected);
-
- // it('...', () => {
- // console.log({ error, validator });
- // });
- // });
- // });
- // });
});
diff --git a/test/unit/units/validateHeaders.test.js b/test/unit/units/validateHeaders.test.js
index 6a4d4c75..4114e4f1 100644
--- a/test/unit/units/validateHeaders.test.js
+++ b/test/unit/units/validateHeaders.test.js
@@ -22,20 +22,8 @@ describe('validateHeaders', () => {
expect(result).to.be.valid;
});
- it('has "HeadersJsonExample" validator', () => {
- expect(result).to.have.validator('HeadersJsonExample');
- });
-
- it('has "application/vnd.apiary.http-headers+json" real type', () => {
- expect(result).to.have.realType(
- 'application/vnd.apiary.http-headers+json'
- );
- });
-
- it('has "application/vnd.apiary.http-headers+json" expected type', () => {
- expect(result).to.have.expectedType(
- 'application/vnd.apiary.http-headers+json'
- );
+ it('has "json" kind', () => {
+ expect(result).to.have.kind('json');
});
it('has no errors', () => {
@@ -63,20 +51,8 @@ describe('validateHeaders', () => {
expect(result).to.not.be.valid;
});
- it('has "HeadersJsonExample" validator', () => {
- expect(result).to.have.validator('HeadersJsonExample');
- });
-
- it('has "application/vnd.apiary.http-headers+json" real type', () => {
- expect(result).to.have.realType(
- 'application/vnd.apiary.http-headers+json'
- );
- });
-
- it('has "application/vnd.apiary.http-headers+json" expected type', () => {
- expect(result).to.have.expectedType(
- 'application/vnd.apiary.http-headers+json'
- );
+ it('has "json" kind', () => {
+ expect(result).to.have.kind('json');
});
describe('produces errors', () => {
@@ -92,7 +68,10 @@ describe('validateHeaders', () => {
it('has pointer to header name', () => {
expect(result)
.to.have.errorAtIndex(index)
- .withPointer(`/${headerName}`);
+ .withLocation({
+ pointer: `/${headerName}`,
+ property: [headerName]
+ });
});
it('has explanatory message', () => {
@@ -122,16 +101,8 @@ describe('validateHeaders', () => {
expect(result).to.not.be.valid;
});
- it('has no validator', () => {
- expect(result).to.have.validator(null);
- });
-
- it('has no real type', () => {
- expect(result).to.have.realType(null);
- });
-
- it('has no expected type', () => {
- expect(result).to.have.expectedType(null);
+ it('has "text" validator', () => {
+ expect(result).to.have.kind('text');
});
describe('produces an error', () => {
diff --git a/test/unit/units/validateMethod.test.js b/test/unit/units/validateMethod.test.js
index 8f206131..d5629846 100644
--- a/test/unit/units/validateMethod.test.js
+++ b/test/unit/units/validateMethod.test.js
@@ -16,16 +16,8 @@ describe('validateMethod', () => {
expect(result).to.be.valid;
});
- it('has "null" validator', () => {
- expect(result).to.have.validator(null);
- });
-
- it('has "text/vnd.apiary.method" real type', () => {
- expect(result).to.have.realType('text/vnd.apiary.method');
- });
-
- it('has "text/vnd.apiary.method" expected type', () => {
- expect(result).to.have.expectedType('text/vnd.apiary.method');
+ it('has "text" kind', () => {
+ expect(result).to.have.kind('text');
});
it('has no errors', () => {
@@ -47,16 +39,8 @@ describe('validateMethod', () => {
expect(result).to.not.be.valid;
});
- it('has "null" validator', () => {
- expect(result).to.have.validator(null);
- });
-
- it('has "text/vnd.apiary.method" real type', () => {
- expect(result).to.have.realType('text/vnd.apiary.method');
- });
-
- it('has "text/vnd.apiary.method" expected type', () => {
- expect(result).to.have.expectedType('text/vnd.apiary.method');
+ it('has "text" kind', () => {
+ expect(result).to.have.kind('text');
});
describe('produces an error', () => {
@@ -67,9 +51,16 @@ describe('validateMethod', () => {
it('has explanatory message', () => {
expect(result)
.to.have.errorAtIndex(0)
- .withMessage(
- 'Expected "method" field to equal "POST", but got "GET".'
- );
+ .withMessage(`Expected method 'POST', but got 'GET'.`);
+ });
+
+ it('includes values', () => {
+ expect(result)
+ .to.have.errorAtIndex(0)
+ .withValues({
+ expected: 'POST',
+ actual: 'GET'
+ });
});
});
});
@@ -88,16 +79,8 @@ describe('validateMethod', () => {
expect(result).to.not.be.valid;
});
- it('has "null" validator', () => {
- expect(result).to.have.validator(null);
- });
-
- it('has "text/vnd.apiary.method" real type', () => {
- expect(result).to.have.realType('text/vnd.apiary.method');
- });
-
- it('has "text/vnd.apiary.method" expected type', () => {
- expect(result).to.have.expectedType('text/vnd.apiary.method');
+ it('has "text" kind', () => {
+ expect(result).to.have.kind('text');
});
describe('produces an error', () => {
@@ -108,7 +91,16 @@ describe('validateMethod', () => {
it('has explanatory message', () => {
expect(result)
.to.have.errorAtIndex(0)
- .withMessage('Expected "method" field to equal "PATCH", but got "".');
+ .withMessage(`Expected method 'PATCH', but got ''.`);
+ });
+
+ it('includes values', () => {
+ expect(result)
+ .to.have.errorAtIndex(0)
+ .withValues({
+ expected: 'PATCH',
+ actual: ''
+ });
});
});
});
diff --git a/test/unit/units/validateStatusCode.test.js b/test/unit/units/validateStatusCode.test.js
index 5985de62..d510ed7c 100644
--- a/test/unit/units/validateStatusCode.test.js
+++ b/test/unit/units/validateStatusCode.test.js
@@ -16,16 +16,8 @@ describe('validateStatusCode', () => {
expect(result).to.be.valid;
});
- it('has "TextDiff" validator', () => {
- expect(result).to.have.validator('TextDiff');
- });
-
- it('has "text/vnd.apiary.status-code" expected type', () => {
- expect(result).to.have.expectedType('text/vnd.apiary.status-code');
- });
-
- it('has "text/vnd.apiary.status-code" real type', () => {
- expect(result).to.have.realType('text/vnd.apiary.status-code');
+ it('has "text" kind', () => {
+ expect(result).to.have.kind('text');
});
it('has no errors', () => {
@@ -47,16 +39,8 @@ describe('validateStatusCode', () => {
expect(result).to.not.be.valid;
});
- it('has "TextDiff" validator', () => {
- expect(result).to.have.validator('TextDiff');
- });
-
- it('has "text/vnd.apiary.status-code" expected type', () => {
- expect(result).to.have.expectedType('text/vnd.apiary.status-code');
- });
-
- it('has "text/vnd.apiary.status-code" real type', () => {
- expect(result).to.have.realType('text/vnd.apiary.status-code');
+ it('has "text" kind', () => {
+ expect(result).to.have.kind('text');
});
describe('produces error', () => {
@@ -67,7 +51,16 @@ describe('validateStatusCode', () => {
it('has explanatory message', () => {
expect(result)
.to.have.errorAtIndex(0)
- .withMessage(`Status code is '200' instead of '400'`);
+ .withMessage(`Expected status code '400', but got '200'.`);
+ });
+
+ it('includes values', () => {
+ expect(result)
+ .to.have.errorAtIndex(0)
+ .withValues({
+ expected: '400',
+ actual: '200'
+ });
});
});
});
diff --git a/test/unit/units/validateURI.test.js b/test/unit/units/validateURI.test.js
index 27f142b4..de706cba 100644
--- a/test/unit/units/validateURI.test.js
+++ b/test/unit/units/validateURI.test.js
@@ -17,16 +17,8 @@ describe('validateURI', () => {
expect(result).to.be.valid;
});
- it('has "null" validator', () => {
- expect(result).to.have.validator(null);
- });
-
- it('has "text/vnd.apiary.uri" real type', () => {
- expect(result).to.have.realType('text/vnd.apiary.uri');
- });
-
- it('has "text/vnd.apiary.uri" expected type', () => {
- expect(result).to.have.expectedType('text/vnd.apiary.uri');
+ it('has "text" kind', () => {
+ expect(result).to.have.kind('text');
});
it('has no errors', () => {
@@ -49,16 +41,8 @@ describe('validateURI', () => {
expect(result).to.be.valid;
});
- it('has "null" validator', () => {
- expect(result).to.have.validator(null);
- });
-
- it('has "text/vnd.apiary.uri" real type', () => {
- expect(result).to.have.realType('text/vnd.apiary.uri');
- });
-
- it('has "text/vnd.apiary.uri" expected type', () => {
- expect(result).to.have.expectedType('text/vnd.apiary.uri');
+ it('has "text" kind', () => {
+ expect(result).to.have.kind('text');
});
it('has no errors', () => {
@@ -81,16 +65,8 @@ describe('validateURI', () => {
expect(result).to.be.valid;
});
- it('has "null" validator', () => {
- expect(result).to.have.validator(null);
- });
-
- it('has "text/vnd.apiary.uri" real type', () => {
- expect(result).to.have.realType('text/vnd.apiary.uri');
- });
-
- it('has "text/vnd.apiary.uri" expected type', () => {
- expect(result).to.have.expectedType('text/vnd.apiary.uri');
+ it('has "text" kind', () => {
+ expect(result).to.have.kind('text');
});
it('has no errors', () => {
@@ -113,16 +89,8 @@ describe('validateURI', () => {
expect(result).to.be.valid;
});
- it('has "null" validator', () => {
- expect(result).to.have.validator(null);
- });
-
- it('has "text/vnd.apiary.uri" real type', () => {
- expect(result).to.have.realType('text/vnd.apiary.uri');
- });
-
- it('has "text/vnd.apiary.uri" expected type', () => {
- expect(result).to.have.expectedType('text/vnd.apiary.uri');
+ it('has "text" kind', () => {
+ expect(result).to.have.kind('text');
});
it('has no errors', () => {
@@ -147,16 +115,8 @@ describe('validateURI', () => {
expect(result).to.not.be.valid;
});
- it('has "null" validator', () => {
- expect(result).to.have.validator(null);
- });
-
- it('has "text/vnd.apiary.uri" real type', () => {
- expect(result).to.have.realType('text/vnd.apiary.uri');
- });
-
- it('has "text/vnd.apiary.uri" expected type', () => {
- expect(result).to.have.expectedType('text/vnd.apiary.uri');
+ it('has "text" kind', () => {
+ expect(result).to.have.kind('text');
});
describe('produces an error', () => {
@@ -167,9 +127,16 @@ describe('validateURI', () => {
it('has explanatory message', () => {
expect(result)
.to.have.errorAtIndex(0)
- .withMessage(
- 'Expected "uri" field to equal "/dashboard", but got: "/profile".'
- );
+ .withMessage(`Expected URI '/dashboard', but got '/profile'.`);
+ });
+
+ it('includes values', () => {
+ expect(result)
+ .to.have.errorAtIndex(0)
+ .withValues({
+ expected: '/dashboard',
+ actual: '/profile'
+ });
});
});
});
@@ -189,16 +156,8 @@ describe('validateURI', () => {
expect(result).to.not.be.valid;
});
- it('has "null" validator', () => {
- expect(result).to.have.validator(null);
- });
-
- it('has "text/vnd.apiary.uri" real type', () => {
- expect(result).to.have.realType('text/vnd.apiary.uri');
- });
-
- it('has "text/vnd.apiary.uri" expected type', () => {
- expect(result).to.have.expectedType('text/vnd.apiary.uri');
+ it('has "text" kind', () => {
+ expect(result).to.have.kind('text');
});
describe('produces an error', () => {
@@ -210,9 +169,18 @@ describe('validateURI', () => {
expect(result)
.to.have.errorAtIndex(0)
.withMessage(
- 'Expected "uri" field to equal "/account?id=123", but got: "/account".'
+ `Expected URI '/account?id=123', but got '/account'.`
);
});
+
+ it('includes values', () => {
+ expect(result)
+ .to.have.errorAtIndex(0)
+ .withValues({
+ expected: '/account?id=123',
+ actual: '/account'
+ });
+ });
});
});
@@ -230,16 +198,8 @@ describe('validateURI', () => {
expect(result).to.not.be.valid;
});
- it('has "null" validator', () => {
- expect(result).to.have.validator(null);
- });
-
- it('has "text/vnd.apiary.uri" real type', () => {
- expect(result).to.have.realType('text/vnd.apiary.uri');
- });
-
- it('has "text/vnd.apiary.uri" expected type', () => {
- expect(result).to.have.expectedType('text/vnd.apiary.uri');
+ it('has "text" kind', () => {
+ expect(result).to.have.kind('text');
});
describe('produces an error', () => {
@@ -251,9 +211,18 @@ describe('validateURI', () => {
expect(result)
.to.have.errorAtIndex(0)
.withMessage(
- 'Expected "uri" field to equal "/account?name=user", but got: "/account?nAmE=usEr".'
+ `Expected URI '/account?name=user', but got '/account?nAmE=usEr'.`
);
});
+
+ it('includes values', () => {
+ expect(result)
+ .to.have.errorAtIndex(0)
+ .withValues({
+ expected: '/account?name=user',
+ actual: '/account?nAmE=usEr'
+ });
+ });
});
});
@@ -271,16 +240,8 @@ describe('validateURI', () => {
expect(result).to.not.be.valid;
});
- it('has "null" validator', () => {
- expect(result).to.have.validator(null);
- });
-
- it('has "text/vnd.apiary.uri" real type', () => {
- expect(result).to.have.realType('text/vnd.apiary.uri');
- });
-
- it('has "text/vnd.apiary.uri" expected type', () => {
- expect(result).to.have.expectedType('text/vnd.apiary.uri');
+ it('has "text" kind', () => {
+ expect(result).to.have.kind('text');
});
describe('produces an error', () => {
@@ -292,9 +253,18 @@ describe('validateURI', () => {
expect(result)
.to.have.errorAtIndex(0)
.withMessage(
- 'Expected "uri" field to equal "/zoo?type=cats&type=dogs", but got: "/zoo?type=dogs&type=cats".'
+ `Expected URI '/zoo?type=cats&type=dogs', but got '/zoo?type=dogs&type=cats'.`
);
});
+
+ it('includes values', () => {
+ expect(result)
+ .to.have.errorAtIndex(0)
+ .withValues({
+ expected: '/zoo?type=cats&type=dogs',
+ actual: '/zoo?type=dogs&type=cats'
+ });
+ });
});
});
});
diff --git a/test/unit/validators/headers-json-example-validator-test.js b/test/unit/validators/headers-json-example-validator-test.js
index 1319c8a7..7c879552 100644
--- a/test/unit/validators/headers-json-example-validator-test.js
+++ b/test/unit/validators/headers-json-example-validator-test.js
@@ -16,7 +16,7 @@ describe('HeadersJsonExample', () => {
describe('when I provede real data as non obejct', () => {
it('should throw an exception', () => {
const fn = () => {
- headersValidator = new HeadersJsonExample('', { header1: 'value1' });
+ headersValidator = new HeadersJsonExample({ header1: 'value1' }, '');
};
assert.throw(fn, 'is not an Object');
});
@@ -25,7 +25,7 @@ describe('HeadersJsonExample', () => {
describe('when I provede expected data as non obejct', () => {
it('should throw an exception', () => {
const fn = () => {
- headersValidator = new HeadersJsonExample({ header1: 'value1' }, '');
+ headersValidator = new HeadersJsonExample('', { header1: 'value1' });
};
assert.throw(fn, 'is not an Object');
});
@@ -62,8 +62,8 @@ describe('HeadersJsonExample', () => {
describe('when provided real and expected headers differ in upper/lower-case state of keys', () => {
before(() => {
headersValidator = new HeadersJsonExample(
- fixtures.sampleHeadersMixedCase,
- fixtures.sampleHeaders
+ fixtures.sampleHeaders,
+ fixtures.sampleHeadersMixedCase
);
});
@@ -78,8 +78,8 @@ describe('HeadersJsonExample', () => {
describe('when provided real and expected headers differ in one value (real change) of a key different by upper/lower', () => {
before(() => {
headersValidator = new HeadersJsonExample(
- fixtures.sampleHeadersMixedCaseDiffers,
- fixtures.sampleHeaders
+ fixtures.sampleHeaders,
+ fixtures.sampleHeadersMixedCaseDiffers
);
});
describe('and I run validate()', () => {
@@ -93,8 +93,8 @@ describe('HeadersJsonExample', () => {
describe('when key is missing in provided headers', () => {
beforeEach(() => {
headersValidator = new HeadersJsonExample(
- fixtures.sampleHeadersMissing,
- fixtures.sampleHeaders
+ fixtures.sampleHeaders,
+ fixtures.sampleHeadersMissing
);
});
describe('and i run validate()', () => {
@@ -113,8 +113,8 @@ describe('HeadersJsonExample', () => {
describe('when value of content negotiation header in provided headers differs', () => {
beforeEach(() => {
headersValidator = new HeadersJsonExample(
- fixtures.sampleHeadersDiffers,
- fixtures.sampleHeaders
+ fixtures.sampleHeaders,
+ fixtures.sampleHeadersDiffers
);
});
@@ -138,8 +138,8 @@ describe('HeadersJsonExample', () => {
describe('when key is added to provided headers', () => {
before(() => {
headersValidator = new HeadersJsonExample(
- fixtures.sampleHeadersAdded,
- fixtures.sampleHeaders
+ fixtures.sampleHeaders,
+ fixtures.sampleHeadersAdded
);
});
@@ -153,7 +153,7 @@ describe('HeadersJsonExample', () => {
describe('when real is empty object and expected is proper object', () => {
before(() => {
- headersValidator = new HeadersJsonExample({}, fixtures.sampleHeaders);
+ headersValidator = new HeadersJsonExample(fixtures.sampleHeaders, {});
});
describe('and i run validate()', () => {
@@ -167,8 +167,8 @@ describe('HeadersJsonExample', () => {
describe('when non content negotiation header header values differs', () => {
before(() => {
headersValidator = new HeadersJsonExample(
- fixtures.sampleHeadersWithNonContentNegotiationChanged,
- fixtures.sampleHeadersNonContentNegotiation
+ fixtures.sampleHeadersNonContentNegotiation,
+ fixtures.sampleHeadersWithNonContentNegotiationChanged
);
});
@@ -184,8 +184,8 @@ describe('HeadersJsonExample', () => {
output = null;
before(() => {
headersValidator = new HeadersJsonExample(
- fixtures.sampleHeadersMissing,
- fixtures.sampleHeaders
+ fixtures.sampleHeaders,
+ fixtures.sampleHeadersMissing
);
output = headersValidator.validate();
});
diff --git a/test/unit/validators/json-example-test.js b/test/unit/validators/json-example-test.js
index 748ae47f..16add7e1 100644
--- a/test/unit/validators/json-example-test.js
+++ b/test/unit/validators/json-example-test.js
@@ -12,10 +12,9 @@ describe('JsonExample', () => {
describe('when I provide non string real data', () => {
it('should throw exception', () => {
const fn = () => {
- bodyValidator = new JsonExample(
- { malformed: 'malformed ' },
- "{'header1': 'value1'}"
- );
+ bodyValidator = new JsonExample("{'header1': 'value1'}", {
+ malformed: 'malformed '
+ });
};
assert.throws(fn);
});
@@ -25,8 +24,8 @@ describe('JsonExample', () => {
it('should not throw exception', () => {
fn = () => {
bodyValidator = new JsonExample(
- '"Number of profiles deleted: com.viacom.auth.infrastructure.DocumentsUpdated@1"',
- '{"header1": "value1"}'
+ '{"header1": "value1"}',
+ '"Number of profiles deleted: com.viacom.auth.infrastructure.DocumentsUpdated@1"'
);
};
assert.doesNotThrow(fn);
@@ -37,8 +36,8 @@ describe('JsonExample', () => {
it('should not throw exception', () => {
const fn = () => {
bodyValidator = new JsonExample(
- '{"header1": "value1"}',
- '"Number of profiles deleted: com.viacom.auth.infrastructure.DocumentsUpdated@1"'
+ '"Number of profiles deleted: com.viacom.auth.infrastructure.DocumentsUpdated@1"',
+ '{"header1": "value1"}'
);
};
assert.doesNotThrow(fn);
@@ -109,8 +108,8 @@ describe('JsonExample', () => {
describe('when key is missing in provided real data', () => {
before(() => {
bodyValidator = new JsonExample(
- fixtures.sampleJsonSimpleKeyMissing,
- fixtures.sampleJson
+ fixtures.sampleJson,
+ fixtures.sampleJsonSimpleKeyMissing
);
});
describe('and i run validate()', () => {
@@ -122,7 +121,7 @@ describe('JsonExample', () => {
describe.skip('when value has different primitive type', () => {
before(() => {
- bodyValidator = new JsonExample('{"a": "a"}', '{"a": 1}');
+ bodyValidator = new JsonExample('{"a": 1}', '{"a": "a"}');
});
describe('and i run validate()', () => {
it('PROPOSAL: should return 1 errors', () => {
@@ -150,8 +149,8 @@ describe('JsonExample', () => {
describe('when key is added to provided data', () => {
before(() => {
bodyValidator = new JsonExample(
- fixtures.sampleJsonComplexKeyAdded,
- fixtures.sampleJson
+ fixtures.sampleJson,
+ fixtures.sampleJsonComplexKeyAdded
);
});
describe('and i run validate()', () => {
@@ -180,8 +179,8 @@ describe('JsonExample', () => {
describe('when key value is a null', () => {
before(() => {
bodyValidator = new JsonExample(
- '{"a": "a","b": null }',
- '{"a":"a", "b": null}'
+ '{"a":"a", "b": null}',
+ '{"a": "a","b": null }'
);
});
describe('and i run validate()', () => {
@@ -195,7 +194,7 @@ describe('JsonExample', () => {
describe('when expected and real data are different on root level', () => {
describe('when expected is object and real is array', () => {
before(() => {
- bodyValidator = new JsonExample('[{"a":1}]', '{"a":1}');
+ bodyValidator = new JsonExample('{"a":1}', '[{"a":1}]');
});
describe('and i run validate()', () => {
it('should not throw exception', () => {
@@ -210,7 +209,7 @@ describe('JsonExample', () => {
describe('when expected is array and real is object', () => {
before(() => {
- bodyValidator = new JsonExample('{"a":1}', '[{"a":1}]');
+ bodyValidator = new JsonExample('[{"a":1}]', '{"a":1}');
});
describe('and i run validate()', () => {
it('should not throw exception', () => {
@@ -224,7 +223,7 @@ describe('JsonExample', () => {
describe('when expected is primitive and real is object', () => {
before(() => {
- bodyValidator = new JsonExample('0', '{"a":1}');
+ bodyValidator = new JsonExample('{"a":1}', '0');
});
describe('and i run validate()', () => {
it('should not throw exception', () => {
@@ -238,7 +237,7 @@ describe('JsonExample', () => {
describe('when expected array and real is object', () => {
before(() => {
- bodyValidator = new JsonExample('[0,1,2]', '{"a":1}');
+ bodyValidator = new JsonExample('{"a":1}', '[0,1,2]');
});
describe('and i run validate()', () => {
it('should not throw exception', () => {
@@ -252,7 +251,7 @@ describe('JsonExample', () => {
describe('when real is empty object and expected is non-empty object', () => {
before(() => {
- bodyValidator = new JsonExample('{}', '{"a":1}');
+ bodyValidator = new JsonExample('{"a":1}', '{}');
});
describe('and i run validate()', () => {
diff --git a/test/unit/validators/json-schema-test.js b/test/unit/validators/json-schema-test.js
index 0a130a8e..64d86e4d 100644
--- a/test/unit/validators/json-schema-test.js
+++ b/test/unit/validators/json-schema-test.js
@@ -13,11 +13,11 @@ describe('JsonSchema', () => {
const dataForTypes = {
string: {
- real: fixtures.sampleJsonComplexKeyMissing,
+ actual: fixtures.sampleJsonComplexKeyMissing,
schema: fixtures.sampleJsonSchemaNonStrict
},
object: {
- real: JSON.parse(fixtures.sampleJsonComplexKeyMissing),
+ actual: JSON.parse(fixtures.sampleJsonComplexKeyMissing),
schema: JSON.parse(fixtures.sampleJsonSchemaNonStrict)
}
};
@@ -34,12 +34,12 @@ describe('JsonSchema', () => {
let validator = null;
beforeEach(() => {
- validator = new JsonSchema(data.real, data.schema);
+ validator = new JsonSchema(data.schema, data.actual);
});
it('should not throw an exception', () => {
const fn = () => {
- new JsonSchema(data.real, data.schema);
+ new JsonSchema(data.schema, data.actual);
};
assert.doesNotThrow(fn);
});
@@ -134,11 +134,11 @@ describe('JsonSchema', () => {
}
);
- describe('when validation performed on real empty object', () => {
+ describe('when validation performed on actual empty object', () => {
it('should return some errors', () => {
validator = new JsonSchema(
- {},
- JSON.parse(fixtures.sampleJsonSchemaNonStrict)
+ JSON.parse(fixtures.sampleJsonSchemaNonStrict),
+ {}
);
result = validator.validate();
assert.notEqual(validator.validate().length, 0);
@@ -176,7 +176,7 @@ describe('JsonSchema', () => {
it('should throw an error for "schema"', () => {
const invalidStringifiedSchema = require('../../fixtures/invalid-stringified-schema');
const fn = () => {
- new JsonSchema({}, invalidStringifiedSchema);
+ new JsonSchema(invalidStringifiedSchema, {});
};
assert.throw(fn);
});
@@ -192,8 +192,8 @@ describe('JsonSchema', () => {
fixtures.sampleJsonBodyTestingAmandaMessages
).length;
validator = new JsonSchema(
- fixtures.sampleJsonBodyTestingAmandaMessages,
- fixtures.sampleJsonSchemaTestingAmandaMessages
+ fixtures.sampleJsonSchemaTestingAmandaMessages,
+ fixtures.sampleJsonBodyTestingAmandaMessages
);
results = validator.validate();
});
@@ -220,7 +220,7 @@ describe('JsonSchema', () => {
before(() => {
const invalidSchema = require('../../fixtures/invalid-schema-v3');
fn = () => {
- validator = new JsonSchema({}, invalidSchema);
+ validator = new JsonSchema(invalidSchema, {});
};
});
@@ -242,7 +242,7 @@ describe('JsonSchema', () => {
before(() => {
const validSchema = require('../../fixtures/valid-schema-v3');
fn = () => {
- validator = new JsonSchema({}, validSchema);
+ validator = new JsonSchema(validSchema, {});
};
});
@@ -263,7 +263,7 @@ describe('JsonSchema', () => {
before(() => {
const invalidSchema = require('../../fixtures/invalid-schema-v4');
fn = () => {
- validator = new JsonSchema({}, invalidSchema);
+ validator = new JsonSchema(invalidSchema, {});
};
});
@@ -285,7 +285,7 @@ describe('JsonSchema', () => {
before(() => {
validSchema = require('../../fixtures/valid-schema-v4');
fn = () => {
- validator = new JsonSchema({}, validSchema);
+ validator = new JsonSchema(validSchema, {});
};
});
@@ -306,7 +306,7 @@ describe('JsonSchema', () => {
validSchema = require('../../fixtures/valid-schema-v3');
delete validSchema['$schema'];
fn = () => {
- validator = new JsonSchema({}, validSchema);
+ validator = new JsonSchema(validSchema, {});
};
});
@@ -326,7 +326,7 @@ describe('JsonSchema', () => {
validSchema = require('../../fixtures/valid-schema-v4');
delete validSchema['$schema'];
fn = () => {
- validator = new JsonSchema({}, validSchema);
+ validator = new JsonSchema(validSchema, {});
};
});
@@ -346,7 +346,7 @@ describe('JsonSchema', () => {
validSchema = require('../../fixtures/invalid-schema-v3-v4');
delete validSchema['$schema'];
fn = () => {
- validator = new JsonSchema({}, validSchema);
+ validator = new JsonSchema(validSchema, {});
};
});
@@ -375,7 +375,7 @@ describe('JsonSchema', () => {
before(() => {
validSchema = require('../../fixtures/valid-schema-v3');
delete validSchema['$schema'];
- validator = new JsonSchema({}, validSchema);
+ validator = new JsonSchema(validSchema, {});
v3 = sinon.stub(validator, 'validateSchemaV3');
v4 = sinon.stub(validator, 'validateSchemaV4');
@@ -404,7 +404,7 @@ describe('JsonSchema', () => {
before(() => {
const validSchema = require('../../fixtures/valid-schema-v4');
delete validSchema['$schema'];
- validator = new JsonSchema({}, validSchema);
+ validator = new JsonSchema(validSchema, {});
sinon.stub(validator, 'validateSchemaV3');
sinon.stub(validator, 'validateSchemaV4');
@@ -434,9 +434,9 @@ describe('JsonSchema', () => {
before(() => {
const validSchema = require('../../fixtures/valid-schema-v4-with-refs');
- const real = JSON.parse('{ "foo": "bar" }');
+ const actual = JSON.parse('{ "foo": "bar" }');
fn = () => {
- validator = new JsonSchema(real, validSchema);
+ validator = new JsonSchema(validSchema, actual);
return validator.validatePrivate();
};
});
@@ -452,9 +452,9 @@ describe('JsonSchema', () => {
before(() => {
const validSchema = require('../../fixtures/valid-schema-v4-with-refs');
- const real = JSON.parse('{ "foo": 1 }');
+ const actual = JSON.parse('{ "foo": 1 }');
fn = () => {
- validator = new JsonSchema(real, validSchema);
+ validator = new JsonSchema(validSchema, actual);
return validator.validatePrivate();
};
});
@@ -475,9 +475,9 @@ describe('JsonSchema', () => {
before(() => {
const invalidSchema = require('../../fixtures/invalid-schema-v4-with-refs');
- const real = JSON.parse('{ "foo": "bar" }');
+ const actual = JSON.parse('{ "foo": "bar" }');
fn = () => {
- validator = new JsonSchema(real, invalidSchema);
+ validator = new JsonSchema(invalidSchema, actual);
return validator.validatePrivate();
};
});
@@ -500,7 +500,7 @@ describe('JsonSchema', () => {
const validSchema = require('../../fixtures/valid-schema-v3');
delete validSchema['$schema'];
fn = () => {
- validator = new JsonSchema({}, validSchema);
+ validator = new JsonSchema(validSchema, {});
validator.jsonSchemaVersion = null;
validator.validatePrivate();
};
diff --git a/test/unit/validators/text-diff-test.js b/test/unit/validators/text-diff-test.js
index b9947726..ca1df7ed 100644
--- a/test/unit/validators/text-diff-test.js
+++ b/test/unit/validators/text-diff-test.js
@@ -1,166 +1,91 @@
-/* eslint-disable */
-const { assert } = require('chai');
-const DiffMatchPatch = require('googlediff');
-
-const fixtures = require('../../fixtures');
+/* eslint-disable no-new */
+const { expect } = require('chai');
const { TextDiff } = require('../../../lib/validators/text-diff');
-const {
- ValidationErrors
-} = require('../../../lib/validators/validation-errors');
describe('TextDiff', () => {
- validator = null;
-
- describe('when i create new instance of validator with incorrect "data" (first argument)', () => {
- validator = null;
-
- it('should throw exception', () => {
+ describe('when expected non-string data', () => {
+ it('should throw an exception', () => {
const fn = () => {
- validator = new TextDiff(null, '');
+ new TextDiff(null, '');
};
- assert.throws(fn);
+ expect(fn).to.throw();
});
});
- describe('when i create new instance of validator with incorrect "expected" (second argument)', () => {
- validator = null;
-
- it('should throw exception', () => {
- fn = () => {
- validator = new TextDiff('', null);
- };
- assert.throws(fn);
- });
- });
-
- describe('when i create new instance of validator with "Iñtërnâtiônàlizætiøn☃" string as "data"', () => {
- validator = null;
-
- it('should not throw exception', () => {
+ describe('when given non-string actual data', () => {
+ it('should throw an exception', () => {
const fn = () => {
- validator = new TextDiff('Iñtërnâtiônàlizætiøn☃', '');
+ new TextDiff('', null);
};
- assert.doesNotThrow(fn);
- });
-
- describe('when I run validate', () => {
- it('should not throw exception', () => {
- const fn = () => validator.validate();
- assert.doesNotThrow(fn);
- });
+ expect(fn).to.throw();
});
});
- describe('when i create new instance of validator with surrogate pair in data', () => {
- validator = null;
+ describe('when expected internationalized string', () => {
+ const expected = 'Iñtërnâtiônàlizætiøn☃';
- it('should not throw exception', () => {
- const fn = () => {
- validator = new TextDiff('text1\uD800', '\uD800text1');
- };
- assert.doesNotThrow(fn);
+ it('should resolve on matching actual string', () => {
+ const validator = new TextDiff(expected, expected);
+ expect(validator.validate()).to.be.true;
});
- describe('when I run validate', () => {
- it('should not throw exception', () => {
- const fn = () => validator.validate();
- assert.doesNotThrow(fn);
- });
+ it('should reject on non-matching actual string', () => {
+ const validator = new TextDiff(expected, 'Nâtiônàl');
+ expect(validator.validate()).to.be.false;
});
});
- describe('when i create new instance of validator with correct data', () => {
- validator = null;
+ describe('when expected textual data', () => {
+ const expected = 'john';
- it('should not throw exception', () => {
- const fn = () => {
- validator = new TextDiff('text1', 'text1');
- };
- assert.doesNotThrow(fn);
+ it('should resolve when given matching actual data', () => {
+ const validator = new TextDiff(expected, 'john');
+ expect(validator.validate()).to.be.true;
});
- describe('when data are same and I run validate', () => {
- validationResult = null;
-
- before(() => {
- validator = new TextDiff('text1', 'text1');
- validationResult = validator.validate();
- });
-
- it('should set output property', () => {
- assert.isDefined(validator.output);
-
- it('output should be a string', () => {
- assert.isString(validator.output);
- });
-
- it('output should be empty string', () => {
- assert.equal(validator.output, '');
- });
- });
+ it('should reject when given non-matching actual data', () => {
+ const validator = new TextDiff(expected, 'barry');
+ expect(validator.validate()).to.be.false;
});
+ });
- describe('when data differs and I run validate', () => {
- validationResult = null;
-
- before(() => {
- validator = new TextDiff('text1', 'text2');
- validationResult = validator.validate();
- });
-
- it('output property should be a string', () => {
- assert.isString(validator.output);
- });
-
- it('output property should not be empty string', () => {
- assert.notEqual(validator.output, '');
- });
-
- it('output property should contain + and -', () => {
- assert.include(validator.output, '-');
- assert.include(validator.output, '+');
- });
+ describe('when evaluating output to results', () => {
+ describe('when expected and actual data match', () => {
+ const validator = new TextDiff('john', 'john');
+ validator.validate();
+ const result = validator.evaluateOutputToResults();
- it('output property should be persed by googlediff to an array', () => {
- dmp = new DiffMatchPatch();
- assert.isArray(dmp.patch_fromText(validator.output));
+ it('should return an empty array', () => {
+ expect(result).to.be.instanceOf(Array);
+ expect(result).to.have.lengthOf(0);
});
});
- });
- describe('.evaluateOutputToResults', () => {
- data = null;
- results = null;
-
- describe('empty validation result', () => {
- before(() => {
- validator = new TextDiff('', '');
- validator.validate();
- results = validator.evaluateOutputToResults();
- });
+ describe('when expected and actual data do not match', () => {
+ const validator = new TextDiff('john', 'barry');
+ validator.validate();
+ const result = validator.evaluateOutputToResults();
it('should return an array', () => {
- assert.isArray(results);
+ expect(result).to.be.instanceOf(Array);
});
- it('should has no results', () => {
- assert.equal(results.length, 0);
+ it('should contain exactly one error', () => {
+ expect(result).to.have.lengthOf(1);
});
- });
- describe('non empty validation result', () => {
- before(() => {
- validator = new TextDiff('abc', 'cde');
- validator.validate();
- results = validator.evaluateOutputToResults();
+ it('error should include the "message"', () => {
+ expect(result[0]).to.have.property(
+ 'message',
+ 'Actual and expected data do not match.'
+ );
});
- it('should return an array', () => {
- assert.isArray(results);
- });
-
- it('should contain one error', () => {
- assert.lengthOf(results, 1);
+ it('error should contain compared values', () => {
+ expect(result[0]).to.have.deep.property('values', {
+ expected: 'john',
+ actual: 'barry'
+ });
});
});
});