src/RelExpr.test.js
import React from 'react';
import {Provider} from 'react-redux';
import configureStore from 'redux-mock-store';
import {render, fireEvent, act} from '@testing-library/react';
import RelExpr from './RelExpr';
import {Selection} from './RelOp';
const mockStore = configureStore([]);
describe('RelExpr', () => {
let store;
beforeEach(() => {
store = mockStore({data: {expr: {}}});
});
/** @test {RelExpr} */
it('correctly renders a complex expression', () => {
const expr = {
rename: {
arguments: {rename: {columns: {firstName: 'name'}}},
children: [
{
projection: {
arguments: {project: ['firstName', 'lastName']},
children: [
{
selection: {
arguments: {
select: {cmp: {lhs: 'salary', op: '$gt', rhs: 130000}},
},
children: [{relation: 'Doctor'}],
},
},
],
},
},
],
},
};
const {asFragment} = render(
<Provider store={store}>
<RelExpr expr={expr} changeExpr={jest.fn()} />
</Provider>
);
expect(asFragment()).toMatchSnapshot();
});
const condTests = [
['$gte', '>='],
['$gt', '>'],
['$lt', '<'],
['$lte', '<='],
['$ne', '!='],
['$eq', '='],
];
/** @test {RelExpr} */
it.each(condTests)('it correctly renders a %s condition as %s', (op, str) => {
const expr = {
selection: {
arguments: {select: {cmp: {lhs: 'salary', op: op, rhs: 130000}}},
children: [{relation: 'Doctor'}],
},
};
const {container} = render(
<Provider store={store}>
<RelExpr expr={expr} changeExpr={jest.fn()} />
</Provider>
);
expect(container).toHaveTextContent('salary ' + str + ' 130000');
});
/** @test {RelExpr} */
it('produces an error for an invalid expression', () => {
const errorObject = console.error;
console.error = jest.fn();
expect(() => {
render(
<Provider store={store}>
<RelExpr expr={{invalidExpr: 42}} changeExpr={jest.fn()} />
</Provider>
);
}).toThrow();
console.error = errorObject;
});
/** @test {RelExpr} */
it('doesnt change the expression when clicked and relation', () => {
const mockAction = jest.fn();
const mockEvent = jest.fn();
const expr = {relation: 'foo'};
const {container} = render(
<Provider store={store}>
<RelExpr
ReactGA={{event: mockEvent}}
expr={expr}
changeExpr={mockAction}
/>
</Provider>
);
// Click on the expression
fireEvent.click(container.firstChild);
// An action changing the expression should fire
// Should not have action attached when type relation
expect(mockAction.mock.calls.length).toBe(0);
// Don't expect analytics on a relation
expect(mockEvent.mock.calls.length).toBe(0);
});
/** @test {RelExpr} */
it('changes the expression when clicked not relation', () => {
const mockAction = jest.fn();
const mockEvent = jest.fn();
const expr = {
selection: {
arguments: {
select: 'foo',
},
children: [{relation: 'bar'}],
},
};
const {container} = render(
<Provider store={store}>
<RelExpr
ReactGA={{event: mockEvent}}
expr={expr}
changeExpr={mockAction}
/>
</Provider>
);
// Click on the expression
fireEvent.click(container.firstChild);
// An action changing the expression should fire
expect(mockAction.mock.calls.length).toBe(1);
expect(mockAction.mock.calls[0][0]).toBe(expr);
// And also an analytics event
expect(mockEvent.mock.calls.length).toBe(1);
expect(mockEvent.mock.calls[0][0].category).toBe(
'User Selecting Relational Algebra Enclosure'
);
expect(mockEvent.mock.calls[0][0].action).toBe('selection');
});
/** @test {RelExpr} */
it('should add (and remove) a class on hover', () => {
const mockAction = jest.fn();
const mockEvent = jest.fn();
const expr = {
selection: {
arguments: {
select: 'foo',
},
children: [{relation: 'bar'}],
},
};
const {container} = render(
<Provider store={store}>
<RelExpr
ReactGA={{event: mockEvent}}
expr={expr}
changeExpr={mockAction}
/>
</Provider>
);
// Hovering class should be off by default
expect(container.firstChild).not.toHaveClass('hovering');
// Hovering should add the class
fireEvent.mouseOver(container.firstChild);
expect(container.firstChild).toHaveClass('hovering');
// Mouse out should remove the class
fireEvent.mouseOut(container.firstChild);
expect(container.firstChild).not.toHaveClass('hovering');
});
});