本文整理汇总了PHP中Drupal::urlGenerator方法的典型用法代码示例。如果您正苦于以下问题:PHP Drupal::urlGenerator方法的具体用法?PHP Drupal::urlGenerator怎么用?PHP Drupal::urlGenerator使用的例子?那么恭喜您, 这里精选的方法代码示例或许可以为您提供帮助。您也可以进一步了解该方法所在类Drupal
的用法示例。
在下文中一共展示了Drupal::urlGenerator方法的15个代码示例,这些例子默认根据受欢迎程度排序。您可以为喜欢或者感觉有用的代码点赞,您的评价将有助于系统推荐出更棒的PHP代码示例。
示例1: testConfigurationRename
/**
* Tests configuration renaming.
*/
public function testConfigurationRename()
{
$content_type = entity_create('node_type', array('type' => Unicode::strtolower($this->randomName(16)), 'name' => $this->randomName()));
$content_type->save();
$staged_type = $content_type->type;
$active = $this->container->get('config.storage');
$staging = $this->container->get('config.storage.staging');
$config_name = $content_type->getEntityType()->getConfigPrefix() . '.' . $content_type->id();
// Emulate a staging operation.
$this->copyConfig($active, $staging);
// Change the machine name of the content type.
$content_type->type = Unicode::strtolower($this->randomName(8));
$content_type->save();
$active_type = $content_type->type;
$renamed_config_name = $content_type->getEntityType()->getConfigPrefix() . '.' . $content_type->id();
$this->assertTrue($active->exists($renamed_config_name), 'The content type has the new name in the active store.');
$this->assertFalse($active->exists($config_name), "The content type's old name does not exist active store.");
$this->configImporter()->reset();
$this->assertEqual(0, count($this->configImporter()->getUnprocessedConfiguration('create')), 'There are no configuration items to create.');
$this->assertEqual(0, count($this->configImporter()->getUnprocessedConfiguration('delete')), 'There are no configuration items to delete.');
$this->assertEqual(0, count($this->configImporter()->getUnprocessedConfiguration('update')), 'There are no configuration items to update.');
// We expect that changing the machine name of the content type will
// rename five configuration entities: the node type, the body field
// instance, two entity form displays, and the entity view display.
// @see \Drupal\node\Entity\NodeType::postSave()
$expected = array('node.type.' . $active_type . '::node.type.' . $staged_type, 'entity.form_display.node.' . $active_type . '.default::entity.form_display.node.' . $staged_type . '.default', 'entity.view_display.node.' . $active_type . '.default::entity.view_display.node.' . $staged_type . '.default', 'entity.view_display.node.' . $active_type . '.teaser::entity.view_display.node.' . $staged_type . '.teaser', 'field.instance.node.' . $active_type . '.body::field.instance.node.' . $staged_type . '.body');
$renames = $this->configImporter()->getUnprocessedConfiguration('rename');
$this->assertIdentical($expected, $renames);
$this->drupalGet('admin/config/development/configuration');
foreach ($expected as $rename) {
$names = $this->configImporter()->getStorageComparer()->extractRenameNames($rename);
$this->assertText(String::format('!source_name to !target_name', array('!source_name' => $names['old_name'], '!target_name' => $names['new_name'])));
// Test that the diff link is present for each renamed item.
$href = \Drupal::urlGenerator()->getPathFromRoute('config.diff', array('source_name' => $names['old_name'], 'target_name' => $names['new_name']));
$this->assertLinkByHref($href);
$hrefs[$rename] = $href;
}
// Ensure that the diff works for each renamed item.
foreach ($hrefs as $rename => $href) {
$this->drupalGet($href);
$names = $this->configImporter()->getStorageComparer()->extractRenameNames($rename);
$config_entity_type = \Drupal::service('config.manager')->getEntityTypeIdByName($names['old_name']);
$entity_type = \Drupal::entityManager()->getDefinition($config_entity_type);
$old_id = ConfigEntityStorage::getIDFromConfigName($names['old_name'], $entity_type->getConfigPrefix());
$new_id = ConfigEntityStorage::getIDFromConfigName($names['new_name'], $entity_type->getConfigPrefix());
// Because table columns can be on multiple lines, need to assert a regex
// pattern rather than normal text.
$id_key = $entity_type->getKey('id');
$text = "{$id_key}: {$old_id}";
$this->assertTextPattern('/\\-\\s+' . preg_quote($text, '/') . '/', "'-{$text}' found.");
$text = "{$id_key}: {$new_id}";
$this->assertTextPattern('/\\+\\s+' . preg_quote($text, '/') . '/', "'+{$text}' found.");
}
// Run the import.
$this->drupalPostForm('admin/config/development/configuration', array(), t('Import all'));
$this->assertText(t('There are no configuration changes.'));
$this->assertFalse(entity_load('node_type', $active_type), 'The content no longer exists with the old name.');
$content_type = entity_load('node_type', $staged_type);
$this->assertIdentical($staged_type, $content_type->type);
}
示例2: setUp
/**
* {@inheritdoc}
*/
protected function setUp()
{
parent::setUp();
$this->installSchema('system', ['router']);
\Drupal::service('router.builder')->rebuild();
$this->urlGenerator = \Drupal::urlGenerator();
}
示例3: testAcceptHeaderRequests
/**
* Tests support for different cache items with different Accept headers.
*/
function testAcceptHeaderRequests()
{
$config = $this->config('system.performance');
$config->set('cache.page.use_internal', 1);
$config->set('cache.page.max_age', 300);
$config->save();
$url_generator = \Drupal::urlGenerator();
$url_generator->setContext(new RequestContext());
$accept_header_cache_uri = $url_generator->getPathFromRoute('system_test.page_cache_accept_header');
$json_accept_header = array('Accept: application/json');
$this->drupalGet($accept_header_cache_uri);
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS', 'HTML page was not yet cached.');
$this->drupalGet($accept_header_cache_uri);
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', 'HTML page was cached.');
$this->assertRaw('<p>oh hai this is html.</p>', 'The correct HTML response was returned.');
$this->drupalGet($accept_header_cache_uri, array(), $json_accept_header);
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS', 'Json response was not yet cached.');
$this->drupalGet($accept_header_cache_uri, array(), $json_accept_header);
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', 'Json response was cached.');
$this->assertRaw('{"content":"oh hai this is json"}', 'The correct Json response was returned.');
// Enable REST support for nodes and hal+json.
\Drupal::service('module_installer')->install(['node', 'rest', 'hal']);
$this->drupalCreateContentType(['type' => 'article']);
$node = $this->drupalCreateNode(['type' => 'article']);
$node_uri = 'node/' . $node->id();
$hal_json_accept_header = ['Accept: application/hal+json'];
/** @var \Drupal\user\RoleInterface $role */
$role = Role::load('anonymous');
$role->grantPermission('restful get entity:node');
$role->save();
$this->drupalGet($node_uri);
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS');
$this->assertEqual($this->drupalGetHeader('Content-Type'), 'text/html; charset=UTF-8');
$this->drupalGet($node_uri);
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT');
$this->assertEqual($this->drupalGetHeader('Content-Type'), 'text/html; charset=UTF-8');
// Now request a HAL page, we expect that the first request is a cache miss
// and it serves HTML.
$this->drupalGet($node_uri, [], $hal_json_accept_header);
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS');
$this->assertEqual($this->drupalGetHeader('Content-Type'), 'application/hal+json');
$this->drupalGet($node_uri, [], $hal_json_accept_header);
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT');
$this->assertEqual($this->drupalGetHeader('Content-Type'), 'application/hal+json');
// Clear the page cache. After that request a HAL request, followed by an
// ordinary HTML one.
\Drupal::cache('render')->deleteAll();
$this->drupalGet($node_uri, [], $hal_json_accept_header);
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS');
$this->assertEqual($this->drupalGetHeader('Content-Type'), 'application/hal+json');
$this->drupalGet($node_uri, [], $hal_json_accept_header);
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT');
$this->assertEqual($this->drupalGetHeader('Content-Type'), 'application/hal+json');
$this->drupalGet($node_uri);
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS');
$this->assertEqual($this->drupalGetHeader('Content-Type'), 'text/html; charset=UTF-8');
$this->drupalGet($node_uri);
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT');
$this->assertEqual($this->drupalGetHeader('Content-Type'), 'text/html; charset=UTF-8');
}
示例4: testDrupalRenderFormElements
/**
* Tests rendering form elements without passing through
* \Drupal::formBuilder()->doBuildForm().
*/
function testDrupalRenderFormElements()
{
// Define a series of form elements.
$element = array('#type' => 'button', '#value' => $this->randomMachineName());
$this->assertRenderedElement($element, '//input[@type=:type]', array(':type' => 'submit'));
$element = array('#type' => 'textfield', '#title' => $this->randomMachineName(), '#value' => $this->randomMachineName());
$this->assertRenderedElement($element, '//input[@type=:type]', array(':type' => 'text'));
$element = array('#type' => 'password', '#title' => $this->randomMachineName());
$this->assertRenderedElement($element, '//input[@type=:type]', array(':type' => 'password'));
$element = array('#type' => 'textarea', '#title' => $this->randomMachineName(), '#value' => $this->randomMachineName());
$this->assertRenderedElement($element, '//textarea');
$element = array('#type' => 'radio', '#title' => $this->randomMachineName(), '#value' => FALSE);
$this->assertRenderedElement($element, '//input[@type=:type]', array(':type' => 'radio'));
$element = array('#type' => 'checkbox', '#title' => $this->randomMachineName());
$this->assertRenderedElement($element, '//input[@type=:type]', array(':type' => 'checkbox'));
$element = array('#type' => 'select', '#title' => $this->randomMachineName(), '#options' => array(0 => $this->randomMachineName(), 1 => $this->randomMachineName()));
$this->assertRenderedElement($element, '//select');
$element = array('#type' => 'file', '#title' => $this->randomMachineName());
$this->assertRenderedElement($element, '//input[@type=:type]', array(':type' => 'file'));
$element = array('#type' => 'item', '#title' => $this->randomMachineName(), '#markup' => $this->randomMachineName());
$this->assertRenderedElement($element, '//div[contains(@class, :class) and contains(., :markup)]/label[contains(., :label)]', array(':class' => 'form-type-item', ':markup' => $element['#markup'], ':label' => $element['#title']));
$element = array('#type' => 'hidden', '#title' => $this->randomMachineName(), '#value' => $this->randomMachineName());
$this->assertRenderedElement($element, '//input[@type=:type]', array(':type' => 'hidden'));
$element = array('#type' => 'link', '#title' => $this->randomMachineName(), '#url' => Url::fromRoute('common_test.destination'), '#options' => array('absolute' => TRUE));
$this->assertRenderedElement($element, '//a[@href=:href and contains(., :title)]', array(':href' => \Drupal::urlGenerator()->generateFromPath('common-test/destination', ['absolute' => TRUE]), ':title' => $element['#title']));
$element = array('#type' => 'details', '#open' => TRUE, '#title' => $this->randomMachineName());
$this->assertRenderedElement($element, '//details/summary[contains(., :title)]', array(':title' => $element['#title']));
$element = array('#type' => 'details', '#open' => TRUE, '#title' => $this->randomMachineName());
$this->assertRenderedElement($element, '//details');
$element['item'] = array('#type' => 'item', '#title' => $this->randomMachineName(), '#markup' => $this->randomMachineName());
$this->assertRenderedElement($element, '//details/div/div[contains(@class, :class) and contains(., :markup)]', array(':class' => 'form-type-item', ':markup' => $element['item']['#markup']));
}
示例5: testMoreLink
/**
* Tests system #type 'more_link'.
*/
function testMoreLink()
{
$elements = array(array('name' => "#type 'more_link' anchor tag generation without extra classes", 'value' => array('#type' => 'more_link', '#url' => Url::fromUri('https://www.drupal.org')), 'expected' => '//div[@class="more-link"]/a[@href="https://www.drupal.org" and text()="More"]'), array('name' => "#type 'more_link' anchor tag generation with different link text", 'value' => array('#type' => 'more_link', '#url' => Url::fromUri('https://www.drupal.org'), '#title' => 'More Titles'), 'expected' => '//div[@class="more-link"]/a[@href="https://www.drupal.org" and text()="More Titles"]'), array('name' => "#type 'more_link' anchor tag generation with attributes on wrapper", 'value' => array('#type' => 'more_link', '#url' => Url::fromUri('https://www.drupal.org'), '#theme_wrappers' => array('container' => array('#attributes' => array('title' => 'description', 'class' => array('more-link', 'drupal', 'test'))))), 'expected' => '//div[@title="description" and contains(@class, "more-link") and contains(@class, "drupal") and contains(@class, "test")]/a[@href="https://www.drupal.org" and text()="More"]'), array('name' => "#type 'more_link' anchor tag with a relative path", 'value' => array('#type' => 'more_link', '#url' => Url::fromRoute('router_test.1')), 'expected' => '//div[@class="more-link"]/a[@href="' . Url::fromRoute('router_test.1')->toString() . '" and text()="More"]'), array('name' => "#type 'more_link' anchor tag with a route", 'value' => array('#type' => 'more_link', '#url' => Url::fromRoute('router_test.1')), 'expected' => '//div[@class="more-link"]/a[@href="' . \Drupal::urlGenerator()->generate('router_test.1') . '" and text()="More"]'), array('name' => "#type 'more_link' anchor tag with an absolute path", 'value' => array('#type' => 'more_link', '#url' => Url::fromRoute('system.admin_content'), '#options' => array('absolute' => TRUE)), 'expected' => '//div[@class="more-link"]/a[@href="' . Url::fromRoute('system.admin_content')->setAbsolute()->toString() . '" and text()="More"]'), array('name' => "#type 'more_link' anchor tag to the front page", 'value' => array('#type' => 'more_link', '#url' => Url::fromRoute('<front>')), 'expected' => '//div[@class="more-link"]/a[@href="' . Url::fromRoute('<front>')->toString() . '" and text()="More"]'));
foreach ($elements as $element) {
$xml = new \SimpleXMLElement(\Drupal::service('renderer')->renderRoot($element['value']));
$result = $xml->xpath($element['expected']);
$this->assertTrue($result, '"' . $element['name'] . '" input rendered correctly by drupal_render().');
}
}
示例6: assertMenuLink
/**
* Fetchs the menu item from the database and compares it to expected item.
*
* @param int $mlid
* Menu item id.
* @param array $item
* Array containing properties to verify.
*/
function assertMenuLink($mlid, array $expected_item)
{
// Retrieve menu link.
$item = entity_load('menu_link', $mlid);
$options = $item->options;
if (!empty($options['query'])) {
$item['link_path'] .= '?' . \Drupal::urlGenerator()->httpBuildQuery($options['query']);
}
if (!empty($options['fragment'])) {
$item['link_path'] .= '#' . $options['fragment'];
}
foreach ($expected_item as $key => $value) {
$this->assertEqual($item[$key], $value);
}
}
示例7: getOptions
/**
* {@inheritdoc}
*/
public function getOptions(Request $request)
{
$options = parent::getOptions($request);
// Append the current path as destination to the query string.
if ($request->attributes->has(RouteObjectInterface::ROUTE_NAME)) {
$route_name = $request->attributes->get(RouteObjectInterface::ROUTE_NAME);
$raw_variables = array();
if ($request->attributes->has('_raw_variables')) {
$raw_variables = $request->attributes->get('_raw_variables')->all();
}
// @todo Use RouteMatch instead of Request.
// https://www.drupal.org/node/2294157
$options['query']['destination'] = \Drupal::urlGenerator()->generateFromRoute($route_name, $raw_variables);
}
return $options;
}
示例8: getValue
/**
* {@inheritdoc}
*/
public function getValue()
{
if (!isset($this->value)) {
if (!isset($this->parent)) {
throw new \InvalidArgumentException('Computed properties require context for computation.');
}
$entity = $this->parent->getEntity();
if ($route_name = $entity->getRouteName()) {
$path = \Drupal::urlGenerator()->getPathFromRoute($route_name, $entity->getRouteParams());
$this->value = trim($path, '/');
} else {
$this->value = NULL;
}
}
return $this->value;
}
示例9: doTestBasicTranslation
protected function doTestBasicTranslation()
{
parent::doTestBasicTranslation();
$entity = entity_load($this->entityTypeId, $this->entityId, TRUE);
foreach ($this->langcodes as $langcode) {
if ($entity->hasTranslation($langcode)) {
$language = new Language(array('id' => $langcode));
// Request the front page in this language and assert that the right
// translation shows up in the shortcut list with the right path.
$this->drupalGet('<front>', array('language' => $language));
$expected_path = \Drupal::urlGenerator()->generateFromRoute('user.page', array(), array('language' => $language));
$label = $entity->getTranslation($langcode)->label();
$elements = $this->xpath('//nav[contains(@class, "toolbar-lining")]/ul[@class="toolbar-menu"]/li/a[contains(@href, :href) and normalize-space(text())=:label]', array(':href' => $expected_path, ':label' => $label));
$this->assertTrue(!empty($elements), format_string('Translated @language shortcut link @label found.', array('@label' => $label, '@language' => $language->getName())));
}
}
}
示例10: testAcceptHeaderRequests
/**
* Tests support for different cache items with different Accept headers.
*/
function testAcceptHeaderRequests()
{
$config = \Drupal::config('system.performance');
$config->set('cache.page.use_internal', 1);
$config->set('cache.page.max_age', 300);
$config->save();
$url_generator = \Drupal::urlGenerator();
$url_generator->setContext(new RequestContext());
$accept_header_cache_uri = $url_generator->getPathFromRoute('system_test.page_cache_accept_header');
$json_accept_header = array('Accept: application/json');
$this->drupalGet($accept_header_cache_uri);
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS', 'HTML page was not yet cached.');
$this->drupalGet($accept_header_cache_uri);
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', 'HTML page was cached.');
$this->assertRaw('<p>oh hai this is html.</p>', 'The correct HTML response was returned.');
$this->drupalGet($accept_header_cache_uri, array(), $json_accept_header);
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS', 'Json response was not yet cached.');
$this->drupalGet($accept_header_cache_uri, array(), $json_accept_header);
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', 'Json response was cached.');
$this->assertRaw('{"content":"oh hai this is json"}', 'The correct Json response was returned.');
}
示例11: get
/**
* @param null $menu_name
* @return ResourceResponse
*/
public function get($menu_name = null) {
$menu_tree = \Drupal::menuTree( );
$generator = \Drupal::urlGenerator();
// Load the tree based on this set of parameters.
$tree = $menu_tree->load($menu_name, new \Drupal\Core\Menu\MenuTreeParameters());
// Transform the tree using the manipulators you want.
$manipulators = array(
// Only show links that are accessible for the current user.
array('callable' => 'menu.default_tree_manipulators:checkAccess'),
// Use the default sorting of menu links.
array('callable' => 'menu.default_tree_manipulators:generateIndexAndSort'),
);
$tree = $menu_tree->transform($tree, $manipulators);
foreach ($tree as $element) {
/** @var \Drupal\Core\Menu\MenuLinkInterface $link */
$link = $element->link;
$link_param = $link->pluginDefinition['route_parameters'];
// echo "<pre>" . print_r($link->pluginDefinition, true) . "</pre>";
$path = $generator->getPathFromRoute($link->getRouteName(), $link_param);
$menu[$link->getRouteName()]['title'] = $link->getTitle();
$menu[$link->getRouteName()]['url'] = $path;
if ($element->subtree) {
$subtree = $menu_tree->build($element->subtree);
foreach ($subtree['#items'] as $key => $value) {
// print_r($value['url']->getRouteParameters());
//$path = $generator->getPathFromRoute($key, $value['url']->getRouteParameters());
$menu[$key]['title'] = $value['title'];
$menu[$key]['url'] = $path;
}
}
}
return new ResourceResponse($menu);
}
示例12: render
/**
* {@inheritdoc}
*
* We override ::render() so that we can add our own content above the table.
* parent::render() is where EntityListBuilder creates the table using our
* buildHeader() and buildRow() implementations.
*/
public function render()
{
$build['description'] = array('#markup' => $this->t('customslider implements a Custom Slider model. These Custom Slider are fieldable entities. You can manage the fields on the <a href="@adminlink">Custom Slider admin page</a>.', array('@adminlink' => \Drupal::urlGenerator()->generateFromRoute('customslider.customslider_settings'))));
$build['table'] = parent::render();
return $build;
}
示例13: urlGenerator
/**
* Gets the URL generator.
*
* @return \Drupal\Core\Routing\UrlGeneratorInterface
* The URL generator.
*/
protected function urlGenerator()
{
if (!$this->urlGenerator) {
$this->urlGenerator = \Drupal::urlGenerator();
}
return $this->urlGenerator;
}
示例14: setUp
/**
* {@inheritdoc}
*/
protected function setUp()
{
parent::setUp();
\Drupal::service('router.builder')->rebuild();
$this->urlGenerator = \Drupal::urlGenerator();
}
示例15: testLinks
/**
* Tests links.html.twig.
*/
function testLinks()
{
// Turn off the query for the _l() function to compare the active
// link correctly.
$original_query = \Drupal::request()->query->all();
\Drupal::request()->query->replace(array());
// Verify that empty variables produce no output.
$variables = array();
$expected = '';
$this->assertThemeOutput('links', $variables, $expected, 'Empty %callback generates no output.');
$variables = array();
$variables['heading'] = 'Some title';
$expected = '';
$this->assertThemeOutput('links', $variables, $expected, 'Empty %callback with heading generates no output.');
// Verify that a list of links is properly rendered.
$variables = array();
$variables['attributes'] = array('id' => 'somelinks');
$variables['links'] = array('a link' => array('title' => 'A <link>', 'url' => Url::fromUri('base://a/link')), 'plain text' => array('title' => 'Plain "text"'), 'front page' => array('title' => 'Front page', 'url' => Url::fromRoute('<front>')), 'router-test' => array('title' => 'Test route', 'url' => Url::fromRoute('router_test.1')), 'query-test' => array('title' => 'Query test route', 'url' => Url::fromRoute('router_test.1'), 'query' => array('key' => 'value')));
$expected_links = '';
$expected_links .= '<ul id="somelinks">';
$expected_links .= '<li class="a-link"><a href="' . Url::fromUri('base://a/link')->toString() . '">' . String::checkPlain('A <link>') . '</a></li>';
$expected_links .= '<li class="plain-text">' . String::checkPlain('Plain "text"') . '</li>';
$expected_links .= '<li class="front-page"><a href="' . _url('<front>') . '">' . String::checkPlain('Front page') . '</a></li>';
$expected_links .= '<li class="router-test"><a href="' . \Drupal::urlGenerator()->generate('router_test.1') . '">' . String::checkPlain('Test route') . '</a></li>';
$query = array('key' => 'value');
$expected_links .= '<li class="query-test"><a href="' . \Drupal::urlGenerator()->generate('router_test.1', $query) . '">' . String::checkPlain('Query test route') . '</a></li>';
$expected_links .= '</ul>';
// Verify that passing a string as heading works.
$variables['heading'] = 'Links heading';
$expected_heading = '<h2>Links heading</h2>';
$expected = $expected_heading . $expected_links;
$this->assertThemeOutput('links', $variables, $expected);
// Restore the original request's query.
\Drupal::request()->query->replace($original_query);
// Verify that passing an array as heading works (core support).
$variables['heading'] = array('text' => 'Links heading', 'level' => 'h3', 'attributes' => array('class' => array('heading')));
$expected_heading = '<h3 class="heading">Links heading</h3>';
$expected = $expected_heading . $expected_links;
$this->assertThemeOutput('links', $variables, $expected);
// Verify that passing attributes for the heading works.
$variables['heading'] = array('text' => 'Links heading', 'level' => 'h3', 'attributes' => array('id' => 'heading'));
$expected_heading = '<h3 id="heading">Links heading</h3>';
$expected = $expected_heading . $expected_links;
$this->assertThemeOutput('links', $variables, $expected);
// Verify that passing attributes for the links work.
$variables['links']['plain text']['attributes'] = array('class' => array('a/class'));
$expected_links = '';
$expected_links .= '<ul id="somelinks">';
$expected_links .= '<li class="a-link"><a href="' . Url::fromUri('base://a/link')->toString() . '">' . String::checkPlain('A <link>') . '</a></li>';
$expected_links .= '<li class="plain-text"><span class="a/class">' . String::checkPlain('Plain "text"') . '</span></li>';
$expected_links .= '<li class="front-page"><a href="' . _url('<front>') . '">' . String::checkPlain('Front page') . '</a></li>';
$expected_links .= '<li class="router-test"><a href="' . \Drupal::urlGenerator()->generate('router_test.1') . '">' . String::checkPlain('Test route') . '</a></li>';
$query = array('key' => 'value');
$expected_links .= '<li class="query-test"><a href="' . \Drupal::urlGenerator()->generate('router_test.1', $query) . '">' . String::checkPlain('Query test route') . '</a></li>';
$expected_links .= '</ul>';
$expected = $expected_heading . $expected_links;
$this->assertThemeOutput('links', $variables, $expected);
// Verify the data- attributes for setting the "active" class on links.
\Drupal::currentUser()->setAccount(new UserSession(array('uid' => 1)));
$variables['set_active_class'] = TRUE;
$expected_links = '';
$expected_links .= '<ul id="somelinks">';
$expected_links .= '<li class="a-link"><a href="' . Url::fromUri('base://a/link')->toString() . '">' . String::checkPlain('A <link>') . '</a></li>';
$expected_links .= '<li class="plain-text"><span class="a/class">' . String::checkPlain('Plain "text"') . '</span></li>';
$expected_links .= '<li data-drupal-link-system-path="<front>" class="front-page"><a href="' . _url('<front>') . '" data-drupal-link-system-path="<front>">' . String::checkPlain('Front page') . '</a></li>';
$expected_links .= '<li data-drupal-link-system-path="router_test/test1" class="router-test"><a href="' . \Drupal::urlGenerator()->generate('router_test.1') . '" data-drupal-link-system-path="router_test/test1">' . String::checkPlain('Test route') . '</a></li>';
$query = array('key' => 'value');
$encoded_query = String::checkPlain(Json::encode($query));
$expected_links .= '<li data-drupal-link-query="' . $encoded_query . '" data-drupal-link-system-path="router_test/test1" class="query-test"><a href="' . \Drupal::urlGenerator()->generate('router_test.1', $query) . '" data-drupal-link-query="' . $encoded_query . '" data-drupal-link-system-path="router_test/test1">' . String::checkPlain('Query test route') . '</a></li>';
$expected_links .= '</ul>';
$expected = $expected_heading . $expected_links;
$this->assertThemeOutput('links', $variables, $expected);
}