velocious 1.0.668 → 1.0.670
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 +13 -6
- package/build/background-jobs/store.js +42 -8
- package/build/src/background-jobs/store.d.ts +30 -2
- package/build/src/background-jobs/store.d.ts.map +1 -1
- package/build/src/background-jobs/store.js +45 -16
- package/build/src/testing/expect-utils.d.ts +1 -51
- package/build/src/testing/expect-utils.d.ts.map +1 -1
- package/build/src/testing/expect-utils.js +2 -205
- package/build/src/testing/expect.d.ts +1 -171
- package/build/src/testing/expect.d.ts.map +1 -1
- package/build/src/testing/expect.js +2 -667
- package/build/src/testing/test.d.ts +1 -13
- package/build/src/testing/test.d.ts.map +1 -1
- package/build/src/testing/test.js +2 -14
- package/build/testing/expect-utils.js +1 -242
- package/build/testing/expect.js +1 -760
- package/build/testing/test.js +3 -14
- package/package.json +3 -4
- package/src/background-jobs/store.js +42 -8
- package/src/testing/expect-utils.js +1 -242
- package/src/testing/expect.js +1 -760
- package/src/testing/test.js +3 -14
- package/build/src/testing/base-expect.d.ts +0 -15
- package/build/src/testing/base-expect.d.ts.map +0 -1
- package/build/src/testing/base-expect.js +0 -16
- package/build/src/testing/expect-to-change.d.ts +0 -32
- package/build/src/testing/expect-to-change.d.ts.map +0 -1
- package/build/src/testing/expect-to-change.js +0 -46
- package/build/testing/base-expect.js +0 -17
- package/build/testing/expect-to-change.js +0 -55
- package/src/testing/base-expect.js +0 -17
- package/src/testing/expect-to-change.js +0 -55
package/build/testing/test.js
CHANGED
|
@@ -3,14 +3,17 @@
|
|
|
3
3
|
import {
|
|
4
4
|
afterAll,
|
|
5
5
|
afterEach,
|
|
6
|
+
arrayContaining,
|
|
6
7
|
beforeAll,
|
|
7
8
|
beforeEach,
|
|
8
9
|
configureTests,
|
|
9
10
|
defaultTestContext,
|
|
10
11
|
describe,
|
|
12
|
+
expect,
|
|
11
13
|
fdescribe,
|
|
12
14
|
fit,
|
|
13
15
|
it,
|
|
16
|
+
objectContaining,
|
|
14
17
|
test,
|
|
15
18
|
waitForEvent,
|
|
16
19
|
xdescribe,
|
|
@@ -18,8 +21,6 @@ import {
|
|
|
18
21
|
xtest
|
|
19
22
|
} from "@velocious/testing"
|
|
20
23
|
import EventEmitter from "../utils/event-emitter.js"
|
|
21
|
-
import Expect from "./expect.js"
|
|
22
|
-
import {arrayContaining, objectContaining} from "./expect-utils.js"
|
|
23
24
|
|
|
24
25
|
/** @typedef {(typeof defaultTestContext.registry.suites)[number]} PackageSuiteDeclaration */
|
|
25
26
|
|
|
@@ -32,18 +33,6 @@ import {arrayContaining, objectContaining} from "./expect-utils.js"
|
|
|
32
33
|
* @property {number} failedConsoleOutputMaxLines - Maximum failed console lines to print inline.
|
|
33
34
|
*/
|
|
34
35
|
|
|
35
|
-
/**
|
|
36
|
-
* Runs expect.
|
|
37
|
-
* @param {ReturnType<typeof JSON.parse>} arg - Arg.
|
|
38
|
-
* @returns {Expect} - The expect.
|
|
39
|
-
*/
|
|
40
|
-
function expect(arg) {
|
|
41
|
-
return new Expect(arg)
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
expect.objectContaining = objectContaining
|
|
45
|
-
expect.arrayContaining = arrayContaining
|
|
46
|
-
|
|
47
36
|
/** Velocious-owned awaited compatibility events. */
|
|
48
37
|
const testEvents = new EventEmitter()
|
|
49
38
|
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"velocious": "build/bin/velocious.js"
|
|
4
4
|
},
|
|
5
5
|
"name": "velocious",
|
|
6
|
-
"version": "1.0.
|
|
6
|
+
"version": "1.0.670",
|
|
7
7
|
"main": "build/index.js",
|
|
8
8
|
"types": "build/index.d.ts",
|
|
9
9
|
"files": [
|
|
@@ -82,7 +82,6 @@
|
|
|
82
82
|
"proxy-addr": "^2.0.7",
|
|
83
83
|
"pure-uuid": "^2.0.0",
|
|
84
84
|
"require-context": "^1.1.0",
|
|
85
|
-
"set-state-compare": "^1.0.61",
|
|
86
85
|
"snapreq": "^0.0.15",
|
|
87
86
|
"sql-escape-string": "^1.1.0",
|
|
88
87
|
"sql.js": "^1.12.0",
|
|
@@ -91,7 +90,7 @@
|
|
|
91
90
|
"ws": "^8.21.3"
|
|
92
91
|
},
|
|
93
92
|
"peerDependencies": {
|
|
94
|
-
"@velocious/testing": "
|
|
93
|
+
"@velocious/testing": "0.0.17",
|
|
95
94
|
"react": ">=18",
|
|
96
95
|
"smtp-connection": "^4.0.2",
|
|
97
96
|
"sqlite": "^5.1.1",
|
|
@@ -121,7 +120,7 @@
|
|
|
121
120
|
"@types/react-dom": "^19.2.3",
|
|
122
121
|
"@types/strftime": "^0.9.8",
|
|
123
122
|
"@types/ws": "^8.18.1",
|
|
124
|
-
"@velocious/testing": "0.0.
|
|
123
|
+
"@velocious/testing": "0.0.17",
|
|
125
124
|
"chokidar-cli": "^3.0.0",
|
|
126
125
|
"cpy-cli": "^7.0.0",
|
|
127
126
|
"esbuild": "^0.28.0",
|
|
@@ -59,6 +59,16 @@ import {
|
|
|
59
59
|
* @property {{failureMessage: string, name: string}} [advisoryLock] - Session lock held around the transaction.
|
|
60
60
|
*/
|
|
61
61
|
|
|
62
|
+
/**
|
|
63
|
+
* BackgroundJobPruneCandidateMetadata type. Immutable metadata for one selected
|
|
64
|
+
* retention candidate batch, exposed to the optional prune barrier hook.
|
|
65
|
+
* @typedef {object} BackgroundJobPruneCandidateMetadata
|
|
66
|
+
* @property {Readonly<Array<string>>} candidates - Job ids selected for this batch.
|
|
67
|
+
* @property {string} status - Terminal status the candidates were selected by.
|
|
68
|
+
* @property {string} column - Terminal timestamp column compared against the cutoff.
|
|
69
|
+
* @property {number} cutoff - Cutoff timestamp the candidates were selected against.
|
|
70
|
+
*/
|
|
71
|
+
|
|
62
72
|
/**
|
|
63
73
|
* BackgroundJobConcurrencyCountRow type.
|
|
64
74
|
* @typedef {object} BackgroundJobConcurrencyCountRow
|
|
@@ -158,13 +168,15 @@ export default class BackgroundJobsStore extends BackgroundJobsAdapter {
|
|
|
158
168
|
* @param {string} [args.databaseIdentifier] - Database identifier.
|
|
159
169
|
* @param {{now: () => number}} [args.clock] - Injectable persistence clock.
|
|
160
170
|
* @param {(producerProof: import("./types.js").BackgroundJobProducerProof) => void | Promise<void>} [args.afterOwnedProducerValidation] - Exact owned-enqueue validation hook.
|
|
171
|
+
* @param {(metadata: BackgroundJobPruneCandidateMetadata) => void | Promise<void>} [args.afterPruneCandidatesSelected] - Optional barrier invoked after one retention batch's candidate discovery and before its serialized delete transaction.
|
|
161
172
|
*/
|
|
162
|
-
constructor({configuration, databaseIdentifier, clock, afterOwnedProducerValidation}) {
|
|
173
|
+
constructor({configuration, databaseIdentifier, clock, afterOwnedProducerValidation, afterPruneCandidatesSelected}) {
|
|
163
174
|
super()
|
|
164
175
|
this.configuration = configuration
|
|
165
176
|
this.databaseIdentifier = databaseIdentifier
|
|
166
177
|
this.clock = clock || {now: () => Date.now()}
|
|
167
178
|
this.afterOwnedProducerValidation = afterOwnedProducerValidation
|
|
179
|
+
this.afterPruneCandidatesSelected = afterPruneCandidatesSelected
|
|
168
180
|
this.logger = new Logger(this)
|
|
169
181
|
this._readyPromise = null
|
|
170
182
|
this._queueConcurrencyReconciled = false
|
|
@@ -1790,7 +1802,14 @@ export default class BackgroundJobsStore extends BackgroundJobsAdapter {
|
|
|
1790
1802
|
|
|
1791
1803
|
/**
|
|
1792
1804
|
* Deletes rows of one terminal status older than a cutoff, batch by batch,
|
|
1793
|
-
* until a page returns fewer than `batchSize` rows.
|
|
1805
|
+
* until a candidate page returns fewer than `batchSize` rows. Candidate
|
|
1806
|
+
* discovery runs on a plain connection — never inside the serialized count
|
|
1807
|
+
* mutation — so a long scan cannot hold the count-revision lock and starve
|
|
1808
|
+
* enqueue acknowledgements; only the short delete transaction is serialized.
|
|
1809
|
+
* The delete revalidates status and cutoff for the selected ids, publishes
|
|
1810
|
+
* the delta from the actual affected-row count, and a page whose candidates
|
|
1811
|
+
* were already removed by a concurrent pruner still ends the pass only when
|
|
1812
|
+
* the page itself is short.
|
|
1794
1813
|
* @param {object} args - Options.
|
|
1795
1814
|
* @param {string} args.status - Terminal status to prune.
|
|
1796
1815
|
* @param {string} args.column - Timestamp column compared against the cutoff.
|
|
@@ -1802,22 +1821,37 @@ export default class BackgroundJobsStore extends BackgroundJobsAdapter {
|
|
|
1802
1821
|
let deleted = 0
|
|
1803
1822
|
|
|
1804
1823
|
for (;;) {
|
|
1805
|
-
const
|
|
1806
|
-
|
|
1824
|
+
const candidates = await this._withDb(async (db) =>
|
|
1825
|
+
await db
|
|
1807
1826
|
.newQuery()
|
|
1808
1827
|
.from(JOBS_TABLE)
|
|
1809
1828
|
.select("id")
|
|
1810
1829
|
.where({status})
|
|
1811
1830
|
.where(`${db.quoteColumn(column)} <= ${db.quote(cutoff)}`)
|
|
1831
|
+
.order({column, direction: "ASC"})
|
|
1832
|
+
.order({column: "id", direction: "ASC"})
|
|
1812
1833
|
.limit(batchSize)
|
|
1813
1834
|
.results()
|
|
1835
|
+
)
|
|
1814
1836
|
|
|
1815
|
-
|
|
1837
|
+
if (candidates.length === 0) break
|
|
1816
1838
|
|
|
1817
|
-
|
|
1839
|
+
const candidateIds = Object.freeze(candidates.map((/** @type {Record<string, ReturnType<typeof JSON.parse>>} */ row) => String(row.id)))
|
|
1840
|
+
|
|
1841
|
+
if (this.afterPruneCandidatesSelected) {
|
|
1842
|
+
await this.afterPruneCandidatesSelected(Object.freeze({
|
|
1843
|
+
candidates: candidateIds,
|
|
1844
|
+
column,
|
|
1845
|
+
cutoff,
|
|
1846
|
+
status
|
|
1847
|
+
}))
|
|
1848
|
+
}
|
|
1849
|
+
|
|
1850
|
+
const removed = await this._serializedCountMutation(async (db) => {
|
|
1851
|
+
const ids = candidateIds.map((id) => db.quote(id)).join(", ")
|
|
1818
1852
|
|
|
1819
1853
|
const removed = await db.affectedRows(
|
|
1820
|
-
`DELETE FROM ${db.quoteTable(JOBS_TABLE)} WHERE ${db.quoteColumn("id")} IN (${ids})`
|
|
1854
|
+
`DELETE FROM ${db.quoteTable(JOBS_TABLE)} WHERE ${db.quoteColumn("id")} IN (${ids}) AND ${db.quoteColumn("status")} = ${db.quote(status)} AND ${db.quoteColumn(column)} <= ${db.quote(cutoff)}`
|
|
1821
1855
|
)
|
|
1822
1856
|
|
|
1823
1857
|
await this._recordCountDelta(db, {all: -removed, [status]: -removed})
|
|
@@ -1826,7 +1860,7 @@ export default class BackgroundJobsStore extends BackgroundJobsAdapter {
|
|
|
1826
1860
|
})
|
|
1827
1861
|
|
|
1828
1862
|
deleted += removed
|
|
1829
|
-
if (
|
|
1863
|
+
if (candidates.length < batchSize) break
|
|
1830
1864
|
}
|
|
1831
1865
|
|
|
1832
1866
|
return deleted
|
|
@@ -1,246 +1,5 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
|
|
3
|
-
import {anythingDifferent} from "set-state-compare/build/diff-utils.js"
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Runs object containing.
|
|
7
|
-
* @param {ReturnType<typeof JSON.parse>} value - Value.
|
|
8
|
-
* @returns {{__velociousMatcher: string, value: ReturnType<typeof JSON.parse>}} - Matcher wrapper.
|
|
9
|
-
*/
|
|
10
|
-
function objectContaining(value) {
|
|
11
|
-
if (value === null || typeof value !== "object") {
|
|
12
|
-
throw new Error(`Expected object but got ${typeof value}`)
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
return {
|
|
16
|
-
__velociousMatcher: "objectContaining",
|
|
17
|
-
value
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Runs array containing.
|
|
23
|
-
* @param {ReturnType<typeof JSON.parse>} value - Value.
|
|
24
|
-
* @returns {{__velociousMatcher: string, value: ReturnType<typeof JSON.parse>}} - Matcher wrapper.
|
|
25
|
-
*/
|
|
26
|
-
function arrayContaining(value) {
|
|
27
|
-
if (!Array.isArray(value)) {
|
|
28
|
-
throw new Error(`Expected array but got ${typeof value}`)
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
return {
|
|
32
|
-
__velociousMatcher: "arrayContaining",
|
|
33
|
-
value
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Runs is object like.
|
|
39
|
-
* @param {ReturnType<typeof JSON.parse>} value - Value.
|
|
40
|
-
* @returns {boolean} - Whether object-like.
|
|
41
|
-
*/
|
|
42
|
-
function isObjectLike(value) {
|
|
43
|
-
return value !== null && typeof value === "object"
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Runs is array containing.
|
|
48
|
-
* @param {ReturnType<typeof JSON.parse>} value - Value.
|
|
49
|
-
* @returns {boolean} - Whether arrayContaining matcher.
|
|
50
|
-
*/
|
|
51
|
-
function isArrayContaining(value) {
|
|
52
|
-
return !!value && typeof value === "object" && (/** @type {ReturnType<typeof JSON.parse>} */ (value)).__velociousMatcher === "arrayContaining"
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Runs is object containing.
|
|
57
|
-
* @param {ReturnType<typeof JSON.parse>} value - Value.
|
|
58
|
-
* @returns {boolean} - Whether objectContaining matcher.
|
|
59
|
-
*/
|
|
60
|
-
function isObjectContaining(value) {
|
|
61
|
-
return !!value && typeof value === "object" && (/** @type {ReturnType<typeof JSON.parse>} */ (value)).__velociousMatcher === "objectContaining"
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Runs is plain object.
|
|
66
|
-
* @param {ReturnType<typeof JSON.parse>} value - Value.
|
|
67
|
-
* @returns {boolean} - Whether plain object.
|
|
68
|
-
*/
|
|
69
|
-
function isPlainObject(value) {
|
|
70
|
-
if (!value || typeof value !== "object") return false
|
|
71
|
-
|
|
72
|
-
const prototype = Object.getPrototypeOf(value)
|
|
73
|
-
|
|
74
|
-
return prototype === Object.prototype || prototype === null
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Runs values equal.
|
|
79
|
-
* @param {ReturnType<typeof JSON.parse>} actual - Actual value.
|
|
80
|
-
* @param {ReturnType<typeof JSON.parse>} expected - Expected value.
|
|
81
|
-
* @returns {boolean} - Whether values are equal.
|
|
82
|
-
*/
|
|
83
|
-
function valuesEqual(actual, expected) {
|
|
84
|
-
if (actual instanceof Date && expected instanceof Date) {
|
|
85
|
-
return actual.getTime() === expected.getTime()
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
if (actual instanceof RegExp && expected instanceof RegExp) {
|
|
89
|
-
return actual.source === expected.source && actual.flags === expected.flags
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
return Object.is(actual, expected)
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Runs collect match differences.
|
|
97
|
-
* @param {ReturnType<typeof JSON.parse>} actual - Actual value.
|
|
98
|
-
* @param {ReturnType<typeof JSON.parse>} expected - Expected value.
|
|
99
|
-
* @param {string} path - Path.
|
|
100
|
-
* @param {Record<string, Array<ReturnType<typeof JSON.parse>>>} differences - Differences.
|
|
101
|
-
* @returns {void} - No return value.
|
|
102
|
-
*/
|
|
103
|
-
function collectMatchDifferences(actual, expected, path, differences) {
|
|
104
|
-
if (isObjectContaining(expected)) {
|
|
105
|
-
collectMatchDifferences(actual, /** @type {ReturnType<typeof JSON.parse>} */ (expected).value, path, differences)
|
|
106
|
-
return
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
if (isArrayContaining(expected)) {
|
|
110
|
-
const {matches} = matchArrayContaining(actual, /** @type {Array<ReturnType<typeof JSON.parse>>} */ (/** @type {ReturnType<typeof JSON.parse>} */ (expected).value))
|
|
111
|
-
|
|
112
|
-
if (!matches) {
|
|
113
|
-
differences[path || "$"] = [expected, actual]
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
return
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
if (Array.isArray(expected)) {
|
|
120
|
-
if (!Array.isArray(actual)) {
|
|
121
|
-
differences[path || "$"] = [expected, actual]
|
|
122
|
-
return
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
for (let i = 0; i < expected.length; i++) {
|
|
126
|
-
const nextPath = `${path}[${i}]`
|
|
127
|
-
collectMatchDifferences(actual[i], expected[i], nextPath, differences)
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
return
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
if (isPlainObject(expected)) {
|
|
134
|
-
if (!isObjectLike(actual)) {
|
|
135
|
-
differences[path || "$"] = [expected, actual]
|
|
136
|
-
return
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
const expectedObject = /** @type {Record<string, ReturnType<typeof JSON.parse>>} */ (expected)
|
|
140
|
-
const actualObject = /** @type {Record<string, ReturnType<typeof JSON.parse>>} */ (actual)
|
|
141
|
-
|
|
142
|
-
for (const key of Object.keys(expectedObject)) {
|
|
143
|
-
const nextPath = path ? `${path}.${key}` : key
|
|
144
|
-
|
|
145
|
-
if (!Object.prototype.hasOwnProperty.call(actualObject, key)) {
|
|
146
|
-
differences[nextPath] = [expectedObject[key], undefined]
|
|
147
|
-
continue
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
collectMatchDifferences(actualObject[key], expectedObject[key], nextPath, differences)
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
return
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
if (!valuesEqual(actual, expected)) {
|
|
157
|
-
differences[path || "$"] = [expected, actual]
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/**
|
|
162
|
-
* Runs match object.
|
|
163
|
-
* @param {ReturnType<typeof JSON.parse>} actual - Actual value.
|
|
164
|
-
* @param {Record<string, ReturnType<typeof JSON.parse>> | Array<ReturnType<typeof JSON.parse>>} expected - Expected value.
|
|
165
|
-
* @returns {{matches: boolean, differences: Record<string, Array<ReturnType<typeof JSON.parse>>>}} - Match result.
|
|
166
|
-
*/
|
|
167
|
-
function matchObject(actual, expected) {
|
|
168
|
-
/**
|
|
169
|
-
* Differences.
|
|
170
|
-
* @type {Record<string, Array<ReturnType<typeof JSON.parse>>>} */
|
|
171
|
-
const differences = {}
|
|
172
|
-
|
|
173
|
-
collectMatchDifferences(actual, expected, "", differences)
|
|
174
|
-
|
|
175
|
-
return {
|
|
176
|
-
matches: Object.keys(differences).length === 0,
|
|
177
|
-
differences
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
/**
|
|
182
|
-
* Runs match array containing.
|
|
183
|
-
* @param {ReturnType<typeof JSON.parse>} actual - Actual value.
|
|
184
|
-
* @param {Array<ReturnType<typeof JSON.parse>>} expected - Expected values.
|
|
185
|
-
* @returns {{matches: boolean, differences: Record<string, Array<ReturnType<typeof JSON.parse>>>}} - Match result.
|
|
186
|
-
*/
|
|
187
|
-
function matchArrayContaining(actual, expected) {
|
|
188
|
-
/**
|
|
189
|
-
* Differences.
|
|
190
|
-
* @type {Record<string, Array<ReturnType<typeof JSON.parse>>>} */
|
|
191
|
-
const differences = {}
|
|
192
|
-
|
|
193
|
-
if (!Array.isArray(actual)) {
|
|
194
|
-
differences["$"] = [expected, actual]
|
|
195
|
-
return {matches: false, differences}
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
const usedIndexes = new Set()
|
|
199
|
-
|
|
200
|
-
for (const expectedItem of expected) {
|
|
201
|
-
let matchedIndex = -1
|
|
202
|
-
|
|
203
|
-
for (let i = 0; i < actual.length; i++) {
|
|
204
|
-
if (usedIndexes.has(i)) continue
|
|
205
|
-
|
|
206
|
-
if (isObjectContaining(expectedItem)) {
|
|
207
|
-
const {matches} = matchObject(actual[i], /** @type {ReturnType<typeof JSON.parse>} */ (expectedItem).value)
|
|
208
|
-
if (matches) {
|
|
209
|
-
matchedIndex = i
|
|
210
|
-
break
|
|
211
|
-
}
|
|
212
|
-
continue
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
if (isArrayContaining(expectedItem)) {
|
|
216
|
-
const {matches} = matchArrayContaining(actual[i], /** @type {ReturnType<typeof JSON.parse>} */ (expectedItem).value)
|
|
217
|
-
if (matches) {
|
|
218
|
-
matchedIndex = i
|
|
219
|
-
break
|
|
220
|
-
}
|
|
221
|
-
continue
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
if (!anythingDifferent(actual[i], expectedItem)) {
|
|
225
|
-
matchedIndex = i
|
|
226
|
-
break
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
if (matchedIndex >= 0) {
|
|
231
|
-
usedIndexes.add(matchedIndex)
|
|
232
|
-
} else {
|
|
233
|
-
differences["$"] = [expected, actual]
|
|
234
|
-
break
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
return {
|
|
239
|
-
matches: Object.keys(differences).length === 0,
|
|
240
|
-
differences
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
|
|
244
3
|
export {
|
|
245
4
|
arrayContaining,
|
|
246
5
|
isArrayContaining,
|
|
@@ -248,4 +7,4 @@ export {
|
|
|
248
7
|
matchArrayContaining,
|
|
249
8
|
matchObject,
|
|
250
9
|
objectContaining
|
|
251
|
-
}
|
|
10
|
+
} from "@velocious/testing"
|