yashaka / selene

User-oriented Web UI browser tests in Python
https://yashaka.github.io/selene/
MIT License
676 stars 145 forks source link

consider pydantic style of PageObjects #505

Open yashaka opened 5 months ago

yashaka commented 5 months ago

Example:

class ReactContinuousSlider(selene.PageModel):
    config = selene.PageModel.Config(url='https://mui.com/material-ui/react-slider/#ContinuousSlider')
    container = selene.PageModel.Element('#ContinuousSlider+*')
    thumb = container.element('.MuiSlider-thumb')
    thumb_input = thumb.element('input')
    volume_up = container.element('[data-testid=VolumeUpIcon]')
    volume_down = container.element('[data-testid=VolumeDownIcon]')
    rail = container.element('.MuiSlider-rail')

reactSlider = ReactContinuousSlider(browser).open()

reactSlider.thumb.perform(command.drag_and_drop_to(reactSlider.volume_up))
reactSlider.thumb_input.should(have.value('100'))

reactSlider.thumb.perform(command.drag_and_drop_to(reactSlider.volume_down))
reactSlider.thumb_input.should(have.value('0'))

reactSlider.thumb.perform(command.drag_and_drop_to(reactSlider.rail))
reactSlider.thumb_input.should(have.value('50'))

as a shortcut to

class ReactContinuousSlider:
    def __init__(self, browser: selene.Browser | None):
        self.browser = browser if browser else selene.browser
        self.container = self.browser.element('#ContinuousSlider+*')
        self.thumb = self.container.element('.MuiSlider-thumb')
        self.thumb_input = self.thumb.element('input')
        self.volume_up = self.container.element('[data-testid=VolumeUpIcon]')
        self.volume_down = self.container.element('[data-testid=VolumeDownIcon]')
        self.rail = self.container.element('.MuiSlider-rail')

    def open(self):
        self.browser.open('https://mui.com/material-ui/react-slider/#ContinuousSlider')
        return self

reactSlider = ReactContinuousSlider(browser).open()

reactSlider.thumb.perform(command.drag_and_drop_to(reactSlider.volume_up))
reactSlider.thumb_input.should(have.value('100'))

reactSlider.thumb.perform(command.drag_and_drop_to(reactSlider.volume_down))
reactSlider.thumb_input.should(have.value('0'))

reactSlider.thumb.perform(command.drag_and_drop_to(reactSlider.rail))
reactSlider.thumb_input.should(have.value('50'))

consider also simplifying container = selene.PageModel.Element('#ContinuousSlider+*') to container = selene.Element('#ContinuousSlider+*')

P.S. related to #439

aleksandr-kotlyar commented 5 months ago

Pay attention to difference between init and class attributes https://stackoverflow.com/questions/46720838/python-init-vs-class-attributes As far as I understand it: If you will need by somehow more than one instance of class, then it will rewrite all attributes of the first instance.

yashaka commented 5 months ago

Pay attention to difference between init and class attributes https://stackoverflow.com/questions/46720838/python-init-vs-class-attributes As far as I understand it: If you will need by somehow more than one instance of class, then it will rewrite all attributes of the first instance.

It will not, because they are not class attributes. In both python dataclasses and pydantic-based classes – the attributes that you define on the class level – are not class attributes – they are instance attributes. This is the main idea of such type of DSL.