mirror of
https://github.com/oven-sh/bun
synced 2026-02-02 15:08:46 +00:00
fix(test): use putDirectMayBeIndex in spyOn for indexed property keys (#25020)
## Summary - Fix `spyOn` crash when using indexed property keys (e.g., `spyOn(arr, 0)`) ## Test plan - [x] Added tests for `spyOn` with numeric indexed properties - [x] Added tests for `spyOn` with string indexed properties (e.g., `"0"`) - [x] All existing `spyOn` tests pass - [x] Full `mock-fn.test.js` test suite passes Fixes ENG-21973 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Bot <claude-bot@bun.sh> Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -380,6 +380,9 @@ public:
|
||||
if (auto* moduleNamespaceObject = tryJSDynamicCast<JSModuleNamespaceObject*>(target)) {
|
||||
moduleNamespaceObject->overrideExportValue(moduleNamespaceObject->globalObject(), this->spyIdentifier, implValue);
|
||||
}
|
||||
} else if (auto index = parseIndex(this->spyIdentifier)) {
|
||||
// Use putDirectIndex for numeric property keys (e.g., spyOn(arr, 0))
|
||||
target->putDirectIndex(globalObject(), *index, implValue, this->spyAttributes, PutDirectIndexLikePutDirect);
|
||||
} else {
|
||||
target->putDirect(this->vm(), this->spyIdentifier, implValue, this->spyAttributes);
|
||||
}
|
||||
@@ -1528,6 +1531,9 @@ BUN_DEFINE_HOST_FUNCTION(JSMock__jsSpyOn, (JSC::JSGlobalObject * lexicalGlobalOb
|
||||
if (JSModuleNamespaceObject* moduleNamespaceObject = tryJSDynamicCast<JSModuleNamespaceObject*>(object)) {
|
||||
moduleNamespaceObject->overrideExportValue(globalObject, propertyKey, mock);
|
||||
mock->spyAttributes |= JSMockFunction::SpyAttributeESModuleNamespace;
|
||||
} else if (auto index = parseIndex(propertyKey)) {
|
||||
// Use putDirectIndex for numeric property keys (e.g., spyOn(arr, 0))
|
||||
object->putDirectIndex(globalObject, *index, mock, attributes, PutDirectIndexLikePutDirect);
|
||||
} else {
|
||||
object->putDirect(vm, propertyKey, mock, attributes);
|
||||
}
|
||||
@@ -1544,6 +1550,9 @@ BUN_DEFINE_HOST_FUNCTION(JSMock__jsSpyOn, (JSC::JSGlobalObject * lexicalGlobalOb
|
||||
if (JSModuleNamespaceObject* moduleNamespaceObject = tryJSDynamicCast<JSModuleNamespaceObject*>(object)) {
|
||||
moduleNamespaceObject->overrideExportValue(globalObject, propertyKey, mock);
|
||||
mock->spyAttributes |= JSMockFunction::SpyAttributeESModuleNamespace;
|
||||
} else if (auto index = parseIndex(propertyKey)) {
|
||||
// For indexed properties, set the mock directly instead of wrapping in GetterSetter
|
||||
object->putDirectIndex(globalObject, *index, mock, attributes, PutDirectIndexLikePutDirect);
|
||||
} else {
|
||||
object->putDirectAccessor(globalObject, propertyKey, JSC::GetterSetter::create(vm, globalObject, mock, mock), attributes);
|
||||
}
|
||||
|
||||
@@ -952,5 +952,66 @@ describe("spyOn", () => {
|
||||
expect(fn).not.toBe(_original);
|
||||
});
|
||||
|
||||
if (isBun) {
|
||||
// Test for spyOn with numeric/indexed property keys
|
||||
test("spyOn works with indexed properties", () => {
|
||||
function original() {
|
||||
return 42;
|
||||
}
|
||||
const arr = [];
|
||||
arr[0] = original;
|
||||
|
||||
const fn = spyOn(arr, 0);
|
||||
expect(fn).toBe(arr[0]);
|
||||
expect(fn).not.toHaveBeenCalled();
|
||||
expect(arr[0]()).toBe(42);
|
||||
expect(fn).toHaveBeenCalled();
|
||||
expect(fn).toHaveBeenCalledTimes(1);
|
||||
expect(fn.mock.calls).toHaveLength(1);
|
||||
|
||||
fn.mockRestore();
|
||||
expect(arr[0]).toBe(original);
|
||||
expect(arr[0]()).toBe(42);
|
||||
expect(fn).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test("spyOn works with indexed properties using string keys", () => {
|
||||
function original() {
|
||||
return 123;
|
||||
}
|
||||
const arr = [];
|
||||
arr[0] = original;
|
||||
|
||||
// Using string "0" instead of number 0
|
||||
const fn = spyOn(arr, "0");
|
||||
expect(fn).toBe(arr[0]);
|
||||
expect(arr[0]()).toBe(123);
|
||||
expect(fn).toHaveBeenCalled();
|
||||
|
||||
fn.mockRestore();
|
||||
expect(arr[0]).toBe(original);
|
||||
});
|
||||
|
||||
test("spyOn works with indexed properties using BigInt keys", () => {
|
||||
function original() {
|
||||
return 456;
|
||||
}
|
||||
const arr = [];
|
||||
arr[14] = original;
|
||||
|
||||
// Using BigInt 14n as property key
|
||||
const fn = spyOn(arr, 14n);
|
||||
expect(fn).toBe(arr[14]);
|
||||
expect(arr[14]()).toBe(456);
|
||||
expect(fn).toHaveBeenCalled();
|
||||
expect(fn).toHaveBeenCalledTimes(1);
|
||||
|
||||
fn.mockRestore();
|
||||
expect(arr[14]).toBe(original);
|
||||
expect(arr[14]()).toBe(456);
|
||||
expect(fn).not.toHaveBeenCalled();
|
||||
});
|
||||
}
|
||||
|
||||
// spyOn does not work with getters/setters yet.
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user