kopia lustrzana https://github.com/wagtail/wagtail
Add testing for streamfield count restrictions passed to StreamField
rodzic
643bbfc600
commit
60263dfdad
|
@ -8,12 +8,12 @@ from django.test import TestCase
|
|||
from django.utils.safestring import SafeString
|
||||
|
||||
from wagtail.core import blocks
|
||||
from wagtail.core.blocks import StreamValue
|
||||
from wagtail.core.blocks import StreamBlockValidationError, StreamValue
|
||||
from wagtail.core.fields import StreamField
|
||||
from wagtail.core.rich_text import RichText
|
||||
from wagtail.images.models import Image
|
||||
from wagtail.images.tests.utils import get_test_image_file
|
||||
from wagtail.tests.testapp.models import StreamModel
|
||||
from wagtail.tests.testapp.models import BlockCountsStreamModel, MinMaxCountStreamModel, StreamModel
|
||||
|
||||
|
||||
class TestLazyStreamField(TestCase):
|
||||
|
@ -255,3 +255,145 @@ class TestRequiredStreamField(TestCase):
|
|||
def test_blank_field_is_not_required(self):
|
||||
field = StreamField([('paragraph', blocks.CharBlock())], blank=True)
|
||||
self.assertFalse(field.stream_block.required)
|
||||
|
||||
|
||||
class TestStreamFieldCountValidation(TestCase):
|
||||
def setUp(self):
|
||||
self.image = Image.objects.create(
|
||||
title='Test image',
|
||||
file=get_test_image_file())
|
||||
|
||||
self.rich_text_body = {'type': 'rich_text', 'value': '<p>Rich text</p>'}
|
||||
self.image_body = {'type': 'image', 'value': self.image.pk}
|
||||
self.text_body = {'type': 'text', 'value': 'Hello, World!'}
|
||||
|
||||
def test_minmax_pass_to_block(self):
|
||||
instance = MinMaxCountStreamModel.objects.create(body=json.dumps([]))
|
||||
internal_block = instance.body.stream_block
|
||||
|
||||
self.assertEqual(internal_block.meta.min_num, 2)
|
||||
self.assertEqual(internal_block.meta.max_num, 5)
|
||||
|
||||
def test_counts_pass_to_block(self):
|
||||
instance = BlockCountsStreamModel.objects.create(body=json.dumps([]))
|
||||
block_counts = instance.body.stream_block.meta.block_counts
|
||||
|
||||
self.assertEqual(block_counts.get('text'), {'min_num': 1})
|
||||
self.assertEqual(block_counts.get('rich_text'), {'max_num': 1})
|
||||
self.assertEqual(block_counts.get('image'), {'min_num': 1, 'max_num': 1})
|
||||
|
||||
def test_minimum_count(self):
|
||||
# Single block should fail validation
|
||||
body = [self.rich_text_body]
|
||||
instance = MinMaxCountStreamModel.objects.create(body=json.dumps(body))
|
||||
with self.assertRaises(StreamBlockValidationError) as catcher:
|
||||
instance.body.stream_block.clean(instance.body)
|
||||
self.assertEqual(catcher.exception.params, {
|
||||
'__all__': ['The minimum number of items is 2']
|
||||
})
|
||||
|
||||
# 2 blocks okay
|
||||
body = [self.rich_text_body, self.text_body]
|
||||
instance = MinMaxCountStreamModel.objects.create(body=json.dumps(body))
|
||||
self.assertTrue(instance.body.stream_block.clean(instance.body))
|
||||
|
||||
def test_maximum_count(self):
|
||||
# 5 blocks okay
|
||||
body = [self.rich_text_body] * 5
|
||||
instance = MinMaxCountStreamModel.objects.create(body=json.dumps(body))
|
||||
self.assertTrue(instance.body.stream_block.clean(instance.body))
|
||||
|
||||
# 6 blocks should fail validation
|
||||
body = [self.rich_text_body, self.text_body] * 3
|
||||
instance = MinMaxCountStreamModel.objects.create(body=json.dumps(body))
|
||||
with self.assertRaises(StreamBlockValidationError) as catcher:
|
||||
instance.body.stream_block.clean(instance.body)
|
||||
self.assertEqual(catcher.exception.params, {
|
||||
'__all__': ['The maximum number of items is 5']
|
||||
})
|
||||
|
||||
def test_block_counts_minimums(self):
|
||||
instance = BlockCountsStreamModel.objects.create(body=json.dumps([]))
|
||||
|
||||
# Zero blocks should fail validation (requires one text, one image)
|
||||
instance = BlockCountsStreamModel.objects.create(body=json.dumps([]))
|
||||
with self.assertRaises(StreamBlockValidationError) as catcher:
|
||||
instance.body.stream_block.clean(instance.body)
|
||||
errors = list(catcher.exception.params['__all__'])
|
||||
self.assertIn('This field is required.', errors)
|
||||
self.assertIn('Text: The minimum number of items is 1', errors)
|
||||
self.assertIn('Image: The minimum number of items is 1', errors)
|
||||
self.assertEqual(len(errors), 3)
|
||||
|
||||
# One plain text should fail validation
|
||||
body = [self.text_body]
|
||||
instance = BlockCountsStreamModel.objects.create(body=json.dumps(body))
|
||||
with self.assertRaises(StreamBlockValidationError) as catcher:
|
||||
instance.body.stream_block.clean(instance.body)
|
||||
self.assertEqual(catcher.exception.params, {
|
||||
'__all__': ['Image: The minimum number of items is 1']
|
||||
})
|
||||
|
||||
# One text, one image should be okay
|
||||
body = [self.text_body, self.image_body]
|
||||
instance = BlockCountsStreamModel.objects.create(body=json.dumps(body))
|
||||
self.assertTrue(instance.body.stream_block.clean(instance.body))
|
||||
|
||||
def test_block_counts_maximums(self):
|
||||
instance = BlockCountsStreamModel.objects.create(body=json.dumps([]))
|
||||
|
||||
# Base is one text, one image
|
||||
body = [self.text_body, self.image_body]
|
||||
instance = BlockCountsStreamModel.objects.create(body=json.dumps(body))
|
||||
self.assertTrue(instance.body.stream_block.clean(instance.body))
|
||||
|
||||
# Two rich text should error
|
||||
body = [self.text_body, self.image_body, self.rich_text_body, self.rich_text_body]
|
||||
instance = BlockCountsStreamModel.objects.create(body=json.dumps(body))
|
||||
|
||||
with self.assertRaises(StreamBlockValidationError):
|
||||
instance.body.stream_block.clean(instance.body)
|
||||
|
||||
# Two images should error
|
||||
body = [self.text_body, self.image_body, self.image_body]
|
||||
instance = BlockCountsStreamModel.objects.create(body=json.dumps(body))
|
||||
|
||||
with self.assertRaises(StreamBlockValidationError) as catcher:
|
||||
instance.body.stream_block.clean(instance.body)
|
||||
self.assertEqual(catcher.exception.params, {
|
||||
'__all__': ['Image: The maximum number of items is 1']
|
||||
})
|
||||
|
||||
# One text, one rich, one image should be okay
|
||||
body = [self.text_body, self.image_body, self.rich_text_body]
|
||||
instance = BlockCountsStreamModel.objects.create(body=json.dumps(body))
|
||||
self.assertTrue(instance.body.stream_block.clean(instance.body))
|
||||
|
||||
def test_streamfield_count_argument_precedence(self):
|
||||
|
||||
class TestStreamBlock(blocks.StreamBlock):
|
||||
heading = blocks.CharBlock()
|
||||
paragraph = blocks.RichTextBlock()
|
||||
|
||||
class Meta:
|
||||
min_num = 2
|
||||
max_num = 5
|
||||
block_counts = {'heading': {'max_num': 1}}
|
||||
|
||||
# args being picked up from the class definition
|
||||
field = StreamField(TestStreamBlock)
|
||||
self.assertEqual(field.stream_block.meta.min_num, 2)
|
||||
self.assertEqual(field.stream_block.meta.max_num, 5)
|
||||
self.assertEqual(field.stream_block.meta.block_counts['heading']['max_num'], 1)
|
||||
|
||||
# args being overridden by StreamField
|
||||
field = StreamField(TestStreamBlock, min_num=3, max_num=6, block_counts={'heading': {'max_num': 2}})
|
||||
self.assertEqual(field.stream_block.meta.min_num, 3)
|
||||
self.assertEqual(field.stream_block.meta.max_num, 6)
|
||||
self.assertEqual(field.stream_block.meta.block_counts['heading']['max_num'], 2)
|
||||
|
||||
# passing None from StreamField should cancel limits set at the block level
|
||||
field = StreamField(TestStreamBlock, min_num=None, max_num=None, block_counts=None)
|
||||
self.assertEqual(field.stream_block.meta.min_num, None)
|
||||
self.assertEqual(field.stream_block.meta.max_num, None)
|
||||
self.assertEqual(field.stream_block.meta.block_counts, None)
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
# Generated by Django 2.1.7 on 2019-03-28 02:30
|
||||
|
||||
from django.db import migrations, models
|
||||
import wagtail.core.blocks
|
||||
import wagtail.core.fields
|
||||
import wagtail.images.blocks
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('tests', '0057_customdocumentwithauthor'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='BlockCountsStreamModel',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('body', wagtail.core.fields.StreamField([('text', wagtail.core.blocks.CharBlock()), ('rich_text', wagtail.core.blocks.RichTextBlock()), ('image', wagtail.images.blocks.ImageChooserBlock())])),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='MinMaxCountStreamModel',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('body', wagtail.core.fields.StreamField([('text', wagtail.core.blocks.CharBlock()), ('rich_text', wagtail.core.blocks.RichTextBlock()), ('image', wagtail.images.blocks.ImageChooserBlock())])),
|
||||
],
|
||||
),
|
||||
]
|
|
@ -1007,12 +1007,38 @@ class StreamModel(models.Model):
|
|||
])
|
||||
|
||||
|
||||
class MinMaxCountStreamModel(models.Model):
|
||||
body = StreamField([
|
||||
('text', CharBlock()),
|
||||
('rich_text', RichTextBlock()),
|
||||
('image', ImageChooserBlock()),
|
||||
],
|
||||
min_num=2,
|
||||
max_num=5,
|
||||
)
|
||||
|
||||
|
||||
class BlockCountsStreamModel(models.Model):
|
||||
body = StreamField([
|
||||
('text', CharBlock()),
|
||||
('rich_text', RichTextBlock()),
|
||||
('image', ImageChooserBlock()),
|
||||
],
|
||||
block_counts={
|
||||
"text": {"min_num": 1},
|
||||
"rich_text": {"max_num": 1},
|
||||
"image": {"min_num": 1, "max_num": 1},
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class ExtendedImageChooserBlock(ImageChooserBlock):
|
||||
"""
|
||||
Example of Block with custom get_api_representation method.
|
||||
If the request has an 'extended' query param, it returns a dict of id and title,
|
||||
otherwise, it returns the default value.
|
||||
"""
|
||||
|
||||
def get_api_representation(self, value, context=None):
|
||||
image_id = super().get_api_representation(value, context=context)
|
||||
if 'request' in context and context['request'].query_params.get('extended', False):
|
||||
|
|
Ładowanie…
Reference in New Issue