Add first Expectation to instruction
This commit is contained in:
parent
f878b59977
commit
d10b740545
5 changed files with 254 additions and 166 deletions
|
|
@ -351,11 +351,12 @@ impl<P: Program + 'static> Tester<P> {
|
||||||
*outcome = Outcome::Failed;
|
*outcome = Outcome::Failed;
|
||||||
}
|
}
|
||||||
emulator::Event::Ready => {
|
emulator::Event::Ready => {
|
||||||
|
*current += 1;
|
||||||
|
|
||||||
if let Some(instruction) =
|
if let Some(instruction) =
|
||||||
self.instructions.get(*current).cloned()
|
self.instructions.get(*current - 1).cloned()
|
||||||
{
|
{
|
||||||
emulator.run(program, instruction);
|
emulator.run(program, instruction);
|
||||||
*current += 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if *current >= self.instructions.len() {
|
if *current >= self.instructions.len() {
|
||||||
|
|
@ -552,7 +553,7 @@ impl<P: Program + 'static> Tester<P> {
|
||||||
outcome,
|
outcome,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
if *current == i {
|
if *current == i + 1 {
|
||||||
Some(match outcome {
|
Some(match outcome {
|
||||||
Outcome::Running => {
|
Outcome::Running => {
|
||||||
theme.palette().primary
|
theme.palette().primary
|
||||||
|
|
@ -572,7 +573,7 @@ impl<P: Program + 'static> Tester<P> {
|
||||||
.color
|
.color
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
} else if *current > i {
|
} else if *current > i + 1 {
|
||||||
Some(
|
Some(
|
||||||
theme
|
theme
|
||||||
.extended_palette()
|
.extended_palette()
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ use crate::core::renderer;
|
||||||
use crate::core::widget;
|
use crate::core::widget;
|
||||||
use crate::core::window;
|
use crate::core::window;
|
||||||
use crate::core::{Element, Size};
|
use crate::core::{Element, Size};
|
||||||
|
use crate::instruction;
|
||||||
use crate::program;
|
use crate::program;
|
||||||
use crate::program::Program;
|
use crate::program::Program;
|
||||||
use crate::runtime::futures::futures::StreamExt;
|
use crate::runtime::futures::futures::StreamExt;
|
||||||
|
|
@ -185,19 +186,41 @@ impl<P: Program + 'static> Emulator<P> {
|
||||||
&mut self.clipboard,
|
&mut self.clipboard,
|
||||||
&mut messages,
|
&mut messages,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
self.cache = Some(user_interface.into_cache());
|
||||||
|
|
||||||
|
let task =
|
||||||
|
Task::batch(messages.into_iter().map(|message| {
|
||||||
|
program.update(&mut self.state, message)
|
||||||
|
}));
|
||||||
|
|
||||||
|
self.wait_for(task);
|
||||||
|
self.resubscribe(program);
|
||||||
}
|
}
|
||||||
|
Instruction::Expect(expectation) => match expectation {
|
||||||
|
instruction::Expectation::Presence(selector) => {
|
||||||
|
use widget::Operation;
|
||||||
|
|
||||||
|
let mut operation = selector.operation();
|
||||||
|
|
||||||
|
user_interface.operate(
|
||||||
|
&self.renderer,
|
||||||
|
&mut widget::operation::black_box(&mut operation),
|
||||||
|
);
|
||||||
|
|
||||||
|
match operation.finish() {
|
||||||
|
widget::operation::Outcome::Some(Some(_)) => {
|
||||||
|
self.runtime.send(Event::Ready);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
self.runtime.send(Event::Failed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.cache = Some(user_interface.into_cache());
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
self.cache = Some(user_interface.into_cache());
|
|
||||||
|
|
||||||
let task = Task::batch(
|
|
||||||
messages
|
|
||||||
.into_iter()
|
|
||||||
.map(|message| program.update(&mut self.state, message)),
|
|
||||||
);
|
|
||||||
|
|
||||||
self.wait_for(task);
|
|
||||||
self.resubscribe(program);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn wait_for(&mut self, task: Task<P::Message>) {
|
pub fn wait_for(&mut self, task: Task<P::Message>) {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
|
use crate::Selector;
|
||||||
use crate::core::keyboard;
|
use crate::core::keyboard;
|
||||||
use crate::core::mouse;
|
use crate::core::mouse;
|
||||||
use crate::core::{Event, Point};
|
use crate::core::{Event, Point};
|
||||||
|
use crate::selector;
|
||||||
use crate::simulator;
|
use crate::simulator;
|
||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
@ -8,6 +10,7 @@ use std::fmt;
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum Instruction {
|
pub enum Instruction {
|
||||||
Interact(Interaction),
|
Interact(Interaction),
|
||||||
|
Expect(Expectation),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Instruction {
|
impl Instruction {
|
||||||
|
|
@ -20,6 +23,7 @@ impl fmt::Display for Instruction {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Instruction::Interact(interaction) => interaction.fmt(f),
|
Instruction::Interact(interaction) => interaction.fmt(f),
|
||||||
|
Instruction::Expect(expectation) => expectation.fmt(f),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -337,6 +341,24 @@ mod format {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum Expectation {
|
||||||
|
Presence(Selector),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Expectation {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Expectation::Presence(Selector::Id(_id)) => {
|
||||||
|
write!(f, "expect id") // TODO
|
||||||
|
}
|
||||||
|
Expectation::Presence(Selector::Text(text)) => {
|
||||||
|
write!(f, "expect text \"{text}\"")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub use parser::Error as ParseError;
|
pub use parser::Error as ParseError;
|
||||||
|
|
||||||
mod parser {
|
mod parser {
|
||||||
|
|
@ -363,7 +385,11 @@ mod parser {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn instruction(input: &str) -> IResult<&str, Instruction> {
|
fn instruction(input: &str) -> IResult<&str, Instruction> {
|
||||||
map(interaction, Instruction::Interact).parse(input)
|
alt((
|
||||||
|
map(interaction, Instruction::Interact),
|
||||||
|
map(expectation, Instruction::Expect),
|
||||||
|
))
|
||||||
|
.parse(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn interaction(input: &str) -> IResult<&str, Interaction> {
|
fn interaction(input: &str) -> IResult<&str, Interaction> {
|
||||||
|
|
@ -414,6 +440,13 @@ mod parser {
|
||||||
.parse(input)
|
.parse(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn expectation(input: &str) -> IResult<&str, Expectation> {
|
||||||
|
map(preceded(tag("expect text "), string), |text| {
|
||||||
|
Expectation::Presence(selector::text(text))
|
||||||
|
})
|
||||||
|
.parse(input)
|
||||||
|
}
|
||||||
|
|
||||||
fn key(input: &str) -> IResult<&str, Key> {
|
fn key(input: &str) -> IResult<&str, Key> {
|
||||||
alt((
|
alt((
|
||||||
map(tag("enter"), |_| Key::Enter),
|
map(tag("enter"), |_| Key::Enter),
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
//! Select widgets of a user interface.
|
//! Select widgets of a user interface.
|
||||||
use crate::core::text;
|
use crate::core::text;
|
||||||
use crate::core::widget;
|
use crate::core::widget;
|
||||||
|
use crate::core::{Rectangle, Vector};
|
||||||
|
|
||||||
/// A selector describes a strategy to find a certain widget in a user interface.
|
/// A selector describes a strategy to find a certain widget in a user interface.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
|
@ -11,6 +12,165 @@ pub enum Selector {
|
||||||
Text(text::Fragment<'static>),
|
Text(text::Fragment<'static>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Selector {
|
||||||
|
pub fn operation<'a>(&self) -> impl widget::Operation<Option<Target>> + 'a {
|
||||||
|
match self {
|
||||||
|
Selector::Id(id) => {
|
||||||
|
struct FindById {
|
||||||
|
id: widget::Id,
|
||||||
|
target: Option<Target>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl widget::Operation<Option<Target>> for FindById {
|
||||||
|
fn container(
|
||||||
|
&mut self,
|
||||||
|
id: Option<&widget::Id>,
|
||||||
|
bounds: Rectangle,
|
||||||
|
operate_on_children: &mut dyn FnMut(
|
||||||
|
&mut dyn widget::Operation<Option<Target>>,
|
||||||
|
),
|
||||||
|
) {
|
||||||
|
if self.target.is_some() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if Some(&self.id) == id {
|
||||||
|
self.target = Some(Target { bounds });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
operate_on_children(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn scrollable(
|
||||||
|
&mut self,
|
||||||
|
id: Option<&widget::Id>,
|
||||||
|
bounds: Rectangle,
|
||||||
|
_content_bounds: Rectangle,
|
||||||
|
_translation: Vector,
|
||||||
|
_state: &mut dyn widget::operation::Scrollable,
|
||||||
|
) {
|
||||||
|
if self.target.is_some() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if Some(&self.id) == id {
|
||||||
|
self.target = Some(Target { bounds });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn text_input(
|
||||||
|
&mut self,
|
||||||
|
id: Option<&widget::Id>,
|
||||||
|
bounds: Rectangle,
|
||||||
|
_state: &mut dyn widget::operation::TextInput,
|
||||||
|
) {
|
||||||
|
if self.target.is_some() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if Some(&self.id) == id {
|
||||||
|
self.target = Some(Target { bounds });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn text(
|
||||||
|
&mut self,
|
||||||
|
id: Option<&widget::Id>,
|
||||||
|
bounds: Rectangle,
|
||||||
|
_text: &str,
|
||||||
|
) {
|
||||||
|
if self.target.is_some() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if Some(&self.id) == id {
|
||||||
|
self.target = Some(Target { bounds });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn custom(
|
||||||
|
&mut self,
|
||||||
|
id: Option<&widget::Id>,
|
||||||
|
bounds: Rectangle,
|
||||||
|
_state: &mut dyn std::any::Any,
|
||||||
|
) {
|
||||||
|
if self.target.is_some() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if Some(&self.id) == id {
|
||||||
|
self.target = Some(Target { bounds });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn finish(
|
||||||
|
&self,
|
||||||
|
) -> widget::operation::Outcome<Option<Target>>
|
||||||
|
{
|
||||||
|
widget::operation::Outcome::Some(self.target)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Box::new(FindById {
|
||||||
|
id: id.clone(),
|
||||||
|
target: None,
|
||||||
|
}) as Box<dyn widget::Operation<_>>
|
||||||
|
}
|
||||||
|
Selector::Text(text) => {
|
||||||
|
struct FindByText {
|
||||||
|
text: text::Fragment<'static>,
|
||||||
|
target: Option<Target>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl widget::Operation<Option<Target>> for FindByText {
|
||||||
|
fn container(
|
||||||
|
&mut self,
|
||||||
|
_id: Option<&widget::Id>,
|
||||||
|
_bounds: Rectangle,
|
||||||
|
operate_on_children: &mut dyn FnMut(
|
||||||
|
&mut dyn widget::Operation<Option<Target>>,
|
||||||
|
),
|
||||||
|
) {
|
||||||
|
if self.target.is_some() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
operate_on_children(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn text(
|
||||||
|
&mut self,
|
||||||
|
_id: Option<&widget::Id>,
|
||||||
|
bounds: Rectangle,
|
||||||
|
text: &str,
|
||||||
|
) {
|
||||||
|
if self.target.is_some() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.text == text {
|
||||||
|
self.target = Some(Target { bounds });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn finish(
|
||||||
|
&self,
|
||||||
|
) -> widget::operation::Outcome<Option<Target>>
|
||||||
|
{
|
||||||
|
widget::operation::Outcome::Some(self.target)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Box::new(FindByText {
|
||||||
|
text: text.clone(),
|
||||||
|
target: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<widget::Id> for Selector {
|
impl From<widget::Id> for Selector {
|
||||||
fn from(id: widget::Id) -> Self {
|
fn from(id: widget::Id) -> Self {
|
||||||
Self::Id(id)
|
Self::Id(id)
|
||||||
|
|
@ -32,3 +192,10 @@ pub fn id(id: impl Into<widget::Id>) -> Selector {
|
||||||
pub fn text(fragment: impl text::IntoFragment<'static>) -> Selector {
|
pub fn text(fragment: impl text::IntoFragment<'static>) -> Selector {
|
||||||
Selector::Text(fragment.into_fragment())
|
Selector::Text(fragment.into_fragment())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A specific area, normally containing a widget.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
pub struct Target {
|
||||||
|
/// The bounds of the area.
|
||||||
|
pub bounds: Rectangle,
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,12 +8,11 @@ use crate::core::theme;
|
||||||
use crate::core::time;
|
use crate::core::time;
|
||||||
use crate::core::widget;
|
use crate::core::widget;
|
||||||
use crate::core::window;
|
use crate::core::window;
|
||||||
use crate::core::{
|
use crate::core::{Element, Event, Font, Point, Settings, Size, SmolStr};
|
||||||
Element, Event, Font, Point, Rectangle, Settings, Size, SmolStr,
|
|
||||||
};
|
|
||||||
use crate::renderer;
|
use crate::renderer;
|
||||||
use crate::runtime::UserInterface;
|
use crate::runtime::UserInterface;
|
||||||
use crate::runtime::user_interface;
|
use crate::runtime::user_interface;
|
||||||
|
use crate::selector;
|
||||||
use crate::{Error, Selector};
|
use crate::{Error, Selector};
|
||||||
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
@ -36,13 +35,6 @@ pub struct Simulator<
|
||||||
messages: Vec<Message>,
|
messages: Vec<Message>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A specific area of a [`Simulator`], normally containing a widget.
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
|
||||||
pub struct Target {
|
|
||||||
/// The bounds of the area.
|
|
||||||
pub bounds: Rectangle,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, Message, Theme, Renderer> Simulator<'a, Message, Theme, Renderer>
|
impl<'a, Message, Theme, Renderer> Simulator<'a, Message, Theme, Renderer>
|
||||||
where
|
where
|
||||||
Theme: theme::Base,
|
Theme: theme::Base,
|
||||||
|
|
@ -111,148 +103,20 @@ where
|
||||||
pub fn find(
|
pub fn find(
|
||||||
&mut self,
|
&mut self,
|
||||||
selector: impl Into<Selector>,
|
selector: impl Into<Selector>,
|
||||||
) -> Result<Target, Error> {
|
) -> Result<selector::Target, Error> {
|
||||||
|
use widget::Operation;
|
||||||
|
|
||||||
let selector = selector.into();
|
let selector = selector.into();
|
||||||
|
let mut operation = selector.operation();
|
||||||
|
|
||||||
match &selector {
|
self.raw.operate(
|
||||||
Selector::Id(id) => {
|
&self.renderer,
|
||||||
struct FindById<'a> {
|
&mut widget::operation::black_box(&mut operation),
|
||||||
id: &'a widget::Id,
|
);
|
||||||
target: Option<Target>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl widget::Operation for FindById<'_> {
|
match operation.finish() {
|
||||||
fn container(
|
widget::operation::Outcome::Some(Some(target)) => Ok(target),
|
||||||
&mut self,
|
_ => Err(Error::NotFound(selector)),
|
||||||
id: Option<&widget::Id>,
|
|
||||||
bounds: Rectangle,
|
|
||||||
operate_on_children: &mut dyn FnMut(
|
|
||||||
&mut dyn widget::Operation<()>,
|
|
||||||
),
|
|
||||||
) {
|
|
||||||
if self.target.is_some() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if Some(self.id) == id {
|
|
||||||
self.target = Some(Target { bounds });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
operate_on_children(self);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn scrollable(
|
|
||||||
&mut self,
|
|
||||||
id: Option<&widget::Id>,
|
|
||||||
bounds: Rectangle,
|
|
||||||
_content_bounds: Rectangle,
|
|
||||||
_translation: core::Vector,
|
|
||||||
_state: &mut dyn widget::operation::Scrollable,
|
|
||||||
) {
|
|
||||||
if self.target.is_some() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if Some(self.id) == id {
|
|
||||||
self.target = Some(Target { bounds });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn text_input(
|
|
||||||
&mut self,
|
|
||||||
id: Option<&widget::Id>,
|
|
||||||
bounds: Rectangle,
|
|
||||||
_state: &mut dyn widget::operation::TextInput,
|
|
||||||
) {
|
|
||||||
if self.target.is_some() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if Some(self.id) == id {
|
|
||||||
self.target = Some(Target { bounds });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn text(
|
|
||||||
&mut self,
|
|
||||||
id: Option<&widget::Id>,
|
|
||||||
bounds: Rectangle,
|
|
||||||
_text: &str,
|
|
||||||
) {
|
|
||||||
if self.target.is_some() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if Some(self.id) == id {
|
|
||||||
self.target = Some(Target { bounds });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn custom(
|
|
||||||
&mut self,
|
|
||||||
id: Option<&widget::Id>,
|
|
||||||
bounds: Rectangle,
|
|
||||||
_state: &mut dyn std::any::Any,
|
|
||||||
) {
|
|
||||||
if self.target.is_some() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if Some(self.id) == id {
|
|
||||||
self.target = Some(Target { bounds });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut find = FindById { id, target: None };
|
|
||||||
self.raw.operate(&self.renderer, &mut find);
|
|
||||||
|
|
||||||
find.target.ok_or(Error::NotFound(selector))
|
|
||||||
}
|
|
||||||
Selector::Text(text) => {
|
|
||||||
struct FindByText<'a> {
|
|
||||||
text: &'a str,
|
|
||||||
target: Option<Target>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl widget::Operation for FindByText<'_> {
|
|
||||||
fn container(
|
|
||||||
&mut self,
|
|
||||||
_id: Option<&widget::Id>,
|
|
||||||
_bounds: Rectangle,
|
|
||||||
operate_on_children: &mut dyn FnMut(
|
|
||||||
&mut dyn widget::Operation<()>,
|
|
||||||
),
|
|
||||||
) {
|
|
||||||
if self.target.is_some() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
operate_on_children(self);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn text(
|
|
||||||
&mut self,
|
|
||||||
_id: Option<&widget::Id>,
|
|
||||||
bounds: Rectangle,
|
|
||||||
text: &str,
|
|
||||||
) {
|
|
||||||
if self.target.is_some() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.text == text {
|
|
||||||
self.target = Some(Target { bounds });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut find = FindByText { text, target: None };
|
|
||||||
self.raw.operate(&self.renderer, &mut find);
|
|
||||||
|
|
||||||
find.target.ok_or(Error::NotFound(selector))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -271,7 +135,7 @@ where
|
||||||
pub fn click(
|
pub fn click(
|
||||||
&mut self,
|
&mut self,
|
||||||
selector: impl Into<Selector>,
|
selector: impl Into<Selector>,
|
||||||
) -> Result<Target, Error> {
|
) -> Result<selector::Target, Error> {
|
||||||
let target = self.find(selector)?;
|
let target = self.find(selector)?;
|
||||||
self.point_at(target.bounds.center());
|
self.point_at(target.bounds.center());
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue