/** * Test if the source is able to pull content in requested language. */ function testCartEnforceSourceLanguage() { $content_type = $this->drupalCreateContentType(); $node_sk = $this->drupalCreateNode(array('title' => $this->randomMachineName(), 'langcode' => 'sk', 'type' => $content_type->id())); $node = $node_sk->addTranslation('en'); $node->title->value = $this->randomMachineName(); $node->body->value = $this->randomMachineName(); $node->save(); $node_cs = $this->drupalCreateNode(array('title' => $this->randomMachineName(), 'langcode' => 'cs', 'type' => $content_type->id())); $this->loginAsTranslator(); $job_item_sk = tmgmt_job_item_create('content', 'node', $node_sk->id()); $job_item_sk->save(); $this->drupalGet('tmgmt-add-to-cart/' . $job_item_sk->id()); $job_items_data[$job_item_sk->getItemId()] = $job_item_sk->getItemType(); $job_item_cs = tmgmt_job_item_create('content', 'node', $node_cs->id()); $job_item_cs->save(); $this->drupalGet('tmgmt-add-to-cart/' . $job_item_cs->id()); $job_items_data[$job_item_cs->getItemId()] = $job_item_cs->getItemType(); $this->drupalPostForm('admin/tmgmt/cart', array('enforced_source_language' => TRUE, 'source_language' => 'en', 'target_language[]' => array('es')), t('Request translation')); $this->assertText(t('One job needs to be checked out.')); $this->assertRaw(t('One item skipped as for the language @language it was not possible to retrieve a translation.', array('@language' => 'English'))); $this->assertText(t('You have enforced the job source language which most likely resulted in having a translation of your original content as the job source text. You should review the job translation received from the translator carefully to prevent the content quality loss.')); $args = explode('/', $this->getUrl()); $tjid = array_pop($args); $this->drupalPostForm(NULL, array(), t('Submit to provider')); // We cannot test for the item data as items without a job are not able to // get the data in case the source language is overridden. Therefore only // testing for item_id and item_type values. foreach (Job::load($tjid)->getItems() as $job_item) { $this->assertEqual($job_items_data[$job_item->getItemId()], $job_item->getItemType()); } $this->drupalGet('admin/tmgmt/cart'); $this->assertText($node_cs->getTitle()); $this->assertNoText($node_sk->getTitle()); // Test that duplicate submission of an item is not allowed. $this->drupalPostForm('admin/tmgmt/cart', array('target_language[]' => array('es')), t('Request translation')); $this->drupalPostForm(NULL, array(), t('Submit to provider')); $this->assertText(t('Test translation created.')); $job_item_cs = tmgmt_job_item_create('content', 'node', $node_cs->id()); $job_item_cs->save(); $this->drupalGet('tmgmt-add-to-cart/' . $job_item_cs->id()); $job_items_data[$job_item_cs->getItemId()] = $job_item_cs->getItemType(); $this->drupalPostForm('admin/tmgmt/cart', array('target_language[]' => array('es')), t('Request translation')); $this->assertText(t('1 item conflict with pending item and will be dropped on submission.')); $this->drupalPostForm(NULL, array(), t('Submit to provider')); $this->assertText(t('All job items are conflicting, the job can not be submitted.')); }
/** * Tests of the task progress. */ public function testLocalProgress() { // Load the local translator. $translator = Translator::load('local'); // Create assignee with the skills. $assignee1 = $this->drupalCreateUser($this->localManagerPermissions); $this->drupalLogin($assignee1); $edit = array('tmgmt_translation_skills[0][language_from]' => 'en', 'tmgmt_translation_skills[0][language_to]' => 'de'); $this->drupalPostForm('user/' . $assignee1->id() . '/edit', $edit, t('Save')); // Login as translator. $this->loginAsTranslator(); // Create the basic html format. $basic_html_format = FilterFormat::create(array('format' => 'basic_html', 'name' => 'Basic HTML')); $basic_html_format->save(); // Create a Job. $job = $this->createJob(); $job->translator = $translator->id(); $item1 = $job->addItem('test_source', 'test', '1'); \Drupal::state()->set('tmgmt.test_source_data', array('title' => array('deep_nesting' => array('#text' => 'Example text', '#label' => 'Label for job item with type test and id 2.', '#translate' => TRUE)), 'text' => array('deep_nesting' => array('#text' => 'Example text', '#label' => 'Label for job item with type test and id 2.', '#translate' => TRUE)))); $job->addItem('test_source', 'test', '2'); $job->save(); $this->drupalGet($job->toUrl()); $this->drupalPostForm(NULL, NULL, t('Submit to provider')); // Login as assignee. $this->drupalLogin($assignee1); // Check the task unassigned icon. $this->drupalGet('/manage-translate'); $this->assertTaskStatusIcon(1, 'manage-translate-task', 'unassigned', 'Unassigned'); // Assign the task and check its icon. $edit = array('action' => 'tmgmt_local_task_assign_to_me', 'tmgmt_local_task_bulk_form[0]' => 1); $this->drupalPostForm(NULL, $edit, t('Apply')); $this->assertTaskStatusIcon(1, 'manage-translate-task', 'assigned', 'Needs action'); // Unassign it back. $edit = array('action' => 'tmgmt_local_task_unassign_multiple', 'tmgmt_local_task_bulk_form[0]' => 1); $this->drupalPostForm(NULL, $edit, t('Apply')); // Check its unassigned icon. $this->drupalGet('/manage-translate'); $this->assertTaskStatusIcon(1, 'manage-translate-task', 'unassigned', 'Unassigned'); $this->drupalGet('/translate'); // Check the unassigned status. $this->assertTaskStatusIcon(1, 'task-overview', 'unassigned', 'Unassigned'); $edit = array('tmgmt_local_task_bulk_form[0]' => 1); $this->drupalPostForm(NULL, $edit, t('Apply')); // Check the needs action status. $this->assertTaskStatusIcon(1, 'task-overview', 'my-tasks', 'Needs action'); // Check if the task is displayed on pending overview. $this->drupalGet('/translate/pending'); $this->assertTaskStatusIcon(1, 'task-overview', 'pending', 'Needs action'); $this->drupalGet('/manage-translate/pending'); $this->assertTaskStatusIcon(1, 'manage-translate-task', 'pending', 'Needs action'); // Check the icons of the task items. $this->drupalGet('/translate/1'); $this->assertTaskItemStatusIcon(1, 'Untranslated'); $this->assertTaskItemStatusIcon(2, 'Untranslated'); $this->assertTaskItemProgress(1, 1, 0, 0); $this->assertTaskItemProgress(2, 2, 0, 0); // Check the progress bar and status of the task. $this->drupalGet('/translate'); $this->assertTaskProgress(1, 'my-tasks', 3, 0, 0); $this->assertTaskStatusIcon(1, 'task-overview', 'my-tasks', 'Needs action'); $this->drupalGet('/manage-translate'); $this->assertTaskStatusIcon(1, 'manage-translate-task', 'assigned', 'Needs action'); // Set two items as translated. $this->drupalPostAjaxForm('/translate/items/1', [], 'finish-dummy|deep_nesting'); $this->drupalPostAjaxForm('/translate/items/2', [], 'finish-title|deep_nesting'); // Check the task items icons and progress. $this->drupalGet('/translate/1'); $this->assertTaskItemStatusIcon(1, 'Untranslated'); $this->assertTaskItemStatusIcon(2, 'Untranslated'); $this->assertTaskItemProgress(1, 0, 1, 0); $this->assertTaskItemProgress(2, 1, 1, 0); // Check the progress bar and status of the task. $this->drupalGet('/translate'); $this->assertTaskProgress(1, 'my-tasks', 1, 2, 0); $this->assertTaskStatusIcon(1, 'task-overview', 'my-tasks', 'Needs action'); $this->drupalGet('/manage-translate'); $this->assertTaskStatusIcon(1, 'manage-translate-task', 'assigned', 'Needs action'); // Save the first item as completed and check item icons and progress. $edit = ['dummy|deep_nesting[translation]' => 'German translation']; $this->drupalPostForm('/translate/items/1', $edit, t('Save as completed')); $this->assertTaskItemStatusIcon(1, 'Translated'); $this->assertTaskItemStatusIcon(2, 'Untranslated'); $this->assertTaskItemProgress(1, 0, 0, 1); $this->assertTaskItemProgress(2, 1, 1, 0); // Check the progress bar and status of the task. $this->drupalGet('/translate'); $this->assertTaskProgress(1, 'my-tasks', 1, 1, 1); $this->assertTaskStatusIcon(1, 'task-overview', 'my-tasks', 'Needs action'); $this->drupalGet('/manage-translate'); $this->assertTaskStatusIcon(1, 'manage-translate-task', 'assigned', 'Needs action'); // Save the second item as completed. $edit = ['title|deep_nesting[translation]' => 'German translation of title', 'text|deep_nesting[translation]' => 'German translation of text']; $this->drupalPostForm('/translate/items/2', $edit, t('Save as completed')); // Check the icon a progress bar of the task. $this->assertTaskProgress(1, 'my-tasks', 0, 0, 3); $this->assertTaskStatusIcon(1, 'task-overview', 'my-tasks', 'In review'); // Check the task items icons. $this->drupalGet('/translate/1'); $this->assertTaskItemStatusIcon(1, 'Translated'); $this->assertTaskItemStatusIcon(2, 'Translated'); $this->assertTaskItemProgress(1, 0, 0, 1); $this->assertTaskItemProgress(2, 0, 0, 2); // Check if the task is displayed on the completed overview. $this->drupalGet('/translate/completed'); $this->assertTaskStatusIcon(1, 'task-overview', 'completed', 'In review'); $this->drupalGet('/manage-translate/completed'); $this->assertTaskStatusIcon(1, 'manage-translate-task', 'completed', 'In review'); // Accept translation of the job items. /** @var \Drupal\tmgmt\Entity\Job $job1 */ $job1 = Job::load($job->id()); /** @var \Drupal\tmgmt\Entity\JobItem $item */ foreach ($job1->getItems() as $item) { $item->acceptTranslation(); } // Check if the task is displayed on the closed overview. $this->drupalGet('/translate/closed'); $this->assertTaskStatusIcon(1, 'task-overview', 'closed', 'Closed'); $this->drupalGet('/manage-translate/closed'); $this->assertTaskStatusIcon(1, 'manage-translate-task', 'closed', 'Closed'); // Assert the legend. $this->drupalGet('/translate/items/' . $item1->id()); $this->assertRaw('class="tmgmt-color-legend'); }
/** * {@inheritdoc} */ public static function preDelete(EntityStorageInterface $storage, array $entities) { // We are never going to have many entities here, so we can risk a loop. foreach ($entities as $key => $name) { // Find active jobs associated with the translator that is being deleted. $job_ids = \Drupal::entityQuery('tmgmt_job')->condition('state', [Job::STATE_ACTIVE, Job::STATE_CONTINUOUS, Job::STATE_UNPROCESSED], 'IN')->condition('translator', $key)->execute(); $jobs = Job::loadMultiple($job_ids); /** @var \Drupal\tmgmt\JobInterface $job */ foreach ($jobs as $job) { $job->aborted('Job has been aborted because the translation provider %provider was deleted.', ['%provider' => $job->getTranslatorLabel()]); } } parent::preDelete($storage, $entities); }
/** * Test abortion of continuous translators. */ public function testContinuousTranslatorsAbortion() { \Drupal::service('router.builder')->rebuild(); // Create a continuous translator. $translator = Translator::load('test_translator'); $this->assertTrue($translator->getPlugin() instanceof ContinuousTranslatorInterface); // Create a node type. $type = NodeType::create(['type' => $this->randomMachineName()]); $type->save(); // Enable the node type for translation. $content_translation_manager = \Drupal::service('content_translation.manager'); $content_translation_manager->setEnabled('node', $type->id(), TRUE); // Create a continuous job. $continuous_job = tmgmt_job_create('en', 'de', 0, ['job_type' => Job::TYPE_CONTINUOUS, 'continuous_settings' => ['content' => ['node' => ['enabled' => TRUE, 'bundles' => [$type->id() => TRUE]]]]]); $continuous_job->translator = $translator; $continuous_job->save(); // Abort a continuous job. $continuous_job->aborted(); // Create a node. $node = Node::create(array('title' => $this->randomMachineName(), 'type' => $type->id(), 'language' => 'en', 'body' => $this->randomMachineName())); $node->save(); // Assert that node has not been captured. $updated_continuous_job = Job::load($continuous_job->id()); $this->assertEqual($updated_continuous_job->getItems(), []); $this->assertEqual($updated_continuous_job->getState(), Job::STATE_ABORTED); }
/** * Helper method to do translation request. * * @param Job $job * TMGMT Job to be used for translation. * @param array|string $q * Text/texts to be translated. * * @return array * Userialized JSON containing translated texts. */ protected function googleRequestTranslation(Job $job, $q) { $translator = $job->getTranslator(); return $this->doRequest($translator, 'translate', array('source' => $job->getRemoteSourceLanguage(), 'target' => $job->getRemoteTargetLanguage(), $this->qParamName => $q), array('headers' => array('Content-Type' => 'text/plain'))); }
/** * Tests of the job item review process. */ public function testContinuous() { // Test that continuous jobs are shown in the job overview. $this->container->get('module_installer')->install(['tmgmt_file'], TRUE); $non_continuous_translator = Translator::create(['name' => strtolower($this->randomMachineName()), 'label' => $this->randomMachineName(), 'plugin' => 'file', 'remote_languages_mappings' => [], 'settings' => []]); $non_continuous_translator->save(); $continuous_job = $this->createJob('en', 'de', 0, ['label' => 'Continuous job', 'job_type' => 'continuous']); $this->drupalGet('admin/tmgmt/jobs', ['query' => ['state' => '6']]); $this->assertText($continuous_job->label(), 'Continuous job is displayed on job overview page with status filter on continuous jobs.'); $this->drupalGet('admin/tmgmt/jobs', ['query' => ['state' => 'in_progress']]); $this->assertNoText($continuous_job->label(), 'Continuous job is not displayed on job overview page if status filter is on in progress jobs.'); // Test that there are source items checkboxes on a continuous job form. $this->drupalGet('admin/tmgmt/jobs/' . $continuous_job->id()); $this->assertText($continuous_job->label()); $this->assertNoFieldChecked('edit-continuous-settings-content-node-enabled', 'There is content checkbox and it is not checked yet.'); $this->assertNoFieldChecked('edit-continuous-settings-content-node-bundles-article', 'There is article checkbox and it is not checked yet.'); $this->assertNoFieldChecked('edit-continuous-settings-content-node-bundles-page', 'There is page checkbox and it is not checked yet.'); // Enable Article source item for continuous job. $edit_continuous_job = ['continuous_settings[content][node][enabled]' => TRUE, 'continuous_settings[content][node][bundles][article]' => TRUE]; $this->drupalPostForm(NULL, $edit_continuous_job, t('Save job')); // Test that continuous settings configuration is saved correctly. $updated_continuous_job = Job::load($continuous_job->id()); $job_continuous_settings = $updated_continuous_job->getContinuousSettings(); $this->assertEqual($job_continuous_settings['content']['node']['enabled'], 1, 'Continuous settings configuration for node is saved correctly.'); $this->assertEqual($job_continuous_settings['content']['node']['bundles']['article'], 1, 'Continuous settings configuration for article is saved correctly.'); $this->assertEqual($job_continuous_settings['content']['node']['bundles']['page'], 0, 'Continuous settings configuration for page is saved correctly.'); // Test that continuous settings checkboxes are checked correctly. $this->clickLink('Manage'); $this->assertText($continuous_job->label()); $this->assertFieldChecked('edit-continuous-settings-content-node-enabled', 'Content checkbox is now checked.'); $this->assertFieldChecked('edit-continuous-settings-content-node-bundles-article', 'Article checkbox now checked.'); $this->assertNoFieldChecked('edit-continuous-settings-content-node-bundles-page', 'Page checkbox is not checked.'); // Create continuous job through the form. $this->loginAsTranslator(['access administration pages', 'create translation jobs', 'submit translation jobs', 'access user profiles'], TRUE); $owner = $this->drupalCreateUser($this->translator_permissions); $this->drupalGet('admin/tmgmt/continuous_jobs/continuous_add'); $this->assertResponse(403, 'Access denied'); $this->loginAsAdmin(); $this->drupalGet('admin/tmgmt/continuous_jobs/continuous_add'); $this->assertNoText($non_continuous_translator->label()); $continuous_job_label = strtolower($this->randomMachineName()); $edit_job = ['label[0][value]' => $continuous_job_label, 'target_language' => 'de', 'uid' => $owner->getDisplayName(), 'translator' => $this->default_translator->id()]; $this->drupalPostForm(NULL, $edit_job, t('Save job')); $this->assertText($continuous_job_label, 'Continuous job was created.'); // Test that previous created job is continuous job. $this->drupalGet('admin/tmgmt/jobs'); $this->assertText($continuous_job_label, 'Created continuous job is displayed on job overview page.'); // Test that job overview page with status to continuous does not have // Submit link. $this->drupalGet('admin/tmgmt/jobs', ['query' => ['state' => Job::STATE_CONTINUOUS]]); $this->assertNoLink('Submit', 'There is no Submit link on job overview with status to continuous.'); // Test that all unnecessary fields and buttons do not exist on continuous // job edit form. $this->clickLink('Manage', 0); $this->assertText($continuous_job_label); $this->assertFieldById('edit-uid', $owner->getDisplayName() . ' (' . $owner->id() . ')', 'Job owner set correctly'); $this->assertNoRaw('<label for="edit-translator">Provider</label>', 'There is no Provider info field on continuous job edit form.'); $this->assertNoRaw('<label for="edit-word-count">Total word count</label>', 'There is no Total word count info field on continuous job edit form.'); $this->assertNoRaw('<label for="edit-tags-count">Total HTML tags count</label>', 'There is no Total HTML tags count info field on continuous job edit form.'); $this->assertNoRaw('<label for="edit-created">Created</label>', 'There is no Created info field on continuous job edit form.'); $this->assertNoRaw('id="edit-job-items-wrapper"', 'There is no Job items field on continuous job edit form.'); $this->assertNoRaw('<div class="tmgmt-color-legend clearfix">', 'There is no Item state legend on continuous job edit form.'); $this->assertNoRaw('id="edit-messages"', 'There is no Translation Job Messages field on continuous job edit form.'); $this->assertNoFieldById('edit-abort-job', NULL, 'There is no Abort job button.'); $this->assertNoFieldById('edit-submit', NULL, 'There is no Submit button.'); $this->assertNoFieldById('edit-resubmit-job', NULL, 'There is Resubmit job button.'); // Remove continuous jobs and assert there is no filter displayed. $this->loginAsAdmin(); $continuous_job->delete(); $this->drupalGet('admin/tmgmt/jobs'); $this->clickLink(t('Delete')); $this->drupalPostForm(NULL, array(), t('Delete')); $this->drupalGet('admin/tmgmt/job_items'); $this->assertNoText(t('Job type')); $this->assertNoFieldByName('job_type'); // Test that the empty text is displayed. $this->drupalGet('admin/tmgmt/job_items', array('query' => array('state' => 5))); $this->assertText(t('No job items for the current selection.')); }
/** * Creates job item and submits according to the configured settings. * * The job item will only be created if the given source plugin for the job is * configured to accept this source. * * The job item will be immediately submitted to the translator unless * this happens on cron runs. * * @param \Drupal\tmgmt\Entity\Job $job * Continuous job. * @param string $plugin * The plugin name. * @param string $item_type * The source item type. * @param string $item_id * The source item id. * * @return \Drupal\tmgmt\Entity\JobItem * Continuous job item. */ public function addItem(Job $job, $plugin, $item_type, $item_id) { // Check if a job item should be created. $most_recent_job_item = $job->getMostRecentItem($plugin, $item_type, $item_id); $should_create_item = $this->sourcePluginManager->createInstance($plugin)->shouldCreateContinuousItem($job, $plugin, $item_type, $item_id); if ($should_create_item) { if ($most_recent_job_item) { // If the most recent job item is active do nothing. if (!$most_recent_job_item->isAborted() && !$most_recent_job_item->isAccepted()) { $most_recent_job_item->addMessage('Source was updated, changes were ignored as job item is still active.'); return NULL; } } // If there are no job items or it's finished/aborted create new one. $job_item = $job->addItem($plugin, $item_type, $item_id); $job_item->addMessage('Continuous job item created'); // Only submit the item if cron submission is disabled. if (!$this->configFactory->get('tmgmt.settings')->get('submit_job_item_on_cron')) { $translator = $job->getTranslatorPlugin(); $translator->requestJobItemsTranslation([$job_item]); } return $job_item; } return NULL; }
/** * {@inheritdoc} */ public function shouldCreateContinuousItem(Job $job, $plugin, $item_type, $item_id) { $continuous_settings = $job->getContinuousSettings(); $entity_manager = \Drupal::entityTypeManager(); $entity = $entity_manager->getStorage($item_type)->load($item_id); $translation_manager = \Drupal::service('content_translation.manager'); $translation = $entity->hasTranslation($job->getTargetLangcode()) ? $entity->getTranslation($job->getTargetLangcode()) : NULL; $metadata = isset($translation) ? $translation_manager->getTranslationMetadata($translation) : NULL; // If a translation exists and is not marked as outdated, no new job items // needs to be created. if (isset($translation) && !$metadata->isOutdated()) { return FALSE; } else { if ($entity && $entity->getEntityType()->hasKey('bundle')) { // The entity type has bundles, check both the entity type setting and // the bundle. if (!empty($continuous_settings[$plugin][$item_type]['bundles'][$entity->bundle()]) && !empty($continuous_settings[$plugin][$item_type]['enabled'])) { return TRUE; } } elseif (!empty($continuous_settings[$plugin][$item_type]['enabled'])) { return TRUE; } } return FALSE; }
/** * {@inheritdoc} */ public function setState($state, $message = NULL, $variables = array(), $type = 'debug') { // Return TRUE if the state could be set. Return FALSE otherwise. if (array_key_exists($state, Job::getStates())) { $this->state = $state; $this->save(); // If a message is attached to this state change add it now. if (!empty($message)) { $this->addMessage($message, $variables, $type); } } return $this->getState(); }
/** * Test the CDATA option for XLIFF export and import. */ function testXLIFFCDATA() { $translator = $this->createTranslator(['plugin' => 'file', 'settings' => ['export_format' => 'xlf', 'xliff_cdata' => TRUE]]); // Get the source text. $source_text = trim(file_get_contents(drupal_get_path('module', 'tmgmt') . '/tests/testing_html/sample.html')); // Create a new job. $job = $this->createJob(); $job->translator = $translator->id(); $job->addItem('test_html_source', 'test', '1'); $job->requestTranslation(); $messages = $job->getMessages(); $message = reset($messages); // Get XLIFF content. $variables = $message->variables; $download_url = $variables->{'@link'}; $this->assertFalse((bool) strpos('< a', $download_url)); $xliff = file_get_contents($download_url); $dom = new \DOMDocument(); $dom->loadXML($xliff); $this->assertTrue($dom->schemaValidate(drupal_get_path('module', 'tmgmt_file') . '/xliff-core-1.2-strict.xsd')); // "Translate" items. $xml = simplexml_import_dom($dom); $translated_text = array(); foreach ($xml->file->body->children() as $group) { foreach ($group->children() as $transunit) { if ($transunit->getName() == 'trans-unit') { // The target should be empty. $this->assertEqual($transunit->target, ''); // Update translations using CDATA. $node = dom_import_simplexml($transunit->target); $owner = $node->ownerDocument; $node->appendChild($owner->createCDATASection($xml->file['target-language'] . '_' . (string) $transunit->source)); // Store the text to allow assertions later on. $translated_text[(string) $group['id']][(string) $transunit['id']] = (string) $transunit->target; } } } $translated_file = 'public://tmgmt_file/translated file.xlf'; $xml->asXML($translated_file); // Import the file and check translation for the "dummy" item. $edit = array('files[file]' => $translated_file); $this->drupalPostForm($job->urlInfo(), $edit, t('Import')); // Reset caches and reload job. \Drupal::entityManager()->getStorage('tmgmt_job')->resetCache(); \Drupal::entityManager()->getStorage('tmgmt_job_item')->resetCache(); $job = Job::load($job->id()); $item_data = $job->getData(array(1, 'dummy', 'deep_nesting')); $this->assertEqual(trim($item_data[1]['#translation']['#text']), str_replace($source_text, $xml->file['target-language'] . '_' . $source_text, $source_text)); }
/** * Test the deletion and abortion of job item. * * @todo There will be some overlap with Aborting items & testAbortJob. */ function testJobItemDelete() { $this->loginAsAdmin(); // Create a translator. $translator = $this->createTranslator(); // Create a job and attach to the translator. $job = $this->createJob(); $job->translator = $translator; $job->settings = array(); $job->save(); $job->setState(Job::STATE_ACTIVE); // Add item to the job. $item = $job->addItem('test_source', 'test', 1); $item->setState(JobItem::STATE_ACTIVE); // Check that there is no delete link on item review form. $this->drupalGet('admin/tmgmt/items/' . $item->id()); $this->assertNoFieldById('edit-delete', NULL, 'There is no delete button.'); $this->drupalGet('admin/tmgmt/jobs/' . $job->id()); // Check that there is no delete link. $this->assertNoLink('Delete'); // Check for abort link. $this->assertLink('Abort'); $this->clickLink('Abort'); $this->assertText(t('Are you sure you want to abort the job item test_source:test:1?')); // Check if cancel button is present or not. $this->assertLink('Cancel'); // Abort the job item. $this->drupalPostForm(NULL, [], t('Confirm')); // Reload job and check its state and state of its item. \Drupal::entityManager()->getStorage('tmgmt_job')->resetCache(); $job = Job::load($job->id()); $this->assertTrue($job->isFinished()); $items = $job->getItems(); $item = reset($items); $this->assertTrue($item->isAborted()); // Check that there is no delete button on item review form. $this->drupalGet('admin/tmgmt/items/' . $item->id()); $this->assertNoFieldById('edit-delete', NULL, 'There is delete button.'); // Check that there is no accept button on item review form. $this->assertNoFieldById('edit-accept', NULL, 'There is no accept button.'); $this->drupalGet('admin/tmgmt/jobs/' . $job->id()); // Check that there is no delete link on job overview. $this->assertNoLink('Delete'); }
/** * Helper function to process the source text. * * @param string $source * Job data array. * @param array $key_array * The source item data key. * * @return string */ protected function processForExport($source, array $key_array) { $tjiid = $key_array[0]; $key_string = \Drupal::service('tmgmt.data')->ensureStringKey($key_array); // The reason why we use DOMDocument object here and not just XMLReader // is the DOMDocument's ability to deal with broken HTML. $dom = new \DOMDocument(); // We need to append the head with encoding so that special characters // are read correctly. $dom->loadHTML("<html><head><meta http-equiv='Content-type' content='text/html; charset=UTF-8' /></head><body>" . $source . '</body></html>'); $iterator = new \RecursiveIteratorIterator(new RecursiveDOMIterator($dom), \RecursiveIteratorIterator::SELF_FIRST); $writer = new \XMLWriter(); $writer->openMemory(); $writer->startDocument('1.0', 'UTF-8'); $writer->startElement('wrapper'); $tray = array(); $non_pair_tags = array('br', 'img'); $xliff_validation = $this->job->getSetting('xliff_validation'); /** @var \DOMElement $node */ foreach ($iterator as $node) { if (in_array($node->nodeName, array('html', 'body', 'head', 'meta'))) { continue; } if ($node->nodeType === XML_ELEMENT_NODE) { // Increment the elements count and compose element id. if (!isset($xliff_validation[$key_string])) { $xliff_validation[$key_string] = 0; } $xliff_validation[$key_string]++; $id = 'tjiid' . $tjiid . '-' . $xliff_validation[$key_string]; $is_pair_tag = !in_array($node->nodeName, $non_pair_tags); if ($is_pair_tag) { $this->writeBPT($writer, $node, $id); } elseif ($node->nodeName == 'img') { $this->writeIMG($writer, $node, $id); } elseif ($node->nodeName == 'br') { $this->writeBR($writer, $node, $id); } // Add to tray new element info. $tray[$id] = array('name' => $node->nodeName, 'id' => $id, 'value' => $node->nodeValue, 'built_text' => '', 'is_pair_tag' => $is_pair_tag); } elseif ($node->nodeName == '#text') { // Add the node value to the text output. $writer->writeCdata($this->toEntities($node->nodeValue)); foreach ($tray as &$info) { $info['built_text'] .= $node->nodeValue; } } // Reverse so that pair tags are closed in the expected order. $reversed_tray = array_reverse($tray); foreach ($reversed_tray as $_info) { // If the build_text equals to the node value and it is not a pair tag // add the end pair tag markup. if ($_info['value'] == $_info['built_text'] && $_info['is_pair_tag']) { // Count also for the closing elements. $xliff_validation[$key_string]++; $this->writeEPT($writer, $_info['name'], $_info['id']); // When the end pair tag has been written unset the element info // from the tray. unset($tray[$_info['id']]); } } } // Set the xliff_validation data and save the job. $this->job->settings->xliff_validation = $xliff_validation; $this->job->save(); $writer->endElement(); // Load the output with XMLReader so that we can easily get the inner xml. $reader = new \XMLReader(); $reader->XML($writer->outputMemory()); $reader->read(); return $reader->readInnerXML(); }
/** * Overrides Drupal\Core\Entity\EntityForm::form(). */ public function form(array $form, FormStateInterface $form_state) { $job = $this->entity; // Handle source language. $available['source_language'] = tmgmt_available_languages(); $job->source_language = $form_state->getValue('source_language') ?: $job->getSourceLangcode(); // Handle target language. $available['target_language'] = tmgmt_available_languages(); $job->target_language = $form_state->getValue('target_language') ?: $job->getTargetLangcode(); // Remove impossible combinations so we don't end up with the same source and // target language in the dropdowns. foreach (array('source_language' => 'target_language', 'target_language' => 'source_language') as $key => $opposite) { if (!empty($job->{$key})) { unset($available[$opposite][$job->{$key}->value]); } } $source = $job->getSourceLanguage() ? $job->getSourceLanguage()->getName() : '?'; if (!$job->getTargetLangcode() || $job->getTargetLangcode() == LanguageInterface::LANGCODE_NOT_SPECIFIED) { $job->target_language = key($available['target_language']); $target = '?'; } else { $target = $job->getTargetLanguage()->getName(); } $states = Job::getStates(); // Set the title of the page to the label and the current state of the job. $form['#title'] = t('@title (@source to @target, @state)', array('@title' => $job->label(), '@source' => $source, '@target' => $target, '@state' => $states[$job->getState()])); $form = parent::form($form, $form_state); $form['info'] = array('#type' => 'container', '#attributes' => array('class' => array('tmgmt-ui-job-info', 'clearfix')), '#weight' => 0); // Check for label value and set for dynamically change. if ($form_state->getValue('label') && $form_state->getValue('label') == $job->label()) { $job->label = NULL; $job->label = $job->label(); $form_state->setValue('label', $job->label()); } $form['label']['widget'][0]['value']['#description'] = t('You can provide a label for this job in order to identify it easily later on. Or leave it empty to use the default one.'); $form['label']['#group'] = 'info'; $form['label']['#prefix'] = '<div id="tmgmt-ui-label">'; $form['label']['#suffix'] = '</div>'; // Make the source and target language flexible by showing either a select // dropdown or the plain string (if preselected). if ($job->getSourceLangcode() || !$job->isSubmittable()) { $form['info']['source_language'] = array('#title' => t('Source language'), '#type' => 'item', '#markup' => isset($available['source_language'][$job->getSourceLangcode()]) ? $available['source_language'][$job->getSourceLangcode()] : '', '#prefix' => '<div id="tmgmt-ui-source-language" class="tmgmt-ui-source-language tmgmt-ui-info-item">', '#suffix' => '</div>', '#value' => $job->getSourceLangcode()); } else { $form['info']['source_language'] = array('#title' => t('Source language'), '#type' => 'select', '#options' => $available['source_language'], '#default_value' => $job->getSourceLangcode(), '#required' => TRUE, '#prefix' => '<div id="tmgmt-ui-source-language" class="tmgmt-ui-source-language tmgmt-ui-info-item">', '#suffix' => '</div>', '#ajax' => array('callback' => array($this, 'ajaxLanguageSelect'))); } if (!$job->isSubmittable()) { $form['info']['target_language'] = array('#title' => t('Target language'), '#type' => 'item', '#markup' => isset($available['target_language'][$job->getTargetLangcode()]) ? $available['target_language'][$job->getTargetLangcode()] : '', '#prefix' => '<div id="tmgmt-ui-target-language" class="tmgmt-ui-target-language tmgmt-ui-info-item">', '#suffix' => '</div>', '#value' => $job->getTargetLangcode()); } else { $form['info']['target_language'] = array('#title' => t('Target language'), '#type' => 'select', '#options' => $available['target_language'], '#default_value' => $job->getTargetLangcode(), '#required' => TRUE, '#prefix' => '<div id="tmgmt-ui-target-language" class="tmgmt-ui-target-language tmgmt-ui-info-item">', '#suffix' => '</div>', '#ajax' => array('callback' => array($this, 'ajaxLanguageSelect'), 'wrapper' => 'tmgmt-ui-target-language')); } // Display selected translator for already submitted jobs. if (!$job->isSubmittable() && !$job->isContinuous()) { $form['info']['translator'] = array('#type' => 'item', '#title' => t('Provider'), '#markup' => $job->getTranslatorLabel(), '#prefix' => '<div class="tmgmt-ui-translator tmgmt-ui-info-item">', '#suffix' => '</div>', '#value' => $job->getTranslatorId()); } if (!$job->isContinuous()) { $form['info']['word_count'] = array('#type' => 'item', '#title' => t('Total words'), '#markup' => number_format($job->getWordCount()), '#prefix' => '<div class="tmgmt-ui-word-count tmgmt-ui-info-item">', '#suffix' => '</div>'); $form['info']['tags_count'] = array('#type' => 'item', '#title' => t('Total HTML tags'), '#markup' => number_format($job->getTagsCount()), '#prefix' => '<div class="tmgmt-ui-tags-count tmgmt-ui-info-item">', '#suffix' => '</div>'); } else { $roles1 = user_roles(TRUE, 'administer tmgmt'); $roles2 = user_roles(TRUE, 'create translation jobs'); $roles3 = user_roles(TRUE, 'submit translation jobs'); $roles4 = user_roles(TRUE, 'accept translation jobs'); $duplicates = array_merge($roles1, $roles2, $roles3, $roles4); $roles = array_unique($duplicates, SORT_REGULAR); if (array_key_exists('authenticated', $roles)) { $filter = []; } else { $ids = array_keys($roles); $roles = array_combine($ids, $ids); $filter = ['type' => 'role', 'role' => $roles]; } $form['info']['uid'] = array('#title' => t('Owner'), '#type' => 'entity_autocomplete', '#target_type' => 'user', '#selection_settings' => ['include_anonymous' => FALSE, 'filter' => $filter, 'field' => 'uid'], '#process_default_value' => TRUE, '#default_value' => $job->getOwnerId() == 0 ? User::load(\Drupal::currentUser()->id()) : $job->getOwner(), '#required' => TRUE, '#prefix' => '<div id="tmgmt-ui-owner" class="tmgmt-ui-owner tmgmt-ui-info-item">', '#suffix' => '</div>'); } if (!$job->isContinuous()) { // Checkout whether given source already has items in translation. $num_of_existing_items = count($job->getConflictingItemIds()); $form['message'] = array('#type' => 'html_tag', '#tag' => 'div', '#value' => \Drupal::translation()->formatPlural($num_of_existing_items, '1 item conflict with pending item and will be dropped on submission.', '@count items conflict with pending items and will be dropped on submission.'), '#prefix' => '<div class="messages existing-items messages--warning hidden">', '#suffix' => '</div>'); if ($num_of_existing_items) { $form['message']['#prefix'] = '<div class="messages existing-items messages--warning">'; } } // Display created time only for jobs that are not new anymore. if (!$job->isUnprocessed() && !$job->isContinuousActive()) { $form['info']['created'] = array('#type' => 'item', '#title' => t('Created'), '#markup' => format_date($job->getCreatedTime()), '#prefix' => '<div class="tmgmt-ui-created tmgmt-ui-info-item">', '#suffix' => '</div>', '#value' => $job->getCreatedTime()); } else { // Indicate the state to the forms css classes. $form['#attributes']['class'][] = 'state-unprocessed'; } if (!$job->isContinuous()) { if ($view = Views::getView('tmgmt_job_items')) { $form['job_items_wrapper'] = array('#type' => 'details', '#title' => t('Job items'), '#open' => in_array($job->getState(), array(Job::STATE_ACTIVE, Job::STATE_UNPROCESSED)), '#weight' => 10, '#prefix' => '<div id="tmgmt-ui-job-checkout-details">', '#suffix' => '</div>'); $form['footer'] = tmgmt_color_job_item_legend(); $form['footer']['#weight'] = 100; // Translation jobs. $output = $view->preview($job->isSubmittable() ? 'checkout' : 'submitted', array($job->id())); $form['job_items_wrapper']['items'] = array('#type' => 'markup', '#title' => $view->storage->label(), '#prefix' => '<div class="' . 'tmgmt-ui-job-items ' . ($job->isSubmittable() ? 'tmgmt-ui-job-submit' : 'tmgmt-ui-job-manage') . '">', 'view' => ['#markup' => $this->renderer->render($output)], '#attributes' => array('class' => array('tmgmt-ui-job-items', $job->isSubmittable() ? 'tmgmt-ui-job-submit' : 'tmgmt-ui-job-manage')), '#suffix' => '</div>'); } // A Wrapper for a button and a table with all suggestions. $form['job_items_wrapper']['suggestions'] = array('#type' => 'container', '#access' => $job->isSubmittable()); // Button to load all translation suggestions with AJAX. $form['job_items_wrapper']['suggestions']['load'] = array('#type' => 'submit', '#value' => t('Load suggestions'), '#submit' => array('::loadSuggestionsSubmit'), '#limit_validation_errors' => array(), '#attributes' => array('class' => array('tmgmt-ui-job-suggestions-load')), '#ajax' => array('callback' => '::ajaxLoadSuggestions', 'wrapper' => 'tmgmt-ui-job-items-suggestions', 'method' => 'replace', 'effect' => 'fade')); $form['job_items_wrapper']['suggestions']['container'] = array('#type' => 'container', '#prefix' => '<div id="tmgmt-ui-job-items-suggestions">', '#suffix' => '</div>'); // Create the suggestions table. $suggestions_table = array('#type' => 'tableselect', '#header' => array(), '#options' => array(), '#multiple' => TRUE); // If this is an AJAX-Request, load all related nodes and fill the table. if ($form_state->isRebuilding() && $form_state->get('rebuild_suggestions')) { $this->buildSuggestions($suggestions_table, $form_state); // A save button on bottom of the table is needed. $suggestions_table = array('suggestions_table' => $suggestions_table, 'suggestions_add' => array('#type' => 'submit', '#value' => t('Add suggestions'), '#submit' => array('::addSuggestionsSubmit'), '#limit_validation_errors' => array(array('suggestions_table')), '#attributes' => array('class' => array('tmgmt-ui-job-suggestions-add')), '#access' => !empty($suggestions_table['#options']))); $form['job_items_wrapper']['suggestions']['container']['suggestions_list'] = array('#type' => 'details', '#title' => t('Suggestions'), '#prefix' => '<div id="tmgmt-ui-job-items-suggestions">', '#suffix' => '</div>', '#open' => TRUE) + $suggestions_table; } } if ($job->isContinuous()) { $form['continuous_settings'] = array('#type' => 'details', '#title' => $this->t('Continuous settings'), '#description' => $this->t('Configure the sources that should be enabled for this continuous job.'), '#open' => TRUE, '#weight' => 10, '#tree' => TRUE); $source_manager = \Drupal::service('plugin.manager.tmgmt.source'); $source_plugins = $source_manager->getDefinitions(); foreach ($source_plugins as $type => $definition) { $plugin_type = $source_manager->createInstance($type); if ($plugin_type instanceof ContinuousSourceInterface) { $form['continuous_settings'][$type] = $plugin_type->continuousSettingsForm($form, $form_state, $job); } } } // Display the checkout settings form if the job can be checked out. if ($job->isSubmittable() || $job->isContinuous()) { $form['translator_wrapper'] = array('#type' => 'details', '#title' => t('Configure provider'), '#weight' => 20, '#prefix' => '<div id="tmgmt-ui-translator-wrapper">', '#suffix' => '</div>', '#open' => TRUE); // Show a list of translators tagged by availability for the selected source // and target language combination. if (!($translators = tmgmt_translator_labels_flagged($job))) { drupal_set_message(t('There are no providers available. Before you can checkout you need to @configure at least one provider.', array('@configure' => \Drupal::l(t('configure'), Url::fromRoute('entity.tmgmt_translator.collection')))), 'warning'); } $preselected_translator = $job->getTranslatorId() && isset($translators[$job->getTranslatorId()]) ? $job->getTranslatorId() : key($translators); $job->translator = $form_state->getValue('translator') ?: $preselected_translator; $form['translator_wrapper']['translator'] = array('#type' => 'select', '#title' => t('Provider'), '#description' => t('The configured provider that will process the translation.'), '#options' => $translators, '#access' => !empty($translators), '#default_value' => $job->getTranslatorId(), '#required' => TRUE, '#ajax' => array('callback' => array($this, 'ajaxTranslatorSelect'), 'wrapper' => 'tmgmt-ui-translator-settings')); $settings = $this->checkoutSettingsForm($form_state, $job); if (!is_array($settings)) { $settings = array(); } $form['translator_wrapper']['settings'] = array('#type' => 'details', '#title' => t('Checkout settings'), '#prefix' => '<div id="tmgmt-ui-translator-settings">', '#suffix' => '</div>', '#tree' => TRUE, '#open' => TRUE) + $settings; } elseif ($job->getTranslatorId() && !$job->isContinuous()) { $form['translator_wrapper'] = array('#type' => 'details', '#title' => t('Provider information'), '#open' => TRUE, '#weight' => 20); $form['translator_wrapper']['checkout_info'] = $this->checkoutInfo($job); } if (!$job->isContinuous() && !$job->isSubmittable() && empty($form['translator_wrapper']['checkout_info'])) { $form['translator_wrapper']['checkout_info'] = array('#type' => 'markup', '#markup' => t('The translator does not provide any information.')); } $form['clearfix'] = array('#markup' => '<div class="clearfix"></div>', '#weight' => 45); if (!$job->isContinuous() && ($view = Views::getView('tmgmt_job_messages'))) { $form['messages'] = array('#type' => 'details', '#title' => $view->storage->label(), '#open' => $job->getTranslatorId() ? TRUE : FALSE, '#weight' => 50); $output = $view->preview('embed', array($job->id())); $form['messages']['view']['#markup'] = drupal_render($output); } $form['#attached']['library'][] = 'tmgmt/admin'; return $form; }
function testBasicWorkflow() { // Submit a translation job. $submit_job = $this->createJobWithItems('submit'); $submit_job->requestTranslation(); $submit_job = Job::load($submit_job->id()); $this->assertTrue($submit_job->isActive()); $messages = $submit_job->getMessages(); $last_message = end($messages); $this->assertEqual('Test submit.', $last_message->message->value); // Translate a job. $translate_job = $this->createJobWithItems('translate'); $translate_job->requestTranslation(); $translate_job = Job::load($translate_job->id()); foreach ($translate_job->getItems() as $job_item) { $this->assertTrue($job_item->isNeedsReview()); } $messages = $translate_job->getMessages(); // array_values() results in numeric keys, which is necessary for list. list($debug, $translated, $needs_review) = array_values($messages); $this->assertEqual('Test translator called.', $debug->message->value); $this->assertEqual('debug', $debug->type->value); $this->assertEqual('Test translation created.', $translated->message->value); $this->assertEqual('status', $translated->type->value); // The third message is specific to a job item and has different state // constants. $this->assertEqual('The translation of <a href=":source_url">@source</a> to @language is finished and can now be <a href=":review_url">reviewed</a>.', $needs_review->message->value); $this->assertEqual('status', $needs_review->type->value); $i = 1; foreach ($translate_job->getItems() as $item) { // Check the translated text. if ($i != 3) { $expected_text = 'de(de-ch): Text for job item with type ' . $item->getItemType() . ' and id ' . $item->getItemId() . '.'; } else { // The third item has an explicitly stored data value. $expected_text = 'de(de-ch): Stored data'; } $item_data = $item->getData(); $this->assertEqual($expected_text, $item_data['dummy']['deep_nesting']['#translation']['#text']); $i++; } foreach ($translate_job->getItems() as $job_item) { $job_item->acceptTranslation(); } // @todo Accepting does not result in messages on the job anymore. // Update once there are job item messages. /* $messages = $translate_job->getMessages(); $last_message = end($messages); $this->assertEqual('Job accepted', $last_message->message->value); $this->assertEqual('status', $last_message->type);*/ // Check if the translations have been "saved". foreach ($translate_job->getItems() as $item) { $this->assertTrue(\Drupal::state()->get('tmgmt_test_saved_translation_' . $item->getItemType() . '_' . $item->getItemId(), FALSE)); } // A rejected job. $reject_job = $this->createJobWithItems('reject'); $reject_job->requestTranslation(); // Still rejected. $this->assertTrue($reject_job->isRejected()); $messages = $reject_job->getMessages(); $last_message = end($messages); $this->assertEqual('This is not supported.', $last_message->message->value); $this->assertEqual('error', $last_message->getType()); // A failing job. $failing_job = $this->createJobWithItems('fail'); $failing_job->requestTranslation(); // Still new. $this->assertTrue($failing_job->isUnprocessed()); $messages = $failing_job->getMessages(); $last_message = end($messages); $this->assertEqual('Service not reachable.', $last_message->message->value); $this->assertEqual('error', $last_message->getType()); }
/** * {@inheritdoc} */ public function validateImport($imported_file, $is_file = TRUE) { $dom = new \DOMDocument(); if (!$dom->loadHTMLFile($imported_file)) { return FALSE; } $xml = simplexml_import_dom($dom); // Collect meta information. $meta_tags = $xml->xpath('//meta'); $meta = array(); foreach ($meta_tags as $meta_tag) { $meta[(string) $meta_tag['name']] = (string) $meta_tag['content']; } // Check required meta tags. foreach (array('JobID', 'languageSource', 'languageTarget') as $name) { if (!isset($meta[$name])) { return FALSE; } } // Attempt to load the job. if (!($job = Job::load($meta['JobID']))) { drupal_set_message(t('The imported file job id @file_id is not available.', array('@file_id' => $meta['JobID'])), 'error'); return FALSE; } // Check language. if ($meta['languageSource'] != $job->getRemoteSourceLanguage() || $meta['languageTarget'] != $job->getRemoteTargetLanguage()) { return FALSE; } // Validation successful. return $job; }
/** * Tests creating and deleting a translator. */ function testTranslatorHandling() { // Create a translator for later deletion. $translator = parent::createTranslator(); // Does the translator exist in the listing? $this->drupalGet('admin/tmgmt/translators'); $this->assertText($translator->label()); $this->assertEqual(count($this->xpath('//tbody/tr')), 2); // Create job, attach to the translator and activate. $job = $this->createJob(); $job->settings = array(); $job->save(); $job->setState(Job::STATE_ACTIVE); $item = $job->addItem('test_source', 'test', 1); $this->drupalGet('admin/tmgmt/items/' . $item->id()); $this->assertText(t('(Undefined)')); $job->translator = $translator; $job->save(); // Try to delete the translator, should fail because of active job. $delete_url = '/admin/tmgmt/translators/manage/' . $translator->id() . '/delete'; $this->drupalGet($delete_url); $this->assertResponse(403); // Create a continuous job. $continuous = $this->createJob('en', 'de', 1, ['label' => 'Continuous', 'job_type' => Job::TYPE_CONTINUOUS]); $continuous->translator = $translator; $continuous->save(); // Delete a provider using an API call and assert that active job and its // job item used by deleted translator were aborted. $translator->delete(); /** @var \Drupal\tmgmt\JobInterface $job */ $job = Job::load($job->id()); $continuous = Job::load($continuous->id()); $this->assertEqual($job->getState(), Job::STATE_ABORTED); $item = $job->getMostRecentItem('test_source', 'test', 1); $this->assertEqual($item->getState(), JobItem::STATE_ABORTED); $this->assertEqual($continuous->getState(), Job::STATE_ABORTED); // Delete a finished job. $translator = parent::createTranslator(); $job = $this->createJob(); $job->translator = $translator; $item = $job->addItem('test_source', 'test', 2); $job->setState(Job::STATE_FINISHED); $delete_url = '/admin/tmgmt/translators/manage/' . $translator->id() . '/delete'; $this->drupalPostForm($delete_url, array(), 'Delete'); $this->assertText(t('Add provider')); // Check if the list of translators has 1 row. $this->assertEqual(count($this->xpath('//tbody/tr')), 1); $this->assertText(t('@label has been deleted.', array('@label' => $translator->label()))); // Check if the clone action works. $this->clickLink('Clone'); $edit = array('name' => $translator->id() . '_clone'); $this->drupalPostForm(NULL, $edit, 'Save'); // Check if the list of translators has 2 row. $this->assertEqual(count($this->xpath('//tbody/tr')), 2); $this->assertText('configuration has been created'); // Assert that the job works and there is a text saying that the translator // is missing. $this->drupalGet('admin/tmgmt/jobs/' . $job->id()); $this->assertText(t('The job has no provider assigned.')); // Assert that also the job items are working. $this->drupalGet('admin/tmgmt/items/' . $item->id()); $this->assertText(t('(Missing)')); // Testing the translators form with no installed translator plugins. // Uninstall the test module (which provides a translator). \Drupal::service('module_installer')->uninstall(array('tmgmt_test'), FALSE); // Assert that job deletion works correctly. \Drupal::service('module_installer')->install(array('tmgmt_file'), FALSE); $this->drupalPostForm('/admin/tmgmt/jobs/' . $job->id() . '/delete', [], t('Delete')); $this->assertResponse(200); $this->assertText(t('The translation job @value has been deleted.', array('@value' => $job->label()))); \Drupal::service('module_installer')->uninstall(array('tmgmt_file'), FALSE); // Get the overview. $this->drupalGet('admin/tmgmt/translators'); $this->assertNoText(t('Add provider')); $this->assertText(t('There are no provider plugins available. Please install a provider plugin.')); }