-
Notifications
You must be signed in to change notification settings - Fork 343
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: support of named profiles #1149
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)
... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
).
}); | ||
assert.instanceOf(profile, FirefoxProfile); | ||
} catch (error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@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
@@ -317,13 +317,47 @@ export function configureProfile( | |||
return Promise.resolve(profile); | |||
} | |||
|
|||
export type getProfileFN = (profileName: string) => Promise<string | void>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit, do you mind to rename this to profileIsDirPath
or isDirPath
?
assert.match( | ||
exception && exception.message, | ||
/Cannot use --keep-profile-changes on a default profile/ | ||
); | ||
}); | ||
|
||
it('rejects to a UsageError when profle is not found', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
small typo: profle
=> profile
() => Promise.resolve(false) | ||
); | ||
let exception; | ||
try { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@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
const promise = testFn(adbUtils); | |
await assert.isRejected(promise, UsageError); | |
await assert.isRejected(promise, /No adb executable has been found/); |
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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'; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit, remove this empty line.
); | ||
}); | ||
|
||
it('returns a finder that looks for a default profile', async () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.