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

Add slimmed-down Selenium project code #596

Open
wants to merge 5 commits into
base: master
Choose a base branch
from

Conversation

martin-martin
Copy link
Contributor

Where to put new files:

  • New files should go into a top-level subfolder, named after the article slug. For example: my-awesome-article

How to merge your changes:

  1. Make sure the CI code style tests all pass (+ run the automatic code formatter if necessary).
  2. Find an RP Team member on Slack and ask them to review & approve your PR.
  3. Once the PR has one positive ("approved") review, GitHub lets you merge the PR.
  4. 🎉

@martin-martin
Copy link
Contributor Author

@bzaczynski this is my attempt to create a slimmed-down version of the project that primarily focuses on Selenium web interactions (now following POM), but also keep the basic functionality of the music player (and a reduced---but existing---separation of concerns).

I've tried to build the project in a way so that it can lead into the project that you wrote, with the idea in mind that learners could follow your SbSP as a next step if they're interested in the music player project. At the same time, I tried to keep it simple and reduced enough that learners who are only there for an intro to Selenium would get their money's worth.

Hope this works and looking forward to your thoughts!

Linking the other PR for reference: #583


## Run the Bandcamp Discover Player

To run the music placer, navigate to the `src/` folder, then execute the module from your command-line:
Copy link
Contributor

Choose a reason for hiding this comment

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

✍️ Typo

Suggested change
To run the music placer, navigate to the `src/` folder, then execute the module from your command-line:
To run the music player, navigate to the `src/` folder, then execute the module from your command-line:

Comment on lines 22 to 23
(venv) $ cd src/
(venv) $ python -m bandcamp
Copy link
Contributor

@bzaczynski bzaczynski Oct 23, 2024

Choose a reason for hiding this comment

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

🔧 Technical Recommendation

That doesn't work:

(venv) $ cd src/
(venv) $ python -m bandcamp/
/home/bartek/.virtualenvs/python-selenium/bin/python: No module named bandcamp/

A better option would be to install the package into the venv first. That way, it won't matter where you'll run the module from:

(venv) $ python -m pip install .
(venv) $ python -m bandcamp/

Copy link
Contributor

@bzaczynski bzaczynski left a comment

Choose a reason for hiding this comment

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

@martin-martin I left a few comments for you. Let me know what you think.

(venv) $ python -m bandcamp
```

You'll see a text-based user interface that allows you to interact with the music player:
Copy link
Contributor

Choose a reason for hiding this comment

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

🤝 Handholding

It seems that you implicitly assume there's a driver for Firefox installed and configured for this to run. I don't have one, and this is what I got:

(venv) $ python -m bandcamp
Traceback (most recent call last):
  ...
selenium.common.exceptions.InvalidArgumentException: Message: binary is not a Firefox executable

Probably better to give a heads-up or provide instructions on what needs to be installed before.

You'll see a text-based user interface that allows you to interact with the music player:

```
Type: [play <track number>], [tracks], [more], [exit]
Copy link
Contributor

Choose a reason for hiding this comment

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

Flagging the message (I'll explain why in another comment.)

Comment on lines 20 to 21
[tool.setuptools.packages.find]
where = ["src"]
Copy link
Contributor

Choose a reason for hiding this comment

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

🔧 Technical Recommendation

These two lines are redundant, as setuptools will automatically discover Python packages following the src-layout.

Comment on lines 1 to 22
appdirs==1.4.4
attrs==24.2.0
certifi==2024.7.4
h11==0.14.0
idna==3.7
jedi==0.19.1
outcome==1.3.0.post0
parso==0.8.4
prompt_toolkit==3.0.47
ptpython==3.0.29
Pygments==2.18.0
PySocks==1.7.1
selenium==4.23.1
sniffio==1.3.1
sortedcontainers==2.4.0
trio==0.26.1
trio-websocket==0.11.1
typing_extensions==4.12.2
urllib3==2.2.2
wcwidth==0.2.13
websocket-client==1.8.0
wsproto==1.2.0
Copy link
Contributor

Choose a reason for hiding this comment

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

🔧 Technical Recommendation

Are you sure you haven't accidentally installed any extra packages besides Selenium? Here's what my requirements.txt file looks like:

attrs==24.2.0
certifi==2024.8.30
h11==0.14.0
idna==3.10
outcome==1.3.0.post0
PySocks==1.7.1
selenium==4.25.0
sniffio==1.3.1
sortedcontainers==2.4.0
trio==0.27.0
trio-websocket==0.11.1
typing_extensions==4.12.2
urllib3==2.2.3
websocket-client==1.8.0
wsproto==1.2.0

Comment on lines 10 to 11
if __name__ == "__main__":
main()
Copy link
Contributor

Choose a reason for hiding this comment

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

🔧 Technical Recommendation

Do we need those two lines here? Since you defined the bandcamp-player script in pyproject.toml, you can invoke the main() function directly:

$ bandcamp-player

Unless, you do want to have two ways of running your TUI:

  • bandcamp-player
  • python -m bandcamp

from bandcamp.app.player import Player


class TUI:
Copy link
Contributor

Choose a reason for hiding this comment

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

🔧 Technical Recommendation

Since this class has no state, only behaviors, there's no need to define it. I'd strongly suggest replacing your class methods with simple top-level functions. It'll make the code less Java-esque ☕



def main():
"""Provides the main entry point for the app."""
Copy link
Contributor

Choose a reason for hiding this comment

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

🔧 Technical Recommendation

According to the widely-adopted docstring conventions (PEP 257), this sort of comments should use the imperative form, e.g.:

Suggested change
"""Provides the main entry point for the app."""
"""Provide the main entry point for the app."""

player.play(track_number)
print(player._current_track._get_track_info())

def tracks(self, player):
Copy link
Contributor

Choose a reason for hiding this comment

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

🔧 Technical Recommendation

Can we use a more descriptive name for this method, such as this?

Suggested change
def tracks(self, player):
def display_tracks(self, player):

It would render the dostring that follows less important 😉

class TUI:
"""Provides a text-based user interface for a Bandcamp music player."""

COLUMN_WIDTH = CW = 30
Copy link
Contributor

Choose a reason for hiding this comment

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

Nice aliasing!

Comment on lines 4 to 14
class HomePageLocatorMixin:
DISCOVER_RESULTS = (By.CLASS_NAME, "discover-results")
TRACK = (By.CLASS_NAME, "discover-item")
PAGINATION_BUTTON = (By.CLASS_NAME, "item-page")


class TrackLocatorMixin:
PLAY_BUTTON = (By.CSS_SELECTOR, "a")
ALBUM = (By.CLASS_NAME, "item-title")
GENRE = (By.CLASS_NAME, "item-genre")
ARTIST = (By.CLASS_NAME, "item-artist")
Copy link
Contributor

Choose a reason for hiding this comment

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

🔧 Technical Recommendation

I really like your approach of using the "mixin" classes to store various element locators. It's a clever way to keep them separate but close at the same time 👏

That being said, I'm not convinced we should call these classes mixins. A mixin class encapsulates behavior, and it typically depends on some attributes defined in the target class, so a mixin can't exist on its own—it needs to be "mixed-in" with another ingredient.

In this case, your classes are just data containers or convenient namespaces for a bunch of constants that can stand on their own. So, I'd suggest to drop the "Mixin" suffix from their names.

Copy link
Contributor

@bzaczynski bzaczynski left a comment

Choose a reason for hiding this comment

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

@martin-martin Perfecto ✨ Thanks for a quick update! Can't wait to review the rest of it 😉

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants