In my code, I am using the relatively new interface of FileSystemFileHandle (documentation here: https://developer.mozilla.org/en-US/docs/Web/API/FileSystemFileHandle/createWritable)
In the documentation, it shows you can just call the createWritable() method, and React knows what to do with it. This would be correct, and my code works. However, when trying to create a test for my new method, Jest thinks createWritable() is not a function. This makes sense, since I didn't have to import any library to use this method.
How can I test my code using createWritable(), or is there any workaround so that the test at least passes?
//code example:
this.state = {
handleFile: []
};
async fileWriter({ data }) {
//request writable stream
const writer = await this.state.handleFile.createWritable();
await writer.write(new Blob([data])); // write the Blob directly
await writer.close(); // end writing
}
Related
I have a scenario like i have to call 1stAPI then depending on the response of 1st API i have to call 2nd API.
I got a solution to test 1st API :
global.fetch = jest
.fn()
.mockImplementation(() => getMockPromise({ Response: resMock }));
I got my 1st response while mocking
but how can I get response in second ? I am using typescript with react
I tried to search multiple places but only got solution for one mock response only for jest testing like below :
global.fetch = jest
.fn()
.mockImplementation(() => getMockPromise({ Response: resMock }));
I thought if I use the above mockImplementation two times for different response it will work, but still same only one is working.
If you are calling fetch() with different arguments each time, you can use a mock implementation that responds with different data depending on the argument it receives.
//test file
const mockedResponses = {
'firstUrl': {}, // first response object
'secondUrl': {}, // second response object
}
global.fetch = jest
.fn()
.mockImplementation((url: string) => getMockPromise({ Response: mockedResponses[url] }));
Generally it's a good idea to have the actual fetch() call be done inside a separate function, e.g. callFirstApi() and callSecondApi(), and then you can just mock those functions instead. This also means you don't end up overwriting a global function.
You can also look into something like the Nock package which lets you do more elaborate testing of external APIs.
I am trying to learn the simplest way to mock fetch with jest. I am trying to start with the official React docs recipe for fetch but it doesnt actually work without some modification.
My aim is to use Jest and (only) native React to wait for component rendering to complete with useEffect[].fetch() call before running assertions and testing initialisation worked.
I have imported the Data fetching recipe from official docs:
https://reactjs.org/docs/testing-recipes.html#data-fetching
into codesandbox here
https://codesandbox.io/s/jest-data-fetching-ov8og
Result: test fail
expect(received).toContain(expected) // indexOf
Expected substring: "123, Charming Avenue"
Received string: "loading..."
Possible cause?: I suspect its failing because global.fetch is not being used in the component which appears to be using real fetch hence is stuck "loading".
UPDATE
I have managed to make the test work through trial and error. Changed the call to fetch in the actual component to global.fetch() to match the test. This is not desirable for working code to refer to global prefix everywhere fetch is used and is also not in the example code.
e.g.
export default function User(props) {
const [user, setUser] = useState(null);
async function fetchUserData(id) {
// this doesnt point to the correct mock function
// const response = await fetch("/" + id);
// this fixes the test by pointing to the correct mock function
const response = await global.fetch("/" + id);
const json = await response.json();
setUser(json);
}
...
any help or advice much appreciated.
I am trying to ensure that the right value is copied to the users clipboard when they click a button. This is my copy method. I am using a ref on the input to access the right value.
protected copyToClipboard() {
console.log("clicked!");
const text = this.controls.copyData;
if (!_.isNil(text)) {
text.current.focus();
text.current.select();
document.execCommand("copy");
this.setState({copied: true});
}
}
For my test:
test("Ensure right value is copied to clipboard", () => {
const wrapper = mount(<MyComponent />);
const copyButton = wrapper.find(".copyBtn");
copyButton.simulate("click");
const copyToClipboardSpy = jest.spyOn(document as any, "execCommand");
wrapper.update();
expect(copyToClipboardSpy).toHaveBeenCalledWith("copy");
});
The error I receive when I run the test is TypeError: document.execCommand is not a function which makes sense, but I am unsure how to approach this.
I am relatively new to testing, just to put that out there. I also have read that I may not be able to access the document.execCommand but have struggled to find a good alternative to hijack the test and access the value being copied. I appreciate any advice that can be given on the matter!
Posting this in case anyone else was in a similar boat. It's doesn't necessarily check the value yet, but one piece I managed was with the document.execCommand method.
I set up a mock function above the wrapper:
document.execCommand = jest.fn();
With this, the test stopped throwing the TypeError. Then my expectations included checking for the spy to have been called, expect my copy state to have changed to true, and:
expect(document.execCommand).toHaveBeenCalledWith("copy");
Test passes! A possible solution for the value is to see if I can "paste" the value and then check it. Will edit this response if/when I can manage that
When you use navigator.clipBoard.writeText instead of using document.exec("copy"), you can refer to this thread for an elegant solution that lets you assert on the content as well.
Being execCommand no longer an option as it is deprecated (see MDN), you should be using navigator.clipboard.writeText('your copied data');.
To mock navigator.clipboard, you could do the following:
// It's important to keep a copy, so your tests don't bleed
const originalClipboard = navigator.clipboard;
const mockedWriteText = jest.fn();
navigator.clipboard = {
writeText: mockedWriteText,
};
const copyComponent = await screen.findByTestId('copy-component');
await userEvent.click(copyComponent);
expect(mockedWriteText).toHaveBeenCalledTimes(1);
expect(mockedWriteText).toHaveBeenCalledWith('your copied data');
// Remember to restore the original clipboard
navigator.clipboard = originalClipboard;
jest.resetAllMocks();
You can also do Object.assignProperty instead of directly modifying the navigator object.
This snippet assumes you are using React Testing Library with User Event.
I have recently started writing React Native code, and am having tremendous difficulty getting either the Chrome debugger or the React Native debugger to work properly with my Async/Await functions and Async/Await arrow functions.
I can successfully attach the debuggers to my code and step through most of my code, but it seems that when the debugger gets inside of my async methods, it loses track of what line is actually executing, making it impossible to work productively.
Some breakpoints just do not get hit, even though console.log statements indicate that the code has been executed. When this happens, usually the current debug line will switch to the line of the function declaration rather than the actually executing line.
I bootstrapped my app using crna, and am running in Windows 10. Not sure if that is relevant.
I see lots of talk about similar behaviour from 2016 in various forums, but there is no recent news on it, so I would assume it was fixed. If not, then what is the workaround? I need a way to debug my code.
I see this problem whenever i set a breakpoint after the use of an await operator.
For example you could have a function:
static async makeRequest(request) {
// Breakpoints here work fine
request.method = 'GET'
// Breakpoints not working anymore because of the await operator
const response = await fetch(request);
const obj = await response.json();
obj.debug = true;
return obj;
}
Putting a breakpoint after one of the await operators does not work.
However, setting a breakpoint before the await operator seems to work fine.
To get around this issue I found that delegating into other functions allow you to put breakpoints. So I would change this to:
static async makeRequest(request) {
request.method = 'GET'
const response = await fetch(request);
const obj = await response.json();
return doSomething(obj);
}
static doSomething(obj) {
// Putting breakpoints here works fine
obj.debug = true;
return obj;
}
One of my co-workers solved this by using react-native-debugger
By following the AngularFire guide, I have synchronized a scope variable with a Firebase array. My code is basically the same as the tutorial (Step 5):
https://www.firebase.com/docs/web/libraries/angular/quickstart.html
Everything in my app is working, but I am really confused about how to properly mock the Firebase call in my Karma unit tests. I guess something along the lines of using $provide to mock the data? But then $add wouldn't work in my controller methods. Help?
Copied from this gist which discusses this topic at length.
The discussion is all inlined in the comments. There is a lot to consider here.
What goes wrong with mocks
Consider this data structure, used for getting a list of names based on membership
/users/<user id>/name
/rooms/members/<user id>/true
Now let's create a couple simple classes without any real consideration for testing structures, assuming we'll use a mock of Firebase to test them. Note that I see these sorts of errors constantly in the wild; this example is not far fetched or exaggerated (it's rather tame in comparison)
class User {
// Accepts a Firebase snapshot to create the user instance
constructor(snapshot) {
this.id = snapshot.key;
this.name = snapshot.val().name;
}
getName() { return this.name; }
// Load a user based on their id
static load(id) {
return firebase.database().ref('users').child(uid)
.once('value').then(snap => new User(snap));
}
}
class MembersList {
// construct a list of members from a Firebase ref
constructor(memberListRef) {
this.users = [];
// This coupling to the Firebase SDK and the nuances of how realtime is handled will
// make our tests pretty difficult later.
this.ref = memberListRef;
this.ref.on('child_added', this._addUser, this);
}
// Get a list of the member names
// Assume we need this for UI methods that accept an array, so it can't be async
//
// It may not be obvious that we've introduced an odd coupling here, since we
// need to know that this is loaded asynchronously before we can use it.
getNames() {
return this.users.map(user => user.getName());
}
// So this kind of stuff shows up incessantly when we couple Firebase into classes and
// it has a big impact on unit testing (shown below)
ready() {
// note that we can't just use this.ref.once() here, because we have to wait for all the
// individual user objects to be loaded, adding another coupling on the User class's internal design :(
return Promise.all(this.promises);
}
// Asynchronously find the user based on the uid
_addUser(memberSnap) {
let promise = User.load(memberSnap.key).then(user => this.users.push(user));
// note that this weird coupling is needed so that we can know when the list of users is available
this.promises.push(promise);
}
destroy() {
this.ref.off('child_added', this._addUser, this);
}
}
/*****
Okay, now on to the actual unit test for list names.
****/
const mockRef = mockFirebase.database(/** some sort of mock data */).ref();
// Note how much coupling and code (i.e. bugs and timing issues we might introduce) is needed
// to make this work, even with a mock
function testGetNames() {
const memberList = new MemberList(mockRef);
// We need to abstract the correct list of names from the DB, so we need to reconstruct
// the internal queries used by the MemberList and User classes (more opportunities for bugs
// and def. not keeping our unit tests simple)
//
// One important note here is that our test unit has introduced a side effect. It has actually cached the data
// locally from Firebase (assuming our mock works like the real thing; if it doesn't we have other problems)
// and may inadvertently change the timing of async/sync events and therefore the results of the test!
mockRef.child('users').once('value').then(userListSnap => {
const userNamesExpected = [];
userListSnap.forEach(userSnap => userNamesExpected.push(userSnap.val().name));
// Okay, so now we're ready to test our method for getting a list of names
// Note how we can't just test the getNames() method, we also have to rely on .ready()
// here, breaking our effective isolation of a single point of logic.
memberList.ready().then(() => assertEqual(memberList.getNames(), userNamesExpected));
// Another really important note here: We need to call .off() on the Firebase
// listeners, or other test units will fail in weird ways, since callbacks here will continue
// to get invoked when the underlying data changes.
//
// But we'll likely introduce an unexpected bug here. If assertEqual() throws, which many testing
// libs do, then we won't reach this code! Yet another strange, intermittent failure point in our tests
// that will take forever to isolate and fix. This happens frequently; I've been a victim of this bug. :(
memberList.destroy(); // (or maybe something like mockFirebase.cleanUpConnections()
});
}
A better approach through proper encapsulation and TDD
Okay, now let's reverse this by starting with an effective test unit design and see if we can design our classes with less coupling to accommodate.
function testGetNames() {
const userList = new UserList();
userList.add( new User('kato', 'Kato Richardson') );
userList.add( new User('chuck', 'Chuck Norris') );
assertEqual( userList.getNames(), ['Kato Richardson', 'Chuck Norris']);
// Ah, this is looking good! No complexities, no async madness. No chance of bugs in my unit test!
}
/**
Note how our classes will be simpler and better designed just by using a good TDD
*/
class User {
constructor(userId, name) {
this.id = userId;
this.name = name;
}
getName() {
return this.name;
}
}
class UserList {
constructor() {
this.users = [];
}
getNames() {
return this.users.map(user => user.getName());
}
addUser(user) {
this.users.push(user);
}
// note how we don't need .destroy() and .ready() methods here just to know
// when the user list is resolved, yay!
}
// This all looks great and wonderful, and the tests are going to run wonderfully.
// But how do we populate the list from Firebase now?? The answer is an isolated
// service that handles this.
class MemberListManager {
constructor( memberListRef ) {
this.ref = memberListRef;
this.ref.on('child_added', this._addUser, this);
this.userList = new UserList();
}
getUserList() {
return this.userList;
}
_addUser(snap) {
const user = new User(snap.key, snap.val().name);
this.userList.push(user);
}
destroy() {
this.ref.off('child_added', this._addUser, this);
}
}
// But now we need to test MemberListManager, too, right? And wouldn't a mock help here? Possibly. Yes and no.
//
// More importantly, it's just one small service that deals with the async and external libs.
// We don't have to depend on mocking Firebase to do this either. Mocking the parts used in isolation
// is much, much simpler than trying to deal with coupled third party dependencies across classes.
//
// Additionally, it's often better to move third party calls like these
// into end-to-end tests instead of unit tests (since we are actually testing
// across third party libs and not just isolated logic)
//
// For more alternatives to a mock sdk, check out AngularFire.
// We often just used the real Firebase Database with set() or push():
// https://github.com/firebase/angularfire/blob/master/tests/unit/FirebaseObject.spec.js#L804
//
// An rely on spies to stub some fake snapshots or refs with results we want, which is much simpler than
// trying to coax a mock or SDK to create error conditions or specific outputs:
// https://github.com/firebase/angularfire/blob/master/tests/unit/FirebaseObject.spec.js#L344