silverstripe-framework/tests/php/Forms/GridField/GridFieldDetailFormTest.php
Sam Minnee 8883413ba7 NEW: Add GridFieldDetailForm::setRedirectMissingRecords()
This new opt-in setting will let grid field detail forms redirect to the
“Correct” URL of a GridField if it’s not found in the current list.

This works by:
 * Looking for the item in the database
 * If it exists, check for a CMSEditLink() method that returns a value
 * If so, redirect to that

This is useful if you have a number of grid fields that each show a
partial list of records, and it’s possible for the user to make changes
such the item no longer appears in the list, but does appear in another
list.

It’s an opt-in feature as I think all changes like this should be
opt-in, based on previous experiences improving GridField and in turn
breaking SecurityAdmin and slowing versioned-data-browsing down. ;-)
2021-05-21 13:16:00 +12:00

439 lines
16 KiB
PHP

<?php
namespace SilverStripe\Forms\Tests\GridField;
use SilverStripe\Control\Controller;
use SilverStripe\Dev\CSSContentParser;
use SilverStripe\Dev\FunctionalTest;
use SilverStripe\Forms\GridField\GridField;
use SilverStripe\Forms\GridField\GridFieldDetailForm;
use SilverStripe\Forms\GridField\GridFieldDetailForm_ItemRequest;
use SilverStripe\Forms\HiddenField;
use SilverStripe\Forms\Tests\GridField\GridFieldDetailFormTest\Category;
use SilverStripe\Forms\Tests\GridField\GridFieldDetailFormTest\CategoryController;
use SilverStripe\Forms\Tests\GridField\GridFieldDetailFormTest\GroupController;
use SilverStripe\Forms\Tests\GridField\GridFieldDetailFormTest\PeopleGroup;
use SilverStripe\Forms\Tests\GridField\GridFieldDetailFormTest\Person;
use SilverStripe\Forms\Tests\GridField\GridFieldDetailFormTest\TestController;
/**
* @skipUpgrade
*/
class GridFieldDetailFormTest extends FunctionalTest
{
protected static $fixture_file = 'GridFieldDetailFormTest.yml';
protected static $extra_dataobjects = [
Person::class,
PeopleGroup::class,
Category::class,
];
protected static $extra_controllers = [
CategoryController::class,
TestController::class,
GroupController::class,
];
protected static $disable_themes = true;
public function testValidator()
{
$this->logInWithPermission('ADMIN');
$response = $this->get('GridFieldDetailFormTest_Controller');
$this->assertFalse($response->isError());
$parser = new CSSContentParser($response->getBody());
$addlinkitem = $parser->getBySelector('.grid-field .new-link');
$addlink = (string) $addlinkitem[0]['href'];
$response = $this->get($addlink);
$this->assertFalse($response->isError());
$parser = new CSSContentParser($response->getBody());
$addform = $parser->getBySelector('#Form_ItemEditForm');
$addformurl = (string) $addform[0]['action'];
$response = $this->post(
$addformurl,
[
'FirstName' => 'Jeremiah',
'ajax' => 1,
'action_doSave' => 1
]
);
$parser = new CSSContentParser($response->getBody());
$errors = $parser->getBySelector('span.required');
$this->assertEquals(1, count($errors));
$response = $this->post(
$addformurl,
[
'ajax' => 1,
'action_doSave' => 1
]
);
$parser = new CSSContentParser($response->getBody());
$errors = $parser->getBySelector('span.required');
$this->assertEquals(2, count($errors));
}
public function testAddForm()
{
$this->logInWithPermission('ADMIN');
$group = PeopleGroup::get()
->filter('Name', 'My Group')
->sort('Name')
->First();
$count = $group->People()->Count();
$response = $this->get('GridFieldDetailFormTest_Controller');
$this->assertFalse($response->isError());
$parser = new CSSContentParser($response->getBody());
$addlinkitem = $parser->getBySelector('.grid-field .new-link');
$addlink = (string) $addlinkitem[0]['href'];
$response = $this->get($addlink);
$this->assertFalse($response->isError());
$parser = new CSSContentParser($response->getBody());
$addform = $parser->getBySelector('#Form_ItemEditForm');
$addformurl = (string) $addform[0]['action'];
$response = $this->post(
$addformurl,
[
'FirstName' => 'Jeremiah',
'Surname' => 'BullFrog',
'action_doSave' => 1
]
);
$this->assertFalse($response->isError());
$group = PeopleGroup::get()
->filter('Name', 'My Group')
->sort('Name')
->First();
$this->assertEquals($count + 1, $group->People()->Count());
}
public function testViewForm()
{
$this->logInWithPermission('ADMIN');
$response = $this->get('GridFieldDetailFormTest_Controller');
$parser = new CSSContentParser($response->getBody());
$viewLink = $parser->getBySelector('.ss-gridfield-items .first .view-link');
$viewLink = (string) $viewLink[0]['href'];
$response = $this->get($viewLink);
$parser = new CSSContentParser($response->getBody());
$firstName = $parser->getBySelector('#Form_ItemEditForm_FirstName');
$surname = $parser->getBySelector('#Form_ItemEditForm_Surname');
$this->assertFalse($response->isError());
$this->assertEquals('Jane', (string) $firstName[0]);
$this->assertEquals('Doe', (string) $surname[0]);
}
public function testEditForm()
{
$this->logInWithPermission('ADMIN');
$group = PeopleGroup::get()
->filter('Name', 'My Group')
->sort('Name')
->First();
$firstperson = $group->People()->First();
$this->assertTrue($firstperson->Surname != 'Baggins');
$response = $this->get('GridFieldDetailFormTest_Controller');
$this->assertFalse($response->isError());
$parser = new CSSContentParser($response->getBody());
$editlinkitem = $parser->getBySelector('.ss-gridfield-items .first .edit-link');
$editlink = (string) $editlinkitem[0]['href'];
$response = $this->get($editlink);
$this->assertFalse($response->isError());
$parser = new CSSContentParser($response->getBody());
$editform = $parser->getBySelector('#Form_ItemEditForm');
$editformurl = (string) $editform[0]['action'];
$response = $this->post(
$editformurl,
[
'FirstName' => 'Bilbo',
'Surname' => 'Baggins',
'action_doSave' => 1
]
);
$this->assertFalse($response->isError());
$group = PeopleGroup::get()
->filter('Name', 'My Group')
->sort('Name')
->First();
$this->assertListContains([['Surname' => 'Baggins']], $group->People());
}
public function testEditFormWithManyMany()
{
$this->logInWithPermission('ADMIN');
// Edit the first person
$response = $this->get('GridFieldDetailFormTest_CategoryController');
// Find the link to add a new favourite group
$parser = new CSSContentParser($response->getBody());
$addLink = $parser->getBySelector('#Form_Form_testgroupsfield .new-link');
$addLink = (string) $addLink[0]['href'];
// Add a new favourite group
$response = $this->get($addLink);
$parser = new CSSContentParser($response->getBody());
$addform = $parser->getBySelector('#Form_ItemEditForm');
$addformurl = (string) $addform[0]['action'];
$response = $this->post(
$addformurl,
[
'Name' => 'My Favourite Group',
'ajax' => 1,
'action_doSave' => 1
]
);
$this->assertFalse($response->isError());
$person = $this->objFromFixture(Person::class, 'jane');
$favouriteGroup = $person->FavouriteGroups()->first();
$this->assertInstanceOf(PeopleGroup::class, $favouriteGroup);
}
public function testEditFormWithManyManyExtraData()
{
$this->logInWithPermission('ADMIN');
// Lists all categories for a person
$response = $this->get('GridFieldDetailFormTest_CategoryController');
$this->assertFalse($response->isError());
$parser = new CSSContentParser($response->getBody());
$editlinkitem = $parser->getBySelector('.ss-gridfield-items .first .edit-link');
$editlink = (string) $editlinkitem[0]['href'];
// Edit a single category, incl. manymany extrafields added manually
// through GridFieldDetailFormTest_CategoryController
$response = $this->get($editlink);
$this->assertFalse($response->isError());
$parser = new CSSContentParser($response->getBody());
$editform = $parser->getBySelector('#Form_ItemEditForm');
$editformurl = (string) $editform[0]['action'];
$manyManyField = $parser->getByXpath('//*[@id="Form_ItemEditForm"]//input[@name="ManyMany[IsPublished]"]');
$this->assertTrue((bool)$manyManyField);
// Test save of IsPublished field
$response = $this->post(
$editformurl,
[
'Name' => 'Updated Category',
'ManyMany' => [
'IsPublished' => 1,
'PublishedBy' => 'Richard'
],
'action_doSave' => 1
]
);
$this->assertFalse($response->isError());
$person = $this->objFromFixture(Person::class, 'jane');
$category = $person->Categories()->filter(['Name' => 'Updated Category'])->First();
$this->assertEquals(
[
'IsPublished' => 1,
'PublishedBy' => 'Richard'
],
$person->Categories()->getExtraData('', $category->ID)
);
// Test update of value with falsey value
$response = $this->post(
$editformurl,
[
'Name' => 'Updated Category',
'ManyMany' => [
'PublishedBy' => ''
],
'action_doSave' => 1
]
);
$this->assertFalse($response->isError());
$person = $this->objFromFixture(Person::class, 'jane');
$category = $person->Categories()->filter(['Name' => 'Updated Category'])->First();
$this->assertEquals(
[
'IsPublished' => 0,
'PublishedBy' => ''
],
$person->Categories()->getExtraData('', $category->ID)
);
}
public function testNestedEditForm()
{
$this->logInWithPermission('ADMIN');
$group = $this->objFromFixture(PeopleGroup::class, 'group');
$person = $group->People()->First();
$category = $person->Categories()->First();
// Get first form (GridField managing PeopleGroup)
$response = $this->get('GridFieldDetailFormTest_GroupController');
$this->assertFalse($response->isError());
$parser = new CSSContentParser($response->getBody());
$groupEditLink = $parser->getByXpath(
'//tr[contains(@class, "ss-gridfield-item") and contains(@data-id, "'
. $group->ID . '")]//a'
);
$this->assertEquals(
'GridFieldDetailFormTest_GroupController/Form/field/testfield/item/' . $group->ID . '/edit',
(string)$groupEditLink[0]['href']
);
// Get second level form (GridField managing Person)
$response = $this->get((string)$groupEditLink[0]['href']);
$this->assertFalse($response->isError());
$parser = new CSSContentParser($response->getBody());
$personEditLink = $parser->getByXpath(
'//fieldset[@id="Form_ItemEditForm_People"]' .
'//tr[contains(@class, "ss-gridfield-item") and contains(@data-id, "' . $person->ID . '")]//a'
);
$this->assertEquals(
sprintf(
'GridFieldDetailFormTest_GroupController/Form/field/testfield/item/%d/ItemEditForm/field/People'
. '/item/%d/edit',
$group->ID,
$person->ID
),
(string)$personEditLink[0]['href']
);
// Get third level form (GridField managing Category)
$response = $this->get((string)$personEditLink[0]['href']);
$this->assertFalse($response->isError());
$parser = new CSSContentParser($response->getBody());
$categoryEditLink = $parser->getByXpath(
'//fieldset[@id="Form_ItemEditForm_Categories"]'
. '//tr[contains(@class, "ss-gridfield-item") and contains(@data-id, "' . $category->ID . '")]//a'
);
$this->assertEquals(
sprintf(
'GridFieldDetailFormTest_GroupController/Form/field/testfield/item/%d/ItemEditForm/field/People'
. '/item/%d/ItemEditForm/field/Categories/item/%d/edit',
$group->ID,
$person->ID,
$category->ID
),
(string)$categoryEditLink[0]['href']
);
// Fourth level form would be a Category detail view
}
public function testCustomItemRequestClass()
{
$this->logInWithPermission('ADMIN');
$component = new GridFieldDetailForm();
$this->assertEquals('SilverStripe\\Forms\\GridField\\GridFieldDetailForm_ItemRequest', $component->getItemRequestClass());
$component->setItemRequestClass('GridFieldDetailFormTest_ItemRequest');
$this->assertEquals('GridFieldDetailFormTest_ItemRequest', $component->getItemRequestClass());
}
public function testItemEditFormCallback()
{
$this->logInWithPermission('ADMIN');
$category = new Category();
$component = new GridFieldDetailForm();
$component->setItemEditFormCallback(
function ($form, $component) {
$form->Fields()->push(new HiddenField('Callback'));
}
);
// Note: A lot of scaffolding to execute the tested logic,
// due to the coupling of form creation with itemRequest handling (and its context)
/** @skipUpgrade */
$itemRequest = new GridFieldDetailForm_ItemRequest(
GridField::create('Categories', 'Categories'),
$component,
$category,
Controller::curr(),
'Form'
);
$itemRequest->setRequest(Controller::curr()->getRequest());
$form = $itemRequest->ItemEditForm();
$this->assertNotNull($form->Fields()->fieldByName('Callback'));
}
/**
* Tests that a has-many detail form is pre-populated with the parent ID.
*/
public function testHasManyFormPrePopulated()
{
$group = $this->objFromFixture(
PeopleGroup::class,
'group'
);
$this->logInWithPermission('ADMIN');
$response = $this->get('GridFieldDetailFormTest_Controller');
$parser = new CSSContentParser($response->getBody());
$addLink = $parser->getBySelector('.grid-field .new-link');
$addLink = (string) $addLink[0]['href'];
$response = $this->get($addLink);
$parser = new CSSContentParser($response->getBody());
$title = $parser->getBySelector('#Form_ItemEditForm_GroupID_Holder span');
$id = $parser->getBySelector('#Form_ItemEditForm_GroupID_Holder input');
$this->assertEquals($group->Name, (string) $title[0]);
$this->assertEquals($group->ID, (string) $id[0]['value']);
}
public function testRedirectMissingRecords()
{
$origAutoFollow = $this->autoFollowRedirection;
$this->autoFollowRedirection = false;
// GridField is filtered people in "My Group", which does't include "jack"
$included = $this->objFromFixture(Person::class, 'joe');
$excluded = $this->objFromFixture(Person::class, 'jack');
$response = $this->get(sprintf(
'GridFieldDetailFormTest_Controller/Form/field/testfield/item/%d/edit',
$included->ID
));
$this->assertFalse(
$response->isRedirect(),
'Existing records are not redirected'
);
$response = $this->get(sprintf(
'GridFieldDetailFormTest_Controller/Form/field/testfield/item/%d/edit',
$excluded->ID
));
$this->assertTrue(
$response->isRedirect(),
'Non-existing records are redirected'
);
$this->autoFollowRedirection = $origAutoFollow;
}
}