class SimplenewsSendTest in Simplenews 3.x
Same name and namespace in other branches
- 8.2 tests/src/Functional/SimplenewsSendTest.php \Drupal\Tests\simplenews\Functional\SimplenewsSendTest
Test cases for creating and sending newsletters.
@group simplenews
Hierarchy
- class \Drupal\Tests\BrowserTestBase extends \PHPUnit\Framework\TestCase uses \Symfony\Bridge\PhpUnit\ExpectDeprecationTrait, FunctionalTestSetupTrait, TestSetupTrait, AssertLegacyTrait, BlockCreationTrait, ConfigTestTrait, ExtensionListTestTrait, ContentTypeCreationTrait, NodeCreationTrait, PhpUnitCompatibilityTrait, RandomGeneratorTrait, TestRequirementsTrait, PhpUnitWarnings, UiHelperTrait, UserCreationTrait, XdebugRequestTrait
- class \Drupal\Tests\simplenews\Functional\SimplenewsTestBase uses AssertMailTrait, CronRunTrait
- class \Drupal\Tests\simplenews\Functional\SimplenewsSendTest
- class \Drupal\Tests\simplenews\Functional\SimplenewsTestBase uses AssertMailTrait, CronRunTrait
Expanded class hierarchy of SimplenewsSendTest
File
- tests/
src/ Functional/ SimplenewsSendTest.php, line 16
Namespace
Drupal\Tests\simplenews\FunctionalView source
class SimplenewsSendTest extends SimplenewsTestBase {
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$admin_user = $this
->drupalCreateUser([
'administer newsletters',
'send newsletter',
'administer nodes',
'administer simplenews subscriptions',
'create simplenews_issue content',
'edit any simplenews_issue content',
'view own unpublished content',
'delete any simplenews_issue content',
]);
$this
->drupalLogin($admin_user);
// Subscribe a few users.
$this
->setUpSubscribers(5);
}
/**
* Creates and sends a node using the API.
*/
public function testProgrammaticNewsletter() {
// Create a very basic node.
$node = Node::create([
'type' => 'simplenews_issue',
'title' => $this
->randomString(10),
'uid' => 0,
'status' => 1,
]);
$node->simplenews_issue->target_id = $this
->getRandomNewsletter();
$node->simplenews_issue->handler = 'simplenews_all';
$node
->save();
// Send the node.
\Drupal::service('simplenews.spool_storage')
->addIssue($node);
// Send mails.
\Drupal::service('simplenews.mailer')
->sendSpool();
\Drupal::service('simplenews.spool_storage')
->clear();
// Update sent status for newsletter admin panel.
\Drupal::service('simplenews.mailer')
->updateSendStatus();
// Verify mails.
$mails = $this
->getMails();
$this
->assertEquals(5, count($mails), t('All mails were sent.'));
foreach ($mails as $mail) {
$this
->assertEqual($mail['subject'], '[Default newsletter] ' . $node
->getTitle(), t('Mail has correct subject'));
$this
->assertTrue(isset($this->subscribers[$mail['to']]), t('Found valid recipient'));
unset($this->subscribers[$mail['to']]);
}
$this
->assertEquals(0, count($this->subscribers), t('all subscribers have been received a mail'));
// Create another node.
$node = Node::create([
'type' => 'simplenews_issue',
'title' => $this
->randomString(10),
'uid' => 0,
'status' => 1,
]);
$node->simplenews_issue->target_id = $this
->getRandomNewsletter();
$node->simplenews_issue->handler = 'simplenews_all';
$node
->save();
// Send the node.
\Drupal::service('simplenews.spool_storage')
->addIssue($node);
// Make sure that they have been added.
$this
->assertEqual(\Drupal::service('simplenews.spool_storage')
->countMails(), 5);
// Mark them as 'in progress', fake a currently running send process.
$this
->assertEqual(count(\Drupal::service('simplenews.spool_storage')
->getMails(2)), 2);
// Those two should be excluded if we get mails a second time.
$this
->assertEqual(count(\Drupal::service('simplenews.spool_storage')
->getMails()), 3);
// The count should still include all the mails because they are still
// in the spool. This is needed for correct operation of code such as
// Mailer::updateSendStatus().
$this
->assertEqual(\Drupal::service('simplenews.spool_storage')
->countMails(), 5);
}
/**
* Send a newsletter without cron.
*/
public function testSendNowNoCron() {
// Disable cron.
$config = $this
->config('simplenews.settings');
$config
->set('mail.use_cron', FALSE);
$config
->save();
// Verify that the newsletter settings are shown.
$this
->drupalGet('node/add/simplenews_issue');
$this
->assertText(t('Create Newsletter Issue'));
$edit = [
'title[0][value]' => $this
->randomString(10),
'simplenews_issue[target_id]' => 'default',
];
$this
->submitForm($edit, 'Save');
$this
->assertEquals(1, preg_match('|node/(\\d+)$|', $this
->getUrl(), $matches), 'Node created');
$node = Node::load($matches[1]);
$this
->clickLink(t('Newsletter'));
$this
->assertText(t('Send'));
$this
->assertText(t('Test'));
$this
->assertNoText(t('Send newsletter when published'));
// Verify state.
$this
->assertEquals(SIMPLENEWS_STATUS_SEND_NOT, $node->simplenews_issue->status, t('Newsletter not sent yet.'));
// Send now.
$this
->submitForm([], 'Send now');
// Verify state.
\Drupal::entityTypeManager()
->getStorage('node')
->resetCache();
$node = Node::load($node
->id());
$this
->assertEquals(SIMPLENEWS_STATUS_SEND_READY, $node->simplenews_issue->status, t('Newsletter sending finished'));
// Verify mails.
$mails = $this
->getMails();
$this
->assertEquals(5, count($mails), t('All mails were sent.'));
foreach ($mails as $mail) {
$this
->assertEqual($mail['subject'], '[Default newsletter] ' . $edit['title[0][value]'], t('Mail has correct subject'));
$this
->assertTrue(isset($this->subscribers[$mail['to']]), t('Found valid recipient'));
unset($this->subscribers[$mail['to']]);
}
$this
->assertEquals(0, count($this->subscribers), t('all subscribers have been received a mail'));
$this
->assertEquals(5, $node->simplenews_issue->sent_count, 'subscriber count is correct');
}
/**
* Send multiple newsletters without cron.
*/
public function testSendMultipleNoCron() {
// Disable cron.
$config = $this
->config('simplenews.settings');
$config
->set('mail.use_cron', FALSE);
$config
->save();
// Verify that the newsletter settings are shown.
$nodes = [];
for ($i = 0; $i < 3; $i++) {
$this
->drupalGet('node/add/simplenews_issue');
$this
->assertText(t('Create Newsletter Issue'));
$edit = [
'title[0][value]' => $this
->randomString(10),
'simplenews_issue[target_id]' => 'default',
// The last newsletter shouldn't be published.
'status[value]' => $i != 2,
];
$this
->submitForm($edit, 'Save');
$this
->assertEqual(1, preg_match('|node/(\\d+)$|', $this
->getUrl(), $matches), 'Node created');
$nodes[] = Node::load($matches[1]);
// Verify state.
$node = current($nodes);
$this
->assertEqual(SIMPLENEWS_STATUS_SEND_NOT, $node->simplenews_issue->status, t('Newsletter not sent yet.'));
}
}
/**
* Send a newsletter using cron and a low throttle.
*/
public function testSendNowCronThrottle() {
$config = $this
->config('simplenews.settings');
$config
->set('mail.throttle', 3);
$config
->save();
// Verify that the newsletter settings are shown.
$this
->drupalGet('node/add/simplenews_issue');
$this
->assertText(t('Create Newsletter Issue'));
$edit = [
'title[0][value]' => $this
->randomString(10),
'simplenews_issue[target_id]' => 'default',
];
$this
->submitForm($edit, 'Save');
$this
->assertEqual(1, preg_match('|node/(\\d+)$|', $this
->getUrl(), $matches), 'Node created');
$node = Node::load($matches[1]);
$this
->clickLink(t('Newsletter'));
$this
->assertText(t('Send'));
$this
->assertText(t('Test'));
// Verify state.
\Drupal::entityTypeManager()
->getStorage('node')
->resetCache();
$node = Node::load($node
->id());
$this
->assertEqual(SIMPLENEWS_STATUS_SEND_NOT, $node->simplenews_issue->status, t('Newsletter not sent yet.'));
// Send now.
$this
->submitForm([], 'Send now');
// Verify state.
\Drupal::entityTypeManager()
->getStorage('node')
->resetCache();
$node = Node::load($node
->id());
$this
->assertEqual(SIMPLENEWS_STATUS_SEND_PENDING, $node->simplenews_issue->status, t('Newsletter sending pending.'));
// Verify that no mails have been sent yet.
$mails = $this
->getMails();
$this
->assertEqual(0, count($mails), t('No mails were sent yet.'));
$spooled = \Drupal::database()
->query('SELECT COUNT(*) FROM {simplenews_mail_spool} WHERE entity_id = :nid AND entity_type = :type', [
':nid' => $node
->id(),
':type' => 'node',
])
->fetchField();
$this
->assertEqual(5, $spooled, t('5 mails have been added to the mail spool'));
// Run cron for the first time.
simplenews_cron();
// Verify state.
\Drupal::entityTypeManager()
->getStorage('node')
->resetCache();
$node = Node::load($node
->id());
$this
->assertEqual(SIMPLENEWS_STATUS_SEND_PENDING, $node->simplenews_issue->status, t('Newsletter sending pending.'));
$this
->assertEqual(3, $node->simplenews_issue->sent_count, 'subscriber count is correct');
$spooled = \Drupal::database()
->query('SELECT COUNT(*) FROM {simplenews_mail_spool} WHERE entity_id = :nid AND entity_type = :type', [
':nid' => $node
->id(),
':type' => 'node',
])
->fetchField();
$this
->assertEqual(2, $spooled, t('2 mails remaining in spool.'));
// Run cron for the second time.
simplenews_cron();
// Verify state.
\Drupal::entityTypeManager()
->getStorage('node')
->resetCache();
$node = Node::load($node
->id());
$this
->assertEqual(SIMPLENEWS_STATUS_SEND_READY, $node->simplenews_issue->status, t('Newsletter sending finished.'));
$spooled = \Drupal::database()
->query('SELECT COUNT(*) FROM {simplenews_mail_spool} WHERE entity_id = :nid AND entity_type = :type', [
':nid' => $node
->id(),
':type' => 'node',
])
->fetchField();
$this
->assertEqual(0, $spooled, t('No mails remaining in spool.'));
// Verify mails.
$mails = $this
->getMails();
$this
->assertEqual(5, count($mails), t('All mails were sent.'));
foreach ($mails as $mail) {
$this
->assertEqual($mail['subject'], '[Default newsletter] ' . $edit['title[0][value]'], t('Mail has correct subject'));
$this
->assertTrue(isset($this->subscribers[$mail['to']]), t('Found valid recipient'));
unset($this->subscribers[$mail['to']]);
}
$this
->assertEqual(0, count($this->subscribers), t('all subscribers have been received a mail'));
$this
->assertEqual(5, $node->simplenews_issue->sent_count);
}
/**
* Send a newsletter using cron.
*/
public function testSendNowCron() {
// Verify that the newsletter settings are shown.
$this
->drupalGet('node/add/simplenews_issue');
$this
->assertText(t('Create Newsletter Issue'));
$edit = [
'title[0][value]' => $this
->randomString(10),
'simplenews_issue[target_id]' => 'default',
];
// Try preview first.
$this
->submitForm($edit, 'Preview');
$this
->clickLink(t('Back to content editing'));
// Then save.
$this
->submitForm([], 'Save');
$this
->assertEqual(1, preg_match('|node/(\\d+)$|', $this
->getUrl(), $matches), 'Node created');
$node = Node::load($matches[1]);
$this
->clickLink(t('Newsletter'));
$this
->assertText(t('Send'));
$this
->assertText(t('Test'));
// Verify state.
\Drupal::entityTypeManager()
->getStorage('node')
->resetCache();
$node = Node::load($node
->id());
$this
->assertEqual(SIMPLENEWS_STATUS_SEND_NOT, $node->simplenews_issue->status, t('Newsletter not sent yet.'));
// Send now.
$this
->submitForm([], 'Send now');
// Verify state.
\Drupal::entityTypeManager()
->getStorage('node')
->resetCache();
$node = Node::load($node
->id());
$this
->assertEqual(SIMPLENEWS_STATUS_SEND_PENDING, $node->simplenews_issue->status, t('Newsletter sending pending.'));
// Verify that no mails have been sent yet.
$mails = $this
->getMails();
$this
->assertEqual(0, count($mails), t('No mails were sent yet.'));
$spooled = \Drupal::database()
->query('SELECT COUNT(*) FROM {simplenews_mail_spool} WHERE entity_id = :nid AND entity_type = :type', [
':nid' => $node
->id(),
':type' => 'node',
])
->fetchField();
$this
->assertEqual(5, $spooled, t('5 mails have been added to the mail spool'));
// Check warning message on node edit form.
$this
->clickLink(t('Edit'));
$this
->assertText(t('This newsletter issue is currently being sent. Any changes will be reflected in the e-mails which have not been sent yet.'));
// Run cron.
simplenews_cron();
// Verify state.
\Drupal::entityTypeManager()
->getStorage('node')
->resetCache();
$node = Node::load($node
->id());
$this
->assertEqual(SIMPLENEWS_STATUS_SEND_READY, $node->simplenews_issue->status, t('Newsletter sending finished.'));
$spooled = \Drupal::database()
->query('SELECT COUNT(*) FROM {simplenews_mail_spool} WHERE entity_id = :nid AND entity_type = :type', [
':nid' => $node
->id(),
':type' => 'node',
])
->fetchField();
$this
->assertEqual(0, $spooled, t('No mails remaining in spool.'));
// Verify mails.
$mails = $this
->getMails();
$this
->assertEqual(5, count($mails), t('All mails were sent.'));
foreach ($mails as $mail) {
$this
->assertEqual($mail['subject'], '[Default newsletter] ' . $edit['title[0][value]'], t('Mail has correct subject'));
$this
->assertTrue(isset($this->subscribers[$mail['to']]), t('Found valid recipient'));
unset($this->subscribers[$mail['to']]);
}
$this
->assertEqual(0, count($this->subscribers), t('all subscribers have been received a mail'));
}
/**
* Send a newsletter on publish without using cron.
*/
public function testSendPublishNoCron() {
// Disable cron.
$config = $this
->config('simplenews.settings');
$config
->set('mail.use_cron', FALSE);
$config
->save();
// Verify that the newsletter settings are shown.
$this
->drupalGet('node/add/simplenews_issue');
$this
->assertText(t('Create Newsletter Issue'));
$edit = [
'title[0][value]' => $this
->randomString(10),
'simplenews_issue[target_id]' => 'default',
'status[value]' => FALSE,
];
$this
->submitForm($edit, 'Save');
$this
->assertEqual(1, preg_match('|node/(\\d+)$|', $this
->getUrl(), $matches), 'Node created');
$node = Node::load($matches[1]);
$this
->clickLink(t('Newsletter'));
$this
->assertText(t('Send'));
$this
->assertText(t('Test'));
// Verify state.
\Drupal::entityTypeManager()
->getStorage('node')
->resetCache();
$node = Node::load($node
->id());
$this
->assertEqual(SIMPLENEWS_STATUS_SEND_NOT, $node->simplenews_issue->status, t('Newsletter not sent yet.'));
// Send now.
$this
->submitForm([], 'Send on publish');
// Verify state.
\Drupal::entityTypeManager()
->getStorage('node')
->resetCache([
$node
->id(),
]);
$node = Node::load($node
->id());
$this
->assertEqual(SIMPLENEWS_STATUS_SEND_PUBLISH, $node->simplenews_issue->status, t('Newsletter set up for sending on publish.'));
$this
->clickLink(t('Edit'));
$this
->submitForm([
'status[value]' => TRUE,
], 'Save');
// Send on publish does not send immediately.
\Drupal::entityTypeManager()
->getStorage('node')
->resetCache([
$node
->id(),
]);
\Drupal::service('simplenews.mailer')
->attemptImmediateSend([], FALSE);
// Verify state.
\Drupal::entityTypeManager()
->getStorage('node')
->resetCache([
$node
->id(),
]);
$node = Node::load($node
->id());
$this
->assertEqual(SIMPLENEWS_STATUS_SEND_READY, $node->simplenews_issue->status, t('Newsletter sending finished'));
// @todo test sent subscriber count.
// Verify mails.
$mails = $this
->getMails();
$this
->assertEqual(5, count($mails), t('All mails were sent.'));
foreach ($mails as $mail) {
$this
->assertEqual($mail['subject'], '[Default newsletter] ' . $edit['title[0][value]'], t('Mail has correct subject'));
$this
->assertTrue(isset($this->subscribers[$mail['to']]), t('Found valid recipient'));
unset($this->subscribers[$mail['to']]);
}
$this
->assertEqual(0, count($this->subscribers), t('all subscribers have been received a mail'));
}
/**
* Test newsletter update.
*
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
*/
public function testUpdateNewsletter() {
// Create a second newsletter.
$this
->drupalGet('admin/config/services/simplenews');
$this
->clickLink(t('Add newsletter'));
$edit = [
'name' => $this
->randomString(10),
'id' => strtolower($this
->randomMachineName(10)),
'description' => $this
->randomString(20),
];
$this
->submitForm($edit, 'Save');
$this
->assertText(t('Newsletter @name has been added', [
'@name' => $edit['name'],
]));
$this
->drupalGet('node/add/simplenews_issue');
$this
->assertText(t('Create Newsletter Issue'));
$first_newsletter_id = $this
->getRandomNewsletter();
$edit = [
'title[0][value]' => $this
->randomString(10),
'simplenews_issue[target_id]' => $first_newsletter_id,
];
$this
->submitForm($edit, 'Save');
$this
->assertEquals(1, preg_match('|node/(\\d+)$|', $this
->getUrl(), $matches), 'Node created.');
// Verify newsletter.
\Drupal::entityTypeManager()
->getStorage('node')
->resetCache();
$node = Node::load($matches[1]);
$this
->assertEquals(SIMPLENEWS_STATUS_SEND_NOT, $node->simplenews_issue->status, t('Newsletter sending not started.'));
$this
->assertEquals($first_newsletter_id, $node->simplenews_issue->target_id);
do {
$second_newsletter_id = $this
->getRandomNewsletter();
} while ($first_newsletter_id == $second_newsletter_id);
$this
->clickLink(t('Edit'));
$update = [
'simplenews_issue[target_id]' => $second_newsletter_id,
];
$this
->submitForm($update, 'Save');
// Verify newsletter.
\Drupal::entityTypeManager()
->getStorage('node')
->resetCache();
$node = Node::load($node
->id());
$this
->assertEquals(SIMPLENEWS_STATUS_SEND_NOT, $node->simplenews_issue->status, t('Newsletter sending not started.'));
$this
->assertEquals($second_newsletter_id, $node->simplenews_issue->target_id, t('Newsletter has newsletter_id @id.', [
'@id' => $second_newsletter_id,
]));
}
/**
* Tests failing to send mails from cron.
*/
public function testSendFail() {
// Create and send an issue.
$issue = Node::create([
'type' => 'simplenews_issue',
'title' => $this
->randomString(10),
'simplenews_issue' => [
'target_id' => $this
->getRandomNewsletter(),
],
]);
$issue
->save();
\Drupal::service('simplenews.spool_storage')
->addIssue($issue);
// Force some mails to fail, then abort.
\Drupal::messenger()
->deleteAll();
$results_alter = [
SpoolStorageInterface::STATUS_PENDING,
SpoolStorageInterface::STATUS_FAILED,
-1,
];
$this->container
->get('state')
->set('simplenews.test_result_alter', $results_alter);
simplenews_cron();
// Check there is no error message.
$this
->assertEqual(count(\Drupal::messenger()
->messagesByType(MessengerInterface::TYPE_ERROR)), 0, t('No error messages printed'));
// Check the status on the newsletter tab. The pending mail should be
// retried.
$this
->drupalGet('node/1/simplenews');
$this
->assertText('Newsletter issue is pending, 0 mails sent out of 5, 1 errors.');
// Allow one mail to succeed, and the pending mail should be treated as an
// error.
$results_alter = [
SpoolStorageInterface::STATUS_DONE,
SpoolStorageInterface::STATUS_PENDING,
SpoolStorageInterface::STATUS_FAILED,
];
$this->container
->get('state')
->set('simplenews.test_result_alter', $results_alter);
simplenews_cron();
$this
->drupalGet('node/1/simplenews');
$this
->assertText('Newsletter issue sent to 2 subscribers, 3 errors.');
}
/**
* Create a newsletter, send mails and then delete.
*/
public function testDelete() {
// Verify that the newsletter settings are shown.
$this
->drupalGet('node/add/simplenews_issue');
$this
->assertText(t('Create Newsletter Issue'));
// Prevent deleting the mail spool entries automatically.
$config = $this
->config('simplenews.settings');
$config
->set('mail.spool_expire', 1);
$config
->save();
$edit = [
'title[0][value]' => $this
->randomString(10),
'simplenews_issue[target_id]' => 'default',
];
$this
->submitForm($edit, 'Save');
$this
->assertEquals(1, preg_match('|node/(\\d+)$|', $this
->getUrl(), $matches), 'Node created');
$node = Node::load($matches[1]);
$this
->clickLink(t('Newsletter'));
$this
->assertText(t('Send'));
$this
->assertText(t('Test'));
// Verify state.
\Drupal::entityTypeManager()
->getStorage('node')
->resetCache();
$node = Node::load($node
->id());
$this
->assertEquals(SIMPLENEWS_STATUS_SEND_NOT, $node->simplenews_issue->status, t('Newsletter not sent yet.'));
// Send now.
$this
->submitForm([], 'Send now');
// Verify state.
\Drupal::entityTypeManager()
->getStorage('node')
->resetCache();
$node = Node::load($node
->id());
$this
->assertEquals(SIMPLENEWS_STATUS_SEND_PENDING, $node->simplenews_issue->status, t('Newsletter sending pending.'));
$spooled = \Drupal::database()
->query('SELECT COUNT(*) FROM {simplenews_mail_spool} WHERE entity_id = :nid AND entity_type = :type', [
':nid' => $node
->id(),
':type' => 'node',
])
->fetchField();
$this
->assertEquals(5, $spooled, t('5 mails remaining in spool.'));
// Verify that deleting isn't possible right now.
$this
->clickLink(t('Edit'));
$this
->assertNoText(t('Delete'));
// Send mails.
simplenews_cron();
// Verify state.
\Drupal::entityTypeManager()
->getStorage('node')
->resetCache();
$node = Node::load($node
->id());
$this
->assertEquals(SIMPLENEWS_STATUS_SEND_READY, $node->simplenews_issue->status, t('Newsletter sending finished'));
$spooled = \Drupal::database()
->query('SELECT COUNT(*) FROM {simplenews_mail_spool} WHERE entity_id = :nid AND entity_type = :type', [
':nid' => $node
->id(),
':type' => 'node',
])
->fetchField();
$this
->assertEquals(5, $spooled, t('Mails are kept in simplenews_mail_spool after being sent.'));
// Verify mails.
$mails = $this
->getMails();
$this
->assertEquals(5, count($mails), t('All mails were sent.'));
foreach ($mails as $mail) {
$this
->assertEqual($mail['subject'], '[Default newsletter] ' . $edit['title[0][value]'], t('Mail has correct subject'));
$this
->assertTrue(isset($this->subscribers[$mail['to']]), t('Found valid recipient'));
unset($this->subscribers[$mail['to']]);
}
$this
->assertEquals(0, count($this->subscribers), t('all subscribers have received a mail'));
// Update timestamp to simulate pending lock expiration.
\Drupal::database()
->update('simplenews_mail_spool')
->fields([
'timestamp' => REQUEST_TIME - $this
->config('simplenews.settings')
->get('mail.spool_progress_expiration') - 1,
])
->execute();
// Verify that kept mail spool rows are not re-sent.
simplenews_cron();
\Drupal::service('simplenews.spool_storage')
->getMails();
$mails = $this
->getMails();
$this
->assertEquals(5, count($mails), t('No additional mails have been sent.'));
// Now delete.
\Drupal::entityTypeManager()
->getStorage('node')
->resetCache();
$this
->drupalGet($node
->toUrl('edit-form'));
$this
->clickLink(t('Delete'));
$this
->submitForm([], 'Delete');
// Verify.
\Drupal::entityTypeManager()
->getStorage('node')
->resetCache();
$this
->assertEmpty(Node::load($node
->id()));
$spooled = \Drupal::database()
->query('SELECT COUNT(*) FROM {simplenews_mail_spool} WHERE entity_id = :nid AND entity_type = :type', [
':nid' => $node
->id(),
':type' => 'node',
])
->fetchField();
$this
->assertEquals(0, $spooled, t('No mails remaining in spool.'));
}
/**
* Test that the correct user is used when sending newsletters.
*/
public function testImpersonation() {
// Create user to manage subscribers.
$admin_user = $this
->drupalCreateUser([
'administer users',
]);
$this
->drupalLogin($admin_user);
// Add users for some existing subscribers.
$subscribers = array_slice($this->subscribers, -3);
$users = [];
foreach ($subscribers as $subscriber) {
$user = User::create([
'name' => $this
->randomMachineName(),
'mail' => $subscriber,
'status' => 1,
]);
$user
->save();
$users[$subscriber] = $user
->id();
}
// Create a very basic node.
$node = Node::create([
'type' => 'simplenews_issue',
'title' => $this
->randomString(10),
'uid' => '0',
'status' => 1,
'body' => 'User ID: [current-user:uid]',
]);
$node->simplenews_issue->target_id = $this
->getRandomNewsletter();
$node->simplenews_issue->handler = 'simplenews_all';
$node
->save();
// Send the node.
\Drupal::service('simplenews.spool_storage')
->addIssue($node);
// Send mails.
\Drupal::service('simplenews.mailer')
->sendSpool();
\Drupal::service('simplenews.spool_storage')
->clear();
// Update sent status for newsletter admin panel.
\Drupal::service('simplenews.mailer')
->updateSendStatus();
// Verify mails.
$mails = $this
->getMails();
// Check the mails sent to subscribers (who are also users) and verify each
// users uid in the mail body.
$mails_with_users = 0;
$mails_without_users = 0;
foreach ($mails as $mail) {
$body = $mail['body'];
$user_mail = $mail['to'];
if (isset($users[$user_mail])) {
if (strpos($body, 'User ID: ' . $users[$user_mail])) {
$mails_with_users++;
}
}
else {
if (strpos($body, 'User ID: not yet assigned')) {
$mails_without_users++;
}
}
}
$this
->assertEquals(3, $mails_with_users, '3 mails with user ids found');
$this
->assertEquals(2, $mails_without_users, '2 mails with no user ids found');
}
/**
* Test the theme suggestions when sending mails.
*/
public function testNewsletterTheme() {
// Install and enable the test theme.
\Drupal::service('theme_installer')
->install([
'simplenews_newsletter_test_theme',
]);
\Drupal::theme()
->setActiveTheme(\Drupal::service('theme.initialization')
->initTheme('simplenews_newsletter_test_theme'));
$node = Node::create([
'type' => 'simplenews_issue',
'title' => $this
->randomString(10),
'uid' => '0',
'status' => 1,
]);
$node->simplenews_issue->target_id = $this
->getRandomNewsletter();
$node->simplenews_issue->handler = 'simplenews_all';
$node
->save();
// Send the node.
\Drupal::service('simplenews.spool_storage')
->addIssue($node);
// Send mails.
\Drupal::service('simplenews.mailer')
->sendSpool();
\Drupal::service('simplenews.spool_storage')
->clear();
// Update sent status for newsletter admin panel.
\Drupal::service('simplenews.mailer')
->updateSendStatus();
$mails = $this
->getMails();
// Check if the correct theme was used in mails.
$this
->assertStringContainsString('Simplenews test theme', $mails[0]['body']);
$this
->assertEquals(1, preg_match('/ID: [0-9]/', $mails[0]['body']), 'Mail contains the subscriber ID');
}
/**
* Test the correct handling of HTML special characters in plain text mails.
*/
public function testHtmlEscaping() {
$title = '><\'"-&&--*';
$name = 'Rise & shine';
$node = Node::create([
'type' => 'simplenews_issue',
'title' => $title,
'uid' => '0',
'status' => 1,
]);
$node->simplenews_issue->target_id = $this
->getRandomNewsletter();
$node->simplenews_issue->handler = 'simplenews_all';
$node
->save();
$newsletter = Newsletter::load($node->simplenews_issue->target_id);
$newsletter->name = $name;
$newsletter->subject = '<[simplenews-newsletter:name]> [node:title]';
$newsletter
->save();
// Send the node.
\Drupal::service('simplenews.spool_storage')
->addIssue($node);
// Send mails.
\Drupal::service('simplenews.mailer')
->sendSpool();
\Drupal::service('simplenews.spool_storage')
->clear();
// Update sent status for newsletter admin panel.
\Drupal::service('simplenews.mailer')
->updateSendStatus();
$mails = $this
->getMails();
// Check subject and body.
// @todo Body is wrong due to
// https://www.drupal.org/project/drupal/issues/3174760
// $this->assertStringContainsString($title, $mails[0]['body']);
$this
->assertEquals("<{$name}> {$title}", $mails[0]['subject']);
}
}
Members
Name | Modifiers | Type | Description | Overrides |
---|---|---|---|---|
AssertLegacyTrait:: |
protected | function | ||
AssertLegacyTrait:: |
protected | function | Asserts whether an expected cache tag was present in the last response. | |
AssertLegacyTrait:: |
protected | function | Asserts that the element with the given CSS selector is not present. | |
AssertLegacyTrait:: |
protected | function | Asserts that the element with the given CSS selector is present. | |
AssertLegacyTrait:: |
protected | function | ||
AssertLegacyTrait:: |
protected | function | Passes if the raw text IS found escaped on the loaded page, fail otherwise. | |
AssertLegacyTrait:: |
protected | function | Asserts that a field exists with the given name or ID. | |
AssertLegacyTrait:: |
protected | function | Asserts that a field exists with the given ID and value. | |
AssertLegacyTrait:: |
protected | function | Asserts that a field exists with the given name and value. | |
AssertLegacyTrait:: |
protected | function | Asserts that a field exists in the current page by the given XPath. | |
AssertLegacyTrait:: |
protected | function | Asserts that a checkbox field in the current page is checked. | |
AssertLegacyTrait:: |
protected | function | Asserts that a field exists in the current page with a given Xpath result. | |
AssertLegacyTrait:: |
protected | function | Checks that current response header equals value. | |
AssertLegacyTrait:: |
protected | function | ||
AssertLegacyTrait:: |
protected | function | ||
AssertLegacyTrait:: |
protected | function | Passes if a link with the specified label is found. | |
AssertLegacyTrait:: |
protected | function | Passes if a link containing a given href (part) is found. | |
AssertLegacyTrait:: |
protected | function | Asserts whether an expected cache tag was absent in the last response. | |
AssertLegacyTrait:: |
protected | function | Passes if the raw text is not found escaped on the loaded page. | |
AssertLegacyTrait:: |
protected | function | Asserts that a field does NOT exist with the given name or ID. | |
AssertLegacyTrait:: |
protected | function | Asserts that a field does not exist with the given ID and value. | |
AssertLegacyTrait:: |
protected | function | Asserts that a field does not exist with the given name and value. | |
AssertLegacyTrait:: |
protected | function | Asserts that a field does not exist or its value does not match, by XPath. | |
AssertLegacyTrait:: |
protected | function | Asserts that a checkbox field in the current page is not checked. | |
AssertLegacyTrait:: |
protected | function | Passes if a link with the specified label is not found. | |
AssertLegacyTrait:: |
protected | function | Passes if a link containing a given href (part) is not found. | |
AssertLegacyTrait:: |
protected | function | Asserts that a select option does NOT exist in the current page. | |
AssertLegacyTrait:: |
protected | function | Triggers a pass if the Perl regex pattern is not found in the raw content. | |
AssertLegacyTrait:: |
protected | function | Passes if the raw text IS not found on the loaded page, fail otherwise. | |
AssertLegacyTrait:: |
protected | function | ||
AssertLegacyTrait:: |
protected | function | Passes if the page (with HTML stripped) does not contains the text. | |
AssertLegacyTrait:: |
protected | function | ||
AssertLegacyTrait:: |
protected | function | Passes if the text is found MORE THAN ONCE on the text version of the page. | |
AssertLegacyTrait:: |
protected | function | Asserts that a select option in the current page exists. | |
AssertLegacyTrait:: |
protected | function | Asserts that a select option with the visible text exists. | |
AssertLegacyTrait:: |
protected | function | Asserts that a select option in the current page is checked. | |
AssertLegacyTrait:: |
protected | function | Triggers a pass if the Perl regex pattern is found in the raw content. | |
AssertLegacyTrait:: |
protected | function | Passes if the raw text IS found on the loaded page, fail otherwise. | |
AssertLegacyTrait:: |
protected | function | Asserts the page responds with the specified response code. | |
AssertLegacyTrait:: |
protected | function | Passes if the page (with HTML stripped) contains the text. | |
AssertLegacyTrait:: |
protected | function | Helper for assertText and assertNoText. | |
AssertLegacyTrait:: |
protected | function | Pass if the page title is the given string. | |
AssertLegacyTrait:: |
protected | function | Passes if the text is found ONLY ONCE on the text version of the page. | |
AssertLegacyTrait:: |
protected | function | Passes if the internal browser's URL matches the given path. | |
AssertLegacyTrait:: |
protected | function | Builds an XPath query. | |
AssertLegacyTrait:: |
protected | function | Helper: Constructs an XPath for the given set of attributes and value. | |
AssertLegacyTrait:: |
protected | function | Get all option elements, including nested options, in a select. | |
AssertLegacyTrait:: |
protected | function | Gets the current raw content. | |
AssertLegacyTrait:: |
protected | function | ||
AssertLegacyTrait:: |
protected | function | ||
AssertMailTrait:: |
protected | function | Asserts that the most recently sent email message has the given value. | |
AssertMailTrait:: |
protected | function | Asserts that the most recently sent email message has the pattern in it. | |
AssertMailTrait:: |
protected | function | Asserts that the most recently sent email message has the string in it. | |
AssertMailTrait:: |
protected | function | Gets an array containing all emails sent during this test case. | |
AssertMailTrait:: |
protected | function | Outputs to verbose the most recent $count emails sent. | |
BlockCreationTrait:: |
protected | function | Creates a block instance based on default settings. Aliased as: drupalPlaceBlock | |
BrowserHtmlDebugTrait:: |
protected | property | The Base URI to use for links to the output files. | |
BrowserHtmlDebugTrait:: |
protected | property | Class name for HTML output logging. | |
BrowserHtmlDebugTrait:: |
protected | property | Counter for HTML output logging. | |
BrowserHtmlDebugTrait:: |
protected | property | Counter storage for HTML output logging. | |
BrowserHtmlDebugTrait:: |
protected | property | Directory name for HTML output logging. | |
BrowserHtmlDebugTrait:: |
protected | property | HTML output output enabled. | |
BrowserHtmlDebugTrait:: |
protected | property | The file name to write the list of URLs to. | |
BrowserHtmlDebugTrait:: |
protected | property | HTML output test ID. | |
BrowserHtmlDebugTrait:: |
protected | function | Formats HTTP headers as string for HTML output logging. | |
BrowserHtmlDebugTrait:: |
protected | function | Returns headers in HTML output format. | 1 |
BrowserHtmlDebugTrait:: |
protected | function | Provides a Guzzle middleware handler to log every response received. | |
BrowserHtmlDebugTrait:: |
protected | function | Logs a HTML output message in a text file. | |
BrowserHtmlDebugTrait:: |
protected | function | Creates the directory to store browser output. | |
BrowserTestBase:: |
protected | property | The base URL. | |
BrowserTestBase:: |
protected | property | The config importer that can be used in a test. | |
BrowserTestBase:: |
protected | property | An array of custom translations suitable for drupal_rewrite_settings(). | |
BrowserTestBase:: |
protected | property | The database prefix of this test run. | |
BrowserTestBase:: |
protected | property | Mink session manager. | |
BrowserTestBase:: |
protected | property | Mink default driver params. | |
BrowserTestBase:: |
protected | property | Mink class for the default driver to use. | 1 |
BrowserTestBase:: |
protected | property | The original container. | |
BrowserTestBase:: |
protected | property | The original array of shutdown function callbacks. | |
BrowserTestBase:: |
protected | property | ||
BrowserTestBase:: |
protected | property | The profile to install as a basis for testing. | 39 |
BrowserTestBase:: |
protected | property | The app root. | |
BrowserTestBase:: |
protected | property | Browser tests are run in separate processes to prevent collisions between code that may be loaded by tests. | |
BrowserTestBase:: |
protected | property | Time limit in seconds for the test. | |
BrowserTestBase:: |
protected | property | The translation file directory for the test environment. | |
BrowserTestBase:: |
protected | function | Clean up the Simpletest environment. | |
BrowserTestBase:: |
protected | function | Configuration accessor for tests. Returns non-overridden configuration. | |
BrowserTestBase:: |
protected | function | Gets the value of an HTTP response header. | |
BrowserTestBase:: |
public static | function | Ensures test files are deletable. | |
BrowserTestBase:: |
protected | function | Gets an instance of the default Mink driver. | |
BrowserTestBase:: |
protected | function | Gets the JavaScript drupalSettings variable for the currently-loaded page. | 1 |
BrowserTestBase:: |
protected | function | Obtain the HTTP client for the system under test. | |
BrowserTestBase:: |
protected | function | Get the Mink driver args from an environment variable, if it is set. Can be overridden in a derived class so it is possible to use a different value for a subset of tests, e.g. the JavaScript tests. | 1 |
BrowserTestBase:: |
protected | function | Helper function to get the options of select field. | |
BrowserTestBase:: |
public | function | Returns Mink session. | |
BrowserTestBase:: |
protected | function | Get session cookies from current session. | |
BrowserTestBase:: |
protected | function |
Retrieves the current calling line in the class under test. Overrides BrowserHtmlDebugTrait:: |
|
BrowserTestBase:: |
protected | function | Visits the front page when initializing Mink. | 3 |
BrowserTestBase:: |
protected | function | Initializes Mink sessions. | 1 |
BrowserTestBase:: |
public | function | Installs Drupal into the Simpletest site. | 1 |
BrowserTestBase:: |
protected | function | Registers additional Mink sessions. | |
BrowserTestBase:: |
protected | function | Sets up the root application path. | |
BrowserTestBase:: |
public static | function | 1 | |
BrowserTestBase:: |
protected | function | 3 | |
BrowserTestBase:: |
protected | function | Transforms a nested array into a flat array suitable for submitForm(). | |
BrowserTestBase:: |
protected | function | Performs an xpath search on the contents of the internal browser. | |
BrowserTestBase:: |
public | function | Prevents serializing any properties. | |
ConfigTestTrait:: |
protected | function | Returns a ConfigImporter object to import test configuration. | |
ConfigTestTrait:: |
protected | function | Copies configuration objects from source storage to target storage. | |
ContentTypeCreationTrait:: |
protected | function | Creates a custom content type based on default settings. Aliased as: drupalCreateContentType | 1 |
CronRunTrait:: |
protected | function | Runs cron on the test site. | |
ExtensionListTestTrait:: |
protected | function | Gets the path for the specified module. | |
ExtensionListTestTrait:: |
protected | function | Gets the path for the specified theme. | |
FunctionalTestSetupTrait:: |
protected | property | The flag to set 'apcu_ensure_unique_prefix' setting. | 1 |
FunctionalTestSetupTrait:: |
protected | property | The class loader to use for installation and initialization of setup. | |
FunctionalTestSetupTrait:: |
protected | property | The "#1" admin user. | |
FunctionalTestSetupTrait:: |
protected | function | Execute the non-interactive installer. | 1 |
FunctionalTestSetupTrait:: |
protected | function | Returns all supported database driver installer objects. | |
FunctionalTestSetupTrait:: |
protected | function | Initialize various configurations post-installation. | 1 |
FunctionalTestSetupTrait:: |
protected | function | Initializes the kernel after installation. | |
FunctionalTestSetupTrait:: |
protected | function | Initialize settings created during install. | |
FunctionalTestSetupTrait:: |
protected | function | Initializes user 1 for the site to be installed. | |
FunctionalTestSetupTrait:: |
protected | function | Installs the default theme defined by `static::$defaultTheme` when needed. | |
FunctionalTestSetupTrait:: |
protected | function | Install modules defined by `static::$modules`. | 1 |
FunctionalTestSetupTrait:: |
protected | function | Returns the parameters that will be used when Simpletest installs Drupal. | 9 |
FunctionalTestSetupTrait:: |
protected | function | Prepares the current environment for running the test. | 20 |
FunctionalTestSetupTrait:: |
protected | function | Creates a mock request and sets it on the generator. | |
FunctionalTestSetupTrait:: |
protected | function | Prepares site settings and services before installation. | 2 |
FunctionalTestSetupTrait:: |
protected | function | Resets and rebuilds the environment after setup. | |
FunctionalTestSetupTrait:: |
protected | function | Rebuilds \Drupal::getContainer(). | |
FunctionalTestSetupTrait:: |
protected | function | Resets all data structures after having enabled new modules. | |
FunctionalTestSetupTrait:: |
protected | function | Changes parameters in the services.yml file. | |
FunctionalTestSetupTrait:: |
protected | function | Sets up the base URL based upon the environment variable. | |
FunctionalTestSetupTrait:: |
protected | function | Rewrites the settings.php file of the test site. | 1 |
NodeCreationTrait:: |
protected | function | Creates a node based on default settings. Aliased as: drupalCreateNode | |
NodeCreationTrait:: |
public | function | Get a node from the database based on its title. Aliased as: drupalGetNodeByTitle | |
PhpUnitWarnings:: |
private static | property | Deprecation warnings from PHPUnit to raise with @trigger_error(). | |
PhpUnitWarnings:: |
public | function | Converts PHPUnit deprecation warnings to E_USER_DEPRECATED. | |
RandomGeneratorTrait:: |
protected | property | The random generator. | |
RandomGeneratorTrait:: |
protected | function | Gets the random generator for the utility methods. | |
RandomGeneratorTrait:: |
protected | function | Generates a unique random string containing letters and numbers. | 1 |
RandomGeneratorTrait:: |
public | function | Generates a random PHP object. | |
RandomGeneratorTrait:: |
public | function | Generates a pseudo-random string of ASCII characters of codes 32 to 126. | |
RandomGeneratorTrait:: |
public | function | Callback for random string validation. | |
RefreshVariablesTrait:: |
protected | function | Refreshes in-memory configuration and state information. | 1 |
SessionTestTrait:: |
protected | property | The name of the session cookie. | |
SessionTestTrait:: |
protected | function | Generates a session cookie name. | |
SessionTestTrait:: |
protected | function | Returns the session name in use on the child site. | |
SimplenewsSendTest:: |
protected | function |
Overrides SimplenewsTestBase:: |
|
SimplenewsSendTest:: |
public | function | Create a newsletter, send mails and then delete. | |
SimplenewsSendTest:: |
public | function | Test the correct handling of HTML special characters in plain text mails. | |
SimplenewsSendTest:: |
public | function | Test that the correct user is used when sending newsletters. | |
SimplenewsSendTest:: |
public | function | Test the theme suggestions when sending mails. | |
SimplenewsSendTest:: |
public | function | Creates and sends a node using the API. | |
SimplenewsSendTest:: |
public | function | Tests failing to send mails from cron. | |
SimplenewsSendTest:: |
public | function | Send multiple newsletters without cron. | |
SimplenewsSendTest:: |
public | function | Send a newsletter using cron. | |
SimplenewsSendTest:: |
public | function | Send a newsletter using cron and a low throttle. | |
SimplenewsSendTest:: |
public | function | Send a newsletter without cron. | |
SimplenewsSendTest:: |
public | function | Send a newsletter on publish without using cron. | |
SimplenewsSendTest:: |
public | function | Test newsletter update. | |
SimplenewsTestBase:: |
protected | property | The Simplenews settings config object. | |
SimplenewsTestBase:: |
protected | property |
The theme to install as the default for testing. Overrides BrowserTestBase:: |
|
SimplenewsTestBase:: |
public static | property |
Modules to enable. Overrides BrowserTestBase:: |
5 |
SimplenewsTestBase:: |
protected | function | Creates and saves a field storage and instance. | |
SimplenewsTestBase:: |
protected | function | Checks if a string is found in the latest sent mail. | |
SimplenewsTestBase:: |
protected | function | Returns the last created Subscriber. | |
SimplenewsTestBase:: |
protected | function | Returns the body content of mail that has been sent. | |
SimplenewsTestBase:: |
protected | function | Select randomly one of the available newsletters. | |
SimplenewsTestBase:: |
protected | function | Generates a random email address. | |
SimplenewsTestBase:: |
protected | function | Visits and submits the user registration form. | |
SimplenewsTestBase:: |
protected | function | Login a user, resetting their password. | |
SimplenewsTestBase:: |
protected | function | Setup subscribers. | |
SimplenewsTestBase:: |
protected | function | Enable newsletter subscription block. | |
SimplenewsTestBase:: |
protected | function | Visits and submits a newsletter management form. | |
StorageCopyTrait:: |
protected static | function | Copy the configuration from one storage to another and remove stale items. | |
TestRequirementsTrait:: |
private | function | Checks missing module requirements. | |
TestRequirementsTrait:: |
protected | function | Check module requirements for the Drupal use case. | 1 |
TestRequirementsTrait:: |
protected static | function | Returns the Drupal root directory. | |
TestSetupTrait:: |
protected static | property | An array of config object names that are excluded from schema checking. | |
TestSetupTrait:: |
protected | property | The dependency injection container used in the test. | |
TestSetupTrait:: |
protected | property | The DrupalKernel instance used in the test. | |
TestSetupTrait:: |
protected | property | The site directory of the original parent site. | |
TestSetupTrait:: |
protected | property | The private file directory for the test environment. | |
TestSetupTrait:: |
protected | property | The public file directory for the test environment. | |
TestSetupTrait:: |
protected | property | The site directory of this test run. | |
TestSetupTrait:: |
protected | property | Set to TRUE to strict check all configuration saved. | 1 |
TestSetupTrait:: |
protected | property | The temporary file directory for the test environment. | |
TestSetupTrait:: |
protected | property | The test run ID. | |
TestSetupTrait:: |
protected | function | Changes the database connection to the prefixed one. | |
TestSetupTrait:: |
protected | function | Gets the config schema exclusions for this test. | |
TestSetupTrait:: |
public static | function | Returns the database connection to the site running Simpletest. | |
TestSetupTrait:: |
protected | function | Generates a database prefix for running tests. | 1 |
UiHelperTrait:: |
protected | property | The current user logged in using the Mink controlled browser. | |
UiHelperTrait:: |
protected | property | The number of meta refresh redirects to follow, or NULL if unlimited. | |
UiHelperTrait:: |
protected | property | The number of meta refresh redirects followed during ::drupalGet(). | |
UiHelperTrait:: |
public | function | Returns WebAssert object. | 1 |
UiHelperTrait:: |
protected | function | Builds an absolute URL from a system path or a URL object. | |
UiHelperTrait:: |
protected | function | Checks for meta refresh tag and if found call drupalGet() recursively. | |
UiHelperTrait:: |
protected | function | Clicks the element with the given CSS selector. | |
UiHelperTrait:: |
protected | function | Follows a link by complete name. | |
UiHelperTrait:: |
protected | function | Searches elements using a CSS selector in the raw content. | |
UiHelperTrait:: |
protected | function | Translates a CSS expression to its XPath equivalent. | |
UiHelperTrait:: |
protected | function | Retrieves a Drupal path or an absolute path. | 2 |
UiHelperTrait:: |
protected | function | Logs in a user using the Mink controlled browser. | |
UiHelperTrait:: |
protected | function | Logs a user out of the Mink controlled browser and confirms. | |
UiHelperTrait:: |
protected | function | Executes a form submission. | |
UiHelperTrait:: |
protected | function | Returns whether a given user account is logged in. | |
UiHelperTrait:: |
protected | function | Takes a path and returns an absolute path. | |
UiHelperTrait:: |
protected | function | Retrieves the plain-text content from the current page. | |
UiHelperTrait:: |
protected | function | Get the current URL from the browser. | |
UiHelperTrait:: |
protected | function | Determines if test is using DrupalTestBrowser. | |
UiHelperTrait:: |
protected | function | Prepare for a request to testing site. | 1 |
UiHelperTrait:: |
protected | function | Fills and submits a form. | |
UserCreationTrait:: |
protected | function | Checks whether a given list of permission names is valid. | |
UserCreationTrait:: |
protected | function | Creates an administrative role. | |
UserCreationTrait:: |
protected | function | Creates a role with specified permissions. Aliased as: drupalCreateRole | |
UserCreationTrait:: |
protected | function | Create a user with a given set of permissions. Aliased as: drupalCreateUser | |
UserCreationTrait:: |
protected | function | Grant permissions to a user role. | |
UserCreationTrait:: |
protected | function | Switch the current logged in user. | |
UserCreationTrait:: |
protected | function | Creates a random user account and sets it as current user. | |
XdebugRequestTrait:: |
protected | function | Adds xdebug cookies, from request setup. |