diff --git a/client/cypress/integration/frontend/modules/snapshots.spec.js b/client/cypress/integration/frontend/modules/snapshots.spec.js index 4ce3dd67..2cf28352 100644 --- a/client/cypress/integration/frontend/modules/snapshots.spec.js +++ b/client/cypress/integration/frontend/modules/snapshots.spec.js @@ -1,5 +1,62 @@ import module from '../../../fixtures/module.minimal'; import {getMinimalMe} from '../../../support/helpers'; +import {hasOperationName} from '../../../support/graphql'; + +const mockDeleteSnapshot = (success) => { + cy.intercept('POST', '/api/graphql', (req) => { + if (hasOperationName(req, 'DeleteSnapshot')) { + let result; + if (success) { + result = { + message: 'yay!', + __typename: 'Success' + }; + } else { + result = { + reason: 'Not the owner', + __typename: 'NotOwner' + }; + } + req.reply({ + data: { + deleteSnapshot: { + result + } + }, + }); + + } + }); +}; + +const mockUpdateSnapshot = (title) => { + cy.intercept('POST', '/api/graphql', (req) => { + if (hasOperationName(req, 'UpdateSnapshot')) { + let snapshot; + if (title) { + snapshot = { + __typename: 'SnapshotNode', + id: 'U25hcHNob3ROb2RlOjQ=', + title, + }; + } else { + snapshot = { + __typename: 'NotOwner', + reason: 'Not the owner' + }; + } + req.reply({ + data: { + updateSnapshot: { + snapshot, + }, + }, + }); + } + }); + +}; + describe('Snapshot', () => { const operations = isTeacher => ({ @@ -25,8 +82,8 @@ describe('Snapshot', () => { ...module, snapshots: [ { - id: 'snapshot-id', - title: 'title', + id: 'U25hcHNob3ROb2RlOjQ=', + title: 'Old Title', created: '2020-01-01', mine: true, shared: false, @@ -88,4 +145,26 @@ describe('Snapshot', () => { cy.getByDataCy('module-title').should('exist'); cy.getByDataCy('snapshot-header').should('not.exist'); }); + + it('Renames Snapshot', () => { + cy.mockGraphqlOps(operations(true)); + const newTitle = 'New Title'; + mockUpdateSnapshot(newTitle); + cy.visit('module/miteinander-reden/snapshots'); + cy.getByDataCy('snapshot-link').should('have.text', 'Old Title'); + cy.getByDataCy('rename-snapshot-button').click(); + cy.getByDataCy('edit-name-input').clear().type(newTitle); + cy.getByDataCy('modal-save-button').click(); + cy.getByDataCy('snapshot-link').should('have.text', 'New Title'); + }); + + it('Deletes Snapshot', () => { + cy.mockGraphqlOps(operations(true)); + mockDeleteSnapshot(true); + cy.visit('module/miteinander-reden/snapshots'); + cy.getByDataCy('snapshot-entry').should('have.length', 1); + cy.getByDataCy('delete-snapshot-button').click(); + cy.getByDataCy('modal-save-button').click(); + cy.getByDataCy('snapshot-entry').should('have.length', 0); + }); }); diff --git a/client/src/App.vue b/client/src/App.vue index 9dcdbda5..cd1dfe2b 100644 --- a/client/src/App.vue +++ b/client/src/App.vue @@ -36,6 +36,7 @@ const EditNoteWizard = () => import(/* webpackChunkName: "content-forms" */'@/components/notes/EditNoteWizard'); const EditClassNameWizard = () => import(/* webpackChunkName: "content-forms" */'@/components/school-class/EditClassNameWizard'); const EditTeamNameWizard = () => import(/* webpackChunkName: "content-forms" */'@/components/profile/EditTeamNameWizard'); + const EditSnapshotTitleWizard = () => import(/* webpackChunkName: "content-forms" */'@/components/snapshots/EditSnapshotTitleWizard'); const DefaultLayout = () => import(/* webpackChunkName: "layouts" */'@/layouts/DefaultLayout'); const SimpleLayout = () => import(/* webpackChunkName: "layouts" */'@/layouts/SimpleLayout'); const FullScreenLayout = () => import(/* webpackChunkName: "layouts" */'@/layouts/FullScreenLayout'); @@ -66,6 +67,7 @@ EditNoteWizard, EditClassNameWizard, EditTeamNameWizard, + EditSnapshotTitleWizard, ...modals }, diff --git a/client/src/components/modules/SnapshotListItem.vue b/client/src/components/modules/SnapshotListItem.vue index aa76c87a..d8eaed5a 100644 --- a/client/src/components/modules/SnapshotListItem.vue +++ b/client/src/components/modules/SnapshotListItem.vue @@ -1,6 +1,9 @@ @@ -26,7 +45,13 @@ import dateformat from '@/helpers/date-format'; import {SNAPSHOT_DETAIL} from '@/router/module.names'; import SHARE_SNAPSHOT_MUTATION from 'gql/mutations/snapshots/share.gql'; + import UPDATE_SNAPSHOT_MUTATION from 'gql/mutations/snapshots/update.gql'; + import DELETE_SNAPSHOT_MUTATION from 'gql/mutations/snapshots/delete.gql'; + import SNAPSHOTS_QUERY from 'gql/queries/moduleSnapshots.gql'; import gql from 'graphql-tag'; + import PenIcon from '@/components/icons/PenIcon'; + import TrashIcon from '@/components/icons/TrashIcon'; + import { removeAtIndex } from '@/graphql/immutable-operations'; export default { props: { @@ -35,6 +60,10 @@ default: () => ({}), }, }, + components: { + PenIcon, + TrashIcon, + }, computed: { meta() { @@ -55,6 +84,78 @@ }, methods: { + changeTitle() { + this.$modal.open('edit-snapshot-title-wizard', {name: this.snapshot.title}) + .then((title) => { + console.log(title); + this.$apollo.mutate({ + mutation: UPDATE_SNAPSHOT_MUTATION, + variables: { + input: { + id: this.snapshot.id, + title: title, + }, + }, + update(store, {data: {updateSnapshot: {snapshot}}}) { + if (snapshot.__typename === 'SnapshotNode') { + const {id, title} = snapshot; + store.writeFragment({ + id, + fragment: gql`fragment SnapshotFragment on SnapshotNode {title}`, + data: { + title, + __typename: 'SnapshotNode', + }, + }); + } + }, + }); + }) + .catch(); + }, + deleteSnapshot() { + this.$modal.open('confirm') + .then(() => { + this.$apollo.mutate({ + mutation: DELETE_SNAPSHOT_MUTATION, + variables: { + input: { + id: this.snapshot.id, + }, + }, + update: (store, {data: {deleteSnapshot: {result}}}) => { + if (result.__typename === 'Success') { + const slug = this.$route.params.slug; + const query = SNAPSHOTS_QUERY; + const variables = { + slug, + }; + const {module} = store.readQuery({ + query, + variables, + }); + const index = module.snapshots.findIndex(snapshot => snapshot.id === this.snapshot.id); + const snapshots = removeAtIndex(module.snapshots, index); + + const data = { + module: { + ...module, + snapshots + } + }; + + store.writeQuery({ + query, + variables, + data + }); + + } + }, + }); + }) + .catch(); + }, share() { this.$apollo.mutate({ mutation: SHARE_SNAPSHOT_MUTATION, @@ -70,10 +171,10 @@ fragment: gql`fragment SnapshotFragment on SnapshotNode { shared }`, data: { shared, - __typename: 'SnapshotNode' - } + __typename: 'SnapshotNode', + }, }); - } + }, }); }, }, @@ -100,6 +201,15 @@ &__link { @include default-link; color: $color-brand; + } + + &__icon { + @include default-icon; + } + + &__actions { + display: flex; + align-items: center; margin-left: auto; } } diff --git a/client/src/components/profile/EditNameWizard.vue b/client/src/components/profile/EditNameWizard.vue index de691aab..879c87a1 100644 --- a/client/src/components/profile/EditNameWizard.vue +++ b/client/src/components/profile/EditNameWizard.vue @@ -9,7 +9,7 @@ @@ -43,6 +43,10 @@ type: String, default: '', }, + placeholder: { + type: String, + default: 'Namen bearbeiten' + } }, components: { Modal, diff --git a/client/src/components/snapshots/EditSnapshotTitleWizard.vue b/client/src/components/snapshots/EditSnapshotTitleWizard.vue new file mode 100644 index 00000000..03eb6034 --- /dev/null +++ b/client/src/components/snapshots/EditSnapshotTitleWizard.vue @@ -0,0 +1,46 @@ + + + + + diff --git a/client/src/graphql/gql/mutations/snapshots/delete.gql b/client/src/graphql/gql/mutations/snapshots/delete.gql new file mode 100644 index 00000000..12e80df3 --- /dev/null +++ b/client/src/graphql/gql/mutations/snapshots/delete.gql @@ -0,0 +1,13 @@ +mutation DeleteSnapshot($input: DeleteSnapshotInput!) { + deleteSnapshot(input: $input) { + result { + __typename + ...on NotOwner { + reason + } + ...on Success { + message + } + } + } +} diff --git a/client/src/graphql/gql/mutations/snapshots/update.gql b/client/src/graphql/gql/mutations/snapshots/update.gql new file mode 100644 index 00000000..1736fb2a --- /dev/null +++ b/client/src/graphql/gql/mutations/snapshots/update.gql @@ -0,0 +1,13 @@ +mutation UpdateSnapshot($input: UpdateSnapshotInput!) { + updateSnapshot(input: $input) { + snapshot { + ...on SnapshotNode { + title + id + } + ...on NotOwner { + reason + } + } + } +} diff --git a/client/src/pages/me/myTeam.vue b/client/src/pages/me/myTeam.vue index 8c3aa711..d8377042 100644 --- a/client/src/pages/me/myTeam.vue +++ b/client/src/pages/me/myTeam.vue @@ -71,6 +71,7 @@ methods: { editTeamName() { + // todo: use this.$modal this.$store.dispatch('editTeamName'); }, leaveTeam() { diff --git a/server/api/types.py b/server/api/types.py new file mode 100644 index 00000000..7f155b90 --- /dev/null +++ b/server/api/types.py @@ -0,0 +1,9 @@ +import graphene + + +class FailureNode(graphene.Interface): + reason = graphene.String() + + +class SuccessNode(graphene.Interface): + message = graphene.String() diff --git a/server/assignments/tests/test_feedback.py b/server/assignments/tests/test_feedback.py index 228c057f..13e39fd0 100644 --- a/server/assignments/tests/test_feedback.py +++ b/server/assignments/tests/test_feedback.py @@ -51,7 +51,7 @@ class SubmissionFeedbackTestCase(SkillboxTestCase): }) def _fetch_assignment_student(self, user): - client = create_client(user) + client = self.get_client(user) query = ''' query AssignmentWithSubmissions($id: ID!) { assignment(id: $id) { @@ -72,7 +72,7 @@ class SubmissionFeedbackTestCase(SkillboxTestCase): }) def _fetch_assignment_teacher(self, user): - client = create_client(user) + client = self.get_client(user) query = ''' query AssignmentWithSubmissions($id: ID!) { assignment(id: $id) { @@ -116,13 +116,13 @@ class SubmissionFeedbackTestCase(SkillboxTestCase): def test_teacher_can_create_feedback(self): result = self._create_submission_feedback(self.teacher, False, 'Balalal', self.student_submission_id) - self.assertIsNone(result.get('errors')) + self.assertIsNone(result.errors) self.assertIsNotNone( - result.get('data').get('updateSubmissionFeedback').get('updatedSubmissionFeedback').get('id')) + result.data.get('updateSubmissionFeedback').get('updatedSubmissionFeedback').get('id')) def test_student_cannot_create_feedback(self): result = self._create_submission_feedback(self.student1, False, 'Balalal', self.student_submission_id) - self.assertIsNotNone(result.get('errors')) + self.assertIsNotNone(result.errors) def test_teacher_can_update_feedback(self): assignment = AssignmentFactory( @@ -136,9 +136,9 @@ class SubmissionFeedbackTestCase(SkillboxTestCase): result = self._create_submission_feedback(self.teacher, True, 'Some', submission_feedback_id) - self.assertIsNone(result.get('errors')) + self.assertIsNone(result.errors) - submission_feedback_response = result.get('data').get('updateSubmissionFeedback').get( + submission_feedback_response = result.data.get('updateSubmissionFeedback').get( 'updatedSubmissionFeedback') self.assertTrue(submission_feedback_response.get('final')) @@ -156,19 +156,19 @@ class SubmissionFeedbackTestCase(SkillboxTestCase): result = self._create_submission_feedback(self.teacher2, True, 'Some', submission_feedback_id) - self.assertIsNotNone(result.get('errors')) + self.assertIsNotNone(result.errors) def test_student_does_not_see_non_final_feedback(self): SubmissionFeedbackFactory(teacher=self.teacher, final=False, student_submission=self.student_submission) result = self._fetch_assignment_student(self.student1) - self.assertIsNone(result.get('data').get('submissionFeedback')) + self.assertIsNone(result.data.get('submissionFeedback')) def test_student_does_see_final_feedback(self): submission_feedback = SubmissionFeedbackFactory(teacher=self.teacher, final=True, student_submission=self.student_submission) result = self._fetch_assignment_student(self.student1) - self.assertEqual(result.get('data').get('assignment').get('submission').get('submissionFeedback') + self.assertEqual(result.data.get('assignment').get('submission').get('submissionFeedback') .get('text'), submission_feedback.text) def test_teacher_can_see_feedback_for_submission(self): @@ -178,7 +178,7 @@ class SubmissionFeedbackTestCase(SkillboxTestCase): self.student_submission.save() result = self._fetch_assignment_teacher(self.teacher) - self.assertEqual(result.get('data').get('assignment').get('submissions')[0].get('submissionFeedback') + self.assertEqual(result.data.get('assignment').get('submissions')[0].get('submissionFeedback') .get('text'), submission_feedback.text) def test_rogue_teacher_cannot_see_feedback(self): @@ -188,4 +188,4 @@ class SubmissionFeedbackTestCase(SkillboxTestCase): self.student_submission.save() result = self._fetch_assignment_teacher(self.teacher2) - self.assertIsNone(result.get('data').get('assignment').get('submissions')[0].get('submissionFeedback')) + self.assertIsNone(result.data.get('assignment').get('submissions')[0].get('submissionFeedback')) diff --git a/server/assignments/tests/test_read_only.py b/server/assignments/tests/test_read_only.py index b85051fe..806db0f6 100644 --- a/server/assignments/tests/test_read_only.py +++ b/server/assignments/tests/test_read_only.py @@ -32,7 +32,7 @@ class AssignmentReadOnlyTestCase(SkillboxTestCase): } } result = self.get_client(self.student1).execute(UPDATE_ASSIGNMENT_MUTATION, variables=variables) - self.assertIsNotNone(result.get('errors')) + self.assertIsNotNone(result.errors) def test_share_assignment_fails(self): variables = { @@ -46,7 +46,7 @@ class AssignmentReadOnlyTestCase(SkillboxTestCase): } } result = self.get_client(self.student1).execute(UPDATE_ASSIGNMENT_MUTATION, variables=variables) - self.assertIsNotNone(result.get('errors')) + self.assertIsNotNone(result.errors) def test_edit_feedback_fails(self): student_submission = StudentSubmissionFactory(assignment=self.assignment, student=self.student1, @@ -62,7 +62,7 @@ class AssignmentReadOnlyTestCase(SkillboxTestCase): } }) - self.assertIsNotNone(result.get('errors')) + self.assertIsNotNone(result.errors) def test_share_feedback_fails(self): @@ -79,4 +79,4 @@ class AssignmentReadOnlyTestCase(SkillboxTestCase): } }) - self.assertIsNotNone(result.get('errors')) + self.assertIsNotNone(result.errors) diff --git a/server/basicknowledge/tests/test_instrument_types_query.py b/server/basicknowledge/tests/test_instrument_types_query.py index 1073b01e..25e28b44 100644 --- a/server/basicknowledge/tests/test_instrument_types_query.py +++ b/server/basicknowledge/tests/test_instrument_types_query.py @@ -26,6 +26,6 @@ class InstrumentTypesQueryTestCase(SkillboxTestCase): InstrumentFactory(new_type=second_type) def test_instrument_types_empty_not_returned(self): - result = self.get_client().get_result(INSTRUMENT_TYPES_QUERY) + result = self.get_client().execute(INSTRUMENT_TYPES_QUERY) self.assertIsNone(result.errors) self.assertEqual(len(result.data['instrumentTypes']), 2) diff --git a/server/books/migrations/0033_snapshot_title.py b/server/books/migrations/0033_snapshot_title.py new file mode 100644 index 00000000..59ce72d4 --- /dev/null +++ b/server/books/migrations/0033_snapshot_title.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.13 on 2022-05-24 19:47 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('books', '0032_auto_20211213_1342'), + ] + + operations = [ + migrations.AddField( + model_name='snapshot', + name='title', + field=models.CharField(blank=True, max_length=255, null=True), + ), + ] diff --git a/server/books/models/snapshot.py b/server/books/models/snapshot.py index e666d74b..cef603bb 100644 --- a/server/books/models/snapshot.py +++ b/server/books/models/snapshot.py @@ -119,6 +119,7 @@ class Snapshot(models.Model): 'objectives.Objective', related_name='hidden_for_snapshots' ) + title = models.CharField(max_length=255, blank=True, null=True) objects = SnapshotManager() diff --git a/server/books/schema/mutations/__init__.py b/server/books/schema/mutations/__init__.py index fab3c1e9..3b0c01e9 100644 --- a/server/books/schema/mutations/__init__.py +++ b/server/books/schema/mutations/__init__.py @@ -1,7 +1,7 @@ from books.schema.mutations.chapter import UpdateChapterVisibility from books.schema.mutations.contentblock import MutateContentBlock, AddContentBlock, DeleteContentBlock from books.schema.mutations.module import UpdateSolutionVisibility, UpdateLastModule, SyncModuleVisibility -from books.schema.mutations.snapshot import CreateSnapshot, ApplySnapshot, ShareSnapshot +from books.schema.mutations.snapshot import CreateSnapshot, ApplySnapshot, ShareSnapshot, UpdateSnapshot, DeleteSnapshot from books.schema.mutations.topic import UpdateLastTopic @@ -17,3 +17,5 @@ class BookMutations(object): create_snapshot = CreateSnapshot.Field() apply_snapshot = ApplySnapshot.Field() share_snapshot = ShareSnapshot.Field() + update_snapshot = UpdateSnapshot.Field() + delete_snapshot = DeleteSnapshot.Field() diff --git a/server/books/schema/mutations/snapshot.py b/server/books/schema/mutations/snapshot.py index 6380e622..c9164e61 100644 --- a/server/books/schema/mutations/snapshot.py +++ b/server/books/schema/mutations/snapshot.py @@ -1,7 +1,7 @@ import graphene -from django.db.models import Q from graphene import relay +from api.types import FailureNode, SuccessNode from api.utils import get_object from books.models import Module, ContentBlock, Chapter from books.models.snapshot import Snapshot @@ -9,6 +9,30 @@ from books.schema.nodes import SnapshotNode, ModuleNode from users.models import SchoolClass +class NotOwner(graphene.ObjectType): + class Meta: + interfaces = (FailureNode,) + + +class Success(graphene.ObjectType): + class Meta: + interfaces = (SuccessNode,) + + +NotOwnerFailure = NotOwner(reason="Not the owner") +DeleteSnapshotSuccess = Success(message='Snapshot deleted successfully') + + +class UpdateSnapshotResult(graphene.Union): + class Meta: + types = (SnapshotNode, NotOwner,) + + +class DeleteSnapshotResult(graphene.Union): + class Meta: + types = (Success, NotOwner,) + + class CreateSnapshot(relay.ClientIDMutation): class Input: module = graphene.String(required=True) @@ -30,6 +54,47 @@ class CreateSnapshot(relay.ClientIDMutation): return cls(snapshot=snapshot, success=True) +class UpdateSnapshot(relay.ClientIDMutation): + class Input: + id = graphene.ID(required=True) + title = graphene.String() + + snapshot = graphene.Field(UpdateSnapshotResult) + + @classmethod + def mutate_and_get_payload(cls, root, info, **args): + id = args.get('id') + title = args.get('title') + user = info.context.user + + snapshot = get_object(Snapshot, id) + if snapshot.creator != user: + return cls(snapshot=NotOwnerFailure) + if title is not None: + snapshot.title = title + snapshot.save() + return cls(snapshot=snapshot) + + +class DeleteSnapshot(relay.ClientIDMutation): + class Input: + id = graphene.ID(required=True) + + result = graphene.Field(DeleteSnapshotResult) + + @classmethod + def mutate_and_get_payload(cls, root, info, **args): + id = args.get('id') + user = info.context.user + + snapshot = get_object(Snapshot, id) + if snapshot.creator != user: + return cls(result=NotOwnerFailure) + + snapshot.delete() + return cls(result=DeleteSnapshotSuccess) + + class ApplySnapshot(relay.ClientIDMutation): class Input: snapshot = graphene.ID(required=True) diff --git a/server/books/schema/nodes/snapshot.py b/server/books/schema/nodes/snapshot.py index 53707b9e..a2cc998f 100644 --- a/server/books/schema/nodes/snapshot.py +++ b/server/books/schema/nodes/snapshot.py @@ -141,7 +141,7 @@ class SnapshotNode(DjangoObjectType): @staticmethod def resolve_title(parent: Snapshot, info, **kwargs): - return f'Snapshot {parent.id}' + return parent.title if parent.title is not None else f'Snapshot {parent.id}' @staticmethod def resolve_meta_title(parent, info, **kwargs): diff --git a/server/books/tests/queries.py b/server/books/tests/queries.py index 69df53cc..ccfc63a3 100644 --- a/server/books/tests/queries.py +++ b/server/books/tests/queries.py @@ -113,17 +113,6 @@ query SnapshotDetail($id: ID!) { } """ -SHARE_SNAPSHOT_MUTATION = """ -mutation ShareSnapshot($input: ShareSnapshotInput!) { - shareSnapshot(input: $input) { - success - snapshot { - shared - } - } -} -""" - MODULE_SNAPSHOTS_QUERY = """ query SnapshotQuery($slug: String!) { module(slug: $slug) { @@ -136,3 +125,46 @@ query SnapshotQuery($slug: String!) { } } """ + +SHARE_SNAPSHOT_MUTATION = """ +mutation ShareSnapshot($input: ShareSnapshotInput!) { + shareSnapshot(input: $input) { + success + snapshot { + shared + } + } +} +""" + +UPDATE_SNAPSHOT_MUTATION = """ +mutation UpdateSnapshot($input: UpdateSnapshotInput!) { + updateSnapshot(input: $input) { + snapshot { + ...on SnapshotNode { + title + id + } + ...on NotOwner { + reason + } + } + } +} +""" + +DELETE_SNAPSHOT_MUTATION = """ +mutation DeleteSnapshot($input: DeleteSnapshotInput!) { + deleteSnapshot(input: $input) { + result { + __typename + ...on NotOwner { + reason + } + ...on Success { + message + } + } + } +} +""" diff --git a/server/books/tests/test_404.py b/server/books/tests/test_404.py index 116bd2f8..66b0cc34 100644 --- a/server/books/tests/test_404.py +++ b/server/books/tests/test_404.py @@ -27,7 +27,7 @@ class ContentBlockTestCase(SkillboxTestCase): result = self.client.execute(TOPIC_QUERY, variables={ "slug": slug }) - self.assertIsNone(result.get('errors')) - topic = result.get('data').get('topic') + self.assertIsNone(result.errors) + topic = result.data.get('topic') self.assertEqual(topic.get('__typename'), 'NotFound') self.assertEqual(topic.get('reason'), 'Not Found') diff --git a/server/books/tests/test_content_blocks.py b/server/books/tests/test_content_blocks.py index d5dbb58a..bacb6546 100644 --- a/server/books/tests/test_content_blocks.py +++ b/server/books/tests/test_content_blocks.py @@ -36,8 +36,8 @@ class ContentBlockTestCase(SkillboxTestCase): result = self.client.execute(CONTENT_BLOCK_QUERY, variables={ "slug": self.slug }) - self.assertIsNone(result.get('errors')) - module = result.get('data').get('module') + self.assertIsNone(result.errors) + module = result.data.get('module') content_block = module['chapters'][0]['contentBlocks'][0] self.assertEqual(content_block['title'], 'Title') self.assertIsNotNone(content_block['type']) diff --git a/server/books/tests/test_own_content_blocks.py b/server/books/tests/test_own_content_blocks.py index 9a3f4c95..c743eaaa 100644 --- a/server/books/tests/test_own_content_blocks.py +++ b/server/books/tests/test_own_content_blocks.py @@ -45,26 +45,26 @@ class OwnContentTestCase(SkillboxTestCase): } } """ - result = self.client.execute(chapterQuery, variables={ + result = self.get_client().execute(chapterQuery, variables={ "id": self.chapter_id }) - self.assertIsNone(result.get('errors')) - self.assertEqual(len(result.get('data').get('chapter').get('contentBlocks')), 1) + self.assertIsNone(result.errors) + self.assertEqual(len(result.data.get('chapter').get('contentBlocks')), 1) custom_content_block = ContentBlock(title='own', slug='own', user_created=True, owner=self.user) self.chapter.specific.add_child(instance=custom_content_block) - result = self.client.execute(chapterQuery, variables={ + result = self.get_client().execute(chapterQuery, variables={ "id": self.chapter_id }) - self.assertEqual(len(result.get('data').get('chapter').get('contentBlocks')), 2) + self.assertEqual(len(result.data.get('chapter').get('contentBlocks')), 2) for school_class in self.user.school_classes.all(): custom_content_block.visible_for.add(school_class) - result = self.client.execute(chapterQuery, variables={ + result = self.get_client().execute(chapterQuery, variables={ "id": self.chapter_id }) - self.assertEqual(len(result.get('data').get('chapter').get('contentBlocks')), 2) + self.assertEqual(len(result.data.get('chapter').get('contentBlocks')), 2) def test_mutate_own_content_block(self): query = """ @@ -76,7 +76,7 @@ class OwnContentTestCase(SkillboxTestCase): } """ - res = self.get_client().get_result(query, variables={'id': self.content_block_id}) + res = self.get_client().execute(query, variables={'id': self.content_block_id}) self.assertIsNone(res.errors) self.assertEqual(res.data['contentBlock']['title'], 'bla') @@ -112,7 +112,7 @@ class OwnContentTestCase(SkillboxTestCase): } } - mutation_result = self.get_client().get_result(mutation, variables=variables) + mutation_result = self.get_client().execute(mutation, variables=variables) self.assertIsNone(mutation_result.errors) content_block = mutation_result.data['mutateContentBlock']['contentBlock'] self.assertEqual(content_block['title'], 'new title') @@ -137,7 +137,7 @@ class OwnContentTestCase(SkillboxTestCase): } } } - list_mutation_result = self.get_client().get_result(mutation, variables=other_variables) + list_mutation_result = self.get_client().execute(mutation, variables=other_variables) self.assertIsNone(list_mutation_result.errors) content_block = list_mutation_result.data['mutateContentBlock']['contentBlock'] self.assertEqual(content_block['title'], 'title for list content') diff --git a/server/books/tests/test_snapshots.py b/server/books/tests/test_snapshots.py index febabb84..aeda62c7 100644 --- a/server/books/tests/test_snapshots.py +++ b/server/books/tests/test_snapshots.py @@ -7,7 +7,7 @@ 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 + MODULE_SNAPSHOTS_QUERY, SHARE_SNAPSHOT_MUTATION, UPDATE_SNAPSHOT_MUTATION, DELETE_SNAPSHOT_MUTATION from core.tests.base_test import SkillboxTestCase from objectives.factories import ObjectiveGroupFactory, ObjectiveFactory from users.factories import SchoolClassFactory @@ -89,8 +89,8 @@ class CreateSnapshotTestCase(SkillboxTestCase): result = client.execute(MODULE_QUERY, variables={ 'slug': self.module.slug }) - self.assertIsNone(result.get('errors')) - module = result.get('data').get('module') + self.assertIsNone(result.errors) + module = result.data.get('module') chapter = module.get('chapters')[0] self.assertIsNotNone(chapter) content_blocks = chapter.get('contentBlocks') @@ -144,8 +144,8 @@ class CreateSnapshotTestCase(SkillboxTestCase): 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') + self.assertIsNone(result.errors) + snapshot = result.data.get('createSnapshot').get('snapshot') chapter = snapshot.get('chapters')[0] self.assertIsNotNone(snapshot.get('created')) @@ -206,7 +206,7 @@ class CreateSnapshotTestCase(SkillboxTestCase): 'selectedClass': to_global_id('SchoolClassNode', self.second_class.pk), } }) - self.assertIsNone(result.get('errors')) + self.assertIsNone(result.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) @@ -219,8 +219,8 @@ class CreateSnapshotTestCase(SkillboxTestCase): snapshot_result = self.client.execute(SNAPSHOT_MODULE_QUERY, variables={ 'id': id }) - self.assertIsNone(snapshot_result.get('errors')) - snapshot = snapshot_result.get('data').get('snapshot') + self.assertIsNone(snapshot_result.errors) + snapshot = snapshot_result.data.get('snapshot') chapters = snapshot.get('chapters') self.assertEqual(len(chapters), 2) chapter = chapters[0] @@ -259,8 +259,8 @@ class CreateSnapshotTestCase(SkillboxTestCase): 'selectedClass': to_global_id('SchoolClassNode', third_class.pk), } }) - self.assertIsNone(result.get('errors')) - snapshot_id = result['data']['createSnapshot']['snapshot']['id'] + self.assertIsNone(result.errors) + snapshot_id = result.data['createSnapshot']['snapshot']['id'] result = self.client.execute(APPLY_SNAPSHOT_MUTATION, variables={ 'input': { @@ -268,13 +268,13 @@ class CreateSnapshotTestCase(SkillboxTestCase): 'selectedClass': to_global_id('SchoolClassNode', self.skillbox_class.pk), } }) - self.assertIsNone(result.get('errors')) + self.assertIsNone(result.errors) result = self.client.execute(MODULE_QUERY, variables={ 'slug': self.module.slug }) - self.assertIsNone(result.get('errors')) - module = result['data']['module'] + self.assertIsNone(result.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']]) @@ -286,8 +286,8 @@ class CreateSnapshotTestCase(SkillboxTestCase): result = self.graphene_client.execute(MODULE_QUERY, variables={ 'slug': self.module.slug }) - self.assertIsNone(result.get('errors')) - chapter = result['data']['module']['chapters'][0] + self.assertIsNone(result.errors) + chapter = result.data['module']['chapters'][0] self.assertEqual(len(chapter['contentBlocks']), 4) result = self.graphene_client.execute(CREATE_SNAPSHOT_MUTATION, variables={ @@ -296,8 +296,8 @@ class CreateSnapshotTestCase(SkillboxTestCase): 'selectedClass': to_global_id('SchoolClassNode', self.skillbox_class.pk), } }) - self.assertIsNone(result.get('errors')) - snapshot_id = result['data']['createSnapshot']['snapshot']['id'] + self.assertIsNone(result.errors) + snapshot_id = result.data['createSnapshot']['snapshot']['id'] teacher2 = User.objects.get(username='teacher2') teacher2_client = self.get_client(user=teacher2) @@ -307,16 +307,15 @@ class CreateSnapshotTestCase(SkillboxTestCase): 'selectedClass': to_global_id('SchoolClassNode', self.second_class.pk), } }) - self.assertIsNone(result.get('errors')) + self.assertIsNone(result.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.assertIsNone(result.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': { @@ -335,10 +334,11 @@ class CreateSnapshotTestCase(SkillboxTestCase): 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.assertIsNone(a_result.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) + self.assertEqual(self.second_class_name in map(lambda x: x['name'], a_chapter['descriptionHiddenFor']), + hidden) assert_chapter_hidden(True) @@ -348,8 +348,8 @@ class CreateSnapshotTestCase(SkillboxTestCase): 'selectedClass': to_global_id('SchoolClassNode', self.skillbox_class.pk), } }) - self.assertIsNone(result.get('errors')) - snapshot = result['data']['createSnapshot']['snapshot'] + self.assertIsNone(result.errors) + snapshot = result.data['createSnapshot']['snapshot'] snapshot_id = snapshot['id'] chapter = snapshot['chapters'][0] self.assertEqual(chapter['titleHidden'], False) @@ -361,7 +361,7 @@ class CreateSnapshotTestCase(SkillboxTestCase): 'selectedClass': to_global_id('SchoolClassNode', self.second_class.pk), } }) - self.assertIsNone(result.get('errors')) + self.assertIsNone(result.errors) assert_chapter_hidden(False) @@ -384,8 +384,8 @@ class SnapshotTestCase(SkillboxTestCase): result = self.client.execute(MODULE_SNAPSHOTS_QUERY, variables={ "slug": self.slug }) - self.assertIsNone(result.get('errors')) - snapshots = result['data']['module']['snapshots'] + self.assertIsNone(result.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}') @@ -397,8 +397,8 @@ class SnapshotTestCase(SkillboxTestCase): 'shared': True } }) - self.assertIsNone(result.get('errors')) - data = result['data']['shareSnapshot'] + self.assertIsNone(result.errors) + data = result.data['shareSnapshot'] self.assertTrue(data['success']) self.assertTrue(data['snapshot']['shared']) snapshot = Snapshot.objects.get(pk=self.snapshot.pk) @@ -413,7 +413,7 @@ class SnapshotTestCase(SkillboxTestCase): 'shared': True } }) - self.assertIsNotNone(result.get('errors')) + self.assertIsNotNone(result.errors) def test_snapshot_without_creator(self): self.snapshot.creator = None @@ -422,5 +422,50 @@ class SnapshotTestCase(SkillboxTestCase): result = self.client.execute(MODULE_SNAPSHOTS_QUERY, variables={ "slug": self.slug }) - self.assertIsNone(result.get('errors')) - self.assertEqual(len(result.get('data').get('module').get('snapshots')), 1) + self.assertIsNone(result.errors) + self.assertEqual(len(result.data.get('module').get('snapshots')), 1) + + def _setup_title_change(self): + self.assertIsNone(self.snapshot.title) + new_title = 'New Snapshot Title' + result = self.get_client().execute(UPDATE_SNAPSHOT_MUTATION, variables={ + 'input': { + 'id': to_global_id('Snapshot', self.snapshot.id), + 'title': new_title + } + }) + return result + + def test_update_snapshot_title(self): + result = self._setup_title_change() + self.assertIsNone(result.errors) + self.assertEqual(result.data.get('updateSnapshot').get('snapshot').get('title'), 'New Snapshot Title') + + def test_update_snapshot_not_owner_fails(self): + self.snapshot.creator = self.teacher2 + self.snapshot.save() + result = self._setup_title_change() + self.assertIsNone(result.errors) + self.assertEqual(result.data.get('updateSnapshot').get('snapshot').get('reason'), 'Not the owner') + + def test_delete_snapshot(self): + result = self.get_client().execute(DELETE_SNAPSHOT_MUTATION, variables={ + 'input': { + 'id': to_global_id('Snapshot', self.snapshot.id), + } + }) + self.assertIsNone(result.errors) + self.assertEqual(result.data.get('deleteSnapshot').get('result').get('__typename'), 'Success') + + def test_delete_snapshot_not_owner_fails(self): + self.snapshot.creator = self.teacher2 + self.snapshot.save() + result = self.get_client().execute(DELETE_SNAPSHOT_MUTATION, variables={ + 'input': { + 'id': to_global_id('Snapshot', self.snapshot.id), + } + }) + self.assertIsNone(result.errors) + result = result.data.get('deleteSnapshot').get('result') + self.assertEqual(result.get('__typename'), 'NotOwner') + self.assertEqual(result.get('reason'), 'Not the owner') diff --git a/server/core/tests/base_test.py b/server/core/tests/base_test.py index 0f349f81..5697ed98 100644 --- a/server/core/tests/base_test.py +++ b/server/core/tests/base_test.py @@ -10,6 +10,10 @@ class GQLClient(Client): def get_result(self, *args, **kwargs): return GQLResult(self.execute(*args, **kwargs)) + def execute(self, *args, **kwargs): + res = super(GQLClient, self).execute(*args, **kwargs) + return GQLResult(res) + class SkillboxTestCase(TestCase): diff --git a/server/portfolio/tests/test_project_mutations.py b/server/portfolio/tests/test_project_mutations.py index 00417afb..d7aef96b 100644 --- a/server/portfolio/tests/test_project_mutations.py +++ b/server/portfolio/tests/test_project_mutations.py @@ -87,7 +87,7 @@ mutation UpdateProjectMutation($input: UpdateProjectInput!){ 'title': 'BAD! THIS IS BAD!' } } - result = self.get_client(self.student2).get_result(mutation, variables={ + result = self.get_client(self.student2).execute(mutation, variables={ 'input': input }) self.assertIsNotNone(result.errors) @@ -115,7 +115,7 @@ mutation UpdateProjectMutation($input: UpdateProjectInput!){ 'title': 'Good! THIS IS good!' } } - result = self.get_client(self.student).get_result(mutation, variables={ + result = self.get_client(self.student).execute(mutation, variables={ 'input': input }) self.assertIsNone(result.errors) diff --git a/server/portfolio/tests/test_project_query.py b/server/portfolio/tests/test_project_query.py index 9c66de0c..64908be3 100644 --- a/server/portfolio/tests/test_project_query.py +++ b/server/portfolio/tests/test_project_query.py @@ -17,7 +17,7 @@ query ProjectQuery($id: ID!) { class ProjectQueryTestCase(SkillboxTestCase): def _test_direct_project_access(self, user: User, should_have_access: bool): - result = self.get_client(user).get_result(project_query, variables={ + result = self.get_client(user).execute(project_query, variables={ 'id': self.project1.graphql_id }) self.assertIsNone(result.errors) @@ -57,22 +57,22 @@ class ProjectQueryTestCase(SkillboxTestCase): result = self.get_client(self.student1).execute(self.query) - self.assertIsNone(result.get('errors')) - self.assertEqual(result.get('data').get('projects')[0].get('title'), self.project1.title) + self.assertIsNone(result.errors) + self.assertEqual(result.data.get('projects')[0].get('title'), self.project1.title) def test_should_not_see_other_projects(self): self.assertEqual(Project.objects.count(), 1) result = self.get_client(self.student2).execute(self.query) - self.assertIsNone(result.get('errors')) - self.assertEqual(len(result.get('data').get('projects')), 0) + self.assertIsNone(result.errors) + self.assertEqual(len(result.data.get('projects')), 0) def test_teacher_should_not_see_unfinished_projects(self): result = self.get_client().execute(self.query) - self.assertIsNone(result.get('errors')) - self.assertEqual(len(result.get('data').get('projects')), 0) + self.assertIsNone(result.errors) + self.assertEqual(len(result.data.get('projects')), 0) def test_teacher_should_only_see_finished_projects(self): self.project1.final = True @@ -81,8 +81,8 @@ class ProjectQueryTestCase(SkillboxTestCase): result = self.get_client().execute(self.query) - self.assertIsNone(result.get('errors')) - self.assertEqual(result.get('data').get('projects')[0].get('title'), + self.assertIsNone(result.errors) + self.assertEqual(result.data.get('projects')[0].get('title'), self.project1.title) def test_other_teacher_should_not_see_projects(self): @@ -92,8 +92,8 @@ class ProjectQueryTestCase(SkillboxTestCase): result = self.get_client(self.teacher2).execute(self.query) - self.assertIsNone(result.get('errors')) - self.assertEqual(len(result.get('data').get('projects')), 0) + self.assertIsNone(result.errors) + self.assertEqual(len(result.data.get('projects')), 0) def test_direct_project_access(self): # student can access own project directly @@ -128,7 +128,7 @@ query ProjectQuery($id: ID!) { } } """ - result = self.get_client(self.student1).get_result(query, variables={ + result = self.get_client(self.student1).execute(query, variables={ 'id': self.project1.graphql_id }) self.assertEqual(result.data['project']['student']['email'], self.student1.email) diff --git a/server/rooms/tests/test_comments.py b/server/rooms/tests/test_comments.py index c87f1380..3f42034c 100644 --- a/server/rooms/tests/test_comments.py +++ b/server/rooms/tests/test_comments.py @@ -33,7 +33,7 @@ mutation AddComment($input: AddCommentInput!) { 'comment': self.text } }) - self.assertIsNone(result.get('errors')) + self.assertIsNone(result.errors) self.assertEqual(room_entry.comments.count(), 1) comment = room_entry.comments.first() self.assertEqual(comment.text, self.text) @@ -53,8 +53,8 @@ query CommentsQuery($id: ID!) { } """ result = self.get_client().execute(query, variables={"id": self.room_entry_id}) - self.assertIsNone(result.get('errors')) - comment_node = result.get('data').get('roomEntry').get('comments')[0] + self.assertIsNone(result.errors) + comment_node = result.data.get('roomEntry').get('comments')[0] self.assertEqual(comment_node['text'], self.text) def test_get_comment_for_other_user(self): @@ -70,6 +70,5 @@ query CommentsQuery($id: ID!) { """ result = self.get_client(self.student_second_class).execute(query, variables={"id": self.room_entry_id}) - gql_result = GQLResult(result) - self.assertIsNone(gql_result.errors) - self.assertIsNone(gql_result.data.get('roomEntry')) + self.assertIsNone(result.errors) + self.assertIsNone(result.data.get('roomEntry')) diff --git a/server/rooms/tests/test_new_room_mutation.py b/server/rooms/tests/test_new_room_mutation.py index 7ed22dba..08e712c1 100644 --- a/server/rooms/tests/test_new_room_mutation.py +++ b/server/rooms/tests/test_new_room_mutation.py @@ -55,7 +55,7 @@ class NewRoomMutationTestCase(SkillboxTestCase): self.assertEqual(Room.objects.count(), 0) title = 'some title' appearance = 'blue' - res = self.get_client().execute(self.mutation, variables={ + result = self.get_client().execute(self.mutation, variables={ 'input': { 'room': { 'title': title, @@ -64,7 +64,6 @@ class NewRoomMutationTestCase(SkillboxTestCase): } } }) - result = GQLResult(res) self.assertIsNone(result.errors) room = GQLRoom(result.data.get('addRoom').get('room')) self.assertEqual(room.title, title) @@ -75,7 +74,7 @@ class NewRoomMutationTestCase(SkillboxTestCase): def test_create_new_room_for_other_school_class(self): self.assertEqual(Room.objects.count(), 0) - result = self.get_client(self.teacher2).get_result(self.mutation, variables={ + result = self.get_client(self.teacher2).execute(self.mutation, variables={ 'input': { 'room': { 'title': 'BIG NO NO!', diff --git a/server/rooms/tests/test_room_entry_mutations.py b/server/rooms/tests/test_room_entry_mutations.py index 76128b68..aaa5a00f 100644 --- a/server/rooms/tests/test_room_entry_mutations.py +++ b/server/rooms/tests/test_room_entry_mutations.py @@ -175,7 +175,7 @@ mutation AddRoomEntry($input: AddRoomEntryInput!){ 'room': self.room.graphql_id } - result = self.get_client(self.yet_another_user).get_result(mutation, variables={ + result = self.get_client(self.yet_another_user).execute(mutation, variables={ 'input': { 'roomEntry': room_entry } diff --git a/server/rooms/tests/test_room_visibility_mutation.py b/server/rooms/tests/test_room_visibility_mutation.py index 7bc40e27..b3f2ce2c 100644 --- a/server/rooms/tests/test_room_visibility_mutation.py +++ b/server/rooms/tests/test_room_visibility_mutation.py @@ -30,41 +30,37 @@ query RoomQuery ($slug: String!) { """ def test_restricted_query(self): - res = self.get_client().execute(self.query, variables={ + result = self.get_client().execute(self.query, variables={ 'slug': self.room.slug }) - result = GQLResult(res) self.assertIsNone(result.errors) self.assertFalse(result.data.get('room').get('restricted')) def test_successful_mutation(self): - res = self.get_client().execute(self.mutation, variables={ + result = self.get_client().execute(self.mutation, variables={ 'input': { 'id': self.room_id, 'restricted': True } }) - result = GQLResult(res) self.assertIsNone(result.errors) self.assertEqual(result.data.get('updateRoomVisibility').get('room').get('restricted'), True) def test_permission_denied(self): - res = self.get_client(self.student1).execute(self.mutation, variables={ + result = self.get_client(self.student1).execute(self.mutation, variables={ 'input': { 'id': self.room_id, 'restricted': True } }) - result = GQLResult(res) self.assertIsNotNone(result.errors) - res = self.get_client(self.teacher2).execute(self.mutation, variables={ + result = self.get_client(self.teacher2).execute(self.mutation, variables={ 'input': { 'id': self.room_id, 'restricted': True } }) - result = GQLResult(res) self.assertIsNotNone(result.errors) diff --git a/server/rooms/tests/test_room_visibility_query.py b/server/rooms/tests/test_room_visibility_query.py index 97b7be0d..ca1c49cf 100644 --- a/server/rooms/tests/test_room_visibility_query.py +++ b/server/rooms/tests/test_room_visibility_query.py @@ -34,10 +34,9 @@ query RoomQuery ($slug: String!) { """ def _test_room(self, user, length, restricted=True): - res = self.get_client(user).execute(self.query, variables={ + result = self.get_client(user).execute(self.query, variables={ 'slug': self.room.slug }) - result = GQLResult(res) self.assertIsNone(result.errors) room = GQLRoom(result.data.get('room')) self.assertEqual(room.restricted, restricted) diff --git a/server/schema.graphql b/server/schema.graphql index a42045bb..4b3f0434 100644 --- a/server/schema.graphql +++ b/server/schema.graphql @@ -444,6 +444,18 @@ type DeleteRoomPayload { clientMutationId: String } +input DeleteSnapshotInput { + id: ID! + clientMutationId: String +} + +type DeleteSnapshotPayload { + result: DeleteSnapshotResult + clientMutationId: String +} + +union DeleteSnapshotResult = Success | NotOwner + type DjangoDebug { sql: [DjangoDebugSQL] } @@ -469,6 +481,10 @@ type DuplicateName { reason: String } +interface FailureNode { + reason: String +} + type FieldError { code: String } @@ -683,6 +699,8 @@ type Mutation { createSnapshot(input: CreateSnapshotInput!): CreateSnapshotPayload applySnapshot(input: ApplySnapshotInput!): ApplySnapshotPayload shareSnapshot(input: ShareSnapshotInput!): ShareSnapshotPayload + updateSnapshot(input: UpdateSnapshotInput!): UpdateSnapshotPayload + deleteSnapshot(input: DeleteSnapshotInput!): DeleteSnapshotPayload _debug: DjangoDebug } @@ -706,6 +724,10 @@ type NotFound { reason: String } +type NotOwner implements FailureNode { + reason: String +} + type NoteNode implements Node { id: ID! text: String! @@ -1065,6 +1087,14 @@ type SubmissionFeedbackNode implements Node { id: ID! } +type Success implements SuccessNode { + message: String +} + +interface SuccessNode { + message: String +} + type SurveyNode implements Node { id: ID! title: String! @@ -1431,6 +1461,19 @@ type UpdateSettingPayload { clientMutationId: String } +input UpdateSnapshotInput { + id: ID! + title: String + clientMutationId: String +} + +type UpdateSnapshotPayload { + snapshot: UpdateSnapshotResult + clientMutationId: String +} + +union UpdateSnapshotResult = SnapshotNode | NotOwner + input UpdateSolutionVisibilityInput { slug: String enabled: Boolean diff --git a/server/users/tests/test_school_classes.py b/server/users/tests/test_school_classes.py index 79d686eb..1dad68e0 100644 --- a/server/users/tests/test_school_classes.py +++ b/server/users/tests/test_school_classes.py @@ -62,13 +62,13 @@ class ModifySchoolClassTest(SkillboxTestCase): 'name': class_name } }) - self.assertIsNone(result.get('errors')) + self.assertIsNone(result.errors) school_class = get_object(SchoolClass, id) self.assertEqual(school_class.name, class_name) def test_update_school_class_not_in_class_fails(self): - client = Client(schema=schema) + client = self.get_client() teacher = TeacherFactory(username='conan') context = Context(user=teacher) school_class = SchoolClass.objects.get(name='skillbox') @@ -81,7 +81,7 @@ class ModifySchoolClassTest(SkillboxTestCase): } } result = client.execute(UPDATE_SCHOOL_CLASS_MUTATION, variables=variables, context=context) - self.assertIsNone(result.get('errors')) + self.assertIsNone(result.errors) def test_update_school_class_fail(self): class_name = 'Nanana' @@ -96,7 +96,7 @@ class ModifySchoolClassTest(SkillboxTestCase): 'name': class_name } }) - self.assertIsNotNone(result.get('errors')) + self.assertIsNotNone(result.errors) def test_create_school_class(self): self.assertEqual(SchoolClass.objects.count(), 2) @@ -107,8 +107,8 @@ class ModifySchoolClassTest(SkillboxTestCase): 'name': class_name } }) - self.assertIsNone(query_result.get('errors')) - result = query_result.get('data').get('createSchoolClass').get('result') + self.assertIsNone(query_result.errors) + result = query_result.data.get('createSchoolClass').get('result') self.assertEqual(result.get('__typename'), 'SchoolClassNode') id = result.get('id') self.assertEqual(SchoolClass.objects.count(), 3) @@ -129,8 +129,8 @@ class ModifySchoolClassTest(SkillboxTestCase): 'name': class_name } }) - self.assertIsNone(query_result.get('errors')) - result = query_result.get('data').get('createSchoolClass').get('result') + self.assertIsNone(query_result.errors) + result = query_result.data.get('createSchoolClass').get('result') self.assertEqual(result.get('__typename'), 'DuplicateName') reason = result.get('reason') self.assertEqual(reason, 'Dieser Name wird bereits verwendet.') @@ -144,4 +144,4 @@ class ModifySchoolClassTest(SkillboxTestCase): 'name': 'No School' } }) - self.assertIsNotNone(result.get('errors')) + self.assertIsNotNone(result.errors) diff --git a/server/users/tests/test_usersettings.py b/server/users/tests/test_usersettings.py index c262027f..eec90e00 100644 --- a/server/users/tests/test_usersettings.py +++ b/server/users/tests/test_usersettings.py @@ -50,17 +50,17 @@ class UserSettingTests(SkillboxTestCase): def test_selects_first_class_on_first_call(self): result = self.make_query() first_class = self.user.school_classes.first() - self.assertIsNone(result.get('errors')) - self.assertEqual(result.get('data').get('me').get('selectedClass').get('name'), first_class.name) - self.assertFalse(result.get('data').get('me').get('selectedClass').get('readOnly')) + self.assertIsNone(result.errors) + self.assertEqual(result.data.get('me').get('selectedClass').get('name'), first_class.name) + self.assertFalse(result.data.get('me').get('selectedClass').get('readOnly')) def test_returns_selected_class(self): selected_class = self.user.school_classes.all()[1] setting = UserSetting.objects.create(user=self.user, selected_class=selected_class) setting.save() result = self.make_query() - self.assertIsNone(result.get('errors')) - self.assertEqual(result.get('data').get('me').get('selectedClass').get('name'), + self.assertIsNone(result.errors) + self.assertEqual(result.data.get('me').get('selectedClass').get('name'), selected_class.name) def test_user_can_select_class(self): @@ -70,19 +70,19 @@ class UserSettingTests(SkillboxTestCase): selected_class = self.user.school_classes.all()[1] mutation_result = self.make_mutation(selected_class.pk) - self.assertIsNone(mutation_result.get('errors')) + self.assertIsNone(mutation_result.errors) query_result = self.make_query() - self.assertIsNone(query_result.get('errors')) - self.assertEqual(query_result.get('data').get('me').get('selectedClass').get('name'), + self.assertIsNone(query_result.errors) + self.assertEqual(query_result.data.get('me').get('selectedClass').get('name'), selected_class.name) def test_user_can_select_class_even_no_settings_exist(self): selected_class = self.user.school_classes.all()[1] mutation_result = self.make_mutation(selected_class.pk) - self.assertIsNone(mutation_result.get('errors')) + self.assertIsNone(mutation_result.errors) query_result = self.make_query() - self.assertIsNone(query_result.get('errors')) - self.assertEqual(query_result.get('data').get('me').get('selectedClass').get('name'), + self.assertIsNone(query_result.errors) + self.assertEqual(query_result.data.get('me').get('selectedClass').get('name'), selected_class.name) @@ -93,7 +93,7 @@ class UserSettingTests(SkillboxTestCase): setting.save() mutation_result = self.make_mutation(self.class3.pk) - self.assertIsNotNone(mutation_result.get('errors')) + self.assertIsNotNone(mutation_result.errors) def test_inactive_class_as_selected_class(self): selected_class = self.class2 @@ -106,6 +106,6 @@ class UserSettingTests(SkillboxTestCase): setting.save() result = self.make_query() - self.assertIsNone(result.get('errors')) - self.assertTrue(result.get('data').get('me').get('selectedClass').get('readOnly')) + self.assertIsNone(result.errors) + self.assertTrue(result.data.get('me').get('selectedClass').get('readOnly'))