diff --git a/redaktions-app/src/backend/mutations/category.ts b/redaktions-app/src/backend/mutations/category.ts index 5628e65..d5b9ca2 100644 --- a/redaktions-app/src/backend/mutations/category.ts +++ b/redaktions-app/src/backend/mutations/category.ts @@ -13,7 +13,10 @@ export const EDIT_CATEGORY = gql` ` export interface EditCategoryResponse { - updateCategory?: BasicCategoryResponse + updateCategory?: { + category: BasicCategoryResponse, + __typename: "UpdateCategoryPayload", + } } export interface EditCategoryVariables { @@ -35,7 +38,8 @@ export const ADD_CATEGORY = gql` export interface AddCategoryResponse { createCategory?: { - category: BasicCategoryResponse + category: BasicCategoryResponse, + __typename: "CreateCategoryPayload", } } @@ -57,7 +61,8 @@ export const DELETE_CATEGORY = gql` export interface DeleteCategoryResponse { deleteCategory?: { - category: BasicCategoryResponse + category: BasicCategoryResponse, + __typename: "DeleteCategoryPayload", } } diff --git a/redaktions-app/src/backend/mutations/question.mock.ts b/redaktions-app/src/backend/mutations/question.mock.ts index 884f993..6250b8c 100644 --- a/redaktions-app/src/backend/mutations/question.mock.ts +++ b/redaktions-app/src/backend/mutations/question.mock.ts @@ -1,22 +1,41 @@ import {MockedResponse} from "@apollo/client/testing"; -import {LoginResponse} from "./login"; -import {EDIT_QUESTION} from "./question"; +import {EDIT_QUESTION, EditQuestionResponse, EditQuestionVariables} from "./question"; +import {BasicQuestionResponse} from "../queries/question"; -export const loginMock: Array> = [ + +const editQuestionVariables: EditQuestionVariables = { + id: 'q1', + title: 'New title for Question 1?', + description: 'Further information for Q1', + categoryRowId: 1, +}; + +const editedQuestionMock: BasicQuestionResponse = { + id: editQuestionVariables.id, + title: editQuestionVariables.title as string, + description: editQuestionVariables.description as string, + categoryByCategoryRowId: { + id: "c1", + rowId: editQuestionVariables.categoryRowId as number, + title: "Category 1", + __typename: "Category" + }, + __typename: "Question" +} + +export const editQuestionMock: Array> = [ { request: { query: EDIT_QUESTION, - variables: { - email: "test@email.com", - password: "password", - } + variables: editQuestionVariables, }, result: { - errors: [ - // { - // message: "Authorization header is not of the correct bearer scheme format." - // } - ] + data: { + updateQuestion: { + question: editedQuestionMock, + __typename: "UpdateQuestionPayload", + } + } }, }, ] diff --git a/redaktions-app/src/backend/mutations/question.ts b/redaktions-app/src/backend/mutations/question.ts index 4620682..0c2210e 100644 --- a/redaktions-app/src/backend/mutations/question.ts +++ b/redaktions-app/src/backend/mutations/question.ts @@ -13,7 +13,10 @@ export const EDIT_QUESTION = gql` ` export interface EditQuestionResponse { - updateQuestion?: BasicQuestionResponse + updateQuestion?: { + question: BasicQuestionResponse, + __typename: "UpdateQuestionPayload", + }, } export interface EditQuestionVariables { @@ -36,7 +39,8 @@ export const ADD_QUESTION = gql` export interface AddQuestionResponse { createQuestion?: { - question: BasicQuestionResponse + question: BasicQuestionResponse, + __typename: "CreateQuestionPayload", } } @@ -60,6 +64,7 @@ export const DELETE_QUESTION = gql` export interface DeleteQuestionResponse { deleteQuestion?: { question: BasicQuestionResponse + __typename: "DeleteQuestionPayload", } } diff --git a/redaktions-app/src/backend/queries/category.mock.ts b/redaktions-app/src/backend/queries/category.mock.ts new file mode 100644 index 0000000..74f5c74 --- /dev/null +++ b/redaktions-app/src/backend/queries/category.mock.ts @@ -0,0 +1,35 @@ +import {MockedResponse} from "@apollo/client/testing"; +import {BasicCategoryResponse, GET_ALL_CATEGORIES, GetAllCategoriesResponse} from "./category"; + + +const categoryNodesMock: Array = [ + { + id: "c1", + rowId: 1, + title: "Category 1", + description: "Further information for Q1", + __typename: "Category" + }, { + id: "c2", + rowId: 2, + title: "Category 2", + description: "Further information for Q2", + __typename: "Category" + }]; + +export const getAllCategoriesMock: Array> = [ + { + request: { + query: GET_ALL_CATEGORIES, + }, + result: { + data: { + allCategories: { + nodes: categoryNodesMock, + __typename: "CategoriesConnection", + }, + }, + }, + }, +] + diff --git a/redaktions-app/src/backend/queries/category.ts b/redaktions-app/src/backend/queries/category.ts index 604d462..6c6dbfe 100644 --- a/redaktions-app/src/backend/queries/category.ts +++ b/redaktions-app/src/backend/queries/category.ts @@ -1,19 +1,20 @@ import {gql} from "@apollo/client"; export const BasicCategoryFragment = gql` - fragment BasicCategoryFragment on Category { - id - rowId - title - description - } - ` + fragment BasicCategoryFragment on Category { + id + rowId + title + description + } +` export interface BasicCategoryResponse { id: string, rowId: number, title: string, description: string | null, + __typename: "Category", } export const GET_ALL_CATEGORIES = gql` @@ -29,7 +30,8 @@ export const GET_ALL_CATEGORIES = gql` export interface GetAllCategoriesResponse { allCategories: { - nodes: Array + nodes: Array, + __typename: "CategoriesConnection", } } diff --git a/redaktions-app/src/backend/queries/question.mock.ts b/redaktions-app/src/backend/queries/question.mock.ts new file mode 100644 index 0000000..ee656d7 --- /dev/null +++ b/redaktions-app/src/backend/queries/question.mock.ts @@ -0,0 +1,48 @@ +import {MockedResponse} from "@apollo/client/testing"; +import {BasicQuestionResponse, GET_ALL_QUESTIONS, GetAllQuestionsResponse} from "./question"; + + +export const questionNodesMock: Array = [{ + id: "q1", + title: "Question 1?", + description: "Further information for Q1", + categoryByCategoryRowId: { + id: "c1", + rowId: 1, + title: "Category 1", + __typename: "Category" + }, + __typename: "Question", +}, + { + id: "q2", + title: "Question 2?", + description: "Further information for Q2", + categoryByCategoryRowId: null, + __typename: "Question", + }, + { + id: "q3", + title: "Question 3?", + description: null, + categoryByCategoryRowId: null, + __typename: "Question", + } +]; + +export const getAllQuestionsMock: Array> = [ + { + request: { + query: GET_ALL_QUESTIONS, + }, + result: { + data: { + allQuestions: { + nodes: questionNodesMock, + __typename: "QuestionsConnection", + } + } + }, + }, +] + diff --git a/redaktions-app/src/backend/queries/question.ts b/redaktions-app/src/backend/queries/question.ts index 7013273..f9e842e 100644 --- a/redaktions-app/src/backend/queries/question.ts +++ b/redaktions-app/src/backend/queries/question.ts @@ -12,6 +12,7 @@ interface GetQuestionsCategoryResponse { id: string, rowId: number, title: string, + __typename: "Category", } export const BasicQuestionFragment = gql` @@ -30,7 +31,8 @@ export interface BasicQuestionResponse { id: string, title: string, description: string | null, - categoryByCategoryRowId: GetQuestionsCategoryResponse | null + categoryByCategoryRowId: GetQuestionsCategoryResponse | null, + __typename: "Question", } export const GET_ALL_QUESTIONS = gql` @@ -46,6 +48,7 @@ export const GET_ALL_QUESTIONS = gql` export interface GetAllQuestionsResponse { allQuestions: { - nodes: Array + nodes: Array, + __typename: "QuestionsConnection", } } diff --git a/redaktions-app/src/components/AccordionWithEdit.tsx b/redaktions-app/src/components/AccordionWithEdit.tsx index 6fa8bc5..0cfb477 100644 --- a/redaktions-app/src/components/AccordionWithEdit.tsx +++ b/redaktions-app/src/components/AccordionWithEdit.tsx @@ -65,11 +65,11 @@ export default function AccordionWithEdit(props: AccordionWithEditProps) { - - + + - - + + diff --git a/redaktions-app/src/integration-tests/edit-question.integration.test.tsx b/redaktions-app/src/integration-tests/edit-question.integration.test.tsx new file mode 100644 index 0000000..2828345 --- /dev/null +++ b/redaktions-app/src/integration-tests/edit-question.integration.test.tsx @@ -0,0 +1,107 @@ +import React from 'react'; +import {fireEvent, queryAllByRole, render, screen, waitFor} from '@testing-library/react' +import {MockedProvider} from '@apollo/client/testing'; +import {MemoryRouter} from 'react-router-dom'; +import QuestionList from "../components/QuestionList"; +import {SnackbarProvider} from "notistack"; +import {getAllQuestionsMock, questionNodesMock} from "../backend/queries/question.mock"; +import {getAllCategoriesMock} from "../backend/queries/category.mock"; +import {editQuestionMock} from "../backend/mutations/question.mock"; + + +function renderQuestionList() { + return render( + + + + + + + + ); +} + +const getInitialQuestionCards = async (): Promise> => { + const numberOfQuestionsInMockQuery = questionNodesMock.length; + let questionCards: Array = []; + await waitFor(() => { + questionCards = screen.queryAllByRole("button", {name: /Question [1-3]\?/}) + expect(questionCards.length).toEqual(numberOfQuestionsInMockQuery); + }); + return questionCards; +} + +// sorry, I found no better way to find a specific icon button... +const queryAllEditIconsButtons = (container?: HTMLElement): Array => { + return (container ? queryAllByRole(container, "button") : screen.queryAllByRole("button")) + .filter(button => button.innerHTML.includes("svg") && button.innerHTML.includes("Anpassen")); +} + +describe('The QuestionList', () => { + test('displays the existing questions, but not the details of it', async () => { + renderQuestionList(); + + const questionCards = await getInitialQuestionCards() + questionCards.forEach(card => { + expect(card.innerHTML).toMatch(/Question [1-3]\?/) + }) + expect(questionCards[0].innerHTML).toMatch(/Category 1/); + expect(queryAllEditIconsButtons()).toHaveLength(0) + }); + + test('enables toggling details on each question', async () => { + renderQuestionList(); + + // Initial state: Every question card is not expanded + const questionCards = await getInitialQuestionCards() + expect(queryAllEditIconsButtons()).toHaveLength(0) + + // Expand first question card + fireEvent.click(questionCards[0]) + await waitFor(() => { + expect(queryAllEditIconsButtons()).toHaveLength(1) + }); + + // Shrink first question card again + fireEvent.click(questionCards[0]) + await waitFor(() => { + expect(queryAllEditIconsButtons()).toHaveLength(0) + }); + }); + + test('enables editing a question title', async () => { + renderQuestionList(); + + const questionCards = await getInitialQuestionCards(); + + // Expand first card + fireEvent.click(questionCards[0]); + let editIcons: Array = []; + await waitFor(() => { + editIcons = queryAllEditIconsButtons(); + expect(editIcons).toHaveLength(1); + }) + + // open edit dialog + expect(screen.queryByText(/Frage bearbeiten/)).toBeNull(); + fireEvent.click(editIcons[0]); + await waitFor(() => { + expect(screen.queryByText(/Frage bearbeiten/)).not.toBeNull(); + }) + + // change question title + const questionTitleField = screen.getByDisplayValue(/Question 1/); + fireEvent.change(questionTitleField, {target: {value: "New title for Question 1?"}}); + await waitFor(() => { + expect(screen.queryByDisplayValue(/New title for /)).not.toBeNull(); + }) + const confirmButton = screen.getByRole("button", {name: /Speichern/}); + + // call backend and assert apollo cache update + fireEvent.click(confirmButton); + await waitFor(() => { + expect(screen.queryByText(/Frage bearbeiten/)).toBeNull(); + expect(screen.queryByText(/New title for Question 1/)).not.toBeNull() + }) + }); +});