Jest custom testing API, how to correct code frame - javascript

I'm looking to simplify my project's testing API where I am aiming for something like this:
testThing((t) => {
t(33);
t(44);
t(42);
})
Now I don't know how to get Jest to show the correct code frames for failed expect's. This is my current stab at an implementation:
const testThing = (callback: any) => {
callback((n: any) => {
test(n.toString(), () => {
expect(n).toBe(42);
});
});
};
Which results in the testThing definition to be shown for every failed test case. Here's a replit if you want to see it in action: https://replit.com/#grgr/jest-frame-issue#thing.test.js

Related

How to use the `PerformanceNavigationTiming` API to get page load time?

I am trying to to use the PerformanceNavigationTiming API to generate a page load metric.
The MDN API document linked above says that the PerformanceEntry.duration should give me what I need because it:
[r]eturns a timestamp that is the difference between the PerformanceNavigationTiming.loadEventEnd and PerformanceEntry.startTime properties.
However, when I check this property, I get simply 0. I'm accessing this API from within a React hook that runs a useEffect function that wait for the window load event and then checks the api like so:
export const useReportPageLoadTime = () => {
useEffect(() => {
const reportTime = () => {
let navPerformance: PerformanceEntry
navPerformance = window.performance.getEntriesByType('navigation')[0]
console.log({
duration: navPerformance.duration,
blob: navPerformance.toJSON()
})
}
if (document.readyState === 'complete') {
reportTime()
return null
} else {
window.addEventListener('load', reportTime)
return () => window.removeEventListener('load', reportTime)
}
}, [])
}
As you can see there, I also call toJSON on the performance entry and indeed it shows that the values upon which duration (startTime and loadEventEnd) are both 0 as well:
Does anyone know why I am getting this value?
I was finally able to get this to work using a different method than the event listener. It certainly is logical that the data should be ready when the load event fires, but the only way I was able to get the data was to use another feature of the Performance API: the PerformanceObserver, which fires a callback when a new piece of data has become available.
Here is the code that worked for me:
export const useReportPageLoadMetrics = () => {
useEffect(() => {
const perfObserver = new PerformanceObserver((observedEntries) => {
const entry: PerformanceEntry =
observedEntries.getEntriesByType('navigation')[0]
console.log('pageload time: ', entry.duration)
})
perfObserver.observe({
type: 'navigation',
buffered: true
})
}, [])
}

Posting result data to a website (like API or a telegram bot) after a test on Cypress

I've finished writing my first Cypress test. Everything is good except I'm struggling to post the result data to a website. Because I want to send the result data and also if any errors occurs the result screenshot to our coworker telegram group.
For the last two days I've tried everything and couldn't find any solution.
I've tried those in my test script (cypress/integration/test.js);
Cypress.on('test:after:run', (test, runnable) => {
console.log('test,runnable', test, runnable)
const details = {
projectKey: Cypress.env('zephyr-project-key'),
testName: test.invocationDetails.relativeFile,
status: test.status,
error: runnable.err.message,
retries: runnable.retries.length,
duration: test.wallClockDuration,
startTime: test.wallClockStartedAt
}
cy.request('POST', 'http://mywebsite.com/notify.php', { body: details })
fetch('http://mywebsite.com/notify.php')
})
Also this didn't work (cypress/plugins/index.js);
module.exports = (on, config) => {
// `on` is used to hook into various events Cypress emits
// `config` is the resolved Cypress config
on('after:run', (results) => {
if (results) {
// results will be undefined in interactive mode
console.log(results.totalPassed, 'out of', results.totalTests, 'passed')
fetch('http://mywebsite.com/notify.php');
}
})
}
Edit: This is day 3 and I still couldn't solve this. What I've seen from Cypress help page is that cy.task() calls do not fire in 'test:after:run' event block;
https://github.com/cypress-io/cypress/issues/4823
I've seen some telegram groups who can do what I'm trying to do. All I need is to be able to get the results and post it to my website.
The third parameter to cy.request() is body, you don't have to wrap it.
Cypress.on('test:after:run', (test, runnable) => {
const details = {
projectKey: Cypress.env('zephyr-project-key'),
testName: test.invocationDetails.relativeFile,
status: test.status,
error: runnable.err?.message, // need err?.message if there is no error
retries: runnable.retries.length,
duration: test.wallClockDuration,
startTime: test.wallClockStartedAt
}
cy.request('POST', 'http://mywebsite.com/notify.php', details) // don't wrap details
.then(res => expect(res.status).to.eq(201)) // confirm result
})

Jest: Mock a _HOC_ or _curried_ function

Given the following function:
./http.js
const http = {
refetch() {
return (component) => component;
}
}
I would like to mock the function in a test as follows:
./__tests__/someTest.js
import { refetch } from './http';
jest.mock('./http', () => {
return {
refetch: jest.fn();
}
}
refetch.mockImplementation((component) => {
// doing some stuff
})
But I'm receiving the error
TypeError: _http.refetch.mockImplementation is not a function
How can I mock the refetch function in the given example?
update:
When I modify the mock function slightly to:
jest.mock(
'../http',
() => ({ refetch: jest.fn() }),
);
I get a different error:
TypeError: (0 , _http.refetch)(...) is not a function
My guess it's something with the syntax where the curried function (or HOC function) is not mapped properly. But I don't know how to solve it.
Some of the real code I'm trying to test.
Note: The example is a bit sloppy. It works in the application. The example given is to give an idea of the workings.
./SettingsContainer
// ...some code
return (
<FormComponent
settingsFetch={settingsFetch}
settingsPutResponse={settingsPutResponse}
/>
);
}
const ConnectedSettingsContainer = refetch(
({
match: { params: { someId } },
}) => ({
settingsFetch: {
url: 'https://some-url.com/api/v1/f',
},
settingsPut: (data) => ({
settingsPutResponse: {
url: 'https://some-url.com/api/v1/p',
}
}),
}),
)(SettingsContainer);
export default ConnectedSettingsContainer;
Then in my component I am getting the settingsPutResponse via the props which react-refetch does.
I want to test if the user can re-submit a form after the server has responded once or twice with a 500 until a 204 is given back.
./FormComponent
// ...code
const FormComp = ({ settingsResponse }) => {
const [success, setSuccess] = useState(false);
useEffect(() => {
if (settingsResponse && settingsResponse.fulfilled) {
setSuccess(true);
}
}, [settingsResponse]);
if (success) {
// state of the form wil be reset
}
return (
<form>
<label htmlFor"username">
<input type="text" id="username" />
<button type="submit">Save</button>
</form>
)
};
The first question to ask yourself about mocking is "do I really need to mock this?" The most straightforward solution here is to test "component" directly instead of trying to fake out an http HOC wrapper around it.
I generally avoid trying to unit test things related to I/O. Those things are best handled with functional or integration tests. You can accomplish that by making sure that, given same props, component always renders the same output. Then, it becomes trivial to unit test component with no mocks required.
Then use functional and/or integration tests to ensure that the actual http I/O happens correctly
To more directly answer you question though, jest.fn is not a component, but React is expecting one. If you want the mock to work, you must give it a real component.
Your sample code here doesn't make sense because every part of your example is fake code. Which real code are you trying to test? I've seen gigantic test files that never actually exercize any real code - they were just testing an elaborate system of mocks. Be careful not to fall into that trap.

Taking screenshot in different route in angular using protractor

I am new to testing using protractor so for testing I have to take screenshots in an angular application for all the different routes in my app. I tried to do it on a small dummy angular app, so I cloned the Tour of heroes repo it has dashboard and Heroes route. I wrote the following code in app.po.ts :
import { browser, element, by } from 'protractor';
export class BlankPage {
navigateTo() {
return browser.get('/heroes');
}
getParagraphText() {
return element(by.tagName('h2')).getText();
}
}
and in app.e2e-spec.ts
import { BlankPage } from './app.po';
import {browser,by,element} from 'protractor';
import { protractor } from 'protractor';
import {createWriteStream} from 'fs' ;
describe('blank App', () => {
let page: BlankPage;
beforeEach(() => {
page = new BlankPage();
});
it('should display message saying app works', () => {
page.navigateTo();
expect(page.getParagraphText()).toEqual('My Heroes');
browser.takeScreenshot().then((png) =>{
var stream = createWriteStream("heroes.png"); /** change the png file name */
stream.write(new Buffer(png, 'base64'));
stream.end;
});
});
});
The idea was to navigate to heroes route and capture the screenshot. I got the screenshot but
Is there a way I can automate the task of going to all the routes and take screenshots ? In my actual website there are a lot of routes
I think the better solution for you is to add some reporter that will do everything for you, like taking screenshots after each test or after each failed tests and e.t.c.
Take a look at some reporters:
allure-jasmine - Highly recommended.
protractor-jasmine2-screenshot-reporter
protractor-jasmine2-html-reporter
protractor-html-reporter-2
protractor-html-screenshot-reporter
protractor-beautiful-reporter
But If you don't want to add any extra libraries to your project you can just put the browser.takeScreenshot() function to the afterEach function to take a screenshot after each test (it).
For instance:
describe('blank App', () => {
let page: BlankPage;
beforeEach(() => {
page = new BlankPage();
});
afterEach(() =>
browser.takeScreenshot().then((png) =>{
var stream = createWriteStream("heroes.png"); /** change the png file name */
stream.write(new Buffer(png, 'base64'));
stream.end;
});
});
it('should display message saying app works', () => {
page.navigateTo();
expect(page.getParagraphText()).toEqual('My Heroes');
});
});
I think the best approach for you would be the have a list of all the routes in your application and create a datadriven test to iterate over each one.
You would need a generic navigation function which could get to each page e.g navigateTo(routeName). That code would look something like this.
var routes = [
'homepage',
'myheroes',
'mainpage',
'heroprofile'
]
describe('blank App', () => {
for (let i = 0; i < routes.length; i++) {
it('should display message saying app works', () => {
navigateTo(routes[i]);
browser.takeScreenshot().then((png) => {
var stream = createWriteStream(routes[i] + ".png"); /** change the png file name */
stream.write(new Buffer(png, 'base64'));
stream.end;
});
});
}
});
protractor-image-compare
Really though I would recommend you use the npm package protractor-image-comparison. I've worked with this package and it does make visual validation very straightforward. It allows you to save new baseline images (golden images as you call them) if they are absent and compares them if they are present. The comparison are very sensitive to change but you can set how much of a difference you want to allow.
There would be no database required with this approach.
Note
Be aware also that different browsers take screenshots differently based. Chrome considers the "viewport" to be the visible portion of the browser but I believe in firefox you can screenshot the entire webpage at once.

jest.mock is not a function in react?

Could you please tell me how to test componentDidMount function using enzyme.I am fetching data from the server in componentDidMount which work perfectly.Now I want to test this function.
here is my code
https://codesandbox.io/s/oq7kwzrnj5
componentDidMount(){
axios
.get('https://*******/getlist')
.then(res => {
this.setState({
items : res.data.data
})
})
.catch(err => console.log(err))
}
I try like this
it("check ajax call", () => {
const componentDidMountSpy = jest.spyOn(List.prototype, 'componentDidMount');
const wrapper = shallow(<List />);
});
see updated code
https://codesandbox.io/s/oq7kwzrnj5
it("check ajax call", () => {
jest.mock('axios', () => {
const exampleArticles:any = {
data :{
data:['A','B','c']
}
}
return {
get: jest.fn(() => Promise.resolve(exampleArticles)),
};
});
expect(axios.get).toHaveBeenCalled();
});
error
You look like you're almost there. Just add the expect():
expect(componentDidMountSpy).toHaveBeenCalled();
If you need to check if it was called multiple times, you can use toHaveBeenCalledTimes(count).
Also, be sure to mockRestore() the mock at the end to make it unmocked for other tests.
List.prototype.componentDidMount.restore();
To mock axios (or any node_modules package), create a folder named __mocks__ in the same directory as node_modules, like:
--- project root
|-- node_modules
|-- __mocks__
Inside of there, make a file named <package_name>.js (so axios.js).
Inside of there, you'll create your mocked version.
If you just need to mock .get(), it can be as simple as:
export default { get: jest.fn() }
Then in your code, near the top (after imports), add:
import axios from 'axios';
jest.mock('axios');
In your test, add a call to axios.get.mockImplementation() to specify what it'll return:
axios.get.mockImplementation(() => Promise.resolve({ data: { data: [1, 2, 3] } });
This will then make axios.get() return whatever you gave it (in this case, a Promise that resolves to that object).
You can then do whatever tests you need to do.
Finally, end the test with:
axios.get.mockReset();
to reset it to it's default mocked implementation.

Categories