-
Notifications
You must be signed in to change notification settings - Fork 10
Show detailed network errors in Felt login UI #337
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
base: enterprise-main
Are you sure you want to change the base?
Conversation
lissyx
left a comment
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.
LGTM
|
Kind of want to add a test before landing. |
|
Also let's wait until #291 lands so we can use the divs that adds to the Felt window. |
c01676a to
7f8fd1b
Compare
| * @param {string|null} [options.body=null] - Request body | ||
| * @returns {Promise<{ok: boolean, status: number, json: Function, text: Function}>} | ||
| */ | ||
| _xhrFetch(url, { method = "GET", headers = {}, body = null } = {}) { |
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.
note: can't use async here because we already construct the Promise manually, it would get wrapped in another Promise. And we can't get rid of that promise because we need to resolve/reject in the callbacks from XMLHttpRequest.
From a callers POV it behaves the same as fetch(), minus the limitations in the comments.
1rneh
left a comment
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 is part 1 of my review. I had quite a lot of thoughts on our error report handling.
I’ll share a follow-up review (part 2) covering the changes to ConsoleClient later today.
| email = self.get_elem("#felt-form__email") | ||
| self._driver.execute_script( | ||
| """ | ||
| arguments[0].value = arguments[1]; | ||
| arguments[0].dispatchEvent(new Event('input', { bubbles: true })); | ||
| """, | ||
| email, | ||
| "random@mozilla.com", | ||
| ) | ||
|
|
||
| btn = self.get_elem("#felt-form__sign-in-btn") | ||
| btn.click() |
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.
Could we add a method in FeltTests e.g. submit_email that we can call in this test (super.submit_email()) and in test_felt_00_chrome_on_email_submit in FeltTests to avoid duplicating code.
| def test_felt_0_load_sso(self, exp): | ||
| return True | ||
|
|
||
| def test_felt_1_perform_sso_auth(self, exp): | ||
| return True |
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.
The do nothing and can be removed, right?
| self._manually_closed_child = True | ||
| return super().teardown() | ||
|
|
||
| def test_felt_00_chrome_on_email_submit(self, exp): |
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.
Could we make the method name describe the test here?
| font-size: medium; | ||
| /* stylelint-disable-next-line stylelint-plugin-mozilla/use-design-tokens */ | ||
| color: var(--icon-color-critical); | ||
| color: var(--text-color-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.
Thanks!
| function hideConnectionError() { | ||
| const errorContainer = document.querySelector(".felt-browser-error"); | ||
| const errorElement = document.querySelector(".felt-browser-error-connection"); | ||
| if (errorElement) { | ||
| errorElement.classList.add("is-hidden"); | ||
| } | ||
| if (errorContainer) { | ||
| const crashError = errorContainer.querySelector( | ||
| ".felt-browser-error-multiple-crashes:not(.is-hidden)" | ||
| ); | ||
| if (!crashError) { | ||
| errorContainer.classList.add("is-hidden"); | ||
| } | ||
| } | ||
| } |
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.
I guess this method generally resets the visible error state. Probably there will be more errors to be added to the list so let's maybe refactor the method to handel that.
| function hideConnectionError() { | |
| const errorContainer = document.querySelector(".felt-browser-error"); | |
| const errorElement = document.querySelector(".felt-browser-error-connection"); | |
| if (errorElement) { | |
| errorElement.classList.add("is-hidden"); | |
| } | |
| if (errorContainer) { | |
| const crashError = errorContainer.querySelector( | |
| ".felt-browser-error-multiple-crashes:not(.is-hidden)" | |
| ); | |
| if (!crashError) { | |
| errorContainer.classList.add("is-hidden"); | |
| } | |
| } | |
| } | |
| function resetErrorReport() { | |
| const errorWrapper = document.querySelector(".felt-browser-error"); | |
| if (!errorWrapper.classList.contains("is-hidden")) { | |
| // No visible error report. | |
| return; | |
| } | |
| errorWrapper.classList.add("is-hidden"); | |
| const errors = errorWrapper.querySelectorAll(".felt-browser-error span:not(.is-hidden)"); | |
| errors.forEach(e => e.classList.add("is-hidden")); | |
| } |
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.
On another thought, I think we could make the error reporting a bit more generic. I think we need the three methods:
ErrorReport.init()which we call once the document is loadedErrorReport.reset()when we retry the login flowErrorReport.update(error)if we encounter an error during the login process and want to add it to the error report.
And then we can have an enum will all errors we're facing to the user.
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.
And then we can have an enum will all errors we're facing to the user.
Not sure about this part. The code is a bit messy because it tries to surface up the actual error instead of a generic "couldn't connect to the console", but this means enumeration would be problematic.
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.
We could still display user-friendly error messages:
const ERR_MESSAGES = {
CONSOLE_CONNECT: "We couldn't connect to the console.",
MULTIPLE_CRASHES: "We couldn't launch Firefox Enterprise due to multiple crashes on startup."
}
And if there is more to show such as the actual error message, we can append that to the error message or in a details section. What do you think?
| data-l10n-id="felt-browser-error-multiple-crashes" | ||
| > | ||
| </span> | ||
| <span class="felt-browser-error-connection is-hidden"></span> |
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.
Could we convert them to div elements so they are not all displayed in one line?
| } catch (err) { | ||
| console.error(`FeltExtension: Failed to connect to console: ${err}`); | ||
| const consoleUrl = lazy.ConsoleClient.consoleBaseURI.origin; | ||
| showConnectionError(`Failed to connect to ${consoleUrl}: ${err.message}`); |
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.
We are localizing the crash error, so we should probably localize Failed to connect to as well.
Tbh I'm not so happy about what we display though. We should have something actionable for the user such as We couldn't connect to the console. Please contact your admin for support. and then a maybe a details section with that error message. Let's make sure we define the UX for this soon.
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.
Part 2:
Thank you for the detailed documentation :)
Just some small suggestion about the changes in ConsoleClient. The rest of it looks good.
| res = await this._xhrFetch(url, { | ||
| method: "POST", | ||
| headers: { | ||
| "Content-Type": "application/json", | ||
| Accept: "application/json", | ||
| }, | ||
| body: JSON.stringify(devicePosture), | ||
| }); |
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.
Hm, calling fetch directly should have been replaced here when we introduced _post in a766fb8. Seems like I missed this one. Could you change that? It also wraps _xhrFetch :)
| res = await this._xhrFetch(url, { | |
| method: "POST", | |
| headers: { | |
| "Content-Type": "application/json", | |
| Accept: "application/json", | |
| }, | |
| body: JSON.stringify(devicePosture), | |
| }); | |
| res = await this._post(url, JSON.stringify(devicePosture)); |
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.
And then in _fetch we also need to add headers.set("Accept", "application/json");.
| let errorName = "NetworkError"; | ||
| if (xhr.channel) { | ||
| errorName = this._getErrorNameForStatus(xhr.channel.status); | ||
| } |
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 :) (we already do it so nicely with the headers above)
| let errorName = "NetworkError"; | |
| if (xhr.channel) { | |
| errorName = this._getErrorNameForStatus(xhr.channel.status); | |
| } | |
| const errorName = xhr.channel | |
| ? this._getErrorNameForStatus(xhr.channel.status) | |
| : "NetworkError" |
| `ConsoleClient: Failed to connect to ${url}: ${err.message}` | ||
| ); | ||
| throw err; | ||
| } |
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.
Logging and rethrowing the same error here seems redundant, as the caller already handles the logging. I’d suggest removing this catch unless we want to add semantic context to the error.
When connecting to the enterprise console fails (e.g., due to an expired certificate), the error was a generic "NetworkError when attempting to fetch resource" which made debugging difficult.
This change: