Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions babel.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// babel.config.js
module.exports = {
presets: [
'@babel/preset-env',
'@babel/preset-react',
],
};
7 changes: 7 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/** @type {import('jest').Config} */
module.exports = {
testEnvironment: 'jsdom',
transformIgnorePatterns: [
'/node_modules/(?!(sinon)/)'
]
};
11,705 changes: 5,385 additions & 6,320 deletions package-lock.json

Large diffs are not rendered by default.

40 changes: 14 additions & 26 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,34 +21,22 @@
"author": "Mauro Carrero",
"license": "MIT",
"dependencies": {
"@sinonjs/fake-timers": "^14.0.0",
"@testing-library/user-event": "^14.6.1",
"axios": "^1.7.2",
"create-react-class": "^15.6.2",
"enzyme": "^3.2.0",
"enzyme-adapter-react-16": "^1.1.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-test-renderer": "^18.3.1",
"sinon": "^17.0.1"
},
"devDependencies": {
"@babel/preset-env": "^7.27.2",
"@babel/preset-react": "^7.27.1",
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.3.0",
"babel-jest": "^30.0.0-beta.3",
"jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0",
"lolex": "^2.3.1",
"prettier": "^1.8.2",
"react": "^16.2.0",
"react-dom": "^16.2.0",
"react-test-renderer": "^16.2.0",
"sinon": "^4.1.2"
},
"jest": {
"setupFilesAfterEnv": [
"<rootDir>/tests/setupTests.js"
],
"unmockedModulePathPatterns": [
"<rootDir>/src/react-component/Button.js",
"<rootDir>/node_modules/axios",
"<rootDir>/node_modules/enzyme",
"<rootDir>/node_modules/enzyme-adapter-react-16",
"<rootDir>/node_modules/lolex",
"<rootDir>/node_modules/react",
"<rootDir>/node_modules/react-dom",
"<rootDir>/node_modules/react-test-renderer",
"<rootDir>/node_modules/sinon"
],
"verbose": true
"prettier": "^3.2.5"
}
}
35 changes: 5 additions & 30 deletions src/react-component/Button.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
const React = require('react');
const { get, GITHUB_URL } = require('./httpService');
const { get: defaultGet, GITHUB_URL } = require('./httpService');

module.exports = class Button extends React.Component {
constructor() {
super();
constructor(props) {
super(props);
this.get = props.get || defaultGet;
this.state = {
task: 'Nothing yet'
};
Expand All @@ -12,7 +13,7 @@ module.exports = class Button extends React.Component {
}

clickHandler() {
get(GITHUB_URL).then(this.doSomething);
this.get(GITHUB_URL).then(this.doSomething);
}

doSomething(task) {
Expand All @@ -38,30 +39,4 @@ module.exports = class Button extends React.Component {
}
};

// TODO: Using create-react-class I was unable to spy on methods. Dig into this.

// const createReactClass = require('create-react-class');

// module.exports = createReactClass({
// clickHandler() {
// console.log('clicked')
// return 'clicked';
// },
// doSomething(task) {
// return task;
// },
// componentDidMount() {
// this.doSomething('something');
// return 'componentDidMount';
// },
// render() {
// return React.createElement(
// 'button',
// {
// onClick: this.clickHandler
// },
// 'Click me'
// );
// }
// });
//
2 changes: 1 addition & 1 deletion tests/__snapshots__/snapshots.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ exports[`should match a React Component 1`] = `
</button>
`;

exports[`should match a function 1`] = `undefined`;
exports[`should match a function 1`] = `279`;
64 changes: 31 additions & 33 deletions tests/poke.into.react.components.test.js
Original file line number Diff line number Diff line change
@@ -1,102 +1,100 @@
const sinon = require('sinon');

const React = require('react');
const { shallow } = require('enzyme');
const { render, screen } = require('@testing-library/react');
const userEvent = require('@testing-library/user-event').default;

const Button = require('../src/react-component/Button');

let wrapper;
let instance;

describe('sinon', function() {
describe('sinon', () => {
let sinonCDMSpy;
let sinonRenderSpy;
let sinonDoSomethingSpy;
let sinonClickHandlerSpy;

beforeEach(function() {
beforeEach(() => {
sinonCDMSpy = sinon.spy(Button.prototype, 'componentDidMount');
sinonRenderSpy = sinon.spy(Button.prototype, 'render');
sinonDoSomethingSpy = sinon.spy(Button.prototype, 'doSomething');
sinonClickHandlerSpy = sinon.spy(Button.prototype, 'clickHandler');

wrapper = shallow(React.createElement(Button));
render(React.createElement(Button));
});

afterEach(function() {
afterEach(() => {
sinonCDMSpy.restore();
sinonRenderSpy.restore();
sinonDoSomethingSpy.restore();
sinonClickHandlerSpy.restore();
});

it('spy on componentDidMount', function() {
expect(sinonCDMSpy.called).toEqual(true);
it('spy on componentDidMount', () => {
expect(sinonCDMSpy.called).toBe(true);
});

it('spy on render', function() {
expect(sinonRenderSpy.called).toEqual(true);
it('spy on render', () => {
expect(sinonRenderSpy.called).toBe(true);
});

it('spy on doSomething', function() {
expect(sinonDoSomethingSpy.called).toEqual(true);
it('spy on doSomething', () => {
expect(sinonDoSomethingSpy.called).toBe(true);
});

it('spy on clickHandler', function() {
expect(sinonClickHandlerSpy.called).toEqual(false);
wrapper.simulate('click');
expect(sinonClickHandlerSpy.called).toEqual(true);
it('spy on clickHandler', async () => {
expect(sinonClickHandlerSpy.called).toBe(false);
const button = screen.getByRole('button'); // Asume que Button renderiza un <button>
await userEvent.click(button);
expect(sinonClickHandlerSpy.called).toBe(true);
});
});

describe('jest', function() {
describe('jest', () => {
let jestCDMSpy;
let jestRenderSpy;
let jestDoSomethingSpy;
let jestClickHandlerSpy;

beforeEach(function() {
beforeEach(() => {
jestCDMSpy = jest.spyOn(Button.prototype, 'componentDidMount');
jestRenderSpy = jest.spyOn(Button.prototype, 'render');
jestDoSomethingSpy = jest.spyOn(Button.prototype, 'doSomething');
jestClickHandlerSpy = jest.spyOn(Button.prototype, 'clickHandler');

wrapper = shallow(React.createElement(Button));
render(React.createElement(Button));
});

afterEach(function() {
afterEach(() => {
jestCDMSpy.mockRestore();
jestRenderSpy.mockRestore();
jestDoSomethingSpy.mockRestore();
jestClickHandlerSpy.mockRestore();
});

it('spy on componentDidMount', function() {
it('spy on componentDidMount', () => {
expect(jestCDMSpy).toHaveBeenCalled();
});

it('spy on doSomething', function() {
it('spy on doSomething', () => {
expect(jestDoSomethingSpy).toHaveBeenCalled();
});

it('spy on clickHandler', function() {
wrapper.simulate('click');

it('spy on clickHandler', async () => {
const button = screen.getByRole('button');
await userEvent.click(button);
expect(jestClickHandlerSpy).toHaveBeenCalled();
});
});

describe('sinon && jest', function() {
it('spying both at a time', function() {
describe('sinon && jest', () => {
it('spying both at a time', () => {
const jestSpy = jest.spyOn(Button.prototype, 'doSomething');
const sinonSpy = sinon.spy(Button.prototype, 'doSomething');

expect(jestSpy).not.toHaveBeenCalled();
expect(sinonSpy.called).toEqual(false);
expect(sinonSpy.called).toBe(false);

wrapper = shallow(React.createElement(Button));
render(React.createElement(Button));

expect(jestSpy).toHaveBeenCalled();
expect(sinonSpy.called).toEqual(true);
expect(sinonSpy.called).toBe(true);
});
});
91 changes: 24 additions & 67 deletions tests/react-components-async.test.js
Original file line number Diff line number Diff line change
@@ -1,84 +1,41 @@
const sinon = require('sinon');

const { act } = require('react');
const React = require('react');
const { shallow } = require('enzyme');
const sinon = require('sinon');
const { render, fireEvent } = require('@testing-library/react');

const Button = require('../src/react-component/Button');
const httpService = require('../src/react-component/httpService');

let httpServiceMock = require('../src/react-component/httpService');

const EXPECTED = 'url was requested: https://api.github.com/';
const INITIAL = 'initial doSomething';

let wrapper;

describe('sinon', function() {
it('should call the mocked service', function(done) {
const stub = sinon.stub(Button.prototype, 'doSomething').callThrough();

wrapper = shallow(React.createElement(Button));

expect(wrapper.state('task')).toEqual(INITIAL);
describe('sinon', () => {
it('should call the mocked service', async () => {
const stub = sinon.stub().resolves('stubbed from sinon');

wrapper.simulate('click');
wrapper.simulate('click');
const { container } = render(React.createElement(Button, { get: stub }));
const button = container.querySelector('button');

process.nextTick(() => {
try {
// Button.prototype.doSomething spy
expect(stub.callCount).toEqual(3);
expect(stub.args[0][0]).toEqual(INITIAL);
expect(stub.args[1][0]).toEqual(EXPECTED);
expect(stub.args[2][0]).toEqual(EXPECTED);

// The state of the component has been updated
expect(wrapper.state('task')).toEqual(EXPECTED);
done();
} catch (e) {
return done(e);
}
await act(async () => {
fireEvent.click(button);
await new Promise((r) => setTimeout(r, 0));
});

expect(httpServiceMock.get.callCount).toEqual(2);

stub.restore();
expect(stub.callCount).toBe(1);
expect(stub.firstCall.args[0]).toBe(httpService.GITHUB_URL);
});
});

describe('jest', function() {
it('should call the mocked service', function(done) {
const jestSpy = jest.spyOn(Button.prototype, 'doSomething');

// httpServiceMock is still a sinon stub since it is implemented in
// the manual mock.
// We use .resetHistory instead of .reset, otherwise behavior is also reset.
httpServiceMock.get.resetHistory();

wrapper = shallow(React.createElement(Button));

console.log('POST', jestSpy.mock.calls[0][0]);

expect(wrapper.state('task')).toEqual(INITIAL);

wrapper.simulate('click');
wrapper.simulate('click');
describe('jest', () => {
it('should call the mocked service', async () => {
const spy = jest.fn().mockResolvedValue('stubbed from jest');

process.nextTick(() => {
try {
// Button.prototype.doSomething spy
expect(jestSpy.mock.calls.length).toEqual(3);
expect(jestSpy.mock.calls[0][0]).toEqual(INITIAL);
const { container } = render(React.createElement(Button, { get: spy }));
const button = container.querySelector('button');

// The state of the component has been updated
expect(wrapper.state('task')).toEqual(EXPECTED);
jestSpy.mockRestore();
done();
} catch (e) {
jestSpy.mockRestore();
return done(e);
}
await act(async () => {
fireEvent.click(button);
await new Promise((r) => setTimeout(r, 0));
});

expect(httpServiceMock.get.callCount).toEqual(2);
expect(spy).toHaveBeenCalledTimes(1);
expect(spy).toHaveBeenCalledWith(httpService.GITHUB_URL);
});
});
14 changes: 2 additions & 12 deletions tests/setupTests.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,2 @@
global.requestAnimationFrame = function(callback) {
setTimeout(callback, 0);
};

const Enzyme = require('enzyme');
const Adapter = require('enzyme-adapter-react-16');

Enzyme.configure({ adapter: new Adapter() });

// Enabling automock since now is disabled by default:
// https://facebook.github.io/jest/blog/2016/09/01/jest-15.html#disabled-automocking
jest.enableAutomock();
// tests/setupTests.js
import '@testing-library/jest-dom';
Loading