feat: support of named profiles#1149
Conversation
|
Thanks! Let me know when you've added tests and I can take a look. |
eb8c173 to
4bef586
Compare
|
@kumar303 Hi, sorry it took me so long to get to this. |
4bef586 to
9258442
Compare
9258442 to
cada5be
Compare
|
@saintsebastian To test Inside the returned function there is also a call to e.g. the test case could be something like the following: // A fake Finder instance.
const fakeFinder = {
readProfiles: sinon.spy(() => { ... }),
getPath: sinon.spy(() => { ... }),
}
// A fake Finder constructor which returns the fake Finder instance.
const FakeFinder = sinon.spy(() => fakeFinder);
// Add a fake localUserDirectory static method to the fake Finder constructor.
FakeFinder.locateUserDirectory = sinon.spy(() => "/tmp/fakeUserDir";
// Define the fake FxProfile to inject as the fake dependency into defaultCreateProfileFinder.
const fakeFxProfile = {Finder: sinon.spy(() => fakeFinder)};
// Create a fakeFS object which provides a fake `stat` method (wrapped by a sinon spy).
const fakeFs = ...
const profileFinder = defaultCreateProfileFinder({FxProfile: fakeFxProfile, fs: fakeFs});
const res = await profileFinder("aFakeProfileName");
sinon.assert.calledOnce(FakeFinder.locateUserDirectory)
... |
rpl
left a comment
There was a problem hiding this comment.
Hey @saintsebastian,
Thank you so much for coming back to this pull request!
Besides the review comments below, another remaining issue is to reach the full code coverage of the changes and make coveralls happy again :-)
Based on the last coveralls report, it seems that we are just missing a couple of more branches inside the "finder" function that defaultCreateProfileFinder returns:
https://coveralls.io/builds/18638522/source?filename=src%2Ffirefox%2Findex.js#L345
The other test cases looks to be already in the right direction, and you just need to add a couple more, using the same approach that is currently used in the ones that you already have, e.g. something like the following should be able to cover the "resolves to undefined when no profiles.ini file has been found" scenario:
it('returns a finder that resolves undefined for no profiles.ini',
async () => {
const FxProfile = {
Finder: sinon.spy(() => () => Promise.resolve({})),
};
// We can define here the fake readProfiles method that rejects with a ENOENT error (and then in a separate test we can do the same for an error that hasn't the ENOENT code)
const fakeReadProfiles = sinon.spy(() => {
return Promise.reject(
new ErrorWithCode('ENOENT', 'fake ENOENT error'));
});
const fakeGetPath = sinon.spy(() => Promise.resolve());
FxProfile.Finder.prototype.readProfiles = fakeReadProfiles;
FxProfile.Finder.prototype.getPath = fakeGetPath;
FxProfile.Finder.prototype.profiles = [{
Name: 'someName',
}];
const userDirectoryPath = '/non/existent/path';
const getter = firefox.defaultCreateProfileFinder({
userDirectoryPath,
FxProfile,
});
const res = await getter('someName');
assert.equal(
res,
undefined,
'Got an undefined result when the profiles.ini file does not exist');
sinon.assert.called(fakeReadProfiles);
sinon.assert.notCalled(fakeGetPath);
});We should definitely test also the scenario in which "readProfiles" raises an unexpected error (one that isn't an ENOENT).
| createProfileFinder, | ||
| }); | ||
| assert.instanceOf(profile, FirefoxProfile); | ||
| } catch (error) { |
There was a problem hiding this comment.
This try...catch is just re-throwing the caught error, it doesn't seem to be needed (same for the other ones in the other new tests below).
src/firefox/index.js
Outdated
| return await getPath(profileName); | ||
| } | ||
| } catch (error) { | ||
| if (isErrorWithCode('ENOENT', error)) { |
There was a problem hiding this comment.
Do you mind to reverse this if and remove the else block?
if (!isErrorWithCode('ENOENT', error)) {
throw error;
}
log.warn('...');Also, I'm thinking that we should tweak the warning message a bit, e.g.
log.warn('Unable to find Firefox profiles.ini');07e7926 to
f94b9e5
Compare
|
@rpl thanks for the review, i think it really helped! what do you think of those tests? |
rpl
left a comment
There was a problem hiding this comment.
@saintsebastian Thanks again for working on this!
I'd like to also test this manually a bit (to double-check that we didn't missed some scenarios with the unit tests), and I'm tempted to say that it would be nice to have some tests that don't mock the Finder class defined in the npm dependency "firefox-profile", especially because we are unit testing it with a number of sinon spies and so we are making a number of assumptions about how the Finder class defined in the npm dependency "firefox-profile" works, and some tests without a mocked Finder class could help us to caught issues with changes in the npm dependency.
If I recall correctly the purpose of the userDirectoryPath (which is never used by the firefox.useProfile method) is exactly to allow us to test some scenarios without a mock of the Finder class (by writing a test that creates a "profiles.ini" file into a temp dir and check that the real Finder class does what it should).
In the meantime, I've collected some additional review comments that I collected by re-looking at the whole change.
src/firefox/index.js
Outdated
| return Promise.resolve(profile); | ||
| } | ||
|
|
||
| export type getProfileFN = (profileName: string) => Promise<string | void>; |
There was a problem hiding this comment.
@saintsebastian sorry I didn't notice this before, other function types names are suffixed using Fn instead of FN, do you mind to rename this one into getProfileFn?
src/firefox/index.js
Outdated
| let destinationDirectory; | ||
| const getProfilePath = createProfileFinder(); | ||
|
|
||
| const profileIsAPath = await isDirectory(profilePath); |
There was a problem hiding this comment.
Nit, do you mind to rename this to profileIsDirPath or isDirPath?
| ); | ||
| }); | ||
|
|
||
| it('rejects to a UsageError when profle is not found', |
| () => Promise.resolve(false) | ||
| ); | ||
| let exception; | ||
| try { |
There was a problem hiding this comment.
@saintsebastian do you mind to rewrite this assertion to use the assert.isRejected instead of the try...catch? as we do here:
web-ext/tests/unit/test-util/test.adb.js
Lines 106 to 109 in 1cd88a1
It should require just a small tweak on these few lines.
| it('resolves to a FirefoxProfile instance', () => withBaseProfile( | ||
| (baseProfile) => { | ||
| async (baseProfile) => { | ||
| const app = 'fennec'; |
There was a problem hiding this comment.
why `const app = 'fennec' is being added to these tests?
it shouldn't be needed.
|
|
||
| it('creates a finder', async () => { | ||
| const FxProfile = { | ||
| Finder: sinon.spy(() => () => Promise.resolve()), |
There was a problem hiding this comment.
Finder is a constructor function and so it seems that its spy should be something like:
const FxProfile = {
Finder: sinon.spy(function () {
return {};
}),
};| sinon.assert.called(FxProfile.Finder); | ||
| sinon.assert.calledWith( | ||
| FxProfile.Finder, | ||
| sinon.notOk, |
There was a problem hiding this comment.
This sinon.assert.calledwith(FxProfile.Finder, sinon.notOk); assertion doesn't seem right (or it is not very readable), shouldn't it be something like the following?
firefox.defaultCreateProfileFinder({FxProfile});
sinon.assert.calledWith(FxProfile.Finder, sinon.match(undefined));| Finder: sinon.spy(() => () => Promise.resolve()), | ||
| }; | ||
| const userDirectoryPath = '/non/existent/path'; | ||
|
|
| ); | ||
| }); | ||
|
|
||
| it('returns a finder that looks for a default profile', async () => { |
There was a problem hiding this comment.
uhm... I think that the description of this test should probably be 'returns a finder that resolves a profile name' or something like that (instead of 'looks for a default profile').
| sinon.assert.notCalled(fakeGetPath); | ||
| }); | ||
|
|
||
| it('returns a finder that throws other errors', |
There was a problem hiding this comment.
Nit, s/other errors/unexpected errors/
1ab754d to
3df74fa
Compare
3df74fa to
5ffead1
Compare
|
@rpl thanks for the thorough as usual review! I think this is pretty close to done |
rpl
left a comment
There was a problem hiding this comment.
Thanks @saintsebastian!
This looks great to me, I also tried it a bit locally and it is working pretty well.
Follows a couple of small nits related to the tests, it should be just a matter of applying a couple of small changes, and then we can merge this PR \o/
|
@saintsebastian I forgot to mention: don't worry about the failure on appveyor, it is an unrelated (and pretty annoying to be honest) intermittent that is happening (too often :-( ) on the windows CI service. |
Fixes #932
I shamelessly reused bits of code from previous attempt to solve this issue.