kandimat/redaktions-app/src/integration-tests/answer-list.integration.test.tsx
2021-06-13 12:55:54 +02:00

253 lines
7.6 KiB
TypeScript

import React from "react";
import {
findByRole,
findByText,
fireEvent,
render,
screen,
waitFor,
} from "@testing-library/react";
import { MockedProvider, MockedResponse } from "@apollo/client/testing";
import { MemoryRouter } from "react-router-dom";
import { SnackbarProvider } from "notistack";
import {
getAllQuestionAnswersMock,
getAnswerByQuestionAndPersonMock,
questionAnswersMock,
} from "../backend/queries/answer.mock";
import {
addAnswerMock,
editAnswerMock,
} from "../backend/mutations/answer.mock";
import {
getNegativePositionPath,
getNeutralPositionPath,
getPositivePositionPath,
getSkippedPositionPath,
queryAllIconButtons,
} from "./test-helper";
import QuestionAnswersList from "../components/QuestionAnswerList";
function renderQuestionAnswerList(additionalMocks?: Array<MockedResponse>) {
const initialMocks = [
...getAllQuestionAnswersMock,
...getAnswerByQuestionAndPersonMock,
];
const allMocks = additionalMocks
? [...initialMocks, ...additionalMocks]
: initialMocks;
return render(
<MockedProvider mocks={allMocks}>
<MemoryRouter>
<SnackbarProvider>
<QuestionAnswersList loggedInPersonRowId={2} />
</SnackbarProvider>
</MemoryRouter>
</MockedProvider>
);
}
const waitForQuestionsToRender = async (): Promise<Array<HTMLElement>> => {
const numberOfAnswersInMockQuery = questionAnswersMock.length;
let questionAnswerCards: Array<HTMLElement> = [];
await waitFor(() => {
questionAnswerCards = screen.queryAllByRole("button", {
name: /Question [1-3]/,
});
expect(questionAnswerCards.length).toEqual(numberOfAnswersInMockQuery);
});
return questionAnswerCards;
};
const getSaveAnswerButton = (parent: HTMLElement) => {
return findByRole(parent, "button", { name: /speichern/i });
};
const getResetAnswerButton = (parent: HTMLElement) => {
return findByRole(parent, "button", { name: /zurücksetzen/i });
};
interface PositionIconButtons {
positive: HTMLElement;
neutral: HTMLElement;
negative: HTMLElement;
skipped: HTMLElement;
}
const getCandidatePositionButtons = async (
parent: HTMLElement
): Promise<PositionIconButtons> => {
const labelAboveButtons = await findByText(parent, /Deine Position/i);
const parentElement = labelAboveButtons.parentElement as HTMLElement;
const positive = queryAllIconButtons(
getPositivePositionPath(),
parentElement
)[0];
const neutral = queryAllIconButtons(
getNeutralPositionPath(),
parentElement
)[0];
const negative = queryAllIconButtons(
getNegativePositionPath(),
parentElement
)[0];
const skipped = queryAllIconButtons(
getSkippedPositionPath(),
parentElement
)[0];
expect(positive).toBeDefined();
expect(neutral).toBeDefined();
expect(negative).toBeDefined();
expect(skipped).toBeDefined();
return {
positive,
neutral,
negative,
skipped,
};
};
interface AccordionAnswerSection {
positionIconButtons: PositionIconButtons;
textField: HTMLElement;
saveButton: HTMLElement;
resetButton: HTMLElement;
}
const expandAccordionAndGetAnswerSection = async (
accordionExpandArea: HTMLElement
): Promise<AccordionAnswerSection | undefined> => {
fireEvent.click(accordionExpandArea);
let fullAccordion = accordionExpandArea.parentElement;
expect(fullAccordion).not.toBeNull();
fullAccordion = fullAccordion as HTMLElement;
const textField = await findByRole(fullAccordion, "textbox");
const saveButton = await getSaveAnswerButton(fullAccordion);
const resetButton = await getResetAnswerButton(fullAccordion);
const positionIconButtons = await getCandidatePositionButtons(fullAccordion);
return {
positionIconButtons,
textField,
saveButton,
resetButton,
};
};
describe("The AnswerList", () => {
test("displays the existing answers, but not the details of it", async () => {
renderQuestionAnswerList();
const questionAnswerCards = await waitForQuestionsToRender();
questionAnswerCards.forEach((card) => {
expect(card.innerHTML).toMatch(/Question [1-3]/);
});
const saveButtons = screen.queryAllByRole("button", { name: /speichern/i });
expect(saveButtons).toHaveLength(0);
expect(questionAnswerCards[0].innerHTML).toContain(
getNeutralPositionPath()
);
expect(questionAnswerCards[1].innerHTML).toContain(
getPositivePositionPath()
);
expect(questionAnswerCards[2].innerHTML).toContain(
getSkippedPositionPath()
);
});
test("enables resetting an answer to the last saved state", async () => {
renderQuestionAnswerList(editAnswerMock);
const questionAnswerCards = await waitForQuestionsToRender();
let answerSection = await expandAccordionAndGetAnswerSection(
questionAnswerCards[1]
);
expect(answerSection).toBeDefined();
answerSection = answerSection as AccordionAnswerSection;
const { textField, resetButton } = answerSection;
expect(textField.outerHTML).toContain("Answer 2");
// change answer title
fireEvent.change(textField, {
target: { value: "New answer" },
});
await waitFor(() => {
expect(textField.outerHTML).toContain("New answer");
});
// reset and verify
fireEvent.click(resetButton);
await waitFor(() => {
expect(textField.outerHTML).toContain("Answer 2");
});
});
test("enables editing an answer", async () => {
renderQuestionAnswerList(editAnswerMock);
const questionAnswerCards = await waitForQuestionsToRender();
let answerSection = await expandAccordionAndGetAnswerSection(
questionAnswerCards[1]
);
expect(answerSection).toBeDefined();
answerSection = answerSection as AccordionAnswerSection;
const { textField, saveButton, resetButton } = answerSection;
expect(textField.outerHTML).toContain("Answer 2");
// change answer title
fireEvent.change(textField, {
target: { value: "New answer" },
});
await waitFor(() => {
expect(textField.outerHTML).toContain("New answer");
});
// call backend and assert apollo cache update (i.e a subsequent reset should reset to the new answer)
fireEvent.click(saveButton);
fireEvent.change(textField, {
target: { value: "something else" },
});
await waitFor(() => {
expect(textField.outerHTML).toContain("something else");
});
fireEvent.click(resetButton);
await waitFor(() => {
expect(textField.outerHTML).toContain("New answer");
});
});
test("enables adding an answer via setting the position", async () => {
renderQuestionAnswerList(addAnswerMock);
const questionAnswerCards = await waitForQuestionsToRender();
const questionWithoutAnswerCard = questionAnswerCards[2];
let answerSection = await expandAccordionAndGetAnswerSection(
questionWithoutAnswerCard
);
expect(answerSection).toBeDefined();
answerSection = answerSection as AccordionAnswerSection;
const { positionIconButtons } = answerSection;
expect(positionIconButtons.skipped.outerHTML).toContain(
'aria-pressed="true"'
);
expect(positionIconButtons.positive.outerHTML).toContain(
'aria-pressed="false"'
);
// press "positive" icon button and wait for accordion header to change -> apollo cache update successful
fireEvent.click(positionIconButtons.positive);
await waitFor(() => {
expect(questionAnswerCards[2].innerHTML).toContain(
getPositivePositionPath()
);
});
expect(positionIconButtons.skipped.outerHTML).toContain(
'aria-pressed="false"'
);
expect(positionIconButtons.positive.outerHTML).toContain(
'aria-pressed="true"'
);
});
});