Skip to content
This repository has been archived by the owner on Apr 27, 2023. It is now read-only.

Support studyEnded #730

Merged
merged 6 commits into from
Oct 15, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Unreleased changes

[Full changelog](https://github.com/mozilla-rally/core-addon/compare/v1.3.3...master)
* [#730](https://github.com/mozilla-rally/rally-core-addon/pull/730): Add support for ending studies with `studyEnded` remote settings.
* [#682](https://github.com/mozilla-rally/rally-core-addon/pull/682): Add latest Stanford IRB changes.

# v1.3.3 (2021-06-14)
Expand Down
20 changes: 13 additions & 7 deletions core-addon/Core.js
Original file line number Diff line number Diff line change
Expand Up @@ -484,24 +484,30 @@ export default class Core {
new Error(`Core._unenrollStudy - Unknown study ${studyAddonId}`));
}

const knownStudy = knownStudies.find(s => s.addonId == studyAddonId);
if (!("schemaNamespace" in knownStudy)) {
return Promise.reject(
new Error(`Core._enrollStudy - No schema namespace specified in remote settings for ${studyAddonId}`));
}

// Attempt to send an uninstall message, but move on if the
// delivery fails: studies will not be able to send anything
// without the Core Add-on anyway. Moreover, they might have been
// removed manually from the addons pages (e.g. about:addons).
try {
await this._sendMessageToStudy(studyAddonId, "uninstall", {});
} catch (e) {
console.error(`Core._unenroll - Unable to uninstall ${studyAddonId}`, e);
console.error(`Core._unenrollStudy - Unable to uninstall ${studyAddonId}`, e);
}

await this._storage.removeActivatedStudy(studyAddonId);

const endedStudies = knownStudies.filter(s => s.studyEnded);
if (endedStudies.map(s => s.addonId).includes(studyAddonId)) {
return Promise.reject(
new Error(`Core._unenrollStudy - Unenrolling study which has ended, not sending deletion pings for ${studyAddonId}`));
}

const knownStudy = knownStudies.find(s => s.addonId == studyAddonId);
if (!("schemaNamespace" in knownStudy)) {
return Promise.reject(
new Error(`Core._unenrollStudy - No schema namespace specified in remote settings for ${studyAddonId}`));
}

unenrollmentMetrics.studyId.set(studyAddonId);
rallyPings.studyUnenrollment.submit();

Expand Down
61 changes: 61 additions & 0 deletions tests/core-addon/unit/Core.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import Glean from "@mozilla/glean/webext";
import Core from '../../../core-addon/Core.js';
import * as rallyMetrics from "../../../public/generated/rally.js";
import * as enrollmentMetrics from "../../../public/generated/enrollment.js";
import * as unenrollmentMetrics from "../../../public/generated/unenrollment.js";
import * as rallyPings from "../../../public/generated/pings.js";


Expand Down Expand Up @@ -683,6 +684,66 @@ describe('Core', function () {
).notCalled
);
});

it('send unenrollment pings when study is uninstalled', async function () {
// Make sure the functions yield during tests!
browser.storage.local.get
.callsArgWith(1, {activatedStudies: [FAKE_STUDY_ID]})
.resolves();
chrome.runtime.sendMessage.yields();

const fakeStudy = FAKE_STUDY_LIST[0];
fakeStudy.studyEnded = false;
await this.core._sendRunState(FAKE_STUDY_LIST, [FAKE_STUDY_ID]);

const unenrollmentPingMock = sinon.mock(rallyPings.studyUnenrollment);
unenrollmentPingMock.expects("submit").once();

// Mock the storage to provide a fake rally id.
const FAKE_UUID = "c0ffeec0-ffee-c0ff-eec0-ffeec0ffeec0";
browser.storage.local.get
.callsArgWith(1, {rallyId: FAKE_UUID})
.resolves();
chrome.storage.local.get.yields(FAKE_UUID);

// Attempt to unenroll from the study.
await this.core._unenrollStudy(FAKE_STUDY_ID);

assert.equal(await unenrollmentMetrics.studyId.testGetValue("study-unenrollment"), FAKE_STUDY_ID);
unenrollmentPingMock.verify();
});

it('handles studyEnded properly', async function () {
// Make sure the functions yield during tests!
browser.storage.local.get
.callsArgWith(1, {activatedStudies: [FAKE_STUDY_ID]})
.resolves();
chrome.runtime.sendMessage.yields();

const fakeStudy = FAKE_STUDY_LIST[0];
fakeStudy.studyEnded = true;
await this.core._sendRunState(FAKE_STUDY_LIST, [FAKE_STUDY_ID]);

const unenrollmentPingMock = sinon.mock(rallyPings.studyUnenrollment);
unenrollmentPingMock.expects("submit").never();

// Mock the storage to provide a fake rally id.
const FAKE_UUID = "c0ffeec0-ffee-c0ff-eec0-ffeec0ffeec0";
browser.storage.local.get
.callsArgWith(1, {rallyId: FAKE_UUID})
.resolves();
chrome.storage.local.get.yields(FAKE_UUID);

// Attempt to unenroll from the study.
assert.rejects(
this.core._unenrollStudy(FAKE_STUDY_ID),
{ message: "Core._unenrollStudy - Unenrolling study which has ended, not sending deletion pings for [email protected]"}
);

// No unenrollment pings are sent.
assert.equal(await unenrollmentMetrics.studyId.testGetValue("study-unenrollment"), undefined);
unenrollmentPingMock.verify();
});
});

afterEach(function () {
Expand Down