Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions src/Testing/Expect.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,19 @@ public function toBeVisible(): void
);
}

public function toBeEmpty(): void
{
if (!$this->subject instanceof LocatorInterface) {
throw new \InvalidArgumentException('toBeEmpty() can only be used with LocatorInterface');
}

$this->retryAssertion(
fn () => $this->subject->isEmpty(),
!$this->negated,
$this->negated ? 'Locator is empty, but expected not to be.' : 'Locator is not empty.'
);
}

public function toHaveText(string $text): void
{
if (!$this->subject instanceof LocatorInterface) {
Expand Down Expand Up @@ -250,6 +263,19 @@ public function toBeFocused(): void
);
}

public function toHaveFocus(): void
{
if (!$this->subject instanceof LocatorInterface) {
throw new \InvalidArgumentException('toHaveFocus() can only be used with LocatorInterface');
}

$this->retryAssertion(
fn () => (bool) $this->subject->evaluate('(element) => document.activeElement === element'),
!$this->negated,
$this->negated ? 'Locator has focus, but expected not to have.' : 'Locator has not focus.'
);
}

public function toHaveTitle(string $title): void
{
if (!$this->subject instanceof PageInterface) {
Expand Down Expand Up @@ -296,6 +322,51 @@ function () use ($url): string {
);
}

public function toHaveClass(string|array $class): void
{
if (!$this->subject instanceof LocatorInterface) {
throw new \InvalidArgumentException('toHaveClass() can only be used with LocatorInterface');
}

$expectedClasses = is_array($class) ? $class : [$class];

$this->retryAssertion(
function () use ($expectedClasses) {
\assert($this->subject instanceof LocatorInterface);
$elementClass = $this->subject->getAttribute('class');
if (null === $elementClass) {
return false;
}
$classes = explode(' ', $elementClass);
foreach ($expectedClasses as $expectedClass) {
if (!in_array($expectedClass, $classes, true)) {
return false;
}
}

return true;
},
!$this->negated,
$this->negated
? 'Locator has the specified class(es), but expected not to.'
: 'Locator does not have the specified class(es).',
function () use ($expectedClasses): string {
\assert($this->subject instanceof LocatorInterface);
$actual = (string) $this->subject->getAttribute('class');
$expected = implode(' ', $expectedClasses);

return $this->negated
? \sprintf('Expected class list not to contain %s. Actual: %s', \json_encode($expected), \json_encode($actual))
: \sprintf('Expected class list to contain %s. Actual: %s', \json_encode($expected), \json_encode($actual));
}
);
}

public function toHaveId(string $id): void
{
$this->toHaveAttribute('id', $id);
}

/**
* Core retry assertion mechanism with configurable timeout and polling.
*/
Expand Down
11 changes: 11 additions & 0 deletions src/Testing/ExpectInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,21 @@ public function toBeDisabled(): void;

public function toHaveCSS(string $name, string $value): void;

public function toHaveId(string $id): void;

/**
* @param string|string[] $class
*/
public function toHaveClass(string|array $class): void;

public function toBeEmpty(): void;

public function toHaveCount(int $count): void;

public function toBeFocused(): void;

public function toHaveFocus(): void;

public function toHaveTitle(string $title): void;

public function toHaveURL(string $url): void;
Expand Down