velocious 1.0.37 → 1.0.39

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/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "velocious": "bin/velocious.js"
4
4
  },
5
5
  "name": "velocious",
6
- "version": "1.0.37",
6
+ "version": "1.0.39",
7
7
  "main": "index.js",
8
8
  "scripts": {
9
9
  "test": "jasmine",
@@ -1,6 +1,7 @@
1
1
  import Dummy from "../../dummy/index.js"
2
2
  import Project from "../../dummy/src/models/project.js"
3
3
  import Task from "../../dummy/src/models/task.js"
4
+ import {ValidationError} from "../../../src/database/record/index.js"
4
5
 
5
6
  describe("Record - create", () => {
6
7
  it("creates a new simple record with relationships and translations", async () => {
@@ -49,7 +50,8 @@ describe("Record - create", () => {
49
50
 
50
51
  throw new Error("Didnt expect to succeed")
51
52
  } catch (error) {
52
- expect(error.message).toEqual("Validation failed: Name can't be blank")
53
+ expect(error).toBeInstanceOf(ValidationError)
54
+ expect(error.message).toEqual("Name can't be blank")
53
55
  }
54
56
 
55
57
  const projectsCount = await Project.count()
@@ -1,12 +1,13 @@
1
1
  import Dummy from "../../dummy/index.js"
2
2
  import Task from "../../dummy/src/models/task.js"
3
+ import {ValidationError} from "../../../src/database/record/index.js"
3
4
 
4
5
  describe("Record - validations", () => {
5
6
  it("raises validations if trying to create an invalid record because of a presence validation", async () => {
6
7
  await Dummy.run(async () => {
7
8
  const task = new Task({name: " "})
8
9
 
9
- await expectAsync(task.save()).toBeRejectedWith(new Error("Validation failed: Name can't be blank"))
10
+ await expectAsync(task.save()).toBeRejectedWith(new ValidationError("Name can't be blank"))
10
11
  })
11
12
  })
12
13
 
@@ -21,7 +22,8 @@ describe("Record - validations", () => {
21
22
 
22
23
  throw new Error("Task 2 save didn't fail")
23
24
  } catch (error) {
24
- expect(error.message).toEqual("Validation failed: Name has already been taken")
25
+ expect(error).toBeInstanceOf(ValidationError)
26
+ expect(error.message).toEqual("Name has already been taken")
25
27
  }
26
28
  })
27
29
  })
@@ -1,6 +1,7 @@
1
1
  import Dummy from "../dummy/index.js"
2
2
  import dummyConfiguration from "../dummy/src/config/configuration.js"
3
3
  import Task from "../dummy/src/models/task.js"
4
+ import {ValidationError} from "../../src/database/record/index.js"
4
5
 
5
6
  describe("database - transactions", () => {
6
7
  it("supports transactions and savepoints", async () => {
@@ -22,7 +23,8 @@ describe("database - transactions", () => {
22
23
  throw new Error("Didn't expect to succeed")
23
24
  })
24
25
  } catch (error) {
25
- expect(error.message).toEqual("Validation failed: Name can't be blank")
26
+ expect(error).toBeInstanceOf(ValidationError)
27
+ expect(error.message).toEqual("Name can't be blank")
26
28
  }
27
29
  })
28
30
  })
@@ -23,7 +23,7 @@ describe("HttpServer", () => {
23
23
 
24
24
  expect(response.status).toEqual(404)
25
25
  expect(response.statusText).toEqual("Not Found")
26
- expect(text).toEqual("Not found!\n")
26
+ expect(text).toEqual("Path not found: /tasks/doesnt-exist\n")
27
27
  }
28
28
  })
29
29
  })
@@ -23,7 +23,7 @@ describe("HttpServer", () => {
23
23
 
24
24
  expect(response.status).toEqual(404)
25
25
  expect(response.statusText).toEqual("Not Found")
26
- expect(text).toEqual("Not found!\n")
26
+ expect(text).toEqual("Path not found: /doesnt-exist\n")
27
27
  }
28
28
  })
29
29
  })
@@ -12,10 +12,10 @@ export default class VelociousCliCommandsInit extends BaseCommand {
12
12
 
13
13
  if (testRunner.isFailed()) {
14
14
  console.error(`Test run failed with ${testRunner.failedTests} failed tests and ${testRunner.successfulTests} successfull`)
15
- process.exit(-1)
15
+ process.exit(1)
16
16
  } else {
17
17
  console.log(`Test run succeeded with ${testRunner.successfulTests} successful tests`)
18
- process.exit(1)
18
+ process.exit(0)
19
19
  }
20
20
  }
21
21
  }
@@ -1,5 +1,6 @@
1
1
  import {digg} from "diggerize"
2
2
  import restArgsError from "./utils/rest-args-error.js"
3
+ import {withTrackedStack} from "./utils/with-tracked-stack.js"
3
4
 
4
5
  export default class VelociousConfiguration {
5
6
  static current(throwError = true) {
@@ -153,8 +154,11 @@ export default class VelociousConfiguration {
153
154
 
154
155
  async withConnections(callback) {
155
156
  const dbs = {}
157
+ const stack = Error().stack
156
158
  const actualCallback = async () => {
157
- return await callback(dbs)
159
+ await withTrackedStack(stack, async () => {
160
+ return await callback(dbs)
161
+ })
158
162
  }
159
163
 
160
164
  let runRequest = actualCallback
package/src/controller.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import {digs} from "diggerize"
2
2
  import ejs from "ejs"
3
+ import {incorporate} from "incorporator"
3
4
  import * as inflection from "inflection"
4
5
  import {Logger} from "./logger.js"
5
6
  import restArgsError from "./utils/rest-args-error.js"
@@ -31,6 +32,18 @@ export default class VelociousController {
31
32
  this._viewPath = viewPath
32
33
  }
33
34
 
35
+ getAction() {
36
+ return this._action
37
+ }
38
+
39
+ getParams() {
40
+ return this._params
41
+ }
42
+
43
+ getRequest() {
44
+ return this._request
45
+ }
46
+
34
47
  async _runBeforeCallbacks() {
35
48
  await this.logger.debug("_runBeforeCallbacks")
36
49
 
@@ -88,8 +101,9 @@ export default class VelociousController {
88
101
  return new Promise((resolve, reject) => {
89
102
  const viewPath = `${this._viewPath}/${inflection.dasherize(inflection.underscore(this._action))}.ejs`
90
103
  const {viewParams} = digs(this, "viewParams")
104
+ const actualViewParams = incorporate({controller: this}, viewParams)
91
105
 
92
- ejs.renderFile(viewPath, viewParams, {}, (err, str) => {
106
+ ejs.renderFile(viewPath, actualViewParams, {}, (err, str) => {
93
107
  if (err) {
94
108
  reject(err)
95
109
  } else {
@@ -10,6 +10,24 @@ import Query from "../query/index.js"
10
10
  import ValidatorsPresence from "./validators/presence.js"
11
11
  import ValidatorsUniqueness from "./validators/uniqueness.js"
12
12
 
13
+ class ValidationError extends Error {
14
+ getModel() {
15
+ return this._model
16
+ }
17
+
18
+ setModel(model) {
19
+ this._model = model
20
+ }
21
+
22
+ getValidationErrors() {
23
+ return this._validationErrors
24
+ }
25
+
26
+ setValidationErrors(validationErrors) {
27
+ this._validationErrors = validationErrors
28
+ }
29
+ }
30
+
13
31
  class VelociousDatabaseRecord {
14
32
  static validatorTypes() {
15
33
  if (!this._validatorTypes) this._validatorTypes = {}
@@ -883,7 +901,12 @@ class VelociousDatabaseRecord {
883
901
  }
884
902
 
885
903
  if (Object.keys(this._validationErrors).length > 0) {
886
- throw new Error(`Validation failed: ${this.fullErrorMessages().join(". ")}`)
904
+ const validationError = new ValidationError(this.fullErrorMessages().join(". "))
905
+
906
+ validationError.setValidationErrors(this._validationErrors)
907
+ validationError.setModel(this)
908
+
909
+ throw validationError
887
910
  }
888
911
  }
889
912
 
@@ -913,4 +936,5 @@ class VelociousDatabaseRecord {
913
936
  VelociousDatabaseRecord.registerValidatorType("presence", ValidatorsPresence)
914
937
  VelociousDatabaseRecord.registerValidatorType("uniqueness", ValidatorsUniqueness)
915
938
 
939
+ export {ValidationError}
916
940
  export default VelociousDatabaseRecord
@@ -1,6 +1,6 @@
1
1
  import {digg} from "diggerize"
2
2
  import {EventEmitter} from "events"
3
- import Incorporator from "incorporator"
3
+ import {incorporate} from "incorporator"
4
4
  import ParamsToObject from "./params-to-object.js"
5
5
  import RequestBuffer from "./request-buffer/index.js"
6
6
 
@@ -33,9 +33,8 @@ export default class VelociousHttpServerClientRequestParser {
33
33
 
34
34
  const paramsToObject = new ParamsToObject(unorderedParams)
35
35
  const newParams = paramsToObject.toObject()
36
- const incorporator = new Incorporator({objects: [this.params, newParams]})
37
36
 
38
- incorporator.merge()
37
+ incorporate(this.params, newParams)
39
38
  }
40
39
 
41
40
  feed = (data) => this.requestBuffer.feed(data)
@@ -82,9 +81,7 @@ export default class VelociousHttpServerClientRequestParser {
82
81
  getProtocol = () => this._getHostMatch()?.protocol
83
82
 
84
83
  requestDone = () => {
85
- const incorporator = new Incorporator({objects: [this.params, this.requestBuffer.params]})
86
-
87
- incorporator.merge()
84
+ incorporate(this.params, this.requestBuffer.params)
88
85
 
89
86
  this.state = "done"
90
87
  this.events.emit("done")
@@ -1 +1 @@
1
- Not found!
1
+ Path not found: <%= controller.getRequest().path() %>
@@ -1,3 +1,4 @@
1
+ import {addTrackedStackToError} from "../utils/with-tracked-stack.js"
1
2
  import Application from "../../src/application.js"
2
3
  import RequestClient from "./request-client.js"
3
4
  import {tests} from "./test.js"
@@ -39,7 +40,7 @@ export default class TestRunner {
39
40
 
40
41
  async importTestFiles() {
41
42
  for (const testFile of this.testFiles) {
42
- const importTestFile = await import(testFile)
43
+ await import(testFile)
43
44
  }
44
45
  }
45
46
 
@@ -50,18 +51,39 @@ export default class TestRunner {
50
51
  async run() {
51
52
  this.failedTests = 0
52
53
  this.successfulTests = 0
53
-
54
54
  await this.importTestFiles()
55
+ this.onlyFocussed = this.areAnyTestsFocussed(tests)
55
56
  await this.runTests(tests, [], 0)
56
57
  }
57
58
 
59
+ areAnyTestsFocussed(tests) {
60
+ for (const testDescription in tests.tests) {
61
+ const testData = tests.tests[testDescription]
62
+ const testArgs = Object.assign({}, testData.args)
63
+
64
+ if (testArgs.focus) {
65
+ return true
66
+ }
67
+ }
68
+
69
+ for (const subDescription in tests.subs) {
70
+ const subTest = tests.subs[subDescription]
71
+ const result = this.areAnyTestsFocussed(subTest)
72
+
73
+ if (result) return true
74
+ }
75
+
76
+ return false
77
+ }
78
+
58
79
  async runTests(tests, descriptions, indentLevel) {
59
80
  const leftPadding = " ".repeat(indentLevel * 2)
60
81
 
61
82
  for (const testDescription in tests.tests) {
62
83
  const testData = tests.tests[testDescription]
63
84
  const testArgs = Object.assign({}, testData.args)
64
- const testName = descriptions.concat([`it ${testDescription}`]).join(" - ")
85
+
86
+ if (this.onlyFocussed && !testArgs.focus) continue
65
87
 
66
88
  if (testArgs.type == "request") {
67
89
  testArgs.application = await this.application()
@@ -77,7 +99,7 @@ export default class TestRunner {
77
99
  this.failedTests++
78
100
 
79
101
  // console.error(`${leftPadding} Test failed: ${error.message}`)
80
- console.error(error.stack)
102
+ console.error(addTrackedStackToError(error))
81
103
  }
82
104
  }
83
105
 
@@ -86,9 +108,10 @@ export default class TestRunner {
86
108
  const subTest = tests.subs[subDescription]
87
109
  const newDecriptions = descriptions.concat([subDescription])
88
110
 
89
- console.log(`${leftPadding}${subDescription}`)
90
-
91
- await this.runTests(subTest, newDecriptions, indentLevel + 1)
111
+ if (!this.onlyFocussed || this.areAnyTestsFocussed(subTest)) {
112
+ console.log(`${leftPadding}${subDescription}`)
113
+ await this.runTests(subTest, newDecriptions, indentLevel + 1)
114
+ }
92
115
  }
93
116
  })
94
117
  }
@@ -153,4 +153,20 @@ function it(description, arg1, arg2) {
153
153
  currentTest.tests[description] = {args: newTestArgs, function: testFunction}
154
154
  }
155
155
 
156
- export {describe, expect, it, tests}
156
+ function fit(description, arg1, arg2) {
157
+ let testArgs, testFunction
158
+
159
+ if (typeof arg1 == "function") {
160
+ testFunction = arg1
161
+ testArgs = {focus: true}
162
+ } else if (typeof arg2 == "function") {
163
+ testFunction = arg2
164
+ testArgs = Object.assign({focus: true}, arg1)
165
+ } else {
166
+ throw new Error(`Invalid arguments for it: ${description}, ${arg1}`)
167
+ }
168
+
169
+ return it(description, testArgs, testFunction)
170
+ }
171
+
172
+ export {describe, expect, fit, it, tests}
@@ -0,0 +1,61 @@
1
+ import {AsyncLocalStorage} from "async_hooks"
2
+
3
+ let asyncLocalStorage
4
+
5
+ if (AsyncLocalStorage) {
6
+ asyncLocalStorage = new AsyncLocalStorage()
7
+ }
8
+
9
+ function addTrackedStackToError(error) {
10
+ // Not supported
11
+ if (!asyncLocalStorage) return
12
+
13
+ const parentStacks = asyncLocalStorage.getStore() || []
14
+ const additionalStackLines = []
15
+
16
+ for (const parentStack of parentStacks) {
17
+ for (const parentStackLine of parentStack) {
18
+ additionalStackLines.push(parentStackLine)
19
+ }
20
+ }
21
+
22
+ // Replace the error message on the first line with this string
23
+ error.stack += "\n" + additionalStackLines.join("\n")
24
+ }
25
+
26
+ async function withTrackedStack(arg1, arg2) {
27
+ let callback, stack
28
+
29
+ if (arg2) {
30
+ callback = arg2
31
+ stack = arg1
32
+ } else {
33
+ callback = arg1
34
+ stack = Error().stack
35
+ }
36
+
37
+ // Not supported
38
+ if (!asyncLocalStorage) return await callback()
39
+
40
+ const parentStacks = asyncLocalStorage.getStore() || []
41
+ const additionalStackLines = [" [WITH TRACKED STACK]"]
42
+ const currentStackLines = stack.split("\n")
43
+
44
+ for (let i = currentStackLines.length; i >= 0; i--) {
45
+ const stackLine = currentStackLines[i]
46
+
47
+ if (stackLine == " [WITH TRACKED STACK]") {
48
+ break
49
+ } else {
50
+ additionalStackLines.unshift(stackLine)
51
+ }
52
+ }
53
+
54
+ const newStacks = [additionalStackLines, ...parentStacks]
55
+
56
+ await asyncLocalStorage.run(newStacks, async () => {
57
+ return await callback()
58
+ })
59
+ }
60
+
61
+ export {addTrackedStackToError, withTrackedStack}