Skip to content
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

fix: issue 1909: call chromiumLaunch with parameter userDataDir instead of --user-data-dir and set of --profile-directory #1920

Merged
merged 19 commits into from
Jul 20, 2020

Conversation

pearnaly
Copy link
Contributor

@pearnaly pearnaly commented Jun 4, 2020

fix: issue 1909: call chromiumLaunch with parameter userDataDir instead of --user-data-dir and set of --profile-directory
See discussion #1909
Thank you @Rob--W

@coveralls
Copy link

coveralls commented Jun 4, 2020

Coverage Status

Coverage remained the same at 100.0% when pulling e344e6c on pearnaly:pearnaly_master into e3f4d7f on mozilla:master.

Copy link
Member

@Rob--W Rob--W left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comment below, and please undo the unrelated formatting changes from the patch.

chromeFlags.push(`--user-data-dir=${this.params.chromiumProfile}`);
const pathParts = path.parse(this.params.chromiumProfile);
const profileDir = pathParts.base;
chromeFlags.push(`--profile-directory=${profileDir}`);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does not implement what I described at #1909 (comment)

According to your comment at #1909 (comment), the wrong --user-data-dir is being used. To fix that, the directory should be passed via userDataDir instead of chromeFlags.

The --profile-directory flag is not necessary. Anyone who cares can use the method that I described in #1909 (comment)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes exactly! I changed it this way to have it fully compatible with --firefox-profile, so that the user provides the full path to the profile, and not the folder containing the profiles. This way the user also doesn't have to deal with the undocumented --profile-directory flag. The documentation of --chromium-profile ("Path to a custom Chromium profile") remains correct this way.

If the default --user-data-dir is correct, it is also possible to provide only the name of the profile, like for Firefox.

Do you have concerns?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To simplify @Rob--W , --chromium-profile = --user-data-dir + --profile-directory

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the most common scenarios (using a temporary directory, or even a copy of a single-user profile), just passing --user-data-dir is sufficient. I don't want to complicate web-ext with --profile-directory, because this is a special case AND already possible without special support from web-ext (i.e. the --arg flag).

Copy link
Contributor Author

@pearnaly pearnaly Jun 6, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay for me @Rob--W, but it will mean:

  • change the documentation of --chromium-profile from "Path to a custom Chromium profile." to "Path to the directory containing the profiles" (or equivalent). Can you organize this change?
  • loose the compatibility with --firefox-profile

I don't agree with it, but if you insist I do the change since I would like to have it finally somehow working.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are two bugs here:

  • According to your comment at Unable to use custom chromium profile. --chromium-profile flag is not getting respected. #1909 (comment) , the --chromium-profile flag fails to match the specified directory on Windows (even though it works fine on Linux). This is a bug that we should certainly fix.
  • Now in this PR and thread, you are advocating for support of a specific "User Account" within a given user data directory, via the --profile-directory flag. This is more work than needed to fix the previous bug, and I didn't feel the need to delay the fix of the previous bug for this new feature.

I'm not adamant against supporting User Accounts, but note that it will take some effort. I'll post a separate comment with lots of more details, I hope that it helps.

@Rob--W
Copy link
Member

Rob--W commented Jun 6, 2020

Since you appear to be very interested in supporting Chrome's "Profiles" other than Default, here are a few notes:

  1. --user-data-dir is the primary mechanism to create separate data directories for running parallel instances of the Chrome browser.
  2. --profile-directory cannot live on its own without --user-data-dir (when unspecified, some default user data directory is chosen).
  3. When --profile-directory is used, the state of the user data directory changes (the default profile then changes to the selected profile directory). Consequently, using a profile within the default user data directory may cause unexpected behavior. For example, when an external application opens a link, a new tab will be opened in the (last opened) testing profile rather than the actual chosen (default) profile from the user.
  4. web-ext run currently supports the selection of Firefox profiles by name rather than path. This is not documented nor supported in web-ext run with Chrome. To have the same feature for Chrome (using the system's default user data dir), we would have to detect the location of the default user data dir, and pass --profile-directory if needed. There is ambiguity: does --chromium-profile "Profile 1" mean "Profile 1" of the default user data dir, or "Profile 1" in the current directory? Chrome will create the user data dir/profile if it does not exist. This ambiguity is bad and is destructive.
  5. The official chrome-launcher package does not support --profile-directory ( How to automate non-default profile dynamically? GoogleChrome/chrome-launcher#172 / Directory "\Default" is added to userDataDir  GoogleChrome/chrome-launcher#190). Once they figure out a standard way to select a specific directory (e.g. even without --user-data-dir support), then it would be easier to support the same here in web-ext.
  6. Changing the meaning of --chromium-profile is a backwards-incompatible change for existing users of web-ext. With the proposed change, Chrome is going to create extra directories / files in the parent directory of the --chromium-profile path, which would be very unexpected and therefore undesirable.
  7. If you are interested in introducing support for combing --user-data-dir and --profile-directory, make sure that there is no ambiguity whatsoever before using the parent directory. You need to make sure that the parent directory is really the user data dir, AND that the given directory is NOT a user data dir (AND maybe even that the given directory is a profile directory). If unclear, treat the given path as a user-data-dir.

(edited to use numbers instead of bullet points)

@pearnaly
Copy link
Contributor Author

pearnaly commented Jun 9, 2020

  1. Hi @Rob--W, okay I see now the conflict between the two chrome mechanisms for profiles. both directories levels can be considered as "profiles", which is confusing. According to the chromium docu, the naming is "profile"/"account", but according to the command line params names and to the chrome://version/ page it is "data dir"/"profile".

  2. Yes you can provide only --profile-directory (on my Windows at least). in this case Chrome takes the default user-data-dir (like for Firefox).

  3. Yes it is true, using --profile-directory changes the default profil in user-data-dir. But is is the same behavious if you provide it using this fix or passing the parameters as you proposed. At least, if the users uses profiles in his everyday user-data-dir to debug, it is his problem (actually wanted) that these data are modified (what I were looking for). Users who don't want it will provide a path different from the defaut one or won't use it to use the temp profile.

  4. It is fortunately not necessary to detect the position of the default used-data-dir. We just have to not provide it and chrome will use the default. But you are right the documentation has to be anyway extended, either to explain that --chromium-profile has the meaning of user-data-dir and profile-directory is not supported by the lib and can be passe manually OR that --chromium-profile has the meaning of the profile path according to chrome://version/ and that (new!) only the profile directory can be provided, as for Firefox.

  5. the chrome launcher doesn't support this flag, and it is probably okay since we can pass it ourselves. But if they start to support it yes we will have to make sure they don't try to set some colliding default.

  6. It is indeed the critical point... I was pushing to this change since I considered as a bug that the docu named chromium-profile what would be the profiles container according to the chrome://version/ naming schema. Is actually also the way @deepTier5 understood it in his comment. I think most of the user will instinctively want to provide the full path. Since the chromium support is quite new and the profile flag probably few used (?) and has this windows bug anyway, it could be our last chance to change (fix) its meaning, but I understand your concern and leave it to you.

  7. No I think double support in the same param is too complicated and even unpossible to tell apart. It will be either/or. Linux users currently using it would have to add /Default. But it is more clear on the long term.

What should I do?

@Rob--W
Copy link
Member

Rob--W commented Jun 9, 2020

2. Yes you can provide only `--profile-directory` (on my Windows at least). in this case Chrome takes the default `user-data-dir` (like for Firefox).

3. Yes it is true, using `--profile-directory` changes the default profil in `user-data-dir`. But is is the same behavious if you provide it using this fix or passing the parameters [as you proposed](https://github.com/mozilla/web-ext/issues/1909#issuecomment-636385559). At least, if the users uses profiles in his everyday `user-data-dir` to debug, it is his problem (actually wanted) that these data are modified (what I were looking for). Users who don't want it will provide a path different from the defaut one or won't use it to use the temp profile.

4. It is fortunately not necessary to detect the position of the default used-data-dir. We just have to not provide it and chrome will use the default.

web-ext intentionally tries to avoid damaging the user profile unless explicitly requested.
When a Firefox profile name is given instead of a path, web-ext run --keep-profile-changes -p [name here] will look up the location and copy it:

/*
* Copies an existing Firefox profile and creates a new temporary profile.
* The new profile will be configured with some preferences required to
* activate extension development.
*
* It resolves with the new profile object.
*
* The temporary profile will be deleted when the system process exits.
*
* The existing profile can be specified as a directory path or a name of
* one that exists in the current user's Firefox directory.
*/
export async function copyProfile(
profileDirectory: string,
{
app,
configureThisProfile = configureProfile,
copyFromUserProfile = defaultUserProfileCopier,
customPrefs = {},
}: CopyProfileOptions = {},
): Promise<FirefoxProfile> {
const copy = promisify(FirefoxProfile.copy);
const copyByName = promisify(copyFromUserProfile);
try {
const dirExists = await isDirectory(profileDirectory);
let profile;
if (dirExists) {
log.debug(`Copying profile directory from "${profileDirectory}"`);
profile = await copy({profileDirectory});
} else {
log.debug(`Assuming ${profileDirectory} is a named profile`);
profile = await copyByName({name: profileDirectory});
}
return configureThisProfile(profile, {app, customPrefs});
} catch (error) {
throw new WebExtError(
`Could not copy Firefox profile from ${profileDirectory}: ${error}`);
}
}

In order to support this for Chrome, there should also be something similar.

5. the chrome launcher doesn't support this flag, and it is probably okay since we can pass it ourselves. But if they start to support it yes we will have to make sure they don't try to set some colliding default.

I mentioned this not only because of potential compat concerns, but also to point out that the odds of having proper support for this feature increases if the official upstream project properly supports the feature.

6. It is indeed the critical point... I was pushing to this change since I considered as a bug that the docu named `chromium-profile` what would be the profiles container according to the `chrome://version/` naming schema. Is actually also the way @deepTier5 understood it in [his comment](https://github.com/mozilla/web-ext/issues/1909#issuecomment-636323174). I think most of the user will instinctively want to provide the full path. Since the chromium support is quite new and the profile flag probably few used (?) and has this windows bug anyway, it could be our last chance to change (fix) its meaning, but I understand your concern and leave it to you.

7. No I think double support in the same param is too complicated and even unpossible to tell apart. It will be either/or. Linux users currently using it would have to add `/Default`. But it is more clear on the long term.

What should I do?

The default behavior should be to treat the flag as --user-data-dir. I see your points on the user taking the directory location from chrome://version (in the specific scenario of re-using an existing profile). That specific case could be supported by detecting the directories (there are some files/directories that are specific to user-data-dir / profile-dir, it is not as impossible as it seems).

I'll have to discuss this with other project members, but I currently believe that the path forwards is 7 and/or awaiting official upstream support (in chrome-launcher).

@Rob--W
Copy link
Member

Rob--W commented Jun 11, 2020

I've discussed this with @rpl and we have agreed that the following approach will provide the requested functionality with reasonable compatibility:

  1. Only one parameter, --chromium-profile.
  2. If the directory exists, check if it is certainly a profile directory and NOT a user-data-dir. If this cannot be decided, keep the current behavior (treat as user-data-dir) (end of steps).
  3. If --keep-profile-changes is used, check if the parent directory is a user-data-dir. If it is, then use the given profile. If it is not, then we should not use the parent directory as the user-data-dir (whether to exit with an error or treating it as --user-data-dir is up to you).
  4. Otherwise (by default, if --keep-profile-changes is not set), create a new directory (to use as user-data-dir) and put the non-Default profile directory in it (and pass it to --profile-directory).

Note that this approach favors the use of --user-data-dir when in doubt. This is reasonable because users can always use --arg=" --profile-directory=Profile 1" if wanted.

To detect whether a directory looks like a user-data-dir: Check if the "Local State" file and "Default" directory exist.
To detect whether a directory looks like a profile-directory: Check if the given directory exists and the "Secure Preferences" file exist.
I looked in a new profile and in a 9-year-old Chromium profile, and confirmed that checking the existence of those files doesn't cause false positives (at first --user-data-dir contained the actual profile. Based on timestamps it seems that somewhere in 2011 the profile moved to the Default subdirectory).

@pearnaly
Copy link
Contributor Author

It's fine for me and I started.
I use sofar fs-extra for the files operations
I will have a hard time with the tests, I will try with mock-fs...

…recognize if it is meant as --user-data-dir or --user-data-dir/--profile-directory
Copy link
Member

@Rob--W Rob--W left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One big function with multiple nested if-else blocks is not very readable. To increase the readability and maintainability, I suggest to move the new logic to a separate method. That avoids the need for bloating the setupInstance function, and makes it easier to specifically create unit tests without too much boilerplate.

Specifically, untangle the current logic and split it in three parts:

  1. Find out user-data-dir and profile-directory
  2. Copy directory locations if needed.
  3. Pass userDataDir / chromeFlags if needed (this is a relatively small part that does belong to setupInstance).

This makes the code easier to follow, and also easier to test.

On the tests: There are three tests, for the following case:

  • path is a profile-directory, with keepProfileChanges
  • path is a profile-directory, but parent is not a user-data-dir, with keepProfileChanges - exit out of caution.
  • path is a profile-directory, parent is not a user-data-dir, without keepProfileChanges.

This only covers some common/desired situations, but is not complete, as there are several conditions that are not covered. Since we are working with user data, I want to make sure that all relevant states are fully covered by unit tests (we don't want to accidentally damage real user profiles). I expect that tests will be smaller and easier to write after splitting the logic in three parts as I suggested.

.eslintrc Outdated Show resolved Hide resolved
default as ChromeLauncher,
launch as defaultChromiumLaunch,
LaunchedChrome, Launcher,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will need to change due to the chromium-launcher update from #1931.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I reverted this import

const localStateCandidate = path.join(dirCandidate, 'Local State');
const defaultCandidate = path.join(dirCandidate, 'Default');
// Local State and Default are typical for the user-data-dir
return fs.pathExistsSync(localStateCandidate)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we're trying hard to minimize the risk of misidentification, let's verify that the given paths are really directories / files. We have util.isDirectory and util.isFile for that: https://github.com/mozilla/web-ext/blob/588550008c1d0381736f435956095255200707d5/src/util/is-directory.js

For files we also have file.fileExists - https://github.com/mozilla/web-ext/blob/588550008c1d0381736f435956095255200707d5/src/util/file-exists.js

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I switched to isDirectory. The use of fs-extra is now reduced, and we could reevaluate if it is still justified.

src/extension-runners/chromium.js Outdated Show resolved Hide resolved
&& !isUserDataDir(chromiumProfile);

if (isProfileDirAndNotUserData) {
const pathParts = path.parse(chromiumProfile);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's use the following:

const { dir: parentDir, base: profileName } = path.parse(chromiumProfile);

The parentDir variable makes it very obvious that we're working with parent directories, which removes the need for the // now check if the parent dir is a user-data-dir comment below.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

like it

// the user provided an existing profile directory but doesn't want
// the changes to be kept. we copy this directory to a temporary
// user data dir.
userDataDir = Launcher.prototype.makeTmpDir();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't use undocumented internal implementation details from an external library.

The web-ext project itself already has an utility to create temporary directories here: https://github.com/mozilla/web-ext/blob/9315b781ec73eead946c07551a24a413ddb1338a/src/util/temp-dir.js

An example of usage is here: https://github.com/mozilla/web-ext/tree/9315b781ec73eead946c07551a24a413ddb1338a/src/extension-runners

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I switched to TempDir, but with TempDir you cannot just get a temp path without creating the directory itself. I have to delete it and rewrite it after, which is quite ugly. My method would like to just get a path, and create the actual dir separately...

src/extension-runners/chromium.js Outdated Show resolved Hide resolved
}
chromeFlags.push(`--profile-directory='${profileDirName}'`);
} else {
userDataDir = chromiumProfile;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prepend comment to explain what chromiumProfile means at this point, so that the reader doesn't have to scroll all the way up.

And this doesn't properly handle the --keep-profile-changes case - see my top-level review comment for suggestions on improving this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made an early return statement for it, so it should be clear now

tests/unit/test-extension-runners/test.chromium.js Outdated Show resolved Hide resolved
@@ -65,6 +65,7 @@
"es6-error": "4.1.1",
"event-to-promise": "0.8.0",
"firefox-profile": "1.3.1",
"fs-extra": "9.0.1",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not keen on adding new dependencies because it generally results in more maintenance load (e.g. having to publish new versions when a security issue is found in the dependency). But since we're already indirectly depending on this module, I'm fine with it.

In a follow-up issue/PR we can probably replace mz with this, since mz's functionality overlaps with fs-extra.

…recognize if it is meant as --user-data-dir or --user-data-dir/--profile-directory
@pearnaly
Copy link
Contributor Author

I think my 3 tests test all the pathes. Do you see another test case?

@pearnaly
Copy link
Contributor Author

I have an issue with the error test. npm run test is passing on my computer but failing on the CI pipeline. I will have a look at it again tomorrow

Copy link
Member

@Rob--W Rob--W left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think my 3 tests test all the pathes. Do you see another test case?

Yes, lots of cases are missing. The purpose of splitting getProfilePaths (renamed from getProfileParts - see below) is to make it easier to unit test every possible case. There are bugs in the implementation, and before fixing the bugs that I pointed out, I suggest that you first write some unit tests with expected behavior, and then run the tests with your current (buggy) implementation. Those tests are then expected to fail. After fixing the bugs, the tests are expected to pass.

Think of all possible ways that getProfilePaths can fail, and for each scenario that you can come up with, create a temporary directory with that filesystem layout. Think of missing files/directories, files being directories, directories being files, a file/directory being a symlink to a file/directory, and generally anything that can cause confusion on whether the given path refers to a user-data-dir or a profile-directory.

With thorough unit tests for getProfilePaths, the number of tests with an instance of ChromiumExtensionRunner is reasonably less. The interesting cases are combinations of: userDataDir exists/does not exist, profileDir exists/does not, with and without --keep-profile-changes.

(the next part is for completeness, but please focus on the previous parts before concerning yourself with the next part.)
Finally, after having those unit tests, I also recommend to actually run web-ext run with special characters in the profile directory name, and see if Chrome starts as expected. Think of double quotes and single quotes (apostrophes) (at the start, in the middle and at the end of the directory name). These characters have special meanings in the shell, so make sure to properly escape them, or use webExt as a library and pass the variables directly as the chromiumProfile parameter. If you find that there is a bug (e.g. the path name is incorrectly interpreted), then fix it and add a unit test.

I have an issue with the error test. npm run test is passing on my computer but failing on the CI pipeline.

The tests themselves pass on CI, but the code coverage check fails because there is a regression in code coverage. When I open the coverage file, I can clearly see an uncovered condition, which should really have been covered by a unit test: https://coveralls.io/builds/31520533/source?filename=src%2Fextension-runners%2Fchromium.js#L129

Sometimes the coveralls report is incorrect, but in this case it did correctly point to missing coverage (and incidentally, an actual bug that would have been revealed if there was test coverage).

const localStateCandidate = path.join(dirCandidate, 'Local State');
const defaultCandidate = path.join(dirCandidate, 'Default');
// Local State and Default are typical for the user-data-dir
return await isDirectory(localStateCandidate)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Local State" is a file, not a directory.

And replace the "Candidate" suffix with "Path", everywhere.

const securePreferencesCandidate = path.join(
dirCandidate,
'Secure Preferences');
//Secure Preferences is typical for a profile dir
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"profile dir" -> "profile dir inside a user data dir."

}

static async getProfileParts(params: any): Promise<{
userDataDir: (string | false),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make it a ?string (= string or null / undefined) instead of a union type composed of string + false.

return await isDirectory(securePreferencesCandidate);
}

static async getProfileParts(params: any): Promise<{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rename to getProfilePaths(chromiumProfile: ?string)

params: any is too broad, see below.

// the changes to be kept. we copy this directory to a temporary
// user data dir.
const tmpDir = new TempDir();
await tmpDir.create();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not the responsibility of this function to create a temporary non-existing directory. getProfilePaths should only parse the path and return its best guess. Callers can then use this to copy the directories if needed.

@@ -127,8 +193,21 @@ export class ChromiumExtensionRunner {
chromeFlags.push(...this.params.args);
}

if (this.params.chromiumProfile) {
chromeFlags.push(`--user-data-dir=${this.params.chromiumProfile}`);
const profileParts =
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of profileParts, expand the variables as follows (note with let instead of const because we may update the value - see further).

let { userDataDir, profileDirName } = ...

and then check keepProfileChanges and copy / change the path if needed:

If keepProfileChanges is falsey:

  • Create a new temp dir.
  • If profileDirName is not set, copy userDataDir's content to that temp dir.
  • If profileDirName is set, copy profileDirName (as a directory, not just its content) to that temp dir.
  • Update userDataDir to that temp dir location.

If keepProfileChanges is truthy:

  • If profileDirName is set, but userDataDir is not a user data dir: Report an error. (basically move part of the logic that you've currently put in getProfileParts here)

await ChromiumExtensionRunner.getProfileParts(this.params);

if (profileParts.userDataDir
&& profileParts.profileDirName
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shows a bug: If the user only specified --user-data-dir, then the absence of --keep-profile-changes is not processed.

&& !this.params.keepProfileChanges
) {
// copy profile dir to this temp user data dir.
fs.copySync(this.params.chromiumProfile, path.join(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's use await fs.copy, as there is no real need for this to be synchronous.

The use of fs-extra is now reduced, and we could reevaluate if it is still justified.

There are multiple (dev) dependencies with overlapping functionality. For example, mz and fs-extra both offer promisified fs methods, and fs-extra and copy-dir both offer ways to copy. I think that fs-extra is the best replacement in the long term.

Copy link
Member

@Rob--W Rob--W left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new tests and changes looks good. I only have a few minor comments below.

);
} else {
await fs.copy(path.join(
userDataDir), path.join(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These path.join calls aren't needed, just pass them as-is to fs.copy.

await fs.outputFile(path.join(tmpPath, 'Local State'), '');

assert.isTrue(await fileExists(path.join(tmpPath, 'Local State')));
assert.isTrue(await isDirectory(path.join(tmpPath, 'Default')));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These two assertions are redundant. It is part of the test setup and we assume that the test setup was successful.

' to chrome', async () => withTempDir(
async (tmpDir) => {
const tmpPath = tmpDir.path();
await fs.mkdirsSync(path.join(tmpPath, 'userDataDir/Default'));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mkdirs

})
);

it('does copy the profile and pass user-data-dir and profile-directory' +
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You also have to verify that the relevant directories/files have been copied as expected.

src/extension-runners/chromium.js Show resolved Hide resolved
'user-data-dir directory');
}
} else if (userDataDir) {
// the user provided an existing profile directory but doesn't want
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's unconditionally create a temporary directory, so that we have full control over whether the temp file is deleted (see also my other comment below about #1957).

Currently, due to the way that chrome-launcher is implemented, passing userDataDir: null (instead of userDataDir: undefined) causes the temporary profile to be left behind: https://github.com/GoogleChrome/chrome-launcher/blob/08406b2804c6efba7b602fb276397df00cd0041f/src/chrome-launcher.ts#L373

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment hasn't been addressed yet.

To address it, you need to remove if (userDataDir) { here (to turn this into } else {) and it to the } else { below.

      } else if (userDataDir) { // <-- Here
        await fs.copy(userDataDir, tmpDirPath);


const tmpPath = tmpDir.path();
await fs.mkdirs(path.join(tmpPath, 'Default'));
await fs.mkdirs(path.join(tmpPath, 'Local State'));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add comment that this should be a file (for those unfamiliar with how the profile is supposed to look like, which is almost everyone, I guess).

Also for other tests below.

Copy link
Member

@Rob--W Rob--W left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for all of your changes. Could you make one more change (see below, because that causes chrome-launcher to leave temp files behind)? After that it should be good to land.

The tests time out for some reason, but I guess that it's unrelated to your patch.

'user-data-dir directory');
}
} else if (userDataDir) {
// the user provided an existing profile directory but doesn't want
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment hasn't been addressed yet.

To address it, you need to remove if (userDataDir) { here (to turn this into } else {) and it to the } else { below.

      } else if (userDataDir) { // <-- Here
        await fs.copy(userDataDir, tmpDirPath);

// the changes to be kept. we copy this directory to a temporary
// user data dir.
const tmpDir = new TempDir();
// TODO: Remove tmpDir - https://github.com/mozilla/web-ext/issues/1957
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the confusion, as Luca mentioned at https://github.com/mozilla/web-ext/pull/1920/files#r454511824 , the tmp library already removes the file at exit. So this comment can be removed.

@Rob--W
Copy link
Member

Rob--W commented Jul 20, 2020

I locally checked out the PR to prepare for merging, but noticed that the test failure is actually caused by your changes.

Minimal reproduction:

$ ./node_modules/.bin/mocha tests/unit/test-extension-runners/test.chromium.js -f 'throws an error on profile in invalid user-data-dir'


  util/extension-runners/chromium
    ✓ throws an error on profile in invalid user-data-dir


  1 passing (40ms)

^C
$ # ^ only exited because I explicitly triggered an exit.

In that test, you're not calling runnerInstance.exit();. As a result, the internal server that it had started doesn't exit and keeps the process alive.

Copy link
Member

@Rob--W Rob--W left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's green \o/

@pearnaly
Copy link
Contributor Author

We got it! Thank you @Rob--W for accompanying me in my fist open source PR.

@Rob--W Rob--W merged commit 29798cb into mozilla:master Jul 20, 2020
@Rob--W
Copy link
Member

Rob--W commented Jul 20, 2020

You're welcome, and thanks again for raising attention on issue #1909 and submitting a patch!

@caitmuenster
Copy link

Thanks for the patch, @pearnaly! 🎉 Your contribution has been added to our recognition wiki.

Would you be interested in creating an account on mozillians.org? I'd be happy to vouch for you!

@pearnaly
Copy link
Contributor Author

pearnaly commented Jul 22, 2020 via email

@caitmuenster
Copy link

Excellent! Your profile has been vouched. 🎉

Welcome onboard! We look forward to seeing you around.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants