Skip to content

Commit

Permalink
feat: add test for adding questions (#390)
Browse files Browse the repository at this point in the history
  • Loading branch information
mateuszszczecina authored Jan 17, 2025
1 parent b8ff9c1 commit fd3abaf
Show file tree
Hide file tree
Showing 8 changed files with 287 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,12 @@ const AnswerSelectQuestion = ({ form, questionIndex }: AnswerSelectQuestionProps
</p>
)}
<div className="ml-14 mt-4 flex gap-2">
<Button type="button" className="bg-primary-700" onClick={handleAddOption}>
<Button
type="button"
data-testid={`add-options-button-${questionIndex}`}
className="bg-primary-700"
onClick={handleAddOption}
>
{t("adminCourseView.curriculum.lesson.button.addOption")}
</Button>
<Button
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -342,19 +342,25 @@ const FillInTheBlanksQuestion = ({ form, questionIndex }: FillInTheBlankQuestion
{isAddingWord && (
<div className="mt-4 flex w-1/3 items-center gap-2">
<Input
data-testid="new-word-input"
type="text"
value={newWord}
onChange={(e) => setNewWord(e.target.value)}
placeholder={t("adminCourseView.curriculum.lesson.placeholder.enterWord")}
className="flex-1"
/>
<Button onClick={handleAddWord} type="button" className="bg-blue-700 text-white">
<Button
onClick={handleAddWord}
data-testid="add-word"
type="button"
className="bg-blue-700 text-white"
>
{t("common.button.add")}
</Button>
<Button
onClick={() => setIsAddingWord(false)}
type="button"
className="bg-color-transparent border border-neutral-200 bg-red-500 text-red-500"
className="bg-color-transparent border border-neutral-200 bg-red-500 text-white"
>
{t("common.button.cancel")}
</Button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,12 @@ const MatchWordsQuestion = ({ form, questionIndex }: MatchWordsQuestionProps) =>
</p>
)}
<div className="ml-14 mt-4 flex gap-2">
<Button type="button" className="bg-primary-700" onClick={handleAddOption}>
<Button
type="button"
data-testid={`add-options-button-${questionIndex}`}
className="bg-primary-700"
onClick={handleAddOption}
>
{t("adminCourseView.curriculum.lesson.button.addOption")}
</Button>
<Button
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,12 @@ const PhotoQuestion = ({ form, questionIndex, lessonToEdit }: PhotoQuestionProps
</p>
)}
<div className="mb-4 ml-14 mt-4 flex gap-2">
<Button className="bg-primary-700" type="button" onClick={handleAddOption}>
<Button
className="bg-primary-700"
data-testid={`add-options-button-${questionIndex}`}
type="button"
onClick={handleAddOption}
>
{t("adminCourseView.curriculum.lesson.button.addOption")}
</Button>
<Button
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,12 @@ const ScaleQuestion = ({ form, questionIndex }: ScaleQuestionProps) => {
</p>
)}
<div className="ml-14 mt-4 flex gap-2">
<Button type="button" className="bg-primary-700" onClick={handleAddOption}>
<Button
type="button"
data-testid={`add-options-button-${questionIndex}`}
className="bg-primary-700"
onClick={handleAddOption}
>
{t("adminCourseView.curriculum.lesson.button.addOption")}
</Button>
<Button
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ const TrueOrFalseQuestion = ({ form, questionIndex }: TrueOrFalseQuestionProps)
<Input
type="text"
value={item.optionText}
name={`questions.${questionIndex}.options.${index}.optionText`}
onChange={(e) =>
handleOptionChange(index as number, "optionText", e.target.value)
}
Expand Down Expand Up @@ -189,7 +190,12 @@ const TrueOrFalseQuestion = ({ form, questionIndex }: TrueOrFalseQuestionProps)
</p>
)}
<div className="mb-4 ml-14 mt-4 flex gap-2">
<Button type="button" className="bg-primary-700" onClick={handleAddOption}>
<Button
type="button"
data-testid={`add-options-button-${questionIndex}`}
className="bg-primary-700"
onClick={handleAddOption}
>
{t("adminCourseView.curriculum.lesson.button.addOption")}
</Button>
<Button
Expand Down
248 changes: 247 additions & 1 deletion apps/web/e2e/tests/createCourse.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,153 @@ export class CreateCourseActions {
const editorInput = descriptionField.locator('div[contenteditable="true"]');
await expect(editorInput).toBeVisible();
await editorInput.click();
await page.keyboard.type(lessonDescription, { delay: 100 });
await page.keyboard.type(lessonDescription, { delay: 5 });
await expect(editorInput).toHaveText(lessonDescription);
await page.getByRole("button", { name: /save/i }).click();
}

async addQuiz(page: Page, chapterLocator: Locator): Promise<void> {
await chapterLocator.getByRole("button", { name: /add lesson/i }).click();
const buttonWithText = await page.locator('h3:has-text("Quiz")');
const lessonButton = buttonWithText.locator("..");
await lessonButton.click();
await page.getByLabel("Title").fill("Quiz for first exam");
}

async addQuestion(
page: Page,
questionType: string,
questionTitle: string,
questionIndex: number,
): Promise<void> {
await expect(page.getByRole("button", { name: /add question/i })).toBeVisible();
await page.getByRole("button", { name: /add question/i }).click();
await expect(page.getByRole("button", { name: new RegExp(questionType, "i") })).toBeVisible();
await page.getByRole("button", { name: new RegExp(questionType, "i") }).click();
await page.locator(`input[name="questions.${questionIndex}.title"]`).fill(questionTitle);
}

async addOptionsAndFillAnswerQuestion(
questionIndex: number,
options: { text: string; isCorrect: boolean }[],
multipleChoice: boolean = false,
): Promise<void> {
for (let i = 0; i < options.length; i++) {
const option = options[i];
const optionTextLocator = `input[name="questions.${questionIndex}.options.${i}.optionText"]`;
const optionIsCorrectLocator = `input[name="questions.${questionIndex}.options.${i}.isCorrect"]`;

await this.page.locator(optionTextLocator).fill(option.text);

if (option.isCorrect) {
if (multipleChoice) {
await this.page.locator(optionIsCorrectLocator).locator("..").locator("button").click();
} else {
await this.page.locator(optionIsCorrectLocator).click();
}
}

if (i >= 1 && i < options.length - 1) {
await this.page.getByTestId(`add-options-button-${questionIndex}`).click();
}
}
}
async addTrueOrFalseQuestion(
page: Page,
questionIndex: number,
correctAnswerIndex: number,
): Promise<void> {
await page.getByTestId(`add-options-button-${questionIndex}`).click();
await page.getByTestId(`add-options-button-${questionIndex}`).click();

const options = [
"CSS allows direct manipulation of a website's database.",
"CSS enables styling HTML elements, such as colors, fonts, and page layouts.",
"CSS only works in web browsers that support JavaScript.",
];

for (let i = 0; i < options.length; i++) {
await page
.locator(`input[name="questions.${questionIndex}.options.${i}.optionText"]`)
.fill(options[i]);
}
await page
.locator(`input[name="questions.${questionIndex}.options.${correctAnswerIndex}.isCorrect"]`)
.first()
.click();
}
async addMatchingQuestion(
page: Page,
questionIndex: number,
options: { optionText: string; matchedWord: string }[],
): Promise<void> {
await page.getByTestId(`add-options-button-${questionIndex}`).click();

for (let i = 0; i < options.length; i++) {
const option = options[i];
await page
.locator(`input[name="questions.${questionIndex}.options.${i}.optionText"]`)
.fill(option.optionText);
await page
.locator(`input[name="questions.${questionIndex}.options.${i}.matchedWord"]`)
.fill(option.matchedWord);
}
}
async addScaleQuestion(page: Page, questionIndex: number, options: string[]): Promise<void> {
await page.getByTestId(`add-options-button-${questionIndex}`).click();
await page.getByTestId(`add-options-button-${questionIndex}`).click();

for (let i = 0; i < options.length; i++) {
await page
.locator(`input[name="questions.${questionIndex}.options.${i}.optionText"]`)
.fill(options[i]);
}
}
async addPhotoQuestion(
page: Page,
questionIndex: number,
imagePath: string,
options: string[],
correctOptionIndex: number,
): Promise<void> {
await page.getByTestId(`add-options-button-${questionIndex}`).click();

const fileInput = await page.locator('input[type="file"]');
await fileInput.setInputFiles(imagePath);

for (let i = 0; i < options.length; i++) {
await page
.locator(`input[name="questions.${questionIndex}.options.${i}.optionText"]`)
.fill(options[i]);
}

await page
.locator(`input[name="questions.${questionIndex}.options.${correctOptionIndex}.isCorrect"]`)
.click();
}

async addFillInTheBlankQuestion(page: Page, word: string) {
await page.getByRole("button", { name: /add words/i }).click();

await page.locator('[data-testid="new-word-input"]').fill(word);

await page.locator('[data-testid="add-word"]').click();

const draggableElement = page.locator(`span:text("${word}")`).locator("..");
await draggableElement.waitFor({ state: "visible" });

const editableElement = page.locator('div[contenteditable="true"]');
await editableElement.click();

await page.keyboard.type(
"The most popular programing language used to styling components is ",
{ delay: 5 },
);

await draggableElement.dragTo(editableElement);

await expect(editableElement).toContainText(word);
}
}

test.describe.serial("Course management", () => {
Expand Down Expand Up @@ -167,6 +310,109 @@ test.describe.serial("Course management", () => {

const lessonLocator = chapterLocator.locator('div[aria-label="Lesson: Introduction to CSS"]');
await expect(lessonLocator).toBeVisible();

await createCourseActions.addQuiz(page, chapterLocator);

await createCourseActions.addQuestion(
page,
"free text",
"Describe what is your CSS and HTML level",
0,
);
await createCourseActions.addQuestion(
page,
"short answer",
"Describe what would you like to learn with this course",
1,
);

await createCourseActions.addQuestion(
page,
"single choice",
"Which css tag is most popular, choose one correct answer",
2,
);
await createCourseActions.addOptionsAndFillAnswerQuestion(2, [
{ text: "<div></div>", isCorrect: true },
{ text: "<p></p>", isCorrect: false },
{ text: "<h1></h1>", isCorrect: false },
]);

await createCourseActions.addQuestion(
page,
"multiple choice",
"Which of the following are valid CSS properties?",
3,
);
await createCourseActions.addOptionsAndFillAnswerQuestion(
3,
[
{ text: "color", isCorrect: true },
{ text: "font-size", isCorrect: true },
{ text: "text-align-center", isCorrect: false },
{ text: "background-image-url", isCorrect: false },
],
true,
);

await createCourseActions.addQuestion(
page,
"true or false",
"Which of the following statements about CSS are true?",
4,
);
await createCourseActions.addTrueOrFalseQuestion(page, 4, 1);

await createCourseActions.addQuestion(
page,
"matching",
"Match the CSS property with its effect",
5,
);
const matchingOptions = [
{ optionText: "Affects the spacing inside an element.", matchedWord: "Padding" },
{
optionText: "Defines how an element is positioned relative to its container.",
matchedWord: "Position",
},
{ optionText: "Adds shadow effects to an element.", matchedWord: "Box shadow" },
];
await createCourseActions.addMatchingQuestion(page, 5, matchingOptions);

await createCourseActions.addQuestion(
page,
"scale 1 to 5",
"How would you rate your CSS knowledge on a scale from 1 to 5?",
6,
);
const scaleOptions = [
"Beginner (I have basic knowledge and am still learning CSS)",
"Intermediate (I understand the basics but am still learning more advanced techniques)",
"Expert (I have advanced knowledge and can create complex styles)",
];
await createCourseActions.addScaleQuestion(page, 6, scaleOptions);

await createCourseActions.addQuestion(
page,
"photo question",
"What code language do you see on this screenshot",
7,
);
const photoOptions = ["JAVA", "PYTHON", "JAVASCRIPT"];
const imagePath = "app/assets/thumbnail-e2e.jpg";
await createCourseActions.addPhotoQuestion(page, 7, imagePath, photoOptions, 2);

await createCourseActions.addQuestion(
page,
"fill in the blanks",
"Fill words in blank space",
8,
);
await createCourseActions.addFillInTheBlankQuestion(page, "CSS");
await page.getByRole("button", { name: /save/i }).click();

const quizLocator = chapterLocator.locator('div[aria-label="Lesson: Quiz for first exam"]');
await expect(quizLocator).toBeVisible();
});

test("should check if course occurs on course list", async () => {
Expand Down
1 change: 1 addition & 0 deletions apps/web/playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ dotenv.config({ path: path.resolve(__dirname, ".env") });
const baseURL = process.env.CI ? "http://localhost:5173" : "https://app.lms.localhost";

const config: PlaywrightTestConfig = {
timeout: 120000,
testDir: "./e2e",
fullyParallel: true,
forbidOnly: !!process.env.CI,
Expand Down

0 comments on commit fd3abaf

Please sign in to comment.