/ */ class Workflow_Test_IndexControllerTest extends ModuleControllerTest { /** * Test manage workflows grid. */ public function testIndexAction() { $this->utility->impersonate('editor'); // verify that workflows grid is accessible $this->dispatch('/workflow'); $this->assertModule('workflow', 'Expected module for dispatching /workflow action.'); $this->assertController('index', 'Expected controller for dispatching /workflow action.'); $this->assertAction('index', 'Expected action for dispatching /workflow action.'); // verify that table and dojo data elements exist $this->assertXpath('//div[@dojotype="dojox.data.QueryReadStore"]', 'Expected dojo.data div'); $this->assertXpath( '//table[@dojotype="p4cms.ui.grid.DataGrid" and @jsid="p4cms.workflow.grid.instance"]', 'Expected dojox.grid table' ); // verify add and reset buttons appear // verify save and restore buttons appear $this->assertQueryContentContains( "div.button button.add-button", "Add Workflow", "Expected existence of Add Workflow button." ); $this->assertQueryContentContains( "div.button button", "Reset to Defaults", "Expected existence of Reset to Defaults button." ); // check initial JSON output $this->resetRequest() ->resetResponse(); $this->dispatch('/workflow/format/json'); $body = $this->response->getBody(); $this->assertModule('workflow', 'Expected module, dispatch #2. '. $body); $this->assertController('index', 'Expected controller, dispatch #2 '. $body); $this->assertAction('index', 'Expected action, dispatch #2 '. $body); // verify there are no items in the grid $data = Zend_Json::decode($body); $this->assertSame( array(), $data['items'], 'Expected no workflows.' ); // add few workflows and verify they appear $this->_createWorkflow('test'); $this->_createWorkflow('foo'); $this->resetRequest() ->resetResponse(); // sort by label $this->dispatch('/workflow/format/json?sort=label'); $body = $this->response->getBody(); $this->assertModule('workflow', 'Expected module, dispatch #3. '. $body); $this->assertController('index', 'Expected controller, dispatch #3 '. $body); $this->assertAction('index', 'Expected action, dispatch #3 '. $body); $data = Zend_Json::decode($body); $expected = array( 0 => array( 'id' => 'foo', 'label' => 'foo label', 'description' => 'foo description', 'states' => array(), 'contentTypes' => array() ), 1 => array( 'id' => 'test', 'label' => 'test label', 'description' => 'test description', 'states' => array(), 'contentTypes' => array() ) ); $this->assertEquals( $expected, $data['items'], 'Expected 2 workflows.' ); } /** * Test the add action. */ public function testAdd() { $this->utility->impersonate('editor'); // verify form markup for different contexts $contexts = array(null, 'partial'); foreach ($contexts as $context) { $format = $context ? "/format/$context" : ""; $this->resetRequest() ->resetResponse(); $this->dispatch('/workflow/add' . $format); $this->assertModule('workflow', 'Expected module.'); $this->assertController('index', 'Expected controller'); $this->assertAction('add', 'Expected action'); // ensure that form inputs are presented correctly $this->assertQuery("form.workflow-form", "Expected add form."); foreach ($this->_getWorkflowFormElementsType() as $field => $type) { $query = $type !== 'textarea' ? 'input' : $type; $this->assertQuery("{$query}[name='$field']", "Expected '$field' input."); } $this->assertQuery("input[type='submit']", "Expected submit button."); // ensure labels are present. $labels = array( 'id' => 'Id', 'label' => 'Label', 'description' => 'Description', 'states' => 'States' ); foreach ($labels as $field => $label) { $this->assertQueryContentContains("label[for='$field']", $label, "Expected $field label."); } } } /** * Test bogus post to add. */ public function testBadAddPost() { $this->utility->impersonate('editor'); // add workflow to allow testing for unique id $this->_createWorkflow('test'); $tests = $this->_getTestWorkflowFormBadData(); $tests[] = array( 'values' => array( 'id' => 'test', 'label' => 'foo', 'description' => 'bar', 'states' => "[baz] label = baz", ), 'errorsCount' => 1, 'message' => 'Id already exists' ); // loop throught all tests foreach ($tests as $test) { $this->_verifyBadPost('/workflow/add', 'add', $test); } } /** * Test good post to add. */ public function testGoodAddPost() { $this->utility->impersonate('editor'); $tests = $this->_getTestWorkflowFormGoodData(); // loop throught all tests foreach ($tests as $test) { $this->_verifyGoodPost('/workflow/add', 'add', $test); } } /** * Test bad post to edit. */ public function testBadEditPost() { $this->utility->impersonate('editor'); $workflow = $this->_createWorkflow('foo'); $workflow->setValue('states', '[foo]\nlabel=foo label') ->save(); $tests = $this->_getTestWorkflowFormBadData(); // loop throught all tests foreach ($tests as $test) { // skip tests with 1 error due to wrong id as id for edit is provided from url if (isset($test['idError']) && $test['idError'] === true) { $test['errorsCount']--; } if ($test['errorsCount'] == 0) { continue; } $this->_verifyBadPost('/workflow/edit/id/foo', 'edit', $test); } } /** * Test edit with bad workflow id. */ public function testBadEditId() { $this->utility->impersonate('editor'); $this->request->setParam('id', 'noexist'); $this->dispatch('/workflow/edit'); $this->assertModule('error', 'Expected module.'); $this->assertController('index', 'Expected controller.'); $this->assertAction('error', 'Expected action.'); } /** * Test good post to edit. */ public function testGoodEditPost() { $this->utility->impersonate('editor'); $workflow = $this->_createWorkflow('bar'); $workflow->setValue('states', '[bar]\nlabel=bar label') ->save(); $tests = $this->_getTestWorkflowFormGoodData(); // loop throught all tests foreach ($tests as $test) { $this->_verifyGoodPost('/workflow/edit/id/bar', 'edit', $test); } } /** * Test delete action with invalid id. */ public function testDeleteInvalidId() { $this->utility->impersonate('editor'); $this->request->setMethod('POST'); $this->dispatch('/workflow/delete/id/does-not-exist'); $this->assertModule('error', __LINE__ .': Last module run should be error module.'); $this->assertController('index', __LINE__ .': Expected controller'); $this->assertAction('error', __LINE__ .': Expected action'); } /** * Test delete action with anonymous access. */ public function testDeleteByAnonymous() { $this->_createWorkflow('test'); $this->request->setMethod('POST'); $this->request->setParam('id', 'test'); $this->dispatch('/workflow/delete'); $this->assertModule('error', __LINE__ .': Last module run should be error module.'); $this->assertController('index', __LINE__ .': Expected controller'); $this->assertAction('access-denied', __LINE__ .': Expected action'); // verify workflow record has not been deleted $this->assertTrue( Workflow_Model_Workflow::exists('test'), "Expected test workflow has not been deleted." ); } /** * Test deleting an invalid request method */ public function testDeleteInvalidRequestMethod() { $this->_createWorkflow('foo'); $this->request->setMethod('GET'); $this->dispatch('/workflow/delete/id/foo'); $this->assertModule('error', 'Expected error module.'); } /** * Test deleting workflow. */ public function testDelete() { $this->utility->impersonate('editor'); $this->_createWorkflow('delete'); $this->_createWorkflow('no-delete'); $this->request->setMethod('POST'); $this->request->setParam('id', 'delete'); $this->dispatch('/workflow/delete'); $this->assertModule('workflow', 'Expected module.'); $this->assertController('index', 'Expected controller'); $this->assertAction('delete', 'Expected action'); // should redirect to the previous page (base url in this case) $this->assertRedirectTo('/workflow/manage', 'Expect redirect to manage page.'); // verify workflow records after delete $this->assertFalse( Workflow_Model_Workflow::exists('delete'), "Expected delete workflow has been deleted." ); $this->assertTrue( Workflow_Model_Workflow::exists('no-delete'), "Expected no-delete workflow has not been deleted." ); } /** * Test deleting workflow in json context. */ public function testDeleteJson() { $this->utility->impersonate('editor'); $this->_createWorkflow('del'); $this->_createWorkflow('del1'); $this->request->setMethod('POST'); $this->request->setPost( array( 'id' => 'del1', 'format' => 'json' ) ); $this->dispatch('/workflow/delete'); $this->assertModule('workflow', 'Expected module.'); $this->assertController('index', 'Expected controller'); $this->assertAction('delete', 'Expected action'); // when delete in context, no redirecting should be made $this->assertNotRedirect( "Expected user is not redirected after workflow delete if within a context." ); $responseBody = $this->response->getBody(); $this->assertEquals( Zend_Json::encode(array('id' => 'del1')), $responseBody, __LINE__ .': Expected json output.' ); // verify workflow records after delete $this->assertFalse( Workflow_Model_Workflow::exists('del1'), "Expected del1 workflow has been deleted." ); $this->assertTrue( Workflow_Model_Workflow::exists('del'), "Expected del workflow has not been deleted." ); } /** * Test workflows reset action. */ public function testReset() { $this->utility->impersonate('editor'); $workflows = array('workflow1', 'workflow2'); foreach ($workflows as $workflow) { $this->_createWorkflow($workflow); } // ensure worflows are there foreach ($workflows as $workflow) { $this->assertTrue( Workflow_Model_Workflow::exists($workflow), "Expected '$workflow' workflow exists." ); } // reset workflows $this->dispatch('/workflow/reset'); $this->assertModule('workflow', 'Expected module when workflow reset.'); $this->assertController('index', 'Expected controller when workflow reset.'); $this->assertAction('reset', 'Expected action when workflow reset.'); // ensure there are only default workflows $defaultWorkflows = Workflow_Model_Workflow::fetchAll(); $this->assertEquals( 1, $defaultWorkflows->count(), "Expected 1 workflow after reset." ); $this->assertTrue( Workflow_Model_Workflow::exists('simple'), "Expected presence of 'Simple' workflow after reset" ); } /** * Return array with types of workflow form elements. */ protected function _getWorkflowFormElementsType() { return array( 'id' => 'text', 'label' => 'text', 'description' => 'textarea', 'states' => 'textarea' ); } /** * Create sample workflow record. * * @param string $id Id of created workflow. * @return Workflow_Model_Workflow Instance of newly created workflow. */ protected function _createWorkflow($id) { return Workflow_Model_Workflow::store( array( 'id' => "$id", 'label' => "$id label", 'description' => "$id description" ) ); } /** * Return list with bogus data suitable for testing the workflow form. */ protected function _getTestWorkflowFormBadData() { // following data are suitable for testing both add and edit actions, // however as id is provided from url for edit action, there must be // an extra flag (idError = true) set if id error contributes to the // total amount of errors (errorsCount) $data = array( array( 'values' => array(), 'errorsCount' => 3, 'idError' => true, 'message' => 'Missing required fields #1' ), array( 'values' => array( 'description' => 'test desc' ), 'errorsCount' => 3, 'idError' => true, 'message' => 'Missing required fields #2' ), array( 'values' => array( 'id' => 'foo' ), 'errorsCount' => 2, 'message' => 'Missing required fields #3' ), array( 'values' => array( 'label' => 'foo', 'description' => 'foo desc', 'states' => "[foo] label = bar", ), 'errorsCount' => 1, 'idError' => true, 'message' => 'Missing id' ), array( 'values' => array( 'id' => '#$%^', 'label' => 'foo', 'states' => '[a]' ), 'errorsCount' => 1, 'idError' => true, 'message' => 'Wrong id #1' ), array( 'values' => array( 'id' => '@1', 'label' => 'foo', 'description' => 'bar', 'states' => '[baz]' ), 'errorsCount' => 1, 'idError' => true, 'message' => 'Wrong id #2' ), array( 'values' => array( 'id' => 'spaces not allowed', 'label' => 'foo', 'description' => 'foo desc', 'states' => "[foo] label = bar", ), 'errorsCount' => 1, 'idError' => true, 'message' => 'Wrong id #3' ), array( 'values' => array( 'id' => 'bar', 'label' => 'Bar', 'description' => 'bar desc', 'states' => "state", ), 'errorsCount' => 1, 'message' => 'Wrong states field (at least one state has to be defined)' ), array( 'values' => array( 'id' => 'bar', 'label' => 'Bar', 'description' => 'bar desc', 'states' => "label=w", ), 'errorsCount' => 1, 'message' => 'Wrong states field (at least one state has to be defined)' ), array( 'values' => array( 'id' => 'baz', 'label' => 'Baz baz', 'description' => 'bazzzZZZzzz', 'states' => "[state label = baz", ), 'errorsCount' => 1, 'message' => 'Wrong states field (not a valid INI format)' ) ); return $data; } /** * Return list with good data suitable for testing the workflow form. */ protected function _getTestWorkflowFormGoodData() { $data = array( array( 'values' => array( 'id' => 'foo', 'label' => 'foo label', 'states' => "[foo] label = bar", ), ), array( 'values' => array( 'id' => 'bar', 'label' => 'bar label', 'description' => 'bar desc', 'states' => "[bar]\nlabel = baz\n[foo]\nlabel = another baz", ), ), array( 'values' => array( 'id' => 'baz', 'label' => 'baz label', 'states' => "[123] a=b", ), ), array( 'values' => array( 'id' => 'test', 'label' => 'x', 'description' => 'y', 'states' => "[ab c]", ), ) ); return $data; } /** * Dispatch to provided url with provided post data and verify that form contains errors. * * @param string $url Url to dispatch to. * @param string $action Workflow action. * @param array $data Array containing post data, expected number of errors * and message for assert output. */ protected function _verifyBadPost($url, $action, array $data) { $this->resetRequest() ->resetResponse(); $this->request->setMethod('POST'); $this->request->setPost($data['values']); $this->dispatch($url); $this->assertModule('workflow', 'Expected module.'); $this->assertController('index', 'Expected controller'); $this->assertAction($action, 'Expected action'); $this->assertResponseCode(400, 'Expected bad request response code.'); // check form errors $this->assertQuery("#layout-main form.workflow-form", "Expected add form."); $this->assertQueryCount( "ul.errors", $data['errorsCount'], "Expected {$data['errorsCount']} form errors for test '{$data['message']}'." ); // edit form should always preserve id from url if ($action === 'edit') { $data['values']['id'] = $this->getRequest()->getParam('id'); } // ensure posted data were preserved $elementTypes = $this->_getWorkflowFormElementsType(); foreach ($data['values'] as $field => $value) { $query = $elementTypes[$field] == 'textarea' ? "dd textarea[name='$field']" : "dd#$field-element"; $this->assertQueryContentContains( "form.workflow-form $query", $value, "Expected preserving of value '$value' for '$field' field for test '{$data['message']}'." ); } } /** * Dispatch to provided url with provided post data and verify that form values have been saved. * * @param string $url Url to dispatch to. * @param string $action Workflow action. * @param array $data Array containing post data. */ protected function _verifyGoodPost($url, $action, array $data) { $this->resetRequest() ->resetResponse(); $this->request->setMethod('POST'); $this->request->setPost($data['values']); $this->dispatch($url); $this->assertModule('workflow', 'Expected module.'); $this->assertController('index', 'Expected controller'); $this->assertAction($action, 'Expected action'); // expect redirect to previous page (base url) $this->assertRedirectTo('/workflow/manage', 'Expect redirect to manage page.'); // edit form should always preserve id from url if ($action === 'edit') { $data['values']['id'] = $this->getRequest()->getParam('id'); } // verify workflow record has been saved $this->assertTrue( Workflow_Model_Workflow::exists($data['values']['id']), "Expected workflow record has been saved/updated." ); // verify model values $workflow = Workflow_Model_Workflow::fetch($data['values']['id']); foreach ($data['values'] as $field => $value) { // states are by default returned as list of state models $workflowValue = $field == 'states' ? $workflow->getStatesAsIni() : $workflow->getValue($field); $this->assertSame( $value, $workflowValue, "Expected value of '$field' returned by workflow model." ); } } }