#11 Add integration test for editing a question

Also:
* introduce __typename to all response types
This commit is contained in:
Christoph Lienhard 2020-12-30 01:05:39 +01:00
parent a07eb576eb
commit 587c0cdeba
Signed by: christoph.lienhard
GPG Key ID: 6B98870DDC270884
9 changed files with 255 additions and 31 deletions

View File

@ -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",
}
}

View File

@ -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<MockedResponse<LoginResponse>> = [
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<MockedResponse<EditQuestionResponse>> = [
{
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",
}
}
},
},
]

View File

@ -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",
}
}

View File

@ -0,0 +1,35 @@
import {MockedResponse} from "@apollo/client/testing";
import {BasicCategoryResponse, GET_ALL_CATEGORIES, GetAllCategoriesResponse} from "./category";
const categoryNodesMock: Array<BasicCategoryResponse> = [
{
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<MockedResponse<GetAllCategoriesResponse>> = [
{
request: {
query: GET_ALL_CATEGORIES,
},
result: {
data: {
allCategories: {
nodes: categoryNodesMock,
__typename: "CategoriesConnection",
},
},
},
},
]

View File

@ -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<BasicCategoryResponse>
nodes: Array<BasicCategoryResponse>,
__typename: "CategoriesConnection",
}
}

View File

@ -0,0 +1,48 @@
import {MockedResponse} from "@apollo/client/testing";
import {BasicQuestionResponse, GET_ALL_QUESTIONS, GetAllQuestionsResponse} from "./question";
export const questionNodesMock: Array<BasicQuestionResponse> = [{
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<MockedResponse<GetAllQuestionsResponse>> = [
{
request: {
query: GET_ALL_QUESTIONS,
},
result: {
data: {
allQuestions: {
nodes: questionNodesMock,
__typename: "QuestionsConnection",
}
}
},
},
]

View File

@ -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<BasicQuestionResponse>
nodes: Array<BasicQuestionResponse>,
__typename: "QuestionsConnection",
}
}

View File

@ -65,11 +65,11 @@ export default function AccordionWithEdit(props: AccordionWithEditProps) {
</AccordionDetails>
<Divider/>
<AccordionActions>
<IconButton size={"small"} aria-label="edit" onClick={props.onEditButtonClick}>
<EditIcon/>
<IconButton data-testid="edit-icon-button" size={"small"} aria-label="edit" onClick={props.onEditButtonClick}>
<EditIcon titleAccess="Anpassen"/>
</IconButton>
<IconButton size={"small"} aria-label="delete" onClick={props.onDeleteButtonClick}>
<DeleteIcon/>
<IconButton data-testid="delete-icon-button" size={"small"} aria-label="delete" onClick={props.onDeleteButtonClick}>
<DeleteIcon titleAccess="Löschen"/>
</IconButton>
</AccordionActions>
</Accordion>

View File

@ -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(
<MockedProvider mocks={[...getAllQuestionsMock, ...getAllCategoriesMock, ...editQuestionMock]}>
<MemoryRouter>
<SnackbarProvider>
<QuestionList/>
</SnackbarProvider>
</MemoryRouter>
</MockedProvider>
);
}
const getInitialQuestionCards = async (): Promise<Array<HTMLElement>> => {
const numberOfQuestionsInMockQuery = questionNodesMock.length;
let questionCards: Array<HTMLElement> = [];
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<HTMLElement> => {
return (container ? queryAllByRole(container, "button") : screen.queryAllByRole("button"))
.filter(button => button.innerHTML.includes("svg") && button.innerHTML.includes("<title>Anpassen</title>"));
}
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<HTMLElement> = [];
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()
})
});
});