def to_python(self, value): if value is None or value == '': return StreamValue(self.stream_block, []) elif isinstance(value, StreamValue): return value elif isinstance(value, str): try: unpacked_value = json.loads(value) except ValueError: # value is not valid JSON; most likely, this field was previously a # rich text field before being migrated to StreamField, and the data # was left intact in the migration. Return an empty stream instead # (but keep the raw text available as an attribute, so that it can be # used to migrate that data to StreamField) return StreamValue(self.stream_block, [], raw_text=value) if unpacked_value is None: # we get here if value is the literal string 'null'. This should probably # never happen if the rest of the (de)serialization code is working properly, # but better to handle it just in case... return StreamValue(self.stream_block, []) return self.stream_block.to_python(unpacked_value) else: # See if it looks like the standard non-smart representation of a # StreamField value: a list of (block_name, value) tuples try: [None for (x, y) in value] except (TypeError, ValueError): # Give up trying to make sense of the value raise TypeError( "Cannot handle %r (type %r) as a value of StreamField" % (value, type(value))) # Test succeeded, so return as a StreamValue-ified version of that value return StreamValue(self.stream_block, value)
def test_resource_list_show_thumbnails_false(self): """ Resource List doesn't show thumbs when show_thumbnails is False""" no_thumbnails_page = BrowsePage( title='No Thumbnails Page', slug='no-thumbnails', ) no_thumbnails_page.content = StreamValue( no_thumbnails_page.content.stream_block, [atomic.snippet_list_show_thumbnails_false], True) publish_page(child=no_thumbnails_page) self.create_resource() response = self.client.get('/no-thumbnails/') self.assertNotContains(response, 'o-resource-list_list-thumbnail')
def test_resource_list_set_col_width(self): """ Resource List Assets column width is fixed when set""" assets_width_page = BrowsePage( title='Assets Width Test Page', slug='assets-width', ) assets_width_page.content = StreamValue( assets_width_page.content.stream_block, [atomic.snippet_list_actions_column_width_40], True) publish_page(child=assets_width_page) self.create_resource() response = self.client.get('/assets-width/') self.assertContains(response, 'u-w40pct"')
def test_resource_list_show_thumbnails_true(self): """ Resource List shows thumbnails when show_thumbnails is True""" thumbnails_page = BrowsePage( title='Thumbnails Page', slug='thumbnails', ) thumbnails_page.content = StreamValue( thumbnails_page.content.stream_block, [atomic.snippet_list_show_thumbnails_true], True) publish_page(child=thumbnails_page) self.create_resource() response = self.client.get('/thumbnails/') self.assertContains(response, 'o-resource-list_list-thumbnail')
def test_quote(self): """Quote value correctly displays on a Learn Page""" learn_page = LearnPage( title='Learn', slug='learn' ) learn_page.content = StreamValue( learn_page.content.stream_block, [atomic.full_width_text], True ) publish_page(child=learn_page) response = self.client.get('/learn/') self.assertContains(response, 'this is a quote') self.assertContains(response, 'a citation')
def setUp(self): super(TestActivityIndexPageSearch, self).setUp() self.ROOT_PAGE = HomePage.objects.get(slug='cfgov') self.ROOT_PAGE.save_revision().publish() self.site = Site.objects.get(is_default_site=True) self.factory = RequestFactory() self.search_page = ActivityIndexPage(live=True, path='search', depth='1', title='Search for activities', slug='search') self.search_page.header = StreamValue( self.search_page.header.stream_block, [atomic.text_introduction], True) # noqa: E501 publish_page(child=self.search_page)
def test_content_with_anchor(self): """Content with anchor value correctly displays on a Learn Page""" learn_page = LearnPage( title='Learn', slug='learn' ) learn_page.content = StreamValue( learn_page.content.stream_block, [atomic.full_width_text], True ) publish_page(child=learn_page) response = self.client.get('/learn/') self.assertContains(response, 'full width text block') self.assertContains(response, 'this is an anchor link')
def test_compare_structblock(self): field = StreamPage._meta.get_field('body') comparison = self.comparison_class( field, StreamPage(body=StreamValue(field.stream_block, [ ('product', {'name': 'a packet of rolos', 'price': '75p'}, '1'), ])), StreamPage(body=StreamValue(field.stream_block, [ ('product', {'name': 'a packet of rolos', 'price': '85p'}, '1'), ])), ) expected = """ <div class="comparison__child-object"><dl> <dt>Name</dt> <dd>a packet of rolos</dd> <dt>Price</dt> <dd><span class="deletion">75p</span><span class="addition">85p</span></dd> </dl></div> """ self.assertHTMLEqual(comparison.htmldiff(), expected) self.assertIsInstance(comparison.htmldiff(), SafeString) self.assertTrue(comparison.has_changed())
def add_feedback_form(slug, cls): feedback_form = { 'type': 'feedback', 'value': [] } page = cls( title=slug, slug=slug, ) page.content = StreamValue( page.content.stream_block, [feedback_form], True, ) publish_page(page)
def setUp(self): self.request = mock.MagicMock() self.limit = 10 self.sublanding_page = SublandingPage(title='title') helpers.publish_page(child=self.sublanding_page) self.post1 = BrowseFilterablePage(title='post 1') self.post2 = BrowseFilterablePage(title='post 2') # the content of this post has both a full_width_text # and a filter_controls self.post1.content = StreamValue( self.post1.content.stream_block, [atomic.full_width_text, atomic.filter_controls], True) # this one only has a filter_controls self.post2.content = StreamValue(self.post1.content.stream_block, [atomic.filter_controls], True) helpers.save_new_page(self.post1, self.sublanding_page) helpers.save_new_page(self.post2, self.sublanding_page) # manually set the publication date of the posts to ensure consistent # order of retrieval in test situations, otherwise the `date_published` # can vary due to commit order self.child1_of_post1 = AbstractFilterPage(title='child 1 of post 1', date_published=dt.date( 2016, 9, 1)) self.child2_of_post1 = AbstractFilterPage(title='child 2 of post 1', date_published=dt.date( 2016, 9, 2)) self.child1_of_post2 = AbstractFilterPage(title='child 1 of post 2', date_published=dt.date( 2016, 9, 3)) helpers.save_new_page(self.child1_of_post1, self.post1) helpers.save_new_page(self.child2_of_post1, self.post1) helpers.save_new_page(self.child1_of_post2, self.post2)
def test_resource_list(self): """ Resource List renders thumbnails when show_thumbnails is True""" browse_page = BrowsePage( title='Browse Page', slug='browse', ) browse_page.content = StreamValue( browse_page.content.stream_block, [atomic.snippet_list_show_thumbnails_false], True) publish_page(child=browse_page) self.create_resource() response = self.client.get('/browse/') self.assertContains(response, 'Test Resource List') self.assertContains(response, 'Test Resource')
def test_notification(self): """Notification correctly displays on a Sublanding Page""" sublanding_page = SublandingPage( title='Sublanding Page', slug='sublanding', ) sublanding_page.content = StreamValue( sublanding_page.content.stream_block, [atomic.notification], True ) publish_page(child=sublanding_page) response = self.client.get('/sublanding/') self.assertContains(response, 'this is a notification message') self.assertContains(response, 'this is a notification explanation') self.assertContains(response, 'this is a notification link')
def add_filterable_page(slug, cls): filterable_page = cls( title=slug, slug=slug, ) filterable_page.content = StreamValue( filterable_page.content.stream_block, [atomic.filter_controls], True ) publish_page(filterable_page) add_children( parent=filterable_page, num=11, slug=slug, )
def test_tableblock(self): """Table correctly displays on a Learn Page""" learn_page = LearnPage( title='Learn Page', slug='learn', ) learn_page.content = StreamValue( learn_page.content.stream_block, [atomic.table_block], True ) publish_page(child=learn_page) response = django_client.get('/learn/') self.assertContains(response, 'Header One') self.assertContains(response, 'Row 1-1') self.assertContains(response, 'Row 2-1')
def test_that_deleting_recommended_articles_should_not_raise_any_errors( self): articles = [self._create_blog_article_page() for _ in range(3)] articles_block = StreamBlock([("page", PageChooserBlock())]) recommended_articles = StreamValue(articles_block, [("page", article) for article in articles]) main_article = self._create_blog_article_page( recommended_articles=recommended_articles) for article in articles: article.delete() main_article.refresh_from_db() self.assertEqual(BlogArticlePage.objects.all().count(), 1) self.assertEqual(len(main_article.recommended_articles), 0)
def set_stream_data(page_or_revision, field_name, stream_data, commit=True): """ Set the stream field data for a given field name on a page or a revision. If commit is True (default) save() is called on the page_or_revision object. """ if is_page(page_or_revision): field = getattr(page_or_revision, field_name) stream_block = field.stream_block stream_value = StreamValue(stream_block, stream_data, is_lazy=True) setattr(page_or_revision, field_name, stream_value) else: revision_content = json.loads(page_or_revision.content_json) revision_content[field_name] = json.dumps(stream_data) page_or_revision.content_json = json.dumps(revision_content) if commit: page_or_revision.save()
def migrate_work_body_to_streamfield(apps, schema_editor): WorkPage = apps.get_model('work.WorkPage') stream_block = WorkPage._meta.get_field('streamfield').stream_block # Update model for work_page in WorkPage.objects.exclude(body='').exclude( body='<p></p>').exclude(body='<p><br/></p>'): # Add body as first block so it appears in the same place on the template work_page.streamfield = StreamValue(stream_block, [ ('paragraph', RichText( work_page.body), str(uuid3(UUID_NAMESPACE, work_page.body))), ] + [(child.block_type, child.value, child.id) for child in work_page.streamfield]) work_page.save() # Update revisions PageRevision = apps.get_model('wagtailcore.PageRevision') ContentType = apps.get_model('contenttypes.ContentType') work_page_content_type = ContentType.objects.get(app_label='work', model='workpage') for revision in PageRevision.objects.filter( page__content_type=work_page_content_type): content = json.loads(revision.content_json) if content['body'] and content['body'] not in [ '<p></p>', '<p><br/></p>' ]: streamfield_json = content.get('streamfield', '') if streamfield_json: streamfield = json.loads(streamfield_json) else: streamfield = [] streamfield.insert( 0, { "type": "paragraph", "value": content['body'], "id": str(uuid3(UUID_NAMESPACE, content['body'])), }) content['streamfield'] = json.dumps(streamfield) revision.content_json = json.dumps(content) revision.save()
def setUp(self): self._set_default_blog_index_page_as_new_root_page_child() self.blog_index_page = self._get_newest_blog_index_page() header_block = StreamBlock([(ArticleBodyBlockNames.HEADER.value, CharBlock())]) body = StreamValue( header_block, [ (ArticleBodyBlockNames.HEADER.value, "Header 1"), (ArticleBodyBlockNames.HEADER.value, "Header 2"), (ArticleBodyBlockNames.HEADER.value, "Header 3"), ], ) self.blog_article_page = self._create_blog_article_page( blog_index_page=self.blog_index_page, body=body, table_of_contents=True)
def test_update_streamfields(self): # Streamfields are special in that they contain content that needs to be synchronised as well as # translatable content. # Copy page for translation, this will have a blank streamfield translated = self.page.copy_for_translation(self.dest_locale) # Set streamfield value on original self.page.test_streamfield = StreamValue( TestPage.test_streamfield.field.stream_block, [{ "id": "id", "type": "test_charblock", "value": "This is some test content", }], is_lazy=True, ) # Save the page revision = self.page.save_revision() revision.publish() self.page.refresh_from_db() source_with_streamfield, created = TranslationSource.update_or_create_from_instance( self.page) # Create a translation for the new context StringTranslation.objects.create( translation_of=self.string, locale=self.dest_locale, context=TranslationContext.objects.get( object_id=self.page.translation_key, path="test_streamfield.id"), data="Ceci est du contenu de test", ) new_page, created = source_with_streamfield.create_or_update_translation( self.dest_locale) self.assertFalse(created) self.assertEqual(new_page, translated) # Check the block was copied into translation self.assertEqual(new_page.test_streamfield[0].id, "id") self.assertEqual(new_page.test_streamfield[0].value, "Ceci est du contenu de test")
def setUp(self): super().setUp() # Create Blog self.blog_page = BlogPageFactory(body=[ ("heading", "Test heading 1"), ("paragraph", RichText("This is a paragraph.")), ("heading", "Test heading 2"), ("image", wagtail_factories.ImageFactory()), ("decimal", decimal.Decimal(1.2)), ("date", datetime.date.today()), ("datetime", datetime.datetime.now()), ( "gallery", { "title": "Gallery title", "images": StreamValue( stream_block=ImageGalleryImages(), stream_data=[ ( "image", { "image": wagtail_factories.ImageChooserBlockFactory( ) }, ), ( "image", { "image": wagtail_factories.ImageChooserBlockFactory( ) }, ), ], ), }, ), ("objectives", ["Read all of article!"]), ("video", { "youtube_link": EmbedValue("https://youtube.com/") }), ])
def _create_blog_article_page( self, blog_index_page=None, title="Simple Article Title", page_title="Simple Article Title", date=datetime.now(), body=None, author=None, read_time=7, table_of_contents=False, recommended_articles=None, views=0, cover_photo=None, article_photo=None, is_main_article=False, ): if body is None: block = StreamBlock([(ArticleBodyBlockNames.MARKDOWN.value, MarkdownBlock())]) body = StreamValue( block, [(ArticleBodyBlockNames.MARKDOWN.value, "Hello, World")]) if author is None: author = BossFactory() blog_article_page = BlogArticlePage( title=title, page_title=page_title, date=date, body=body, author=author, read_time=read_time, table_of_contents=table_of_contents, recommended_articles=recommended_articles, views=views, cover_photo=cover_photo, article_photo=article_photo, is_main_article=is_main_article, ) if blog_index_page is None: blog_index_page = self._get_newest_blog_index_page() blog_index_page.add_child(instance=blog_article_page) blog_article_page.save() return blog_article_page
def test_export_script_assemble_output(self): answer = Answer(id=1234) answer.save() page = AnswerPage( slug="mock-question1-en-1234", title="Mock question1" ) page.answer_base = answer page.question = "Mock question1" page.answer_content = StreamValue( page.answer_content.stream_block, [{"type": "text", "value": {"content": "Mock answer"}}], True, ) helpers.publish_page(page) output = assemble_output()[0] self.assertEqual(output.get("ASK_ID"), 1234) self.assertEqual(output.get("URL"), "/mock-question1-en-1234/") self.assertEqual(output.get("Question"), "Mock question1")
def test_cache_gets_called_when_visiting_filterable_page(self): # Create a filterable page page = BrowseFilterablePage(title='test browse filterable page', slug='test-browse-filterable-page') page.content = StreamValue(page.content.stream_block, [atomic.filter_controls], True) publish_page(page) # Add a child to that filterable page so that there are results # with a post preview child_page = BlogPage(title='test blog page', slug='test-blog-page') page.add_child(instance=child_page) cache = caches['post_preview'] with patch.object(cache, 'add') as add_to_cache: # Navigate to the filterable page so that `post-preview.html` loads self.client.get('/test-browse-filterable-page/') self.assertTrue(add_to_cache.called)
def test_that_article_intro_should_take_multiple_paragraphs_under_account( self): paragraph_1 = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam laoreet venenatis enim, non luctus nisi finibus ut." paragraph_2 = "Mauris porta eleifend massa, nec maximus lacus luctus a. Aenean libero felis, placerat non malesuada a, maximus id erat." paragraph_3 = "Nulla ut purus elementum, auctor orci eget, facilisis est. Nullam aliquet volutpat massa, vel bibendum libero venenatis ut. Integer ac sapien et urna sollicitudin." expected_string = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam laoreet venenatis enim, non luctus nisi finibus ut. Mauris porta eleifend massa, nec maximus lacus luctus a. Aenean libero felis, placerat non malesuada a, maximus id erat. Nulla ut" body_block = StreamBlock([(ArticleBodyBlockNames.PARAGRAPH.value, RichTextBlock())]) body = StreamValue( body_block, [ (ArticleBodyBlockNames.PARAGRAPH.value, RichText(paragraph_1)), (ArticleBodyBlockNames.PARAGRAPH.value, RichText(paragraph_2)), (ArticleBodyBlockNames.PARAGRAPH.value, RichText(paragraph_3)), ], ) blog_article = self._create_blog_article_page(body=body) self.assertEqual(blog_article.intro, expected_string + INTRO_ELLIPSIS)
def test_sidebar_contact_info(self): """Sidebar contact info correctly displays on a Landing Page""" landing_page = LandingPage( title='Landing Page', slug='landing', ) contact = self.get_contact() landing_page.sidefoot = StreamValue( landing_page.sidefoot.stream_block, [atomic.sidebar_contact(contact.id)], True) publish_page(child=landing_page) response = django_client.get('/landing/') self.assertContains(response, '*****@*****.**') self.assertContains(response, '(515) 123-4567') self.assertContains(response, 'Ext. 1234') self.assertContains(response, '123 abc street') self.assertContains(response, 'this is a heading') self.assertContains(response, 'this is a body') self.assertContains( response, 'Contact Information') # This is specific to sidebar
def test_main_contact_info(self): """Main contact info correctly displays on a Sublanding Page""" sublanding_page = SublandingPage( title='Sublanding Page', slug='sublanding', ) contact = self.get_contact() sublanding_page.content = StreamValue( sublanding_page.content.stream_block, [atomic.main_contact_info(contact.id)], True) publish_page(child=sublanding_page) response = django_client.get('/sublanding/') self.assertContains(response, '*****@*****.**') self.assertContains(response, '(515) 123-4567') self.assertContains(response, 'Ext. 1234') self.assertContains(response, '123 abc street') self.assertContains(response, 'this is a heading') self.assertContains(response, 'this is a body') self.assertNotContains(response, 'Contact Information') # Only shown on sidebar
def test_export_script_assemble_output(self): answer = Answer(id=1234) answer.save() page = AnswerPage(slug='mock-question1-en-1234', title='Mock question1') page.answer_base = answer page.question = 'Mock question1' page.answer_content = StreamValue(page.answer_content.stream_block, [{ 'type': 'text', 'value': { 'content': 'Mock answer' } }], True) helpers.publish_page(page) output = assemble_output()[0] self.assertEqual(output.get('ASK_ID'), 1234) self.assertEqual(output.get('URL'), '/mock-question1-en-1234/') self.assertEqual(output.get('Question'), 'Mock question1')
def setUp(self): super().setUp() # Create Blog self.blog_page = BlogPageFactory(body=[ ("heading", "Test heading 1"), ("paragraph", RichText("This is a paragraph.")), ("heading", "Test heading 2"), ("image", wagtail_factories.ImageFactory()), ("decimal", decimal.Decimal(1.2)), ("date", datetime.date.today()), ("datetime", datetime.datetime.now()), ( "gallery", { "title": "Gallery title", "images": StreamValue( stream_block=ImageGalleryImages(), stream_data=[ ( "image", { "image": wagtail_factories.ImageChooserBlockFactory( ) }, ), ( "image", { "image": wagtail_factories.ImageChooserBlockFactory( ) }, ), ], ), }, ), ])
def test_chart_block(self): """ Chart Block correctly renders fields on a Browse Page""" browse_page = BrowsePage( title='Browse Page', slug='browse', ) # Adds a AUT market to a browse page browse_page.content = StreamValue(browse_page.content.stream_block, [atomic.chart_block], True) publish_page(child=browse_page) response = self.client.get('/browse/') self.assertContains(response, 'Volume of credit cards originated') self.assertContains(response, 'foo/bar.csv') self.assertContains(response, 'Data not final.') self.assertContains( response, 'The most recent data available in this visualization are for April 2016' ) self.assertContains(response, 'January 2018')
def test_clear_live_post_content_streamchild(blog_page_factory): live_posts = json.dumps([ { "type": "live_post", "id": "some-id", "value": { "message_id": "some-id", "created": "2021-01-01T12:00:00", "modified": "2021-01-01T12:00:00", "show": True, "content": [], }, }, ]) page = blog_page_factory(channel_id="some-id", live_posts=live_posts) live_post = page.get_live_post_by_index(live_post_index=0) text_block = construct_text_block(text="Some text") add_block_to_live_post(TEXT, text_block, live_post) clear_live_post_content(live_post) assert isinstance(live_post, StreamValue.StreamChild) assert live_post.value["content"] == StreamValue(ContentBlock(), [])