Skip to content
This repository was archived by the owner on Jan 11, 2023. It is now read-only.

Commit 699e358

Browse files
authored
Open FxA/Sync Prefs (#101)
1 parent ed6a4c5 commit 699e358

9 files changed

Lines changed: 149 additions & 15 deletions

File tree

src/experiments/sync/api.js

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,22 +16,22 @@ ChromeUtils.defineModuleGetter(this, "UIState",
1616
ChromeUtils.defineModuleGetter(this, "ObjectUtils",
1717
"resource://gre/modules/ObjectUtils.jsm");
1818

19+
// STATUS_LOGIN_FAILED means the password was changed on another device, and the
20+
// user needs to log in again.
21+
// STATUS_NOT_VERIFIED means the user has logged in with an unverified email.
22+
// In both cases, the user needs to take action elsewhere in Firefox.
23+
const FxAErrors = [UIState.STATUS_LOGIN_FAILED, UIState.STATUS_NOT_VERIFIED];
24+
1925
const getProfileInfo = async () => {
2026
const uiState = UIState.get();
21-
// STATUS_LOGIN_FAILED means the password was changed on another device, and the
22-
// user needs to log in again.
23-
// STATUS_NOT_VERIFIED means the user has logged in with an unverified email.
24-
// In both cases, the user needs to take action elsewhere in Firefox.
25-
const errors = [UIState.STATUS_LOGIN_FAILED, UIState.STATUS_NOT_VERIFIED];
26-
2727
let profileInfo;
2828

2929
// STATUS_NOT_CONFIGURED means the user is not logged in.
3030
if (uiState.status === UIState.STATUS_NOT_CONFIGURED) {
3131
profileInfo = null;
3232
} else { // UIState.STATUS_SIGNED_IN, the user is logged in and verified.
3333
const fxa = await UIState._internal.fxAccounts.getSignedInUser();
34-
const isErrorStatus = errors.includes(uiState.status);
34+
const isErrorStatus = FxAErrors.includes(uiState.status);
3535

3636
profileInfo = {
3737
id: fxa && fxa.uid,
@@ -45,6 +45,16 @@ const getProfileInfo = async () => {
4545
return profileInfo;
4646
};
4747

48+
const openPrefs = async (origin, entrypoint) => {
49+
const win = Services.wm.getMostRecentWindow("navigator:browser");
50+
51+
win.openPreferences("paneSync", { origin, urlParams: { entrypoint } });
52+
};
53+
const openSignIn = async (entrypoint) => {
54+
// TODO: be smarter like browser?
55+
return openPrefs("fxaError", entrypoint);
56+
};
57+
4858
this.sync = class extends ExtensionAPI {
4959
getAPI(context) {
5060
const EventManager = ExtensionCommon.EventManager;
@@ -58,6 +68,21 @@ this.sync = class extends ExtensionAPI {
5868
const profileInfo = await getProfileInfo();
5969
return profileInfo;
6070
},
71+
72+
async openPreferences(entrypoint) {
73+
const uiState = UIState.get();
74+
75+
switch (uiState.status) {
76+
case UIState.STATUS_SIGNED_IN:
77+
return openPrefs("fxaSignedin", entrypoint);
78+
case UIState.STATUS_NOT_VERIFIED:
79+
return openPrefs("fxaError", entrypoint);
80+
case UIState.STATUS_LOGIN_FAILED:
81+
return openSignIn(entrypoint);
82+
}
83+
84+
return openPrefs("fxa", entrypoint);
85+
},
6186
onUserProfileChanged: new EventManager(context, "sync.onUserProfileChanged", async (fire) => {
6287
let oldValue = await getProfileInfo();
6388
const callback = (value) => {

src/experiments/sync/schema.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,17 @@
8888
"description": "Returns a Promise that resolves to the UserProfileInfo, if the user is logged in, or null otherwise.",
8989
"async": true,
9090
"parameters": []
91+
},
92+
{
93+
"name": "openPreferences",
94+
"type": "function",
95+
"description": "Opens the Profile/Sync management page.",
96+
"async": true,
97+
"parameters": [{
98+
"name": "entrypoint",
99+
"type": "string",
100+
"description": "The name of the entrypoint into the management page; e.g., the caller"
101+
}]
91102
}
92103
]
93104
}

src/list/common.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,3 +53,7 @@ export const openWebsite = (url, closeWindow = true) => {
5353
window.close();
5454
}
5555
};
56+
57+
export const openSyncPrefs = () => {
58+
browser.experiments.sync.openPreferences("lockbox-addon");
59+
};

src/list/manage/containers/app-header.js

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { Localized } from "fluent-react";
77
import PropTypes from "prop-types";
88
import React from "react";
99
import { connect } from "react-redux";
10-
import { openWebsite } from "../../common";
10+
import { openWebsite, openSyncPrefs } from "../../common";
1111
import { classNames } from "../../../common";
1212
import {
1313
selectTabLogins,
@@ -235,11 +235,7 @@ export default connect(
235235
onClickMenuConnect: () => {
236236
// TODO: Issue TBD
237237
},
238-
onClickMenuAccount: () => {
239-
// TODO: Issue #92 / Issue #61
240-
},
241-
onClickMenuSignIn: () => {
242-
// TODO: Issue #92 / Issue #61
243-
},
238+
onClickMenuAccount: () => openSyncPrefs(),
239+
onClickMenuSignIn: () => openSyncPrefs(),
244240
})
245241
)(AppHeader);

test/integration/sync-api-test.js

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import chaiAsPromised from "chai-as-promised";
1111
chai.use(chaiAsPromised);
1212

1313
describe("sync API", () => {
14-
let webext, driver, webdriver, By, until;
14+
let webext, driver, testpage, webdriver, By, until;
1515

1616
const clickButton = async (id) => {
1717
await webext.inContent();
@@ -30,6 +30,7 @@ describe("sync API", () => {
3030
const loadTestPage = async () => {
3131
await webext.inContent();
3232
await driver.get(webext.url("/test/integration/test-pages/sync-api.html"));
33+
testpage = await driver.getWindowHandle();
3334
};
3435

3536

@@ -54,6 +55,29 @@ describe("sync API", () => {
5455
return output;
5556
};
5657

58+
const openSyncPrefs = async () => {
59+
await clickButton("open-sync-prefs");
60+
};
61+
62+
const findAndCloseSyncPrefs = async () => {
63+
await webext.inContent();
64+
let found = false;
65+
66+
let handles = await driver.getAllWindowHandles();
67+
for (let h of handles) {
68+
await driver.switchTo().window(h);
69+
const url = await driver.getCurrentUrl();
70+
if (url.startsWith("about:preferences") && url.endsWith("#sync")) {
71+
found = true;
72+
await driver.close();
73+
}
74+
}
75+
76+
await driver.switchTo().window(testpage);
77+
78+
return found;
79+
};
80+
5781
// Force an update of the UIState. This method exists on the UIState API to
5882
// aid with rapid state transitions during testing.
5983
const refreshUIState = async () => {
@@ -256,7 +280,40 @@ describe("sync API", () => {
256280
});
257281
});
258282

283+
describe("browser.experiments.sync.openPreferences", () => {
284+
beforeEach(async () => {
285+
});
286+
287+
it("should open FxA/sync preference page when not configured", async () => {
288+
await setNotConfigured();
289+
await loadTestPage();
290+
await openSyncPrefs();
291+
const found = await findAndCloseSyncPrefs();
292+
expect(found).to.equal(true);
293+
});
294+
295+
it("should open FxA/sync preference page when signed-in", async () => {
296+
await setLoggedIn();
297+
await loadTestPage();
298+
await openSyncPrefs();
299+
const found = await findAndCloseSyncPrefs();
300+
expect(found).to.equal(true);
301+
});
302+
303+
it("should open FxA/sync preference page when there's a problem", async () => {
304+
await setLoginFailed();
305+
await loadTestPage();
306+
await openSyncPrefs();
307+
const found = await findAndCloseSyncPrefs();
308+
expect(found).to.equal(true);
309+
});
310+
});
311+
259312
describe("browser.experiments.sync.getUserProfileInfo", () => {
313+
beforeEach(async () => {
314+
await webext.inContent();
315+
});
316+
260317
it("should return null if the user is not logged in", async () => {
261318
await setNotConfigured();
262319
await loadTestPage();

test/integration/test-pages/sync-api.html

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ <h1>Sync API Test File</h1>
99
<button id="get-profile-info">Get Profile Info</button>
1010
<pre id="get-profile-info-results"></pre>
1111

12+
<p>Click the Open Preferences button to open the preferences page to the FxA/Sync pane.
13+
<button id="open-sync-prefs">Open Preferences</button>
14+
<pre id="open-sync-prefs-results"></pre>
15+
1216
<p>Click the Register Listener button to register an onUserProfileChanged listener.
1317
Any events will be appended to a list in the results field.
1418
<button id="register-listener">Register Listener</button>

test/integration/test-pages/sync-api.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,12 @@ document.querySelector("#check-passwords-pref").addEventListener("click", () =>
1818
then(log, log);
1919
});
2020

21+
document.querySelector("#open-sync-prefs").addEventListener("click", () => {
22+
const log = getLogger("open-sync-prefs");
23+
browser.experiments.sync.openPreferences("lockbox-addon-tests").
24+
then(log, log);
25+
});
26+
2127
document.querySelector("#register-listener").addEventListener("click", () => {
2228
const events = [];
2329
const log = getLogger("register-listener");

test/unit/list/common-test.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/* This Source Code Form is subject to the terms of the Mozilla Public
2+
* License, v. 2.0. If a copy of the MPL was not distributed with this
3+
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4+
5+
import chai, { expect } from "chai";
6+
import sinon from "sinon";
7+
import sinonChai from "sinon-chai";
8+
import "test/unit/mocks/browser";
9+
10+
import * as common from "src/list/common";
11+
12+
chai.use(sinonChai);
13+
14+
describe("list > common", () => {
15+
describe("openSyncPrefs", () => {
16+
let spyOpenPrefs;
17+
18+
beforeEach(() => {
19+
spyOpenPrefs = sinon.spy(browser.experiments.sync, "openPreferences");
20+
});
21+
afterEach(() => {
22+
spyOpenPrefs.restore();
23+
});
24+
25+
it("call WebExt API", async () => {
26+
common.openSyncPrefs();
27+
expect(spyOpenPrefs).to.have.been.calledWith("lockbox-addon");
28+
});
29+
});
30+
});

test/unit/mocks/browser.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,7 @@ window.browser = {
218218
},
219219
sync: {
220220
async getUserProfileInfo() { },
221+
async openPreferences() { },
221222
onUserProfileChanged: new MockListener(),
222223
},
223224
},

0 commit comments

Comments
 (0)