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

New Plugin: Browser Check #2209

Merged
merged 28 commits into from Nov 5, 2021
Merged

New Plugin: Browser Check #2209

merged 28 commits into from Nov 5, 2021

Conversation

jodeleeuw
Copy link
Member

This is currently a work in progress.

This plugin will record a variety of information about the browser in the data and allow the developer to specify a set of inclusion criteria.

@changeset-bot
Copy link

changeset-bot bot commented Oct 7, 2021

🦋 Changeset detected

Latest commit: 4493864

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
@jspsych/plugin-browser-check Major
jspsych Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@jodeleeuw
Copy link
Member Author

One thing that I'm planning to do in this plugin is provide information about the browser through userAgent parsing. I know this is generally frowned upon in favor of feature detection, but I think running scientific experiments is a case where userAgent sniffing is justified. The plugin can do feature detection too.

One issue I'm running into with this is that I'd like to write tests in jest, but the library I'm using for parsing the userAgent doesn't know what to do with jsdom's user agent string. It looks like the userAgent can be set manually in the jest environment, but I'm not sure where to put that in our new set up.

@bjoluc where can we set config options for the testing environment that are specific to the plugin package?

@bjoluc
Copy link
Member

bjoluc commented Oct 7, 2021

I know this is generally frowned upon in favor of feature detection, but I think running scientific experiments is a case where userAgent sniffing is justified. The plugin can do feature detection too.

Good one, I've been manually doing this a lot in my experiments. I've also often logged the result of vsync-estimate in an initial trial. Worth including something like that as well?

@bjoluc where can we set config options for the testing environment that are specific to the plugin package?

Simply in jest.config.cjs: makePackageConfig() returns a standard jest config object that you can modify arbitrarily before exporting it (e.g. myConfig.testEnvironmentOptions.userAgent = "foo";). But if you'd like to set the user agent on a describe-block level (or even multiple times in individual tests), something like

beforeAll(() => {
  jest.spyOn(navigator, "userAgent", "get").mockReturnValue("foo");
});

may be a good alternative.

@becky-gilbert becky-gilbert added this to the 7.1 milestone Oct 8, 2021
@becky-gilbert becky-gilbert added this to To do - would be nice in MOSS milestone 3 via automation Oct 8, 2021
@becky-gilbert becky-gilbert moved this from To do - would be nice to In progress in MOSS milestone 3 Oct 8, 2021
@jodeleeuw
Copy link
Member Author

Note to self: add mic check, webcam check (just in terms of whether they can be accessed at all).

@becky-gilbert
Copy link
Collaborator

Linking to this discussion post by @amunn about browser detection, in case it's useful: #1984

if (trial.inclusion_function(Object.fromEntries(feature_data))) {
end_trial();
} else {
this.jsPsych.endExperiment(trial.exclusion_message);
Copy link
Member Author

Choose a reason for hiding this comment

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

On second thought, don't do it this way.

Want to record the exclusion in the data before ending the experiment. Will need to think about how to do that since it doesn't quite mesh with plugin norms.

Copy link
Member

Choose a reason for hiding this comment

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

Should we add a second parameter (e.g. endExperiment) to finishTrial that ends the experiment after finishing the trial?

Copy link
Member Author

Choose a reason for hiding this comment

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

This solution is growing on me. Do you think it's better to adjust finishTrial like this:

finishTrial(data = {}, end_experiment = false, end_message = '') {

or better to change endExperiment like this

endExperiment(end_message: string, data = {})

Right now endExperiment actually sets a flag on the timeline and invokes finishTrial. Then when the timeline goes to the next trial it checks the flag and the experiment ends. So the second modification is probably easier to make in the current implementation.

Copy link
Member Author

Choose a reason for hiding this comment

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

And in case it isn't clear, my intention above is that endExperiment would then be able to optionally save data in the final trial object.

TBH, the current implementation of endExperiment is probably not ideal and should change with some larger overhaul of the timeline code.

Copy link
Member

Choose a reason for hiding this comment

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

Funny, I was initially writing a list that contained exactly these two items but then decided to drop the second option entirely. But now that you bring it up again, I'm not too sure anymore. I think what made me discard it was that I didn't know that endExperiment() calls finishTrial() internally. The more I think about it now, the more I like the extension to endExperiment(). It is explicit and easy to maintain during future refactoring. Maybe finishTrial will also be replaced with returning from an async trial() function one day, and we'd be lucky to have chosen endExperiment(end_message: string, data = {}) then!

@jodeleeuw jodeleeuw self-assigned this Oct 13, 2021
@jodeleeuw
Copy link
Member Author

@bjoluc I'm not sure exactly why the changesets for this release are bumping all the plugins by a major version. I saw this problem popped up in your other PR recently and you fixed it. What should I do here?

@bjoluc
Copy link
Member

bjoluc commented Oct 21, 2021

@jodeleeuw That's because changesets updates peer dependencies by default, and we're bumping the jspsych package. There's a flag in the changesets config to disable this, which I added on main, so merging main into this should fix it.

requestAnimationFrame(start);
});
},
webcam: () => {
Copy link
Member Author

Choose a reason for hiding this comment

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

I'm not sure this is the right way to handle checking for a webcam and checking for a microphone. Some sources indicate that enumerateDevices() will provide information about hardware capabilities without requesting permission from the user. Other sources make it sound like permission is needed. When I test locally through file:// or a local server I don't get a permission prompt. However, I don't have easy access to devices without a mic or webcam so I can't confirm that this actually works as intended yet.

Copy link
Collaborator

Choose a reason for hiding this comment

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

In case it's helpful, here's how I did the mic detection in the audio-response plugin: https://github.com/becky-gilbert/jsPsych/blob/6fd6a536d04e9fd032df00cacca149e432c00de6/plugins/jspsych-image-audio-response.js#L205-L220
We've been using this plugin for a while and haven't had any problems with it. But this plugin always requests mic permission from the user (I think via navigator.mediaDevices.getUserMedia here), and I can't remember if it still requests permission when a mic isn't found.
Let me know if I can help by testing on a device without mic/webcam access.

Copy link
Contributor

Choose a reason for hiding this comment

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

I'd imagine there's significant variation between browsers and individual configurations. I think testing on chrome and firefox on desktop and mobile will be sufficient for most cases. Combined they account for most browsers and ime firefox tends towards more restrictions on web resources for security and privacy reasons.

As for testing on devices without mics and webcams, I'd suggest an ubuntu or debian virtual machine and deny it access to host hardware. It should behave like a machine without a mic or webcam.

Copy link
Member Author

Choose a reason for hiding this comment

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

I've tested Chome and Firefox. Thanks for the suggestion about using a virtual machine. I'll try to get to that soon.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I think we also need to test on MacOS/iOS and Safari (2nd most used after Chrome), as these seem more likely to not support certain features (or they do but implement them differently 😫). I have an iPad I can test on, if that would help?

@bjoluc
Copy link
Member

bjoluc commented Oct 21, 2021

@jodeleeuw Is your local main branch behind origin?

@jodeleeuw
Copy link
Member Author

I was just being lazy, but I've updated and merged. Not sure why the .changeset folder wasn't pulled from the remote version.

@jodeleeuw
Copy link
Member Author

I think this one is basically ready to go.

Reviews welcome (@becky-gilbert @bjoluc @chrisbrickhouse or whoever!)

Copy link
Contributor

@chrisbrickhouse chrisbrickhouse left a comment

Choose a reason for hiding this comment

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

LGTM; one actionable suggestion and two comments to consider for future development

docs/overview/exclude-browser.md Show resolved Hide resolved
| minimum_width | int | 0 | If `allow_window_resize` is `true`, then this is the minimum width of the window (in pixels) that must be met before continuing.
| window_resize_message | string | see description | The message that will be displayed during the interactive resize when `allow_window_resize` is `true` and the window is too small. If the message contains HTML elements with the special IDs `browser-check-min-width`, `browser-check-min-height`, `browser-check-actual-height`, and/or `browser-check-actual-width`, then the contents of those elements will be dynamically updated to reflect the `minimum_width`, `minimum_height` and measured width and height of the browser. The default message is: `<p>Your browser window is too small to complete this experiment. Please maximize the size of your browser window. If your browser window is already maximized, you will not be able to complete this experiment.</p><p>The minimum window width is <span id="browser-check-min-width"></span> px.</p><p>Your current window width is <span id="browser-check-actual-width"></span> px.</p><p>The minimum window height is <span id="browser-check-min-height"></span> px.</p><p>Your current window height is <span id="browser-check-actual-height"></span> px.</p>`.
resize_fail_button_text | string | `"I cannot make the window any larger"` | During the interactive resize, a button with this text will be displayed below the `window_resize_message` for the participant to click if the window cannot meet the minimum size needed. When the button is clicked, the experiment will end and `exclusion_message` will be displayed.
inclusion_function | function | `() => { return true; }` | A function that evaluates to `true` if the browser meets all of the inclusion criteria for the experiment, and `false` otherwise. The first argument to the function will be an object containing key value pairs with the measured features of the browser. The keys will be the same as those listed in `features`. See example below.
Copy link
Contributor

Choose a reason for hiding this comment

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

This is fine for the initial release, but in some later version I would recommend adding parameters which automate common inclusion criteria like mobile, operating system, and device restrictions. Callbacks are great and this opens the door to a lot of customization, but compared to the old interface it adds complexity that might give users some trouble when migrating.

Copy link
Member Author

Choose a reason for hiding this comment

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

I can see the case for this. I wanted something that was both simple in terms of parameter bloat and also flexible, so this seemed like the simplest path forward for now. I wouldn't be opposed to adding parameters like allowed_browsers or whatever in a future release.

requestAnimationFrame(start);
});
},
webcam: () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd imagine there's significant variation between browsers and individual configurations. I think testing on chrome and firefox on desktop and mobile will be sufficient for most cases. Combined they account for most browsers and ime firefox tends towards more restrictions on web resources for security and privacy reasons.

As for testing on devices without mics and webcams, I'd suggest an ubuntu or debian virtual machine and deny it access to host hardware. It should behave like a machine without a mic or webcam.

@becky-gilbert
Copy link
Collaborator

Sorry, I should've said that I'm happy to make any of the suggested changes myself - just let me know!

@jodeleeuw jodeleeuw merged commit 2922bc5 into main Nov 5, 2021
MOSS milestone 3 automation moved this from In progress to Done Nov 5, 2021
@jodeleeuw jodeleeuw deleted the plugin-exclusions branch November 5, 2021 22:28
@github-actions github-actions bot mentioned this pull request Nov 5, 2021
@github-actions github-actions bot mentioned this pull request Nov 30, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
No open projects
Development

Successfully merging this pull request may close these issues.

Parameterize browser exclusion messages for better language support
4 participants