Skip to content

Commit

Permalink
Issue CollaboraOnline#28: Use waitFor() from Drupal core to avoid sta…
Browse files Browse the repository at this point in the history
…le element reference.

This is a combination of 4 commits by Francesco:
- Attempt to run tests without stale checks.
- Add messages for each failed assertion.
- The word count is an element and not an input.

And one commit to remove the older code.
  • Loading branch information
brummbar authored and donquixote committed Nov 5, 2024
1 parent 75c718b commit fdf2b2f
Showing 1 changed file with 14 additions and 92 deletions.
106 changes: 14 additions & 92 deletions tests/src/ExistingSiteJavascript/CollaboraIntegrationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,51 +34,23 @@ public function testCollaboraPreview(): void {
$this->drupalGet('/cool/view/' . $media->id());
$this->getSession()->switchToIFrame('collabora-online-viewer');

$this->waitUntilNoMessage(function (): string|null {
$canvas = $this->getCurrentPage()->find('css', 'canvas#document-canvas');
if (!$canvas) {
return 'Canvas element not found.';
}
return NULL;
});
$assert_session = $this->assertSession();
$canvas = $assert_session->waitForElement('css', 'canvas#document-canvas');
$this->assertNotNull($canvas, 'The canvas element was not found after 10 seconds.');

// Make sure the correct document was opened.
// Check the document name at the top of the editor.
// Wait until the document name element appears and has non-empty text.
$this->waitUntilNoMessage(function(): string|null {
// Get a fresh element reference in each iteration, to avoid a
// StaleElementReference exception.
$element = $this->getCurrentPage()->find('css', 'input#document-name-input');
if (!$element) {
return 'Document name element not found.';
}
$text = $element->getValue();
if (!$text) {
return 'Document name is empty: ' . var_export($text, TRUE);
}
$this->assertSame('shopping-list.txt', $text);
return NULL;
$document_field = $assert_session->waitForElement('css', 'input#document-name-input');
$this->assertNotNull($document_field, 'The document name input was not found after 10 seconds.');
$this->getCurrentPage()->waitFor(10, function() use ($document_field) {
return $document_field->getValue() === 'shopping-list.txt';
});
$this->assertEquals('shopping-list.txt', $document_field->getValue(), 'The document name input did not contain the correct value after 10 seconds.');

// The document text is in a canvas element, so instead we compare the
// word count and character count.
// Wait until the word counter element appears and has a value.
$this->waitUntilNoMessage(
fn (): string|null => $this->retryIfStaleElement(
function (): string|null {
$element = $this->getCurrentPage()->find('css', 'div#StateWordCount');
if (!$element) {
return 'Word count element not found.';
}
$count_string = $element->getText();
if (!$count_string) {
return 'Word count string is empty.';
}
$this->assertSame('2 words, 18 characters', $count_string);
return NULL;
},
),
);
$word_count_element = $assert_session->waitForElement('css', 'div#StateWordCount');
$this->assertNotNull($word_count_element, 'The word count element was not found after 10 seconds.');
$this->getCurrentPage()->waitFor(10, function() use ($word_count_element) {
return $word_count_element->getText() === '2 words, 18 characters';
});
$this->assertEquals('2 words, 18 characters', $word_count_element->getText(), 'The word count element did not contain the correct text after 10 seconds.');
}

/**
Expand Down Expand Up @@ -117,54 +89,4 @@ protected function createDocumentMedia(string $media_name, string $file_basename
return $media;
}

/**
* Calls a callback until it does not throw a StaleElementReference.
*
* @template T
*
* @param callable(): T $callback
* Callback to call and retry.
* @param int $max_calls
* Maximum number of times the callback should be called.
*
* @return T
* Return value from the last call to the callback.
*/
protected function retryIfStaleElement(callable $callback, int $max_calls = 10): mixed {
for ($i = 1; $i < $max_calls; ++$i) {
try {
return $callback();
}
catch (StaleElementReference) {
// Next iteration.
}
}
// Call it one more time, without try/catch.
return $callback();
}

/**
* Waits until a callback returns NULL.
*
* @param callable(): (string|null) $callback
* Callback that is called in each iteration.
* It should return NULL to wait no longer, or a string message to wait
* and start another iteration.
* @param int|float $max_seconds
* Maximum seconds of wait time.
*/
protected function waitUntilNoMessage(callable $callback, int|float $max_seconds = 10): void {
$start = microtime(TRUE);
$end = $start + $max_seconds;
do {
$message = $callback();
if ($message === NULL) {
return;
}
usleep(10000);
} while (microtime(TRUE) < $end);

$this->fail('Timeout: ' . $message);
}

}

0 comments on commit fdf2b2f

Please sign in to comment.