-
Notifications
You must be signed in to change notification settings - Fork 16
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
type(feat): Implemented Notification Classes #322
Conversation
7055a30
to
dc844fc
Compare
Hi @b4handjr I haven't written the tests for the classes yet. I just want to run the implementation I have here by you to know if there are any adjustments before I proceed with writing the tests. I would appreciate you looking over it. Thanks. |
This looks like a fine start, I am waiting a bit to merge a fix for the tests as I am hoping there are some more attempts at it. You could probably work on tests but you might not be able to push the code for a little while. |
Oh, okay. Thanks for looking through this I will proceed with writing the tests. |
ad0fd03
to
9dcb409
Compare
@b4handjr While I written the test for On the other for On investigation, I discovered geckodriver isn't capturing the logs for the progress element. I pasted the driver logs here: https://paste.mozilla.org/f2FMCbpc Here is the most important part. The 1730047021342 Marionette TRACE [11] Received event DOMContentLoaded for http://127.0.0.1:37233/
1730047021342 Marionette TRACE [11] Received event pageshow for http://127.0.0.1:37233/
1730047021342 Marionette DEBUG 0 <- [1,3,null,{"value":null}]
1730047021348 webdriver::server DEBUG <- 200 OK {"value":null}
1730047021349 webdriver::server DEBUG -> POST /session/7f28824c-61cf-483d-b24c-df67c05c7c10/element {"using": "link text", "value": "largewebextension.xpi"}
1730047021351 Marionette DEBUG 0 -> [0,4,"WebDriver:FindElement",{"using":"link text","value":"largewebextension.xpi"}]
1730047021353 RemoteAgent TRACE WebDriverProcessData actor created for PID 1180094
1730047021353 Marionette TRACE [11] MarionetteCommands actor created for window id 10737418241
1730047021365 Marionette DEBUG 0 <- [1,4,null,{"value":{"element-6066-11e4-a52e-4f735466cecf":"8b07f1d7-50a4-4def-9418-d4f6a9c692fa"}}]
1730047021365 webdriver::server DEBUG <- 200 OK {"value":{"element-6066-11e4-a52e-4f735466cecf":"8b07f1d7-50a4-4def-9418-d4f6a9c692fa"}}
1730047021366 webdriver::server DEBUG -> POST /session/7f28824c-61cf-483d-b24c-df67c05c7c10/element/8b07f1d7-50a4-4def-9418-d4f6a9c692fa/click {}
1730047021367 Marionette DEBUG 0 -> [0,5,"WebDriver:ElementClick",{"id":"8b07f1d7-50a4-4def-9418-d4f6a9c692fa"}]
1730047021379 Marionette TRACE Received DOM event click for http://127.0.0.1:37233/largewebextension.xpi
1730047021383 Marionette TRACE [11] Received event beforeunload for http://127.0.0.1:37233/
1730047028355 Marionette TRACE Canceled page load listener because no navigation has been detected
1730047028356 Marionette DEBUG 0 <- [1,5,null,{"value":null}]
1730047028356 webdriver::server DEBUG <- 200 OK {"value":null}
1730047028358 webdriver::server DEBUG -> GET /session/7f28824c-61cf-483d-b24c-df67c05c7c10/moz/context
1730047028358 Marionette DEBUG 0 -> [0,6,"Marionette:GetContext",{}]
1730047028359 Marionette DEBUG 0 <- [1,6,null,{"value":"content"}]
1730047028359 webdriver::server DEBUG <- 200 OK {"value":"content"}
1730047028360 webdriver::server DEBUG -> POST /session/7f28824c-61cf-483d-b24c-df67c05c7c10/moz/context {"context": "chrome"}
1730047028361 Marionette DEBUG 0 -> [0,7,"Marionette:SetContext",{"value":"chrome"}]
1730047028361 Marionette DEBUG 0 <- [1,7,null,{"value":null}]
1730047028361 webdriver::server DEBUG <- 200 OK {"value":null}
1730047028362 webdriver::server DEBUG -> POST /session/7f28824c-61cf-483d-b24c-df67c05c7c10/element {"using": "css selector", "value": "#notification-popup popupnotification"}
1730047028362 Marionette DEBUG 0 -> [0,8,"WebDriver:FindElement",{"using":"css selector","value":"#notification-popup popupnotification"}]
1730047028364 RemoteAgent TRACE WebDriverProcessData actor created for PID 1179885
1730047028364 Marionette TRACE [1] MarionetteCommands actor created for window id 2
1730047028365 Marionette DEBUG 0 <- [1,8,null,{"value":{"element-6066-11e4-a52e-4f735466cecf":"f489ac48-6eb8-47f8-8b94-2aa970a366c8"}}]
1730047028365 webdriver::server DEBUG <- 200 OK {"value":{"element-6066-11e4-a52e-4f735466cecf":"f489ac48-6eb8-47f8-8b94-2aa970a366c8"}}
1730047028366 webdriver::server DEBUG -> GET /session/7f28824c-61cf-483d-b24c-df67c05c7c10/element/f489ac48-6eb8-47f8-8b94-2aa970a366c8/property/id
1730047028367 Marionette DEBUG 0 -> [0,9,"WebDriver:GetElementProperty",{"id":"f489ac48-6eb8-47f8-8b94-2aa970a366c8","name":"id"}]
1730047028367 Marionette DEBUG 0 <- [1,9,null,{"value":"addon-install-failed-notification"}]
1730047028367 webdriver::server DEBUG <- 200 OK {"value":"addon-install-failed-notification"}
|
As a follow up on this, I asked in the Webdriver channel, and I got to learn that there is a bit of time when the main thread is blocked while installing the addon, which could be a plausible reason. |
9dcb409
to
7d031a8
Compare
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.
Had some initial thoughts but it seems you're still working on tests.
tests/test_notifications.py
Outdated
def get_path(self, size="default"): | ||
"""Returns web extension path.""" | ||
return self.paths.get(size, self.paths["default"]) |
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.
What is size
here?
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 could also be a property so we can use setters and getters on it.
tests/test_notifications.py
Outdated
Returns: | ||
:py:class:`AddOnInstallFailed`: Firefox notification. | ||
""" | ||
selenium.get(webserver.url()) |
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.
Unrelated but we should change url
to just be a property.
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 will work on this
def is_downloading(self): | ||
"""Check if the add-on is currently downloading. | ||
|
||
Returns: | ||
bool: True if the download and verification is in progress. | ||
""" | ||
with self.selenium.context(self.selenium.CONTEXT_CHROME): | ||
return "Downloading and verifying add-on…" in self.find_description().text |
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.
So what's the idea with this. I can see some use cases but I would like to know you're thinking. How would the API look for a user who wants to use this?
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 added this as property here because I think this is a good identifier of the Firefox Progress notification. The idea is for the property to return a boolean. A simple true or false could be used to easily determine if the Progress notification is active or not. In the context of the test for notification, if progress_notification
returns the AddOnProgress
class, then the API will be progress_notification.is_downloading
.
def wait_until_complete(self, timeout=None): | ||
"""Wait until the progress notification disappears, indicating completion. | ||
|
||
Args: | ||
timeout (int, optional): Maximum time to wait in seconds. | ||
""" | ||
self.window.wait_for_notification(None, timeout=timeout) |
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.
Should this return something when it has been completed? Even returning the self.window.wait_for_notification(None, timeout=timeout)
seems fine, as that returns the notification
itself.
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 for pointing that out. I think this could be potentially confusing if there is no return. Actually, from what I have observed of the Firefox notification for progress, the notification disappears once the download is completed. So I think it will be more accurate to return a boolean that shows that the notification is no longer active or visible.
return self.window.wait_for_notification(None, timeout=timeout) is None
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.
Well I notice that your call to wait_for_notification
uses None
, I think we would want to wait for this AddOnProgress
notification right? I know that you can wait for any notification with that method, but it wouldn't seems right to have that be within this class. So do we need this method at all? When a download is completed there should be only a few different results from that, it seems to make sense to check for those. I think if you write a test for this it will help clarify what you had in mind.
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.
Actually, when a download completes, no result is usually displayed that shows the download has completed. When a download completes, the notification will no longer be visible. Usually, it is followed by the Add-on install complete notification or Blocked notification if it is potentially, a suspicious extension.
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.
Right, so we could wait for one of those, since we know they will be shown after it is completed.
@b4handjr Here is a feedback on the report I made as regards the |
Follow up: As suspected, a time delay variable with respect to an event that occurs during a page load, https://bugzilla.mozilla.org/show_bug.cgi?id=1931297 I will be sending in a patch. |
Very nice, glad we could find that bug. Great work! |
Yeah! |
Update: This is the fix that was made. It wasn't ideal to completely remove the timer variable that had blocked the capture of the progress notification. It served a key purpose during navigation events (beforeunload and unload events), so it can't be removed. However, a work around was implemented. This fix is when the I have now made a commit for |
201b775
to
0b480a4
Compare
Also, I think I can remove the test class for |
tests/test_notifications.py
Outdated
@property | ||
def path(self): | ||
"""Default path.""" | ||
return self._paths["default"] | ||
|
||
def get_path(self, ext_path): | ||
"""Returns the web extension path.""" | ||
return self._paths.get(ext_path) |
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 like where this is going but it is not the proper use of a property. get_path
shouldn't need to exist, as we should be able to set a path and return it from the property itself. Here is a good article on what I mean, https://realpython.com/python-getter-setter/#using-properties-instead-of-getters-and-setters-the-python-way
Yes, that sounds good |
0f0c70a
to
d089330
Compare
@b4handjr I have made the requested changes. |
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.
Making good progress here!
self._paths = { | ||
"default": "webextension.xpi", | ||
"corrupt": "corruptwebextension.xpi", | ||
"large": "largewebextension.xpi", | ||
} | ||
if path_key not in self._paths: | ||
raise ValueError(f"Invalid path key: {path_key}") | ||
self._path_key = path_key |
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 like the paths
dict here.
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!
def close(self): | ||
"""Close the failed installation notification.""" | ||
with self.selenium.context(self.selenium.CONTEXT_CHROME): | ||
self.find_primary_button().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.
Can you add this to the Failed test?
else: | ||
raise ValueError(f"Invalid path key: {ext_path}") |
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.
Can we have a test for this error case?
tests/test_notifications.py
Outdated
def failed_notification( | ||
addon: AddOn, browser: BrowserWindow, webserver: WebServer, selenium: WebDriver | ||
): |
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 doesn't have a return type
tests/test_notifications.py
Outdated
addon: AddOn, browser: BrowserWindow, webserver: WebServer, selenium: WebDriver | ||
) -> BaseNotification: |
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.
Can this not return AddOnProgress
?
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.
It can. I have addressed it.
tests/test_notifications.py
Outdated
browser: BrowserWindow, blocked_notification: AddOnInstallBlocked | ||
) -> BaseNotification | None: |
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.
Can you fix this to just return None
since it is a test.
@pytest.mark.parametrize( | ||
"firefox_options", [{"page_load_strategy_none": True}], indirect=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.
Nice use of parrametrization!
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!
d089330
to
277b2ef
Compare
@b4handjr I have made the requested changes. Thanks for pointing out the issues. (Also, I left a message for you in the Desktop Test Engineering in Mozilla Chat. It is an inquiry and I will be grateful if you can look into it. thanks). |
@b4handjr Can this merged yet or there are still more changes I need to make? Thanks. |
It's a busy week here at Mozilla, I will try and get to it this week though. |
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.
Had 1 more question
tests/test_notifications.py
Outdated
) -> None: | ||
"""Verify downloading status is reported correctly.""" | ||
description = progress_notification.is_downloading | ||
assert description is not None |
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.
is_downloading
is said to be type bool
. Is it hard to check if it's true or has been set to 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.
@b4handjr Oh, not at all. Let me change it to assert description is True
to clarify it.
f68870f
to
7c6eb3f
Compare
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.
Great work, thanks for this!
The Nightly tests are failing due to the |
Because
AddOnInstallFailed
,AddOnProgress
, andAddOnInstallRestart
classes are not implemented yet.This commit
AddOnInstallFailed
,AddOnProgress
, andAddOnInstallRestart
classesFixes #311