/** * Tests ConfigurableEventHandlerEntityBundle. * * Test that rules are triggered correctly based upon the fully qualified * event name as well as the base event name. * * @todo Add integrity check that node.field_integer is detected by Rules. */ public function testConfigurableEventHandler() { // Create rule1 with the 'rules_entity_presave:node--page' event. $rule1 = $this->expressionManager->createRule(); $rule1->addAction('rules_test_log', ContextConfig::create()->map('message', 'node.field_integer.0.value')); $config_entity1 = $this->storage->create(['id' => 'test_rule1']); $config_entity1->set('events', [['event_name' => 'rules_entity_presave:node--page']]); $config_entity1->set('expression', $rule1->getConfiguration()); $config_entity1->save(); // Create rule2 with the 'rules_entity_presave:node' event. $rule2 = $this->expressionManager->createRule(); $rule2->addAction('rules_test_log', ContextConfig::create()->map('message', 'node.field_integer.1.value')); $config_entity2 = $this->storage->create(['id' => 'test_rule2']); $config_entity2->set('events', [['event_name' => 'rules_entity_presave:node']]); $config_entity2->set('expression', $rule2->getConfiguration()); $config_entity2->save(); // The logger instance has changed, refresh it. $this->logger = $this->container->get('logger.channel.rules'); // Add node.field_integer.0.value to rules log message, read result. $this->node->field_integer->setValue(['0' => 11, '1' => 22]); // Trigger node save. $entity_type_id = $this->node->getEntityTypeId(); $event = new EntityEvent($this->node, [$entity_type_id => $this->node]); $event_dispatcher = \Drupal::service('event_dispatcher'); $event_dispatcher->dispatch("rules_entity_presave:{$entity_type_id}", $event); // Test that the action in the rule1 logged node value. $this->assertRulesLogEntryExists(11, 1); // Test that the action in the rule2 logged node value. $this->assertRulesLogEntryExists(22, 0); }
/** * Tests executing a rule providing context based upon given context. */ public function testRuleExecutionWithContext() { $rule = $this->rulesExpressionManager->createRule(); $rule->addAction('rules_test_string', ContextConfig::create()->map('text', 'text')); $result = RulesComponent::create($rule)->addContextDefinition('text', ContextDefinition::create('string'))->provideContext('concatenated')->setContextValue('text', 'foo')->execute(); // Ensure the provided context is returned. $this->assertTrue(isset($result['concatenated']) && $result['concatenated'] == 'foofoo'); }
/** * Tests auto saving after an action execution. */ public function testActionAutoSave() { $rule = $this->rulesExpressionManager->createRule(); // Just leverage the entity save action, which by default uses auto-saving. $rule->addAction('rules_entity_save', ContextConfig::create()->map('entity', 'entity')); $entity = $this->prophesizeEntity(EntityInterface::class); $entity->save()->shouldBeCalledTimes(1); RulesComponent::create($rule)->addContextDefinition('entity', ContextDefinition::create('entity'))->setContextValue('entity', $entity->reveal())->execute(); }
/** * Tests that NULL values for contexts are allowed if specified. */ public function testAllowNullValue() { // Configure a simple rule with the data set action which allows NULL // values. $action = $this->expressionManager->createInstance('rules_action', ContextConfig::create()->setConfigKey('action_id', 'rules_data_set')->map('data', 'null_variable')->map('value', 'new_value')->toArray()); $rule = $this->expressionManager->createRule()->addExpressionObject($action); $component = RulesComponent::create($rule)->addContextDefinition('null_variable', ContextDefinition::create('string'))->addContextDefinition('new_value', ContextDefinition::create('string'))->setContextValue('null_variable', NULL)->setContextValue('new_value', 'new value'); $component->execute(); $this->assertEquals('new value', $component->getState()->getVariableValue('null_variable')); }
/** * Tests that the loop list item is removed after the loop. */ public function testPrepareAfterLoop() { $rule = $this->rulesExpressionManager->createRule(); $loop = $this->rulesExpressionManager->createInstance('rules_loop', ['list' => 'string_list']); $action = $this->rulesExpressionManager->createAction('rules_test_string')->setConfiguration(ContextConfig::create()->setValue('text', 'x')->toArray()); $loop->addExpressionObject($action); $rule->addExpressionObject($loop); $state = RulesComponent::create($rule)->addContextDefinition('string_list', ContextDefinition::create('string')->setMultiple())->getMetadataState(); $found = $rule->prepareExecutionMetadataState($state); $this->assertFalse($state->hasDataDefinition('list_item')); $this->assertNull($found); }
/** * Tests that the numeric offset plugin works. */ public function testNumericOffset() { // Configure a simple rule with one action. $action = $this->expressionManager->createInstance('rules_action', ContextConfig::create()->map('message', 'message')->map('type', 'type')->process('message', 'rules_numeric_offset', ['offset' => 1])->setConfigKey('action_id', 'rules_system_message')->toArray()); $component = RulesComponent::create($this->expressionManager->createRule())->addContextDefinition('message', ContextDefinition::create('string'))->addContextDefinition('type', ContextDefinition::create('string'))->setContextValue('message', 1)->setContextValue('type', 'status'); $component->getExpression()->addExpressionObject($action); $component->execute(); $messages = drupal_set_message(); // The original value was 1 and the processor adds 1, so the result should // be 2. $this->assertEquals((string) $messages['status'][0], '2'); }
/** * Tests that date tokens are formatted correctly. */ public function testSystemDateToken() { // Configure a simple rule with one action. and token replacements enabled. $action = $this->expressionManager->createInstance('rules_action', ContextConfig::create()->map('message', 'message')->map('type', 'type')->process('message', 'rules_tokens')->setConfigKey('action_id', 'rules_system_message')->toArray()); $rule = $this->expressionManager->createRule(['context_definitions' => ['message' => ContextDefinition::create('string')->toArray(), 'type' => ContextDefinition::create('string')->toArray()]]); $rule->setContextValue('message', 'The date is [date:custom:Y-m]!'); $rule->setContextValue('type', 'status'); $rule->addExpressionObject($action); $rule->execute(); $messages = drupal_set_message(); $date = format_date(time(), 'custom', 'Y-m'); $this->assertEqual((string) $messages['status'][0], "The date is {$date}!"); }
/** * Tests that date tokens are formatted correctly. */ public function testSystemDateToken() { // Configure a simple rule with one action. and token replacements enabled. $action = $this->expressionManager->createInstance('rules_action', ContextConfig::create()->setValue('message', "The date is {{ date | format_date('custom', 'Y-m') }}!")->setValue('type', 'status')->process('message', 'rules_tokens')->setConfigKey('action_id', 'rules_system_message')->toArray()); $rule = $this->expressionManager->createRule(); $rule->addExpressionObject($action); RulesComponent::create($rule)->addContextDefinition('date', ContextDefinition::create('timestamp'))->setContextValue('date', REQUEST_TIME)->execute(); $messages = drupal_set_message(); /** @var \Drupal\Core\Datetime\DateFormatterInterface $date_formatter */ $date_formatter = $this->container->get('date.formatter'); $date = $date_formatter->format(REQUEST_TIME, 'custom', 'Y-m'); $this->assertEquals("The date is {$date}!", (string) $messages['status'][0]); }
/** * Test that the user login hook triggers the Rules event listener. */ public function testUserLoginEvent() { $rule = $this->expressionManager->createRule(); $rule->addCondition('rules_test_true'); $rule->addAction('rules_test_log', ContextConfig::create()->map('message', 'account.name.0.value')); $config_entity = $this->storage->create(['id' => 'test_rule', 'events' => [['event_name' => 'rules_user_login']], 'expression' => $rule->getConfiguration()]); $config_entity->save(); // The logger instance has changed, refresh it. $this->logger = $this->container->get('logger.channel.rules'); $account = User::create(['name' => 'test_user']); // Invoke the hook manually which should trigger the rule. rules_user_login($account); // Test that the action in the rule logged something. $this->assertRulesLogEntryExists('test_user'); }
/** * Tests that a missing required context triggers an exception. * * @covers ::mapContext * * @expectedException \Drupal\rules\Exception\RulesEvaluationException * * @expectedExceptionMessage Required context test is missing for plugin testplugin. */ public function testMissingContext() { // Set 'getContextValue' as mocked method. $trait = $this->getMockForTrait(ContextHandlerTrait::class, [], '', TRUE, TRUE, TRUE, ['getContextValue']); $context_definition = $this->prophesize(ContextDefinitionInterface::class); // Let the trait work with an empty configuration. $trait->configuration = ContextConfig::create()->toArray(); // Make the context required in the definition. $context_definition->isRequired()->willReturn(TRUE)->shouldBeCalledTimes(1); $plugin = $this->prophesize(ContextAwarePluginInterface::class); $plugin->getContextDefinitions()->willReturn(['test' => $context_definition->reveal()])->shouldBeCalled(1); $plugin->getPluginId()->willReturn('testplugin')->shouldBeCalledTimes(1); $state = $this->prophesize(ExecutionStateInterface::class); // Make the 'mapContext' method visible. $reflection = new \ReflectionClass($trait); $method = $reflection->getMethod('mapContext'); $method->setAccessible(TRUE); $method->invokeArgs($trait, [$plugin->reveal(), $state->reveal()]); }
/** * Tests that context values get data processed with processor mappings. */ public function testDataProcessor() { $condition = new RulesCondition(['condition_id' => 'test_condition'] + ContextConfig::create()->process('test', 'foo', [])->toArray(), '', [], $this->conditionManager->reveal(), $this->processorManager->reveal()); // Build some mocked context and definitions for our mock condition. $context = $this->prophesize(ContextInterface::class); $condition->setContext('test', $context->reveal()); $this->trueCondition->getContextDefinitions()->willReturn(['test' => $this->prophesize(ContextDefinitionInterface::class)->reveal()])->shouldBeCalledTimes(2); $this->trueCondition->getProvidedContextDefinitions()->willReturn([])->shouldBeCalledTimes(1); // Mock some original old value that will be replaced by the data processor. $this->trueCondition->getContextValue('test')->willReturn('old_value')->shouldBeCalledTimes(1); // The outcome of the data processor needs to get set on the condition. $this->trueCondition->setContextValue('test', 'new_value')->shouldBeCalledTimes(1); $this->trueCondition->refineContextDefinitions()->shouldBeCalledTimes(1); $this->conditionManager->createInstance('test_condition', ['negate' => FALSE])->willReturn($this->trueCondition->reveal())->shouldBeCalledTimes(1); $this->conditionManager->createInstance('test_condition')->willReturn($this->trueCondition->reveal())->shouldBeCalledTimes(1); $data_processor = $this->prophesize(DataProcessorInterface::class); $data_processor->process('old_value', Argument::any())->willReturn('new_value')->shouldBeCalledTimes(1); $this->processorManager->createInstance('foo', [])->willReturn($data_processor->reveal())->shouldBeCalledTimes(1); $this->assertTrue($condition->execute()); }
/** * Tests using provided variables with refined context. */ public function testUsingRefinedProvidedVariables() { $rule = $this->rulesExpressionManager->createRule(); $rule->addAction('rules_variable_add', ContextConfig::create()->setValue('type', 'string')->setValue('value', 'foo')); $rule->addAction('rules_system_message', ContextConfig::create()->map('message', 'variable_added')->setValue('type', 'status')); // The message action requires a string, thus if the context is not refined // it will end up as "any" and integrity check would fail. $violation_list = RulesComponent::create($rule)->checkIntegrity(); $this->assertEquals(0, iterator_count($violation_list)); }
/** * Tests that the data set action works on nodes. */ public function testDataSet() { $entity_manager = $this->container->get('entity.manager'); $entity_manager->getStorage('node_type')->create(['type' => 'page'])->save(); $node = $entity_manager->getStorage('node')->create(['title' => 'test', 'type' => 'page']); // Configure a simple rule with one action. $action = $this->expressionManager->createInstance('rules_action', ContextConfig::create()->setConfigKey('action_id', 'rules_data_set')->map('data', 'node:title')->map('value', 'new_title')->toArray()); $rule = $this->expressionManager->createRule(['context_definitions' => ['node' => ContextDefinition::create('entity:node')->toArray(), 'new_title' => ContextDefinition::create('string')->toArray()]]); $rule->setContextValue('node', $node); $rule->setContextValue('new_title', 'new title'); $rule->addExpressionObject($action); $rule->execute(); $this->assertEqual('new title', $node->getTitle()); $this->assertNotNull($node->id(), 'Node ID is set, which means that the node has been auto-saved.'); }
/** * {@inheritdoc} */ public function submitForm(array &$form, FormStateInterface $form_state) { $context_config = ContextConfig::create(); foreach ($form_state->getValue('context') as $context_name => $value) { if ($form_state->get("context_{$context_name}") == 'selector') { $context_config->map($context_name, $value['setting']); } else { $context_config->setValue($context_name, $value['setting']); } } $configuration = $context_config->toArray(); $configuration['action_id'] = $form_state->getValue('action'); $this->actionExpression->setConfiguration($configuration); }
/** * Tests that a complex data context is assigned something that matches. */ public function testComplexTypeViolation() { $rule = $this->rulesExpressionManager->createRule(); // The condition expects a node context but gets a list instead which cause // the violation. $rule->addCondition('rules_node_is_of_type', ContextConfig::create()->map('node', 'list_variable')->map('types', 'list_variable')); $violation_list = RulesComponent::create($rule)->addContextDefinition('list_variable', ContextDefinition::create('list'))->checkIntegrity(); $this->assertEquals(1, iterator_count($violation_list)); $this->assertEquals('Expected a complex data type for context <em class="placeholder">Node</em> but got a list data type instead.', (string) $violation_list[0]->getMessage()); }
/** * Tests asserted metadata of negated conditions is ignored. */ public function testAssertingOfNegatedConditions() { // Negate the condition only and make sure it is ignored. $rule = $this->expressionManager->createRule(); $rule->addCondition('rules_entity_is_of_bundle', ContextConfig::create()->map('entity', 'node')->setValue('type', 'node')->setValue('bundle', 'page'))->negate(TRUE); $rule->addAction('rules_system_message', ContextConfig::create()->map('message', 'node.field_text.value')->setValue('type', 'status')); $violation_list = RulesComponent::create($rule)->addContextDefinition('node', ContextDefinition::create('entity:node'))->checkIntegrity(); $this->assertEquals(1, iterator_count($violation_list)); // Add an negated AND and make sure it is ignored. $rule = $this->expressionManager->createRule(); $and = $this->expressionManager->createAnd(); $and->addCondition('rules_entity_is_of_bundle', ContextConfig::create()->map('entity', 'node')->setValue('type', 'node')->setValue('bundle', 'page')); $and->negate(TRUE); $rule->addExpressionObject($and); $rule->addAction('rules_system_message', ContextConfig::create()->map('message', 'node.field_text.value')->setValue('type', 'status')); $violation_list = RulesComponent::create($rule)->addContextDefinition('node', ContextDefinition::create('entity:node'))->checkIntegrity(); $this->assertEquals(1, iterator_count($violation_list)); }
/** * Tests that auto saving is only triggered once with nested components. */ public function testAutosaveOnlyOnce() { $entity = $this->prophesizeEntity(EntityInterface::class); $nested_rule = $this->rulesExpressionManager->createRule(); $nested_rule->addAction('rules_entity_save', ContextConfig::create()->map('entity', 'entity')); $rules_config = new RulesComponentConfig(['id' => 'test_rule', 'label' => 'Test rule'], 'rules_component'); $rules_config->setExpression($nested_rule); $rules_config->setContextDefinitions(['entity' => ContextDefinition::create('entity')]); $this->prophesizeStorage([$rules_config]); // Create a rule with a nested rule. Overall there are 2 actions to set the // entity then. $rule = $this->rulesExpressionManager->createRule(); $rule->addAction('rules_component:test_rule', ContextConfig::create()->map('entity', 'entity')); $rule->addAction('rules_entity_save', ContextConfig::create()->map('entity', 'entity')); // Auto-saving should only be triggered once on the entity. $entity->save()->shouldBeCalledTimes(1); RulesComponent::create($rule)->addContextDefinition('entity', ContextDefinition::create('entity'))->setContextValue('entity', $entity->reveal())->execute(); }
/** * Tests that multiple actions can consume and provide context variables. */ public function testActionProvidedContext() { // @todo: Convert the test to make use of actions instead of conditions. $rule = $this->expressionManager->createRule(); // The condition provides a "provided_text" variable. $rule->addCondition('rules_test_provider'); // The action provides a "concatenated" variable. $rule->addAction('rules_test_string', ContextConfig::create()->map('text', 'provided_text')); // Add the same action again which will provide a "concatenated2" variable // now. $rule->addAction('rules_test_string', ContextConfig::create()->map('text', 'concatenated')->provideAs('concatenated', 'concatenated2')); $state = ExecutionState::create(); $rule->executeWithState($state); // Check that the created variables exists and have the provided values. $concatenated = $state->getVariable('concatenated'); $this->assertEqual($concatenated->getValue(), 'test valuetest value'); $concatenated2 = $state->getVariable('concatenated2'); $this->assertEqual($concatenated2->getValue(), 'test valuetest valuetest valuetest value'); }
/** * {@inheritdoc} */ public function submitForm(array &$form, FormStateInterface $form_state) { // Nothing todo as long as the first step is not completed. if (!$form_state->get('condition_id')) { return; } $context_config = ContextConfig::create(); foreach ($form_state->getValue('context') as $context_name => $value) { if ($form_state->get("context_{$context_name}") == 'selector') { $context_config->map($context_name, $value['setting']); } else { $context_config->setValue($context_name, $value['setting']); } } $configuration = $context_config->toArray(); $configuration['condition_id'] = $form_state->get('condition_id'); $this->conditionExpression->setConfiguration($configuration); }
/** * Tests using global context. */ public function testGlobalContext() { $account = User::create(['name' => 'hubert']); $account->save(); $this->container->get('current_user')->setAccount($account); $rule = $this->expressionManager->createRule()->addAction('rules_system_message', ContextConfig::create()->map('message', '@user.current_user_context:current_user.name.value')->setValue('type', 'status')); $component = RulesComponent::create($rule); $this->assertEquals(0, $component->checkIntegrity()->count()); // Ensure the execution-state is aware of global context. $result = $component->getState()->hasVariable('@user.current_user_context:current_user'); $this->assertTrue($result); // Test asking for non-existing variables. $this->assertFalse($component->getState()->hasVariable('@user.current_user_context:invalid')); $this->assertFalse($component->getState()->hasVariable('@user.invalid_service')); $this->assertFalse($component->getState()->hasVariable('invalid-var')); // Test using global context during execution. $component->execute(); $messages = drupal_set_message(); $this->assertEquals((string) $messages['status'][0], 'hubert'); }
/** * Tests that the loop list item variable is not available after the loop. * * @expectedException \Drupal\rules\Exception\RulesEvaluationException * * @expectedExceptionMessage Unable to get variable list_item, it is not defined. */ public function testOutOfScopeVariableExecution() { $rule = $this->rulesExpressionManager->createRule(); $loop = $this->rulesExpressionManager->createInstance('rules_loop', ['list' => 'string_list']); $rule->addExpressionObject($loop); $rule->addAction('rules_test_string', ContextConfig::create()->map('text', 'list_item')); RulesComponent::create($rule)->addContextDefinition('string_list', ContextDefinition::create('string')->setMultiple())->setContextValue('string_list', ['one', 'two'])->execute(); }