Reputation: 23
I'm trying to mock knex for testing. Everything seems to work but attaching a proxy to an array as prototype seems to remove the iteratability of arrays.
Here is the mock function. Following works fine with objects.
const mock = (data) => {
const orgData = structuredClone(data)
Object.setPrototypeOf(
data,
new Proxy(
{},
{
get(_, prop) {
if (prop === 'then') {
return orgData
}
if (typeof prop === 'symbol') {
return orgData
}
return () => mock(orgData)
},
}
)
)
return data
}
const mm = mock([
{
name: 'user',
},
])
const run = async () => {
const res = await mm.select('*').where('name', 'user')
const [first] = res
// ^^^^^
// TypeError: res is not iterable
// at run (.../test.js:35:18)
}
run()
When there is await
present before the chain, proxy get
is called with then
as the value of prop
. And that's the last call. That returns [ { name: 'user' } ]
correctly. However the value that's assigned to the res
variable is looks like this { '0': { name: 'user' } }
. I'm wondering if it's possible to assign a prototype
to an array without making it an object.
Upvotes: 0
Views: 63
Reputation: 3069
I'm the same guy who posted this question. The resolution was not to set the prototype at all and instead, directly proxy the promise itself.
const mock = (data) => {
return new Proxy(Promise.resolve(data), {
get(target, prop) {
if (prop in target) {
const promise = Promise.resolve(target)
// NOTE: when `then` is called independently, this will be global so it
// wouldn't be resolved correctly. So here binding the context to the
// function to make it work
return promise.then.bind(promise)
}
return () => mock(target)
},
})
}
const mm = mock([
{
name: 'user',
},
])
const run = async () => {
const res = await await mm.select('*').where('name', 'user')
console.log(res)
const [first] = res
console.log(first)
}
run()
Output:
:!node test.js
[ { name: 'user' } ]
{ name: 'user' }
Upvotes: 0