velocious 1.0.40 → 1.0.42
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/package.json +2 -1
- package/peak_flow.yml +8 -4
- package/spec/cli/commands/db/create-spec.js +1 -0
- package/spec/cli/commands/db/migrate-spec.js +28 -11
- package/spec/cli/commands/test/test-files-finder-spec.js +5 -4
- package/spec/database/record/create-spec.js +6 -0
- package/spec/database/record/destroy-spec.js +6 -3
- package/spec/database/record/find-spec.js +3 -1
- package/spec/database/record/query-spec.js +7 -3
- package/spec/database/record/translation-fallbacks-spec.js +1 -1
- package/spec/database/record/update-spec.js +2 -1
- package/spec/database/record/validations-spec.js +10 -6
- package/spec/database/transactions-spec.js +7 -5
- package/spec/dummy/index.js +18 -29
- package/spec/dummy/src/config/configuration.example.js +3 -2
- package/spec/dummy/src/config/configuration.peakflow.mariadb.js +3 -7
- package/spec/dummy/src/config/configuration.peakflow.mssql.js +3 -2
- package/spec/dummy/src/config/configuration.peakflow.pgsql.js +3 -7
- package/spec/dummy/src/config/configuration.peakflow.sqlite.js +3 -7
- package/spec/dummy/src/config/testing.js +34 -0
- package/spec/dummy/src/database/migrations/20250912183605-create-users.js +15 -0
- package/spec/dummy/src/database/migrations/20250912183606-create-authentication-tokens.js +15 -0
- package/spec/dummy/src/models/authentication-token.js +8 -0
- package/spec/dummy/src/models/task.js +2 -2
- package/spec/dummy/src/models/user.js +15 -0
- package/spec/dummy/src/routes/projects/controller.js +7 -1
- package/spec/http-server/client-spec.js +1 -1
- package/spec/http-server/get-spec.js +1 -1
- package/spec/http-server/post-spec.js +21 -8
- package/spec/http-server/root-get-spec.js +1 -1
- package/src/cli/commands/db/create.js +11 -8
- package/src/cli/commands/db/drop.js +19 -0
- package/src/cli/commands/db/migrate.js +1 -1
- package/src/cli/commands/db/reset.js +7 -1
- package/src/cli/commands/test.js +10 -4
- package/src/configuration.js +27 -6
- package/src/controller.js +7 -19
- package/src/database/drivers/base-column.js +22 -0
- package/src/database/drivers/base-columns-index.js +34 -0
- package/src/database/drivers/base-table.js +43 -0
- package/src/database/drivers/base.js +44 -16
- package/src/database/drivers/mssql/column.js +43 -2
- package/src/database/drivers/mssql/columns-index.js +9 -0
- package/src/database/drivers/mssql/index.js +26 -14
- package/src/database/drivers/mssql/table.js +16 -1
- package/src/database/drivers/mysql/column.js +47 -2
- package/src/database/drivers/mysql/columns-index.js +10 -0
- package/src/database/drivers/mysql/index.js +5 -8
- package/src/database/drivers/mysql/table.js +3 -1
- package/src/database/drivers/pgsql/column.js +37 -2
- package/src/database/drivers/pgsql/columns-index.js +4 -0
- package/src/database/drivers/pgsql/index.js +6 -5
- package/src/database/drivers/pgsql/table.js +3 -1
- package/src/database/drivers/sqlite/base.js +6 -4
- package/src/database/drivers/sqlite/column.js +46 -2
- package/src/database/drivers/sqlite/columns-index.js +22 -0
- package/src/database/drivers/sqlite/connection-sql-js.js +1 -1
- package/src/database/drivers/sqlite/table.js +3 -1
- package/src/database/migrator.js +27 -7
- package/src/database/query/create-index-base.js +10 -1
- package/src/database/query/create-table-base.js +56 -2
- package/src/database/query/drop-table-base.js +8 -2
- package/src/database/table-data/index.js +2 -1
- package/src/database/use-database.js +1 -1
- package/src/http-server/client/request-buffer/index.js +9 -5
- package/src/http-server/client/request.js +6 -6
- package/src/routes/base-route.js +11 -0
- package/src/routes/namespace-route.js +24 -0
- package/src/routes/resolver.js +1 -1
- package/src/templates/configuration.js +1 -1
- package/src/testing/test-runner.js +86 -26
- package/src/testing/test.js +155 -7
- package/src/utils/with-tracked-stack.js +5 -3
|
@@ -3,7 +3,7 @@ import restArgsError from "../../utils/rest-args-error.js"
|
|
|
3
3
|
class TableColumn {
|
|
4
4
|
constructor(name, args) {
|
|
5
5
|
if (args) {
|
|
6
|
-
const {autoIncrement, default: columnDefault, foreignKey, maxLength, name, null: argsNull, primaryKey, type, ...restArgs} = args
|
|
6
|
+
const {autoIncrement, default: columnDefault, foreignKey, index, maxLength, name, null: argsNull, primaryKey, type, ...restArgs} = args
|
|
7
7
|
|
|
8
8
|
restArgsError(restArgs)
|
|
9
9
|
}
|
|
@@ -15,6 +15,7 @@ class TableColumn {
|
|
|
15
15
|
getAutoIncrement = () => this.args?.autoIncrement
|
|
16
16
|
getDefault = () => this.args?.default
|
|
17
17
|
getForeignKey = () => this.args?.foreignKey
|
|
18
|
+
getIndex = () => this.args?.index
|
|
18
19
|
getMaxLength = () => this.args?.maxLength
|
|
19
20
|
getName = () => this.name
|
|
20
21
|
getNull = () => this.args?.null
|
|
@@ -14,7 +14,7 @@ const loadMigrations = function loadMigrations({migrationsRequireContext, ...res
|
|
|
14
14
|
instance.running = true
|
|
15
15
|
|
|
16
16
|
try {
|
|
17
|
-
await Configuration.current().
|
|
17
|
+
await Configuration.current().ensureConnections(async () => {
|
|
18
18
|
const migrator = new Migrator({configuration: Configuration.current()})
|
|
19
19
|
|
|
20
20
|
await migrator.prepare()
|
|
@@ -166,12 +166,16 @@ export default class RequestBuffer {
|
|
|
166
166
|
this.multiPartyFormData = true
|
|
167
167
|
this.setState("multi-part-form-data")
|
|
168
168
|
} else {
|
|
169
|
-
if (
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
169
|
+
if (this.contentLength === 0) {
|
|
170
|
+
this.completeRequest()
|
|
171
|
+
} else if (!this.contentLength) {
|
|
172
|
+
throw new Error("Content length hasn't been set")
|
|
173
|
+
} else {
|
|
174
|
+
this.postBodyBuffer = new ArrayBuffer(this.contentLength)
|
|
175
|
+
this.postBodyChars = new Uint8Array(this.postBodyBuffer)
|
|
173
176
|
|
|
174
|
-
|
|
177
|
+
this.setState("post-body")
|
|
178
|
+
}
|
|
175
179
|
}
|
|
176
180
|
} else {
|
|
177
181
|
throw new Error(`Unknown HTTP method: ${this.httpMethod}`)
|
|
@@ -7,13 +7,13 @@ export default class VelociousHttpServerClientRequest {
|
|
|
7
7
|
this.requestParser = new RequestParser({configuration})
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
baseURL
|
|
11
|
-
feed
|
|
12
|
-
header
|
|
13
|
-
httpMethod
|
|
14
|
-
host
|
|
10
|
+
baseURL() { return `${this.protocol()}://${this.hostWithPort()}` }
|
|
11
|
+
feed(data) { return this.requestParser.feed(data) }
|
|
12
|
+
header(headerName) { return this.requestParser.requestBuffer.getHeader(headerName)?.value }
|
|
13
|
+
httpMethod() { return this.requestParser.getHttpMethod() }
|
|
14
|
+
host() { return this.requestParser.getHost() }
|
|
15
15
|
|
|
16
|
-
hostWithPort
|
|
16
|
+
hostWithPort() {
|
|
17
17
|
const port = this.port()
|
|
18
18
|
const protocol = this.protocol()
|
|
19
19
|
let hostWithPort = `${this.host()}`
|
package/src/routes/base-route.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import GetRoute from "./get-route.js"
|
|
2
|
+
import NamespaceRoute from "./namespace-route.js"
|
|
2
3
|
import PostRoute from "./post-route.js"
|
|
3
4
|
import ResourceRoute from "./resource-route.js"
|
|
4
5
|
|
|
@@ -20,6 +21,16 @@ export function initBaseRoute() {
|
|
|
20
21
|
throw new Error(`No 'matchWithPath' implemented on ${this.constructor.name}`)
|
|
21
22
|
}
|
|
22
23
|
|
|
24
|
+
namespace(name, callback) {
|
|
25
|
+
const route = new NamespaceRoute({name})
|
|
26
|
+
|
|
27
|
+
this.routes.push(route)
|
|
28
|
+
|
|
29
|
+
if (callback) {
|
|
30
|
+
callback(route)
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
23
34
|
post(name, args) {
|
|
24
35
|
const route = new PostRoute({name, args})
|
|
25
36
|
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import BaseRoute, {initBaseRoute} from "./base-route.js"
|
|
2
|
+
import escapeStringRegexp from "escape-string-regexp"
|
|
3
|
+
|
|
4
|
+
initBaseRoute()
|
|
5
|
+
|
|
6
|
+
export default class VelociousRouteNamespaceRoute extends BaseRoute {
|
|
7
|
+
constructor({name}) {
|
|
8
|
+
super()
|
|
9
|
+
this.name = name
|
|
10
|
+
this.regExp = new RegExp(`^(${escapeStringRegexp(name)})(.*)$`)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
matchWithPath({params, path}) {
|
|
14
|
+
const match = path.match(this.regExp)
|
|
15
|
+
|
|
16
|
+
if (match) {
|
|
17
|
+
const [_beginnigSlash, _matchedName, restPath] = match
|
|
18
|
+
|
|
19
|
+
params.controller = this.name
|
|
20
|
+
|
|
21
|
+
return {restPath}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
package/src/routes/resolver.js
CHANGED
|
@@ -59,7 +59,7 @@ export default class VelociousRoutesResolver {
|
|
|
59
59
|
throw new Error(`Missing action on controller: ${controller}#${action}`)
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
await this.configuration.
|
|
62
|
+
await this.configuration.ensureConnections(async () => {
|
|
63
63
|
await controllerInstance._runBeforeCallbacks()
|
|
64
64
|
await controllerInstance[action]()
|
|
65
65
|
})
|
|
@@ -43,7 +43,7 @@ export default new Configuration({
|
|
|
43
43
|
const requireContextModels = requireContext(modelsPath, true, /^(.+)\.js$/)
|
|
44
44
|
const initializerFromRequireContext = new InitializerFromRequireContext({requireContext: requireContextModels})
|
|
45
45
|
|
|
46
|
-
await configuration.
|
|
46
|
+
await configuration.ensureConnections(async () => {
|
|
47
47
|
await initializerFromRequireContext.initialize({configuration})
|
|
48
48
|
})
|
|
49
49
|
},
|
|
@@ -45,45 +45,87 @@ export default class TestRunner {
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
isFailed() {
|
|
48
|
-
return this.
|
|
48
|
+
return this._failedTests > 0
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
|
|
52
|
-
this.
|
|
53
|
-
|
|
51
|
+
getFailedTests() {
|
|
52
|
+
return this._failedTests
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
getSuccessfulTests() {
|
|
56
|
+
return this._successfulTests
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
getTestsCount() {
|
|
60
|
+
return this._testsCount
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async prepare() {
|
|
64
|
+
this._failedTests = 0
|
|
65
|
+
this._successfulTests = 0
|
|
66
|
+
this._testsCount = 0
|
|
54
67
|
await this.importTestFiles()
|
|
55
|
-
|
|
56
|
-
|
|
68
|
+
await this.analyzeTests(tests)
|
|
69
|
+
this._onlyFocussed = this.anyTestsFocussed
|
|
70
|
+
|
|
71
|
+
const testingConfigPath = this.configuration.getTesting()
|
|
72
|
+
|
|
73
|
+
if (testingConfigPath) {
|
|
74
|
+
await import(testingConfigPath)
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async run() {
|
|
79
|
+
await this.configuration.ensureConnections(async () => {
|
|
80
|
+
await this.runTests({
|
|
81
|
+
afterEaches: [],
|
|
82
|
+
beforeEaches: [],
|
|
83
|
+
tests,
|
|
84
|
+
descriptions: [],
|
|
85
|
+
indentLevel: 0
|
|
86
|
+
})
|
|
87
|
+
})
|
|
57
88
|
}
|
|
58
89
|
|
|
59
|
-
|
|
90
|
+
analyzeTests(tests) {
|
|
91
|
+
let anyTestsFocussedFound = false
|
|
92
|
+
|
|
60
93
|
for (const testDescription in tests.tests) {
|
|
61
94
|
const testData = tests.tests[testDescription]
|
|
62
95
|
const testArgs = Object.assign({}, testData.args)
|
|
63
96
|
|
|
97
|
+
this._testsCount++
|
|
98
|
+
|
|
64
99
|
if (testArgs.focus) {
|
|
65
|
-
|
|
100
|
+
anyTestsFocussedFound = true
|
|
101
|
+
this.anyTestsFocussed = true
|
|
66
102
|
}
|
|
67
103
|
}
|
|
68
104
|
|
|
69
105
|
for (const subDescription in tests.subs) {
|
|
70
106
|
const subTest = tests.subs[subDescription]
|
|
71
|
-
const
|
|
107
|
+
const {anyTestsFocussed} = this.analyzeTests(subTest)
|
|
108
|
+
|
|
109
|
+
if (anyTestsFocussed) {
|
|
110
|
+
anyTestsFocussedFound = true
|
|
111
|
+
}
|
|
72
112
|
|
|
73
|
-
|
|
113
|
+
subTest.anyTestsFocussed = anyTestsFocussed
|
|
74
114
|
}
|
|
75
115
|
|
|
76
|
-
return
|
|
116
|
+
return {anyTestsFocussed: anyTestsFocussedFound}
|
|
77
117
|
}
|
|
78
118
|
|
|
79
|
-
async runTests(tests, descriptions, indentLevel) {
|
|
119
|
+
async runTests({afterEaches, beforeEaches, tests, descriptions, indentLevel}) {
|
|
80
120
|
const leftPadding = " ".repeat(indentLevel * 2)
|
|
121
|
+
const newAfterEaches = [...afterEaches, ...tests.afterEaches]
|
|
122
|
+
const newBeforeEaches = [...beforeEaches, ...tests.beforeEaches]
|
|
81
123
|
|
|
82
124
|
for (const testDescription in tests.tests) {
|
|
83
125
|
const testData = tests.tests[testDescription]
|
|
84
126
|
const testArgs = Object.assign({}, testData.args)
|
|
85
127
|
|
|
86
|
-
if (this.
|
|
128
|
+
if (this._onlyFocussed && !testArgs.focus) continue
|
|
87
129
|
|
|
88
130
|
if (testArgs.type == "request") {
|
|
89
131
|
testArgs.application = await this.application()
|
|
@@ -93,26 +135,44 @@ export default class TestRunner {
|
|
|
93
135
|
console.log(`${leftPadding}it ${testDescription}`)
|
|
94
136
|
|
|
95
137
|
try {
|
|
138
|
+
for (const beforeEachData of newBeforeEaches) {
|
|
139
|
+
await beforeEachData.callback({testArgs, testData})
|
|
140
|
+
}
|
|
141
|
+
|
|
96
142
|
await testData.function(testArgs)
|
|
97
|
-
this.
|
|
143
|
+
this._successfulTests++
|
|
98
144
|
} catch (error) {
|
|
99
|
-
this.
|
|
145
|
+
this._failedTests++
|
|
100
146
|
|
|
101
147
|
// console.error(`${leftPadding} Test failed: ${error.message}`)
|
|
102
|
-
|
|
103
|
-
}
|
|
104
|
-
}
|
|
148
|
+
addTrackedStackToError(error)
|
|
105
149
|
|
|
106
|
-
|
|
107
|
-
for (const subDescription in tests.subs) {
|
|
108
|
-
const subTest = tests.subs[subDescription]
|
|
109
|
-
const newDecriptions = descriptions.concat([subDescription])
|
|
150
|
+
const stackLines = error.stack.split("\n")
|
|
110
151
|
|
|
111
|
-
|
|
112
|
-
console.
|
|
113
|
-
|
|
152
|
+
for (const stackLine of stackLines) {
|
|
153
|
+
console.error(`${leftPadding} ${stackLine}`)
|
|
154
|
+
}
|
|
155
|
+
} finally {
|
|
156
|
+
for (const afterEachData of newAfterEaches) {
|
|
157
|
+
await afterEachData.callback({testArgs, testData})
|
|
114
158
|
}
|
|
115
159
|
}
|
|
116
|
-
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
for (const subDescription in tests.subs) {
|
|
163
|
+
const subTest = tests.subs[subDescription]
|
|
164
|
+
const newDecriptions = descriptions.concat([subDescription])
|
|
165
|
+
|
|
166
|
+
if (!this._onlyFocussed || subTest.anyTestsFocussed) {
|
|
167
|
+
console.log(`${leftPadding}${subDescription}`)
|
|
168
|
+
await this.runTests({
|
|
169
|
+
afterEaches: newAfterEaches,
|
|
170
|
+
beforeEaches: newBeforeEaches,
|
|
171
|
+
tests: subTest,
|
|
172
|
+
descriptions: newDecriptions,
|
|
173
|
+
indentLevel: indentLevel + 1
|
|
174
|
+
})
|
|
175
|
+
}
|
|
176
|
+
}
|
|
117
177
|
}
|
|
118
178
|
}
|
package/src/testing/test.js
CHANGED
|
@@ -1,13 +1,28 @@
|
|
|
1
|
+
import {anythingDifferent} from "set-state-compare/src/diff-utils.js"
|
|
1
2
|
import restArgsError from "../utils/rest-args-error.js"
|
|
2
3
|
|
|
3
4
|
const tests = {
|
|
5
|
+
afterEaches: [],
|
|
4
6
|
args: {},
|
|
7
|
+
beforeEaches: [],
|
|
5
8
|
subs: {},
|
|
6
9
|
tests: {}
|
|
7
10
|
}
|
|
8
11
|
|
|
9
12
|
let currentPath = [tests]
|
|
10
13
|
|
|
14
|
+
function beforeEach(callback) {
|
|
15
|
+
const currentTest = currentPath[currentPath.length - 1]
|
|
16
|
+
|
|
17
|
+
currentTest.beforeEaches.push({callback})
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function afterEach(callback) {
|
|
21
|
+
const currentTest = currentPath[currentPath.length - 1]
|
|
22
|
+
|
|
23
|
+
currentTest.afterEaches.push({callback})
|
|
24
|
+
}
|
|
25
|
+
|
|
11
26
|
class ExpectToChange {
|
|
12
27
|
constructor({changeCallback, expect, ...restArgs}) {
|
|
13
28
|
restArgsError(restArgs)
|
|
@@ -45,7 +60,61 @@ class Expect {
|
|
|
45
60
|
this.expectations = []
|
|
46
61
|
}
|
|
47
62
|
|
|
63
|
+
andChange(...args) {
|
|
64
|
+
return this.toChange(...args)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
get not() {
|
|
68
|
+
this._not = true
|
|
69
|
+
|
|
70
|
+
return this
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
toBe(result) {
|
|
74
|
+
if (this._not) {
|
|
75
|
+
if (this._object === result) {
|
|
76
|
+
throw new Error(`${this._object} was unexpected not to be ${result}`)
|
|
77
|
+
}
|
|
78
|
+
} else {
|
|
79
|
+
if (this._object !== result) {
|
|
80
|
+
throw new Error(`${this._object} wasn't expected be ${result}`)
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
toBeDefined() {
|
|
86
|
+
if (this._not) {
|
|
87
|
+
if (this._object !== undefined) {
|
|
88
|
+
throw new Error(`${this._object} wasn´t expected to be defined`)
|
|
89
|
+
}
|
|
90
|
+
} else {
|
|
91
|
+
if (this._object === undefined) {
|
|
92
|
+
throw new Error(`${this._object} wasn't expected be undefined`)
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
toBeInstanceOf(klass) {
|
|
98
|
+
if (!(this._object instanceof klass)) {
|
|
99
|
+
throw new Error(`Expected ${this._object?.constructor?.name || "null"} to be a ${klass.name} but it wasn't`)
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
toBeFalse() {
|
|
104
|
+
this.toBe(false)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
toBeUndefined() {
|
|
108
|
+
this.toBe(undefined)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
toBeTrue() {
|
|
112
|
+
this.toBe(true)
|
|
113
|
+
}
|
|
114
|
+
|
|
48
115
|
toChange(changeCallback) {
|
|
116
|
+
if (this._not) throw new Error("not stub")
|
|
117
|
+
|
|
49
118
|
const expectToChange = new ExpectToChange({changeCallback, expect: this})
|
|
50
119
|
|
|
51
120
|
this.expectations.push(expectToChange)
|
|
@@ -53,13 +122,81 @@ class Expect {
|
|
|
53
122
|
return expectToChange
|
|
54
123
|
}
|
|
55
124
|
|
|
56
|
-
|
|
57
|
-
|
|
125
|
+
toContain(valueToContain) {
|
|
126
|
+
if (this._not) throw new Error("not stub")
|
|
127
|
+
|
|
128
|
+
if (!this._object.includes(valueToContain)) {
|
|
129
|
+
throw new Error(`${this._object} doesn't contain ${valueToContain}`)
|
|
130
|
+
}
|
|
58
131
|
}
|
|
59
132
|
|
|
60
133
|
toEqual(result) {
|
|
61
|
-
if (this.
|
|
62
|
-
|
|
134
|
+
if (this._not) {
|
|
135
|
+
if (typeof this._object == "object" && typeof result == "object") {
|
|
136
|
+
if (!anythingDifferent(this._object, result)) {
|
|
137
|
+
throw new Error(`${this._object} was unexpected equal to ${result}`)
|
|
138
|
+
}
|
|
139
|
+
} else {
|
|
140
|
+
if (this._object == result) {
|
|
141
|
+
throw new Error(`${this._object} was unexpected equal to ${result}`)
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
} else {
|
|
145
|
+
if (typeof this._object == "object" && typeof result == "object") {
|
|
146
|
+
if (anythingDifferent(this._object, result)) {
|
|
147
|
+
throw new Error(`${JSON.stringify(this._object)} wasn't equal to ${JSON.stringify(result)}`)
|
|
148
|
+
}
|
|
149
|
+
} else {
|
|
150
|
+
if (this._object != result) {
|
|
151
|
+
throw new Error(`${this._object} wasn't equal to ${result}`)
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
toMatch(regex) {
|
|
158
|
+
const match = this._object.match(regex)
|
|
159
|
+
|
|
160
|
+
if (this._not) {
|
|
161
|
+
if (match) {
|
|
162
|
+
throw new Error(`${this._object} shouldn't match ${regex}`)
|
|
163
|
+
}
|
|
164
|
+
} else {
|
|
165
|
+
if (!match) {
|
|
166
|
+
throw new Error(`${this._object} didn't match ${regex}`)
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
async toThrowError(expectedError) {
|
|
172
|
+
if (this._not) throw new Error("not stub")
|
|
173
|
+
|
|
174
|
+
let failedError
|
|
175
|
+
|
|
176
|
+
try {
|
|
177
|
+
await this._object()
|
|
178
|
+
} catch (error) {
|
|
179
|
+
failedError = error
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (!failedError) throw new Error("Expected to fail but didn't")
|
|
183
|
+
|
|
184
|
+
let expectedErrorMessage, failedErrorMessage
|
|
185
|
+
|
|
186
|
+
if (typeof failedError == "string") {
|
|
187
|
+
failedErrorMessage = failedError
|
|
188
|
+
} else {
|
|
189
|
+
failedErrorMessage = failedError.message
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (typeof expectedError == "string") {
|
|
193
|
+
expectedErrorMessage = expectedError
|
|
194
|
+
} else {
|
|
195
|
+
expectedErrorMessage = expectedError.message
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (failedErrorMessage != expectedErrorMessage) {
|
|
199
|
+
throw new Error(`Expected to fail with '${expectedErrorMessage}' but failed with '${failedErrorMessage}'`)
|
|
63
200
|
}
|
|
64
201
|
}
|
|
65
202
|
|
|
@@ -82,6 +219,8 @@ class Expect {
|
|
|
82
219
|
}
|
|
83
220
|
|
|
84
221
|
toHaveAttributes(result) {
|
|
222
|
+
if (this._not) throw new Error("not stub")
|
|
223
|
+
|
|
85
224
|
const differences = {}
|
|
86
225
|
|
|
87
226
|
for (const key in result) {
|
|
@@ -93,8 +232,9 @@ class Expect {
|
|
|
93
232
|
}
|
|
94
233
|
}
|
|
95
234
|
|
|
96
|
-
if (Object.keys(differences).length > 0)
|
|
97
|
-
|
|
235
|
+
if (Object.keys(differences).length > 0) {
|
|
236
|
+
throw new Error(`Object had differet values: ${JSON.stringify(differences)}`)
|
|
237
|
+
}
|
|
98
238
|
}
|
|
99
239
|
}
|
|
100
240
|
|
|
@@ -118,7 +258,7 @@ async function describe(description, arg1, arg2) {
|
|
|
118
258
|
throw new Error(`Duplicate test description: ${description}`)
|
|
119
259
|
}
|
|
120
260
|
|
|
121
|
-
const newTestData = {args: newTestArgs, subs: {}, tests: {}}
|
|
261
|
+
const newTestData = {afterEaches: [], args: newTestArgs, beforeEaches: [], subs: {}, tests: {}}
|
|
122
262
|
|
|
123
263
|
currentTest.subs[description] = newTestData
|
|
124
264
|
currentPath.push(newTestData)
|
|
@@ -169,4 +309,12 @@ function fit(description, arg1, arg2) {
|
|
|
169
309
|
return it(description, testArgs, testFunction)
|
|
170
310
|
}
|
|
171
311
|
|
|
312
|
+
// Make the methods global so they can be used in test files
|
|
313
|
+
globalThis.afterEach = afterEach
|
|
314
|
+
globalThis.beforeEach = beforeEach
|
|
315
|
+
globalThis.describe = describe
|
|
316
|
+
globalThis.expect = expect
|
|
317
|
+
globalThis.it = it
|
|
318
|
+
globalThis.fit = fit
|
|
319
|
+
|
|
172
320
|
export {describe, expect, fit, it, tests}
|
|
@@ -38,16 +38,18 @@ async function withTrackedStack(arg1, arg2) {
|
|
|
38
38
|
if (!asyncLocalStorage) return await callback()
|
|
39
39
|
|
|
40
40
|
const parentStacks = asyncLocalStorage.getStore() || []
|
|
41
|
-
const additionalStackLines = [
|
|
41
|
+
const additionalStackLines = []
|
|
42
42
|
const currentStackLines = stack.split("\n")
|
|
43
43
|
|
|
44
|
+
currentStackLines[0] = " [WITH TRACKED STACK]"
|
|
45
|
+
|
|
44
46
|
for (let i = currentStackLines.length; i >= 0; i--) {
|
|
45
47
|
const stackLine = currentStackLines[i]
|
|
46
48
|
|
|
49
|
+
additionalStackLines.unshift(stackLine)
|
|
50
|
+
|
|
47
51
|
if (stackLine == " [WITH TRACKED STACK]") {
|
|
48
52
|
break
|
|
49
|
-
} else {
|
|
50
|
-
additionalStackLines.unshift(stackLine)
|
|
51
53
|
}
|
|
52
54
|
}
|
|
53
55
|
|