crossword-8.x-1.x-dev/tests/src/FunctionalJavascript/CrosswordFormatterTestBase.php
tests/src/FunctionalJavascript/CrosswordFormatterTestBase.php
<?php
namespace Drupal\Tests\crossword\FunctionalJavascript;
use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
use Drupal\node\Entity\Node;
/**
* Tests basic js functionality of crossword formatters.
*
* @group crossword
*/
abstract class CrosswordFormatterTestBase extends WebDriverTestBase {
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* The machine name of the formatter to test.
*
* @var string
*/
protected $formatter;
/**
* Name of test puzzle.
*
* @var string
*/
protected $testPuzzleFilename = 'test.txt';
/**
* Test the interactive crossword field formatter plugin.
*/
public function testCrosswordFormatter() {
$node = $this->createTestNode();
$this->assertEquals(1, $node->id());
// View the crossword node.
$this->drupalGet("crossword-tests/1/{$this->formatter}");
$assertSession = $this->assertSession();
$session = $this->getSession();
$page = $session->getPage();
// Check for presence of various test.
$assertSession->elementTextContains('css', '.crossword-title', "Test Puzzle");
$assertSession->elementTextContains('css', '.crossword-author', "Test Author");
// Columns classes.
$assertSession->elementExists('css', '.crossword-grid.crossword-columns-3.crossword-rows-3');
// No img in the black square.
$assertSession->elementNotExists('css', '[data-col="1"][data-row="0"] img');
// Check instructions toggle.
$assertSession->pageTextNotContains('Move active square');
$page->find('css', '.button-instructions')->click();
$assertSession->pageTextContains('Move active square');
$page->find('css', '.button-instructions')->click();
$assertSession->pageTextNotContains('Move active square');
// Initial settings.
$assertSession->checkboxNotChecked('show-errors');
$assertSession->checkboxChecked('show-references');
$assertSession->elementExists('css', 'div.show-references, body.show-references');
// Check initial active clue.
$assertSession->elementTextContains('css', '#active-clues', "Three-A Second square has a circle");
// Click on bottom left square. Check that active clues update.
$square = $page->find('css', '[data-col="0"][data-row="2"]');
$input = $page->find('css', '[data-col="0"][data-row="2"] input');
$clue_across = $page->find('css', '.crossword-clue[data-clue-index-across="1"]');
$clue_down = $page->find('css', '.crossword-clue[data-clue-index-down="0"]');
$square->click();
$this->assertSame('TWO', $square->getAttribute('data-fill'));
$this->assertTrue($square->hasClass('active'));
$this->assertTrue($clue_across->hasClass('active'));
$this->assertFalse($clue_down->hasClass('active'));
$assertSession->elementTextContains('css', '#active-clues', "Five-A Has a reference to 3-Across and 1-Down");
$assertSession->elementTextContains('css', '#active-clues', "Three-A Second square has a circle");
$assertSession->elementTextContains('css', '#active-clues', "One-D is AB2");
// Check the aria text.
$expected_aria_label = '5 across. Five-A Has a reference to 3-Across and 1-Down. 3 letters.';
$this->assertEquals($input->getAttribute('aria-label'), $expected_aria_label);
// Uncheck references. Make sure active clues update.
$page->find('css', '#show-references')->click();
$assertSession->elementTextContains('css', '#active-clues', "Five-A Has a reference to 3-Across and 1-Down");
$assertSession->elementTextNotContains('css', '#active-clues', "Three-A Second square has a circle");
$assertSession->elementTextNotContains('css', '#active-clues', "One-D is AB2");
// Click on bottom left square again. Check that direction toggles.
$square->click();
$this->assertTrue($square->hasClass('active'));
$this->assertFalse($clue_across->hasClass('active'));
$this->assertTrue($clue_down->hasClass('active'));
$assertSession->elementTextNotContains('css', '#active-clues', "Five-A Has a reference to 3-Across and 1-Down");
$assertSession->elementTextContains('css', '#active-clues', "One-D is AB2");
// Check that errors work on squares and clues while testing rebus.
$this->assertFalse($square->hasClass('error'));
$this->assertFalse($clue_across->hasClass('error'));
$square->click();
$input->setValue('b');
$this->assertTrue($square->hasClass('error'));
$this->assertTrue($clue_across->hasClass('error'));
// Go back.
$square->click();
// Check the aria text.
$expected_aria_label = '5 across. Five-A Has a reference to 3-Across and 1-Down. Answer: 3 letters. B. blank. blank.';
$this->assertEquals($input->getAttribute('aria-label'), $expected_aria_label);
// Show errors and check again.
$page->find('css', '#show-errors')->click();
$square->click();
$square->click();
$expected_aria_label = '5 across. Five-A Has a reference to 3-Across and 1-Down. Answer: 3 letters. B. blank. blank. Contains error.';
$this->assertEquals($input->getAttribute('aria-label'), $expected_aria_label);
$page->find('css', '#show-errors')->click();
// Back to setting values.
$input->setValue('T');
$this->assertTrue($square->hasClass('error'));
$this->assertTrue($clue_across->hasClass('error'));
// Activate rebus entry.
$square->click();
$page->find('css', '#rebus-entry')->click();
$input->setValue('W');
$this->assertTrue($square->hasClass('error'));
$this->assertTrue($clue_across->hasClass('error'));
$input->setValue('O');
$this->assertFalse($square->hasClass('error'));
$this->assertFalse($clue_across->hasClass('error'));
$page->find('css', '#rebus-entry')->click();
// Check that some buttons work.
// Previous across clue button.
$page->find('css', '.crossword-clue-change.prev')->click();
$new_active_square = $page->find('css', '[data-col="0"][data-row="1"]');
$new_active_clue = $page->find('css', '.crossword-clue[data-clue-index-across="0"]');
$this->assertTrue($new_active_square->hasClass('active'));
$this->assertTrue($new_active_clue->hasClass('active'));
$this->assertFalse($square->hasClass('active'));
$this->assertFalse($clue_across->hasClass('active'));
// Cheat button.
$page->find('css', '.button-cheat')->click();
$this->assertTrue($new_active_square->hasClass('cheat'));
$this->assertTrue($new_active_clue->hasClass('cheat'));
$assertSession->elementTextContains('css', '[data-col="0"][data-row="1"] .square-fill', 'B');
// Check out undo and redo. First cheat one more time.
$page->find('css', '.button-cheat')->click();
$assertSession->elementTextContains('css', '[data-col="1"][data-row="1"] .square-fill', 'C');
$assertSession->elementTextContains('css', '[data-col="0"][data-row="1"] .square-fill', 'B');
// Now undo.
$page->find('css', '.button-undo')->click();
$assertSession->elementTextNotContains('css', '[data-col="1"][data-row="1"] .square-fill', 'C');
$assertSession->elementTextContains('css', '[data-col="0"][data-row="1"] .square-fill', 'B');
// Now undo again.
$page->find('css', '.button-undo')->click();
$assertSession->elementTextNotContains('css', '[data-col="1"][data-row="1"] .square-fill', 'C');
$assertSession->elementTextNotContains('css', '[data-col="0"][data-row="1"] .square-fill', 'B');
// Now redo.
$page->find('css', '.button-redo')->click();
$assertSession->elementTextNotContains('css', '[data-col="1"][data-row="1"] .square-fill', 'C');
$assertSession->elementTextContains('css', '[data-col="0"][data-row="1"] .square-fill', 'B');
// Now redo again.
$page->find('css', '.button-redo')->click();
$assertSession->elementTextContains('css', '[data-col="1"][data-row="1"] .square-fill', 'C');
$assertSession->elementTextContains('css', '[data-col="0"][data-row="1"] .square-fill', 'B');
// Next clue express should advance to next across clue.
$page->find('css', '.next-clue-express')->click();
$new_active_square = $page->find('css', '[data-col="0"][data-row="2"]');
$new_active_clue = $page->find('css', '.crossword-clue[data-clue-index-across="1"]');
$this->assertTrue($new_active_square->hasClass('active'));
$this->assertTrue($new_active_clue->hasClass('active'));
// Now next clue express should get to the first down clue.
$page->find('css', '.next-clue-express')->click();
$new_active_square = $page->find('css', '[data-col="0"][data-row="0"]');
$new_active_clue = $page->find('css', '.crossword-clue[data-clue-index-down="0"]');
$this->assertTrue($new_active_square->hasClass('active'));
$this->assertTrue($new_active_clue->hasClass('active'));
// Test complete class on clues. Three-A is almost complete. Use it.
$clue = $page->find('css', '.crossword-clue[data-clue-index-across="0"]');
$clue->click();
$this->assertFalse($clue->hasClass('complete'));
$page->find('css', '[data-col="2"][data-row="1"]')->click();
$page->find('css', '.button-cheat')->click();
$this->assertTrue($clue->hasClass('complete'));
$page->find('css', '.button-undo')->click();
$this->assertFalse($clue->hasClass('complete'));
// Aria text should not be on last square of clue.
$this->assertEmpty($page->find('css', '[data-col="2"][data-row="1"] input')->getAttribute('aria-label'));
// Check that local storage works.
// Reload the page.
$this->drupalGet("crossword-tests/1/{$this->formatter}");
$assertSession = $this->assertSession();
$session = $this->getSession();
$page = $session->getPage();
$assertSession->elementTextNotContains('css', '[data-col="0"][data-row="0"] .square-fill', 'A');
$assertSession->elementTextNotContains('css', '[data-col="2"][data-row="0"] .square-fill', 'ONE');
$assertSession->elementTextContains('css', '[data-col="0"][data-row="1"] .square-fill', 'B');
$assertSession->elementTextContains('css', '[data-col="1"][data-row="1"] .square-fill', 'C');
$assertSession->elementTextNotContains('css', '[data-col="2"][data-row="1"] .square-fill', 'D');
$assertSession->elementTextContains('css', '[data-col="0"][data-row="2"] .square-fill', 'TWO');
$assertSession->elementTextNotContains('css', '[data-col="1"][data-row="2"] .square-fill', 'E');
$assertSession->elementTextNotContains('css', '[data-col="2"][data-row="2"] .square-fill', 'F');
// Settings should persist: errors and references should both be off.
$assertSession->checkboxNotChecked('show-errors');
$assertSession->checkboxNotChecked('show-references');
$assertSession->elementNotExists('css', 'div.show-references, body.show-references');
// Test crossword-revealed and crossword-solved classes and congrats.
// This also covers solution and clear buttons.
$assertSession->elementNotExists('css', '.crossword.crossword-solved, body.crossword-solved');
$assertSession->elementNotExists('css', '.crossword.crossword-revealed, body.crossword-revealed');
$assertSession->pageTextNotContains('Well done!');
$page->find('css', '.button-solution')->click();
$session->getDriver()->getWebDriverSession()->accept_alert();
$assertSession->elementNotExists('css', '.crossword.crossword-solved, body.crossword-solved');
$assertSession->elementExists('css', '.crossword.crossword-revealed, body.crossword-revealed');
$assertSession->pageTextNotContains('Well done!');
// Check aria-text of top left square.
$page->find('css', '[data-col="0"][data-row="0"]')->click();
$expected_aria_label = 'The puzzle has been revealed. 1 down. One-D is AB2. Answer: 3 letters. A. B. TWO.';
$this->assertEquals($page->find('css', '[data-col="0"][data-row="0"] input')->getAttribute('aria-label'), $expected_aria_label);
// Check express button on solved puzzle. Should go to next down clue.
$page->find('css', '.next-clue-express')->click();
$new_active_square = $page->find('css', '[data-col="2"][data-row="0"]');
$new_active_clue = $page->find('css', '.crossword-clue[data-clue-index-down="1"]');
$this->assertTrue($new_active_square->hasClass('active'));
$this->assertTrue($new_active_clue->hasClass('active'));
// "Revealed" status should persist on page load.
$this->drupalGet("crossword-tests/1/{$this->formatter}");
$assertSession->elementNotExists('css', '.crossword.crossword-solved, body.crossword-solved');
$assertSession->elementExists('css', '.crossword.crossword-revealed, body.crossword-revealed');
$assertSession->pageTextNotContains('Well done!');
$page->find('css', '[data-col="0"][data-row="0"]')->click();
$expected_aria_label = 'The puzzle has been revealed. 1 down. One-D is AB2. Answer: 3 letters. A. B. TWO.';
$this->assertEquals($page->find('css', '[data-col="0"][data-row="0"] input')->getAttribute('aria-label'), $expected_aria_label);
// Clear button should clear classes.
$page->find('css', '.button-clear')->click();
$session->getDriver()->getWebDriverSession()->accept_alert();
$assertSession->elementNotExists('css', '.crossword.crossword-solved, body.crossword-solved');
$assertSession->elementNotExists('css', '.crossword.crossword-revealed, body.crossword-revealed');
$assertSession->pageTextNotContains('Well done!');
$page->find('css', '[data-col="0"][data-row="0"]')->click();
$expected_aria_label = '1 down. One-D is AB2. 3 letters.';
$this->assertEquals($page->find('css', '[data-col="0"][data-row="0"] input')->getAttribute('aria-label'), $expected_aria_label);
// Enter all correct answers (by cheating).
$page->find('css', '[data-col="0"][data-row="0"]')->click();
$page->find('css', '.button-cheat')->click();
$page->find('css', '.button-cheat')->click();
$page->find('css', '.button-cheat')->click();
$page->find('css', '.button-cheat')->click();
$page->find('css', '.button-cheat')->click();
$page->find('css', '.button-cheat')->click();
$page->find('css', '.button-cheat')->click();
$page->find('css', '.button-cheat')->click();
$this->drupalGet("crossword-tests/1/{$this->formatter}");
$assertSession->elementExists('css', '.crossword.crossword-solved, body.crossword-solved');
$assertSession->elementNotExists('css', '.crossword.crossword-revealed, body.crossword-revealed');
$assertSession->pageTextContains('Well done!');
$page->find('css', '[data-col="0"][data-row="0"]')->click();
$expected_aria_label = 'The puzzle has been solved. Well done! 1 down. One-D is AB2. Answer: 3 letters. A. B. TWO.';
$this->assertEquals($page->find('css', '[data-col="0"][data-row="0"] input')->getAttribute('aria-label'), $expected_aria_label);
$page->find('css', '#show-errors')->click();
$page->find('css', '[data-col="0"][data-row="0"]')->click();
$expected_aria_label = 'The puzzle has been solved. Well done! 1 down. One-D is AB2. Answer: 3 letters. A. B. TWO.';
$this->assertEquals($page->find('css', '[data-col="0"][data-row="0"] input')->getAttribute('aria-label'), $expected_aria_label);
$page->find('css', '#show-errors')->click();
$page->find('css', '.button-clear')->click();
$this->assertEquals('Do you really want to clear? This action cannot be undone.', $session->getDriver()->getWebDriverSession()->getAlert_text());
$session->getDriver()->getWebDriverSession()->accept_alert();
$assertSession->elementNotExists('css', '.crossword.crossword-solved, body.crossword-solved');
$assertSession->elementNotExists('css', '.crossword.crossword-revealed, body.crossword-revealed');
$assertSession->pageTextNotContains('Well done!');
}
/**
* Helper function to create node that can be viewed and used for testing.
*
* @return Drupal\node\Entity\Node
* A crossword node that can be used in tests.
*/
protected function createTestNode() {
// First we move a test file to the file system.
$contents = file_get_contents(\Drupal::service('extension.list.module')->getPath('crossword') . "/tests/files/{$this->testPuzzleFilename}");
$file = \Drupal::service('file.repository')->writeData($contents, "public://{$this->testPuzzleFilename}");
// Now use that file in a new crossword node.
$node = Node::create(['type' => 'crossword']);
$node->set('title', 'Test Crossword Node');
$node->set('field_crossword', $file->id());
$node->set('status', 1);
$node->save();
return $node;
}
}
