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.
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' }