-
-
Notifications
You must be signed in to change notification settings - Fork 6.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix mocking inherited static properties and prototype-less objects #7003
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,11 +12,14 @@ const vm = require('vm'); | |
|
||
describe('moduleMocker', () => { | ||
let moduleMocker; | ||
let mockContext; | ||
let mockGlobals; | ||
|
||
beforeEach(() => { | ||
const mock = require('../'); | ||
const global = vm.runInNewContext('this'); | ||
moduleMocker = new mock.ModuleMocker(global); | ||
mockContext = vm.createContext(); | ||
mockGlobals = vm.runInNewContext('this', mockContext); | ||
moduleMocker = new mock.ModuleMocker(mockGlobals); | ||
}); | ||
|
||
describe('getMetadata', () => { | ||
|
@@ -137,7 +140,7 @@ describe('moduleMocker', () => { | |
|
||
expect(typeof foo.nonEnumMethod).toBe('function'); | ||
|
||
expect(mock.nonEnumMethod.mock).not.toBeUndefined(); | ||
expect(mock.nonEnumMethod.mock).toBeDefined(); | ||
expect(mock.nonEnumGetter).toBeUndefined(); | ||
}); | ||
|
||
|
@@ -180,9 +183,140 @@ describe('moduleMocker', () => { | |
|
||
expect(typeof foo.foo).toBe('function'); | ||
expect(typeof instanceFooMock.foo).toBe('function'); | ||
expect(instanceFooMock.foo.mock).not.toBeUndefined(); | ||
expect(instanceFooMock.foo.mock).toBeDefined(); | ||
|
||
expect(instanceFooMock.toString.mock).not.toBeUndefined(); | ||
expect(instanceFooMock.toString.mock).toBeDefined(); | ||
}); | ||
|
||
it('mocks ES2015 non-enumerable static properties and methods', () => { | ||
class ClassFoo { | ||
static foo() {} | ||
} | ||
ClassFoo.fooProp = () => {}; | ||
|
||
class ClassBar extends ClassFoo {} | ||
|
||
const ClassBarMock = moduleMocker.generateFromMetadata( | ||
moduleMocker.getMetadata(ClassBar), | ||
); | ||
|
||
expect(typeof ClassBarMock.foo).toBe('function'); | ||
expect(typeof ClassBarMock.fooProp).toBe('function'); | ||
expect(ClassBarMock.foo.mock).toBeDefined(); | ||
expect(ClassBarMock.fooProp.mock).toBeDefined(); | ||
}); | ||
|
||
it('mocks methods in all the prototype chain (null prototype)', () => { | ||
const Foo = Object.assign(Object.create(null), {foo() {}}); | ||
const Bar = Object.assign(Object.create(Foo), {bar() {}}); | ||
|
||
const BarMock = moduleMocker.generateFromMetadata( | ||
moduleMocker.getMetadata(Bar), | ||
); | ||
expect(typeof BarMock.foo).toBe('function'); | ||
expect(typeof BarMock.bar).toBe('function'); | ||
}); | ||
|
||
it('does not mock methods from Object.prototype', () => { | ||
const Foo = {foo() {}}; | ||
const Bar = Object.assign(Object.create(Foo), {bar() {}}); | ||
|
||
const BarMock = moduleMocker.generateFromMetadata( | ||
moduleMocker.getMetadata(Bar), | ||
); | ||
|
||
expect(BarMock).toBeInstanceOf(mockGlobals.Object); | ||
expect( | ||
Object.prototype.hasOwnProperty.call(BarMock, 'hasOwnProperty'), | ||
).toBe(false); | ||
expect(BarMock.hasOwnProperty).toBe( | ||
mockGlobals.Object.prototype.hasOwnProperty, | ||
); | ||
}); | ||
|
||
it('does not mock methods from Object.prototype (in mock context)', () => { | ||
const Bar = vm.runInContext( | ||
` | ||
const Foo = { foo() {} }; | ||
const Bar = Object.assign(Object.create(Foo), { bar() {} }); | ||
Bar; | ||
`, | ||
mockContext, | ||
); | ||
|
||
const BarMock = moduleMocker.generateFromMetadata( | ||
moduleMocker.getMetadata(Bar), | ||
); | ||
|
||
expect(BarMock).toBeInstanceOf(mockGlobals.Object); | ||
expect( | ||
Object.prototype.hasOwnProperty.call(BarMock, 'hasOwnProperty'), | ||
).toBe(false); | ||
expect(BarMock.hasOwnProperty).toBe( | ||
mockGlobals.Object.prototype.hasOwnProperty, | ||
); | ||
}); | ||
|
||
it('does not mock methods from Function.prototype', () => { | ||
class Foo {} | ||
class Bar extends Foo {} | ||
|
||
const BarMock = moduleMocker.generateFromMetadata( | ||
moduleMocker.getMetadata(Bar), | ||
); | ||
|
||
expect(BarMock).toBeInstanceOf(mockGlobals.Function); | ||
expect(Object.prototype.hasOwnProperty.call(BarMock, 'bind')).toBe(false); | ||
expect(BarMock.bind).toBe(mockGlobals.Function.prototype.bind); | ||
}); | ||
|
||
it('does not mock methods from Function.prototype (in mock context)', () => { | ||
const Bar = vm.runInContext( | ||
` | ||
class Foo {} | ||
class Bar extends Foo {} | ||
Bar; | ||
`, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There's a weird alignment going on here... Do instead: const Bar = vm.runInContext(
`
class Foo {}
class Bar extends Foo {}
Bar;
`,
mockContext,
); There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's all prettier 🤷🏻♂️ There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you can add a space inside the literal to align the backticks, prettier won't strip them There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Correct; once you're in backtick mode, prettier is not changing the contents. |
||
mockContext, | ||
); | ||
|
||
const BarMock = moduleMocker.generateFromMetadata( | ||
moduleMocker.getMetadata(Bar), | ||
); | ||
|
||
expect(BarMock).toBeInstanceOf(mockGlobals.Function); | ||
expect(Object.prototype.hasOwnProperty.call(BarMock, 'bind')).toBe(false); | ||
expect(BarMock.bind).toBe(mockGlobals.Function.prototype.bind); | ||
}); | ||
|
||
it('does not mock methods from RegExp.prototype', () => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Didn't we say we were going to remove the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That would be a breaking change. |
||
const bar = /bar/; | ||
|
||
const barMock = moduleMocker.generateFromMetadata( | ||
moduleMocker.getMetadata(bar), | ||
); | ||
|
||
expect(barMock).toBeInstanceOf(mockGlobals.RegExp); | ||
expect(Object.prototype.hasOwnProperty.call(barMock, 'test')).toBe(false); | ||
expect(barMock.test).toBe(mockGlobals.RegExp.prototype.test); | ||
}); | ||
|
||
it('does not mock methods from RegExp.prototype (in mock context)', () => { | ||
const bar = vm.runInContext( | ||
` | ||
const bar = /bar/; | ||
bar; | ||
`, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Weird alignment again... |
||
mockContext, | ||
); | ||
|
||
const barMock = moduleMocker.generateFromMetadata( | ||
moduleMocker.getMetadata(bar), | ||
); | ||
|
||
expect(barMock).toBeInstanceOf(mockGlobals.RegExp); | ||
expect(Object.prototype.hasOwnProperty.call(barMock, 'test')).toBe(false); | ||
expect(barMock.test).toBe(mockGlobals.RegExp.prototype.test); | ||
}); | ||
|
||
it('mocks methods that are bound multiple times', () => { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is why I had to revert #6921. In this specific test we use builtins from the host, not the ones passed to
ModuleMockerClass
. This test covers that too.