253 lines
7.6 KiB
TypeScript
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"'
|
|
);
|
|
});
|
|
});
|