UNPKG

@opra/testing

Version:

Opra testing package

273 lines (272 loc) 9.03 kB
import typeIs from '@browsery/type-is'; import { omitNullish, updateErrorMessage } from '@jsopen/objects'; import { MimeTypes, OpraFilter } from '@opra/common'; import { expect } from 'expect'; import ruleJudgmentLib from 'rule-judgment'; import { ApiExpectBase } from './api-expect-base.js'; const ruleJudgment = typeof ruleJudgmentLib === 'object' ? ruleJudgmentLib.default : ruleJudgmentLib; export class ApiExpectCollection extends ApiExpectBase { get not() { return new ApiExpectCollection(this.response, !this.isNot); } /** * Tests if Collection have number of items in payload * @param min Minimum number of items. Default 1 * @param max Maximum number of items */ toReturnItems(min, max) { let msg = ''; try { const data = typeIs.is(this.response.contentType, [ MimeTypes.opra_response_json, ]) ? this.response.body.payload : this.response.body; msg += `Payload should be array.`; this._expect(Array.isArray(data)).toBeTruthy(); msg += `The length of payload array do not match. `; const l = data.length; this._expect(l).toBeGreaterThanOrEqual(min || 1); if (max) this._expect(l).toBeLessThanOrEqual(max); } catch (e) { if (msg) e.message = msg + '\n\n' + e.message; if (typeof Error.captureStackTrace === 'function') Error.captureStackTrace(e, this.toReturnItems); else updateErrorMessage(e, e.message); throw e; } return this; } toContainTotalMatches(min, max) { let msg = ''; try { msg += `The value of "totalMatches" do not match. `; const l = this.response.body.totalMatches; this._expect(l).toBeGreaterThanOrEqual(min || 1); if (max) this._expect(l).toBeLessThanOrEqual(max); } catch (e) { if (msg) e.message = msg + '\n\n' + e.message; if (typeof Error.captureStackTrace === 'function') Error.captureStackTrace(e, this.toReturnItems); else updateErrorMessage(e, e.message); throw e; } return this; } /** * Tests if Collection items matches given object * @param expected */ toMatch(expected) { try { expected = omitNullish(expected); const data = typeIs.is(this.response.contentType, [ MimeTypes.opra_response_json, ]) ? this.response.body.payload : this.response.body; for (const x of data) { this._expect(x).toMatchObject(expected); } } catch (e) { Error.captureStackTrace(e, this.toMatch); throw e; } return this; } /** * Tests if Collection items has all of provided fields. * @param fields */ toContainFields(fields) { try { fields = Array.isArray(fields) ? fields : [fields]; const data = typeIs.is(this.response.contentType, [ MimeTypes.opra_response_json, ]) ? this.response.body.payload : this.response.body; for (const item of data) { this._expect(Object.keys(item)).toEqual(expect.arrayContaining(fields)); } } catch (e) { Error.captureStackTrace(e, this.toContainFields); throw e; } return this; } /** * Tests if Collection items only contains all of provided fields. * @param fields */ toContainAllFields(fields) { try { fields = Array.isArray(fields) ? fields : [fields]; const data = typeIs.is(this.response.contentType, [ MimeTypes.opra_response_json, ]) ? this.response.body.payload : this.response.body; for (const item of data) { this._expect(Object.keys(item)).toEqual(fields); } } catch (e) { Error.captureStackTrace(e, this.toContainAllFields); throw e; } return this; } /** * Tests if Collection is sorted by given field(s). * @param fields */ toBeSortedBy(fields) { try { fields = Array.isArray(fields) ? fields : [fields]; const data = typeIs.is(this.response.contentType, [ MimeTypes.opra_response_json, ]) ? this.response.body.payload : this.response.body; this._expect(data).opraCollectionToBeSortedBy(fields); } catch (e) { Error.captureStackTrace(e, this.toBeSortedBy); throw e; } return this; } /** * Tests if Collection is filtered by given condition. * @param filter */ toBeFilteredBy(filter) { const f = convertFilter(filter); if (f) { const j = ruleJudgment(f); const data = typeIs.is(this.response.contentType, [ MimeTypes.opra_response_json, ]) ? this.response.body.payload : this.response.body; const filtered = data.filter(j); try { this._expect(data).toStrictEqual(filtered); } catch (e) { Error.captureStackTrace(e, this.toBeFilteredBy); throw e; } } return this; } } expect.extend({ opraCollectionToBeSortedBy(received, properties) { const fieldsMap = properties.map(x => x.split('.')); const getValue = (obj, fieldMap) => { let v = obj; let i = 0; while (v && i < fieldMap.length) { v = v[fieldMap[i++]]; } return v; }; let pass = true; let message; if (pass) { const sorted = [...(received || [])]; sorted.sort((a, b) => { for (const sortField of fieldsMap) { const l = getValue(a, sortField); const r = getValue(b, sortField); if (l < r) return -1; if (l > r) return 1; } return 0; }); try { expect(received).toEqual(sorted); } catch { pass = false; message = () => 'Items are not sorted as expected'; } } return { actual: received, message, pass, }; }, }); export function convertFilter(str) { const ast = typeof str === 'string' ? OpraFilter.parse(str) : str; if (!ast) return; if (ast instanceof OpraFilter.ComparisonExpression) { const left = convertFilter(ast.left); const right = convertFilter(ast.right); switch (ast.op) { case '=': return { $eq: { [left]: right } }; case '!=': return { $ne: { [left]: right } }; case '>': return { $gt: { [left]: right } }; case '>=': return { $gte: { [left]: right } }; case '<': return { $lt: { [left]: right } }; case '<=': return { $lte: { [left]: right } }; case 'in': return { $in: { [left]: right } }; case '!in': return { $nin: { [left]: right } }; default: throw new Error(`ComparisonExpression operator (${ast.op}) not implemented yet`); } } if (ast instanceof OpraFilter.QualifiedIdentifier) { return ast.value; } if (ast instanceof OpraFilter.NumberLiteral || ast instanceof OpraFilter.StringLiteral || ast instanceof OpraFilter.BooleanLiteral || ast instanceof OpraFilter.NullLiteral || ast instanceof OpraFilter.DateLiteral || ast instanceof OpraFilter.TimeLiteral) { return ast.value; } if (ast instanceof OpraFilter.ArrayExpression) { return ast.items.map(convertFilter); } if (ast instanceof OpraFilter.LogicalExpression) { if (ast.op === 'or') return { $or: ast.items.map(convertFilter) }; return { $and: ast.items.map(convertFilter) }; } if (ast instanceof OpraFilter.ArrayExpression) { return ast.items.map(convertFilter); } if (ast instanceof OpraFilter.ParenthesizedExpression) { return convertFilter(ast.expression); } throw new Error(`${ast.kind} is not implemented yet`); }