from django.test import RequestFactory from graphene.test import Client from graphql_relay import to_global_id, from_global_id from api.schema import schema from api.utils import get_object from books.factories import ModuleFactory, ChapterFactory, ContentBlockFactory from books.models import Snapshot, ChapterSnapshot from books.tests.queries import MODULE_QUERY, SNAPSHOT_MODULE_QUERY, CREATE_SNAPSHOT_MUTATION, APPLY_SNAPSHOT_MUTATION, \ MODULE_SNAPSHOTS_QUERY, SHARE_SNAPSHOT_MUTATION from core.tests.base_test import SkillboxTestCase from objectives.factories import ObjectiveGroupFactory, ObjectiveFactory from users.factories import SchoolClassFactory from users.models import User, SchoolClass class CreateSnapshotTestCase(SkillboxTestCase): def _test_objective(self, objective, text, hidden): self.assertEqual(objective['text'], text) self.assertEqual(objective['hidden'], hidden) def _test_content_block(self, content_block, title, hidden): self.assertEqual(content_block['title'], title) self.assertEqual(content_block['hidden'], hidden) @property def graphene_client(self): return self.get_client() def setUp(self): self.createDefault() self.client = self.get_client() # teacher will create snapshot self.slug = 'some-module' self.module = ModuleFactory(slug=self.slug) self.skillbox_class = SchoolClass.objects.get(name='skillbox') self.teacher2 = User.objects.get(username='teacher2') self.second_class_name = 'second_class' self.second_class = SchoolClass.objects.get(name=self.second_class_name) self.admin = User.objects.get(username='admin') # module M has a chapter self.chapter = ChapterFactory(parent=self.module, slug='some-chapter', owner=self.admin) ChapterFactory(parent=self.module, slug='some-other-chapter', owner=self.admin) # chapter has some content blocks a, b, c self.title_visible = 'visible' self.title_hidden = 'hidden' self.title_custom = 'custom' self.title_custom_hidden = 'custom-hidden' self.visible_content_block = ContentBlockFactory(parent=self.chapter, module=self.module, title=self.title_visible, slug='cb-a', owner=self.admin) self.hidden_content_block = ContentBlockFactory(parent=self.chapter, module=self.module, title=self.title_hidden, slug='cb-b', owner=self.admin) # content block c is user created self.custom_content_block = ContentBlockFactory(parent=self.chapter, owner=self.teacher, user_created=True, module=self.module, title=self.title_custom, slug='cb-c') # content block d is user created, but hidden self.custom_hidden_content_block = ContentBlockFactory(parent=self.chapter, owner=self.teacher, user_created=True, module=self.module, title=self.title_custom_hidden, slug='cb-d') # content block a and c are visible to school class X self.hidden_content_block.hidden_for.add(self.skillbox_class) self.custom_content_block.visible_for.add(self.skillbox_class) # chapter title is hidden for school class X self.chapter.title_hidden_for.add(self.skillbox_class) # we make a snapshot S of the module M # snapshot S looks like module M for school class X objective_group = ObjectiveGroupFactory(module=self.module) second_objective_group = ObjectiveGroupFactory(module=self.module) self.visible_objective = ObjectiveFactory(text='visible-objective', group=objective_group) self.hidden_objective = ObjectiveFactory(text='hidden-objective', group=objective_group) self.custom_objective = ObjectiveFactory(text='custom-objective', group=objective_group, owner=self.teacher) self.custom_hidden_objective = ObjectiveFactory(text='custom-hidden-objective', group=objective_group, owner=self.teacher) self.hidden_objective.hidden_for.add(self.skillbox_class) self.custom_objective.visible_for.add(self.skillbox_class) second_objective_group.hidden_for.add(self.skillbox_class) def _test_module_visibility(self, client, school_class_name): result = client.execute(MODULE_QUERY, variables={ 'slug': self.module.slug }) self.assertIsNone(result.get('errors')) module = result.get('data').get('module') chapter = module.get('chapters')[0] self.assertIsNotNone(chapter) content_blocks = chapter.get('contentBlocks') content_block_titles = [content_block['title'] for content_block in content_blocks] self.assertTrue(self.title_visible in content_block_titles) self.assertTrue(self.title_hidden in content_block_titles) self.assertTrue(self.title_custom in content_block_titles) hidden_content_block = [content_block for content_block in content_blocks if content_block['title'] == self.title_hidden][0] custom_content_block = [content_block for content_block in content_blocks if content_block['title'] == self.title_custom][0] # check if hidden content block is hidden for this school class self.assertTrue( school_class_name in [school_class['name'] for school_class in hidden_content_block.get('hiddenFor')]) # check if the custom content block is visible for this school class self.assertTrue( school_class_name in [school_class['name'] for school_class in custom_content_block.get('visibleFor')]) objectives = module['objectiveGroups'][0]['objectives'] self.assertEqual(len(objectives), 4) hidden_objective = [objective for objective in objectives if objective['text'] == self.hidden_objective.text][0] custom_objective = [objective for objective in objectives if objective['text'] == self.custom_objective.text][0] # check if hidden objective is hidden for this school class self.assertTrue( school_class_name in [school_class['name'] for school_class in hidden_objective.get('hiddenFor')]) # check if the custom objective is visible for this school class self.assertTrue( school_class_name in [school_class['name'] for school_class in custom_objective.get('visibleFor')]) return module def _compare_content_blocks(self, content_blocks): self.assertEqual(len(content_blocks), 4) first, second, third, fourth = content_blocks self._test_content_block(first, self.title_visible, False) self._test_content_block(second, self.title_hidden, True) self._test_content_block(third, self.title_custom, False) self._test_content_block(fourth, self.title_custom_hidden, True) def test_setup(self): # make sure everything is setup correctly self._test_module_visibility(self.client, 'skillbox') def _test_create_snapshot(self, result, num_snapshots=1): self.assertIsNone(result.get('errors')) snapshot = result.get('data').get('createSnapshot').get('snapshot') chapter = snapshot.get('chapters')[0] self.assertIsNotNone(snapshot.get('created')) self.assertEqual(snapshot.get('creator'), f'{self.teacher.first_name} {self.teacher.last_name}') self.assertTrue(chapter['titleHidden']) self.assertFalse(chapter['descriptionHidden']) _, chapter_id = from_global_id(chapter['id']) self.assertEqual(int(chapter_id), self.chapter.id) content_blocks = chapter['contentBlocks'] self._compare_content_blocks(content_blocks) self.assertEqual(ChapterSnapshot.objects.count(), 2 * num_snapshots) visible, hidden, custom, custom_hidden = snapshot['objectiveGroups'][0]['objectives'] self._test_objective(objective=visible, text=self.visible_objective.text, hidden=False) self._test_objective(objective=hidden, text=self.hidden_objective.text, hidden=True) self._test_objective(objective=custom, text=self.custom_objective.text, hidden=False) self._test_objective(objective=custom_hidden, text=self.custom_hidden_objective.text, hidden=True) id = snapshot['id'] snapshot = get_object(Snapshot, id) self.assertEqual(snapshot.objective_groups.count(), 2) def test_create_snapshot(self): result = self.client.execute(CREATE_SNAPSHOT_MUTATION, variables={ 'input': { 'module': self.slug, 'selectedClass': to_global_id('SchoolClassNode', self.skillbox_class.pk), } }) self._test_create_snapshot(result) def test_create_two_snapshots(self): self.client.execute(CREATE_SNAPSHOT_MUTATION, variables={ 'input': { 'module': self.slug, 'selectedClass': to_global_id('SchoolClassNode', self.skillbox_class.pk), } }) result = self.client.execute(CREATE_SNAPSHOT_MUTATION, variables={ 'input': { 'module': self.slug, 'selectedClass': to_global_id('SchoolClassNode', self.skillbox_class.pk), } }) self._test_create_snapshot(result, num_snapshots=2) def test_apply_snapshot(self): self.snapshot = Snapshot.objects.create_snapshot(module=self.module, school_class=self.skillbox_class, user=self.teacher) self.assertEqual(Snapshot.objects.count(), 1) self.assertEqual(self.snapshot.custom_objectives.count(), 2) result = self.get_client(self.teacher2).execute(APPLY_SNAPSHOT_MUTATION, variables={ 'input': { 'snapshot': to_global_id('SnapshotNode', self.snapshot.pk), 'selectedClass': to_global_id('SchoolClassNode', self.second_class.pk), } }) self.assertIsNone(result.get('errors')) module = self._test_module_visibility(self.get_client(self.teacher2), self.second_class_name) original_creator = module['chapters'][0]['contentBlocks'][2].get('originalCreator') self.assertIsNotNone(original_creator) self.assertEqual(original_creator.get('id'), to_global_id('PublicUserNode', self.teacher.pk)) def test_display_snapshot_module(self): self.snapshot = Snapshot.objects.create_snapshot(module=self.module, school_class=self.skillbox_class, user=self.teacher) id = to_global_id('SnapshotNode', self.snapshot.id) snapshot_result = self.client.execute(SNAPSHOT_MODULE_QUERY, variables={ 'id': id }) self.assertIsNone(snapshot_result.get('errors')) snapshot = snapshot_result.get('data').get('snapshot') chapters = snapshot.get('chapters') self.assertEqual(len(chapters), 2) chapter = chapters[0] content_blocks = chapter.get('contentBlocks') self._compare_content_blocks(content_blocks) objective_groups = snapshot['objectiveGroups'] self.assertEqual(len(objective_groups), 2) objective_group1, objective_group2 = objective_groups objective1, objective2, objective3, objective4 = objective_group1['objectives'] self._test_objective(objective1, self.visible_objective.text, False) self._test_objective(objective2, self.hidden_objective.text, True) self._test_objective(objective3, self.custom_objective.text, False) self._test_objective(objective4, self.custom_hidden_objective.text, True) self.assertEqual(objective_group2['hidden'], True) changes = snapshot['changes'] self.assertEqual(changes['newContentBlocks'], 1) self.assertEqual(changes['hiddenContentBlocks'], 1) self.assertEqual(changes['newObjectives'], 1) self.assertEqual(changes['hiddenObjectives'], 1) def test_apply_initial_snapshot(self): third_class = SchoolClassFactory( users=[self.teacher2], name='third_class' ) # make a neutral snapshot, nothing new, nothing hidden result = self.get_client(user=self.teacher2).execute(CREATE_SNAPSHOT_MUTATION, variables={ 'input': { 'module': self.slug, 'selectedClass': to_global_id('SchoolClassNode', third_class.pk), } }) self.assertIsNone(result.get('errors')) snapshot_id = result['data']['createSnapshot']['snapshot']['id'] result = self.client.execute(APPLY_SNAPSHOT_MUTATION, variables={ 'input': { 'snapshot': snapshot_id, 'selectedClass': to_global_id('SchoolClassNode', self.skillbox_class.pk), } }) self.assertIsNone(result.get('errors')) result = self.client.execute(MODULE_QUERY, variables={ 'slug': self.module.slug }) self.assertIsNone(result.get('errors')) module = result['data']['module'] chapter1, chapter2 = module['chapters'] visible, hidden, custom, custom_hidden = chapter1['contentBlocks'] self.assertTrue(self.skillbox_class.name not in [sc['name'] for sc in visible['hiddenFor']]) self.assertTrue(self.skillbox_class.name not in [sc['name'] for sc in hidden['hiddenFor']]) self.assertTrue(self.skillbox_class.name not in [sc['name'] for sc in custom['visibleFor']]) self.assertTrue(self.skillbox_class.name not in [sc['name'] for sc in custom_hidden['visibleFor']]) def test_create_apply_view_snapshot(self): result = self.graphene_client.execute(MODULE_QUERY, variables={ 'slug': self.module.slug }) self.assertIsNone(result.get('errors')) chapter = result['data']['module']['chapters'][0] self.assertEqual(len(chapter['contentBlocks']), 4) result = self.graphene_client.execute(CREATE_SNAPSHOT_MUTATION, variables={ 'input': { 'module': self.slug, 'selectedClass': to_global_id('SchoolClassNode', self.skillbox_class.pk), } }) self.assertIsNone(result.get('errors')) snapshot_id = result['data']['createSnapshot']['snapshot']['id'] teacher2 = User.objects.get(username='teacher2') teacher2_client = self.get_client(user=teacher2) result = teacher2_client.execute(APPLY_SNAPSHOT_MUTATION, variables={ 'input': { 'snapshot': snapshot_id, 'selectedClass': to_global_id('SchoolClassNode', self.second_class.pk), } }) self.assertIsNone(result.get('errors')) result = self.graphene_client.execute(MODULE_QUERY, variables={ 'slug': self.module.slug }) self.assertIsNone(result.get('errors')) chapter = result['data']['module']['chapters'][0] self.assertEqual(len(chapter['contentBlocks']), 4) def test_snapshot_chapter_visibility_after_apply(self): result = self.graphene_client.execute(CREATE_SNAPSHOT_MUTATION, variables={ 'input': { 'module': self.slug, 'selectedClass': to_global_id('SchoolClassNode', self.skillbox_class.pk), } }) self.chapter.title_hidden_for.remove(self.skillbox_class) self.chapter.description_hidden_for.remove(self.skillbox_class) self.chapter.title_hidden_for.add(self.second_class) self.chapter.description_hidden_for.add(self.second_class) def assert_chapter_hidden(hidden): a_result = self.get_client(self.teacher2).execute(MODULE_QUERY, variables={ 'slug': self.module.slug }) self.assertIsNone(a_result.get('errors')) a_chapter = a_result['data']['module']['chapters'][0] self.assertEqual(self.second_class_name in map(lambda x: x['name'], a_chapter['titleHiddenFor']), hidden) self.assertEqual(self.second_class_name in map(lambda x: x['name'], a_chapter['descriptionHiddenFor']), hidden) assert_chapter_hidden(True) result = self.graphene_client.execute(CREATE_SNAPSHOT_MUTATION, variables={ 'input': { 'module': self.slug, 'selectedClass': to_global_id('SchoolClassNode', self.skillbox_class.pk), } }) self.assertIsNone(result.get('errors')) snapshot = result['data']['createSnapshot']['snapshot'] snapshot_id = snapshot['id'] chapter = snapshot['chapters'][0] self.assertEqual(chapter['titleHidden'], False) self.assertEqual(chapter['descriptionHidden'], False) result = self.get_client(self.teacher2).execute(APPLY_SNAPSHOT_MUTATION, variables={ 'input': { 'snapshot': snapshot_id, 'selectedClass': to_global_id('SchoolClassNode', self.second_class.pk), } }) self.assertIsNone(result.get('errors')) assert_chapter_hidden(False) class SnapshotTestCase(SkillboxTestCase): def setUp(self) -> None: self.createDefault() self.client = self.get_client() self.slug = 'some-module' self.teacher2 = User.objects.get(username='teacher2') self.module = ModuleFactory(slug=self.slug) self.skillbox_class = SchoolClass.objects.get(name='skillbox') self.snapshot = Snapshot.objects.create_snapshot(module=self.module, school_class=self.skillbox_class, user=self.teacher) Snapshot.objects.create_snapshot(module=self.module, school_class=self.skillbox_class, user=self.teacher2) def test_show_only_own_snapshots(self): result = self.client.execute(MODULE_SNAPSHOTS_QUERY, variables={ "slug": self.slug }) self.assertIsNone(result.get('errors')) snapshots = result['data']['module']['snapshots'] self.assertEqual(len(snapshots), 1) self.assertEqual(snapshots[0]['creator'], f'{self.teacher.first_name} {self.teacher.last_name}') def test_share_snapshot(self): self.assertFalse(self.snapshot.shared) result = self.client.execute(SHARE_SNAPSHOT_MUTATION, variables={ 'input': { 'snapshot': to_global_id('Snapshot', self.snapshot.id), 'shared': True } }) self.assertIsNone(result.get('errors')) data = result['data']['shareSnapshot'] self.assertTrue(data['success']) self.assertTrue(data['snapshot']['shared']) snapshot = Snapshot.objects.get(pk=self.snapshot.pk) self.assertTrue(snapshot.shared) def test_dont_share_foreign_snapshot(self): self.assertFalse(self.snapshot.shared) teacher2_client = self.get_client(self.teacher2) result = teacher2_client.execute(SHARE_SNAPSHOT_MUTATION, variables={ 'input': { 'snapshot': to_global_id('Snapshot', self.snapshot.id), 'shared': True } }) self.assertIsNotNone(result.get('errors'))