ajax_response = $this->mock_ajax_response(); } protected function tearDown() : void { // Clean up AJAX response $this->ajax_response->initResponseArray(); $ref = new \ReflectionProperty('\\EGroupware\\Api\\Json\\Response', 'response'); $ref->setAccessible(true); $ref->setValue(null, null); } /** * Use a known, public callback so we can hook into it, if needed */ public static function public_callback($content) { static::$mocked_exec_result = $content; } /** * Mocks what is needed to fake a call to exec, and catch its output. * The resulting array of information, which would normally be sent to the * client as JSON, is returned for evaluation. * * @param Etemplate $etemplate * @param array $content * @param array $sel_options * @param array $readonlys * @param array $preserv */ protected function mockedExec(Etemplate $etemplate, array $content,array $sel_options=null,array $readonlys=null,array $preserv=null) { ob_start(); // Exec the template $etemplate->exec(__CLASS__ . '::public_callback', $content, $sel_options, $readonlys, $preserv, 4); $result = $this->ajax_response->returnResult(); // Store & clean the request //$etemplate->destroy_request(); ob_end_clean(); return $result; } /** * Mocks what is needed to fake a call to Etemplate->exec(), and catch its output. * The resulting array of information, which would normally be sent to the * client as JSON, is then processed and validated by the server as if it had * been sent from the client. * * @param \EGroupware\Api\Etemplate $etemplate * @param array $content * @param array $sel_options * @param array $readonlys * @param array $preserv * @return type */ protected function mockedRoundTrip(\EGroupware\Api\Etemplate $etemplate, array $content,array $sel_options=null,array $readonlys=null,array $preserv=null) { $result = $this->mockedExec($etemplate, $content, $sel_options, $readonlys, $preserv); // Check for the load $data = array(); foreach($result as $command) { if($command['type'] == 'et2_load') { $data = $command['data']; break; } } Etemplate::ajax_process_content($data['data']['etemplate_exec_id'], $data['data']['content'], false); $content = static::$mocked_exec_result; static::$mocked_exec_result = array(); return $content; } /** * Mock the ajax response and override it so it doesn't try to send headers, * which generates errors since PHPUnit has already done that */ protected function mock_ajax_response() { $response = $this->getMockBuilder('\\EGroupware\\Api\\Json\\Response') ->disableOriginalConstructor() ->setMethods(['get'/*,'generic'*/]) ->getMock(); // Replace protected self reference with mock object $ref = new \ReflectionProperty('\\EGroupware\\Api\\Json\\Response', 'response'); $ref->setAccessible(true); $ref->setValue(null, $response); $response ->method('get') ->with(function() { // Don't send headers, like the real one does return self::$response; }); return $response; } /** * Exec the template with the provided content, change the values according to * $set_values to simulate client side changes by the user, then validate * against $expected_values. Optionally, it can check that validation errors * are created by particular widgets. * * * @param \EGroupware\Api\Etemplate $etemplate * @param array $content * @param array $set_values * @param array $expected_values * @param array $validation_errors Indexed by widget ID, we just check that an error * was found, not what that error was. */ protected function validateRoundTrip(\EGroupware\Api\Etemplate $etemplate, Array $content, Array $set_values, Array $expected_values = null, Array $validation_errors = array()) { if(is_null($expected_values)) { $expected_values = $set_values; } $result = $this->mockedExec($etemplate, $content, array(), array(), array()); // Check for the load $data = array(); foreach($result as $command) { if($command['type'] == 'et2_load') { $data = $command['data']; break; } } // 'Edit' the data client side $data['data']['content'] = $set_values; // Let it validate Etemplate::ajax_process_content($data['data']['etemplate_exec_id'], $data['data']['content'], false); $content = static::$mocked_exec_result; static::$mocked_exec_result = array(); return $this->validateTest($content, $expected_values, $validation_errors); } /** * Test that the content matches expected_values, and any widgets listed in * $validation_errors actually did raise a validation error. * * Note that in most (all?) cases, a validation error will clear the value. * * @param array $content * @param array $expected_values * @param array $validation_errors */ protected function validateTest(Array $content, Array $expected_values, Array $validation_errors = array()) { // Make validation errors accessible $ref = new \ReflectionProperty('\\EGroupware\\Api\\Etemplate\\Widget', 'validation_errors'); $ref->setAccessible(true); $errors = $ref->getValue(); // Test values foreach($expected_values as $widget_id => $value) { $this->assertEquals($value, $content[$widget_id], 'Widget "' . $widget_id . '" did not get expected value'); } // Check validation errors foreach($validation_errors as $widget_id => $errored) { $this->assertTrue(array_key_exists($widget_id, $errors), "Widget $widget_id did not cause a validation error"); } $ref->setValue(array()); } }