UNPKG

4.19 kBJavaScriptView Raw
1'use strict'
2
3/**
4 * Module dependencies.
5 */
6
7const got = require('got')
8const JSPath = require('jspath')
9const assert = require('assert').strict
10
11/**
12 * Initializes `Test`.
13 *
14 * @method freddo
15 * @api public
16 */
17
18function Test(url, options) {
19 this.data = {
20 url,
21 options,
22 response: {},
23 error: null
24 }
25 this.promise = Promise.resolve(null)
26 this.request()
27}
28
29const freddo = (url, options = null) => new Test(url, options)
30
31Test.prototype.extend = function(promise, that) {
32 for (const key in that) {
33 promise[key] = that[key]
34 }
35}
36
37Test.prototype.next = function(what) {
38 this.promise = this.promise.then(what)
39 this.extend(this.promise, this)
40 return this.promise
41}
42
43Test.prototype.request = function() {
44 return this.next(async () => {
45 let response
46 if (this.data.options != undefined) {
47 response = await got(this.data.url, this.data.options)
48 } else {
49 response = await got(this.data.url)
50 }
51 this.data.response = response
52 return true
53 })
54}
55
56Test.prototype.verify = async function(key, expected, isHeader) {
57 let check = expected
58 let value, location
59 if (key instanceof Expression) {
60 if (typeof this.data.response.body != 'string') {
61 this.data.response.body = JSON.stringify(this.data.response.body)
62 }
63 value = key.apply(JSON.parse(this.data.response.body))
64 location = `expression ${JSON.stringify(key.expression)}`
65 } else {
66 if (isHeader) {
67 value = this.data.response.headers[key]
68 } else {
69 value = this.data.response[key]
70 }
71 if (typeof value === 'undefined') {
72 throw new Error(`Key ${JSON.stringify(key)} does not exist`)
73 }
74 location = `key ${JSON.stringify(key)}`
75 }
76 if (typeof expected !== 'function') {
77 check = (actual, location) => {
78 try {
79 assert.deepStrictEqual(actual, expected)
80 } catch (e) {
81 return {
82 result: false,
83 error: `Expected ${location} to be ${JSON.stringify(expected)}, but got ${JSON.stringify(actual)}`
84 }
85 }
86 return true
87 }
88 }
89 let result = check(value, location)
90 let error = `Custom assertion failed: ${check}`
91 if (typeof result !== 'boolean') {
92 if (typeof result.result === 'undefined') {
93 throw new Error('Custom assertion functions must return a boolean or a {result, error} object')
94 }
95 if (typeof result.error !== 'undefined') {
96 error = result.error
97 }
98 result = result.result
99 }
100 if (!result) {
101 this.data.error = error
102 }
103 return result
104}
105
106Test.prototype.expect = function(key, expected, isHeader = false) {
107 return this.next((prev) => prev && this.verify(key, expected, isHeader))
108}
109
110Test.prototype.status = function(expected) {
111 return this.expect('statusCode', expected)
112}
113
114Test.prototype.header = function(key, expected) {
115 return this.expect(key, String(expected), true)
116}
117
118Test.prototype.body = function(expected, expression = null) {
119 if (expression == null) {
120 return this.expect('body', expected)
121 }
122 return this.expect(expression, expected)
123}
124
125Test.prototype.redirectsTo = function(url) {
126 return this.status((code) => [301, 302, 303, 307, 308].includes(code))
127 .header('location', url)
128}
129
130Test.prototype.ensure = async function() {
131 if (!(await this)) {
132 throw new Error(this.data.error)
133 }
134}
135
136const exists = (actual, location) => {
137 if (actual.length != 0) {
138 return true
139 }
140 return {
141 result: false,
142 error: `Expected ${location} to contain a value, but it does not exist`
143 }
144}
145
146class Expression {
147 constructor(expression) {
148 this.expression = expression
149 }
150 apply(haystack) {
151 let result = JSPath.apply(this.expression, haystack)
152 return result
153 }
154}
155
156const expr = expression => new Expression(expression)
157
158/**
159 * Expose `freddo`, `expr`, and `exists`.
160 */
161
162module.exports = { freddo, expr, exists }
\No newline at end of file