Skip to content

Commit

Permalink
[GSoC'24] M1.1: Fix a part of oppia#20374 : Acceptance test coverage …
Browse files Browse the repository at this point in the history
…for Curriculum admin's CUJs. (oppia#20379)

* updates

* updates

* updates

* updates

* updates

* updates

* updates

* updates

* updates

* updates

* updates
  • Loading branch information
Akhilesh-max authored Jun 11, 2024
1 parent afcc0c3 commit efd8370
Show file tree
Hide file tree
Showing 13 changed files with 641 additions and 165 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ jobs:
suite:
- blog-admin/assign-roles-to-users-and-change-tag-properties
- blog-editor/try-to-publish-a-duplicate-blog-post-and-get-blocked
- curriculum-admin/create-and-publish-topics-and-stories
- curriculum-admin/create-publish-unpublish-and-delete-topic-and-skill
- exploration-editor/load-complete-and-restart-exploration-preview
- exploration-editor/create-exploration-and-change-basic-settings
- logged-out-user/click-all-buttons-on-about-page
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@
</div>
<div class="skill-nav-dropdown-option"
(click)="selectQuestionsTab()">
<i class="fas fa-book-open navbar-tab-icon"></i>
<i class="fas fa-book-open navbar-tab-icon e2e-test-mobile-questions-tab"></i>
<span>Questions</span>
</div>
<div class="skill-nav-dropdown-option"
Expand All @@ -76,7 +76,7 @@
</div>
<div class="skill-nav-dropdown-icon"
(click)="toggleNavigationOptions()">
<i class="fa fa-caret-down"></i>
<i class="fa fa-caret-down e2e-test-mobile-skill-nav-dropdown-icon"></i>
</div>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@
<button type="button"
(click)="unpublishTopic()"
*ngIf="topicRights.isPublished() && topicRights.canPublishTopic()"
class="btn btn-light"
class="btn btn-light e2e-test-unpublish-topic-button"
[disabled]="isTopicSaveable()">
Unpublish Topic
</button>
Expand Down Expand Up @@ -138,6 +138,7 @@
</div>
<div (click)="unpublishTopic()"
*ngIf="topicRights.isPublished() && topicRights.canPublishTopic()"
class="e2e-test-mobile-unpublish-topic-button"
[attr.disabled]="isTopicSaveable()">
<i class="fas fa-pen"></i>
<span>Unpublish Topic</span>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@
</div>
<div class="delete-icon-box" (click)="deleteSkill(skill.id)">
<span *ngIf="userCanDeleteSkill">
<i class="fa fa-trash-alt"></i>
<i class="fa fa-trash-alt e2e-test-mobile-delete-skill-button"></i>
<span>Delete</span>
</span>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -225,14 +225,14 @@
</div>
</div>
<div class="topic-item-options">
<button class="fas fa-ellipsis-v topic-edit-box-icon" role="button" aria-label="Edit Options" (click)="changeEditOptions(topic.id)"></button>
<button class="fas fa-ellipsis-v topic-edit-box-icon e2e-test-mobile-topic-edit-box" role="button" aria-label="Edit Options" (click)="changeEditOptions(topic.id)"></button>
<div *ngIf="showEditOptions(topic.id)" class="topic-edit-options">
<a class="topic-edit-options-edit" [href]="getTopicEditorUrl(topic.id)">
<i class="fa fa-pen"></i>
<span>Edit</span>
</a>
<a *ngIf="userCanDeleteTopic" (click)="deleteTopic(topic.id, topic.name)">
<i class="fas fa-trash-alt"></i>
<i class="fas fa-trash-alt e2e-test-mobile-delete-topic-button"></i>
<span>Delete</span>
</a>
</div>
Expand Down Expand Up @@ -296,14 +296,14 @@
</div>
</div>
<div class="topic-item-options">
<button class="fas fa-ellipsis-v topic-edit-box-icon" role="button" aria-label="Edit Options" (click)="changeEditOptions(topic.id)"></button>
<button class="fas fa-ellipsis-v topic-edit-box-icon e2e-test-mobile-topic-edit-box" role="button" aria-label="Edit Options" (click)="changeEditOptions(topic.id)"></button>
<div *ngIf="showEditOptions(topic.id)" class="topic-edit-options">
<a class="topic-edit-options-edit" [href]="getTopicEditorUrl(topic.id)">
<i class="fa fa-pen"></i>
<span>Edit</span>
</a>
<a *ngIf="userCanDeleteTopic" (click)="deleteTopic(topic.id, topic.name)">
<i class="fas fa-trash-alt"></i>
<i class="fas fa-trash-alt e2e-test-mobile-delete-topic-button"></i>
<span>Delete</span>
</a>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,43 +19,42 @@
import {UserFactory} from '../../utilities/common/user-factory';
import testConstants from '../../utilities/common/test-constants';
import {CurriculumAdmin} from '../../utilities/user/curriculum-admin';
import {ExplorationEditor} from '../../utilities/user/exploration-editor';
import {LoggedInUser} from '../../utilities/user/logged-in-user';
import {ConsoleReporter} from '../../utilities/common/console-reporter';

const DEFAULT_SPEC_TIMEOUT_MSECS = testConstants.DEFAULT_SPEC_TIMEOUT_MSECS;
const ROLES = testConstants.Roles;

// To ignore console errors that occur when checking if the
// topic link returns a 404 status upon unpublishing.
ConsoleReporter.setConsoleErrorsToIgnore([
/HttpErrorResponse:.*404 Not Found/,
/Occurred at http:\/\/localhost:8181\/learn\/staging\/test-topic-one \.?/,
/http:\/\/localhost:8181\/learn\/staging\/test-topic-one \.?/,
/Failed to load resource: the server responded with a status of 404 \(Not Found\)/,
]);

describe('Curriculum Admin', function () {
let curriculumAdmin: CurriculumAdmin & ExplorationEditor;
let explorationId: string | null;
let curriculumAdmin: CurriculumAdmin;
let loggedInUser: LoggedInUser;

beforeAll(async function () {
curriculumAdmin = await UserFactory.createNewUser(
'curriculumAdm',
'[email protected]',
[ROLES.CURRICULUM_ADMIN]
);

loggedInUser = await UserFactory.createNewUser(
'loggedInUser',
'[email protected]'
);
}, DEFAULT_SPEC_TIMEOUT_MSECS);

it(
'should create and publish topics, subtopics, skills, stories and chapters.',
'should manage topics and skills: create, assign, publish, unpublish, and delete.',
async function () {
await curriculumAdmin.navigateToCreatorDashboardPage();
await curriculumAdmin.navigateToExplorationEditorPage();
await curriculumAdmin.dismissWelcomeModal();
await curriculumAdmin.createExplorationWithMinimumContent(
'Test Exploration',
'End Exploration'
);
await curriculumAdmin.saveExplorationDraft();
explorationId = await curriculumAdmin.publishExplorationWithContent(
'Test Exploration Title 1',
'Test Exploration Goal',
'Algebra'
);
if (!explorationId) {
throw new Error('Error publishing exploration successfully.');
}

await curriculumAdmin.navigateToTopicAndSkillsDashboardPage();
await curriculumAdmin.createTopic('Test Topic 1', 'test-topic-one');
await curriculumAdmin.createSubtopicForTopic(
'Test Subtopic 1',
Expand All @@ -76,19 +75,28 @@ describe('Curriculum Admin', function () {
);

await curriculumAdmin.publishDraftTopic('Test Topic 1');
await curriculumAdmin.createAndPublishStoryWithChapter(
'Test Story 1',
'test-story-one',
explorationId,
'Test Topic 1'
);
await curriculumAdmin.expectTopicToBePublishedInTopicsAndSkillsDashboard(
'Test Topic 1',
1,
1,
1
);

await curriculumAdmin.unpublishTopic('Test Topic 1');
await loggedInUser.expectTopicLinkReturns404('test-topic-one');

await curriculumAdmin.deleteTopic('Test Topic 1');
await curriculumAdmin.expectTopicNotInTopicsAndSkillDashboard(
'Test Topic 1'
);

// User must remove all questions from the skill before deleting it.
await curriculumAdmin.removeAllQuestionsFromTheSkill('Test Skill 1');
await curriculumAdmin.deleteSkill('Test Skill 1');
await curriculumAdmin.expectSkillNotInTopicsAndSkillsDashboard(
'Test Skill 1'
);
},

DEFAULT_SPEC_TIMEOUT_MSECS
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,13 @@
* @fileoverview Utility File for the Acceptance Tests.
*/

import puppeteer, {Page, Browser, Viewport, ElementHandle} from 'puppeteer';
import puppeteer, {
Page,
Frame,
Browser,
Viewport,
ElementHandle,
} from 'puppeteer';
import testConstants from './test-constants';
import isElementClickable from '../../functions/is-element-clickable';
import {ConsoleReporter} from './console-reporter';
Expand Down Expand Up @@ -304,6 +310,83 @@ export class BaseUser {
await this.page.click(selector);
}
}

/**
* The function clicks the element using the text on the button
* and wait until the new page is fully loaded.
*/
async clickAndWaitForNavigation(selector: string): Promise<void> {
/** Normalize-space is used to remove the extra spaces in the text.
* Check the documentation for the normalize-space function here :
* https://developer.mozilla.org/en-US/docs/Web/XPath/Functions/normalize-space */
const [button] = await this.page.$x(
`\/\/*[contains(text(), normalize-space('${selector}'))]`
);
// If we fail to find the element by its XPATH, then the button is undefined and
// we try to find it by its CSS selector.
if (button !== undefined) {
await this.waitForElementToBeClickable(button);
await Promise.all([
this.page.waitForNavigation({
waitUntil: ['networkidle2', 'load'],
}),
button.click(),
]);
} else {
await this.waitForElementToBeClickable(selector);
await Promise.all([
this.page.waitForNavigation({
waitUntil: ['networkidle2', 'load'],
}),
this.page.click(selector),
]);
}
}

/**
* Clicks an element on the page.
* @param {Page | Frame | ElementHandle} context - The Puppeteer context, usually a Page or Frame.
* @param {string} selector - The CSS selector of the element to click.
*/
async clickElement(
context: Page | Frame | ElementHandle,
selector: string
): Promise<void> {
const element = await context.$(selector);
if (!element) {
throw new Error(`Element ${selector} not found`);
}
await this.waitForElementToBeClickable(element);

try {
await element.click();
} catch (error) {
throw new Error(`Failed to click on element ${selector}`);
}
}

/**
* This function retrieves the text content of a specified element.
*/
async getElementText(selector: string): Promise<string> {
await this.page.waitForSelector(selector);
const element = await this.page.$(selector);
if (element === null) {
throw new Error(`No element found for the selector: ${selector}`);
}
const textContent = await this.page.evaluate(el => el.textContent, element);
return textContent ?? '';
}

/**
* This function checks if a particular text exists on the current page.
* @param {string} text - The text to check for.
*/
async isTextPresentOnPage(text: string): Promise<boolean> {
const pageContent = await this.page.content();
return pageContent.includes(text);
}

/**
* The function selects all text content and delete it.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ export default {
CURRICULUM_ADMIN: 'curriculum admin',
QUESTION_ADMIN: 'question admin',
VOICEOVER_ADMIN: 'voiceover admin',
TOPIC_MANAGER: 'topic manager',
} as const,
BlogRights: {
BLOG_ADMIN: 'BLOG_ADMIN',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
ExplorationEditor,
} from '../user/exploration-editor';
import {CurriculumAdminFactory} from '../user/curriculum-admin';
import {TopicManagerFactory} from '../user/topic-manager';
import {LoggedInUserFactory, LoggedInUser} from '../user/logged-in-user';
import testConstants from './test-constants';

Expand All @@ -47,6 +48,7 @@ const USER_ROLE_MAPPING = {
[ROLES.CURRICULUM_ADMIN]: CurriculumAdminFactory,
[ROLES.QUESTION_ADMIN]: QuestionAdminFactory,
[ROLES.VOICEOVER_ADMIN]: VoiceoverAdminFactory,
[ROLES.TOPIC_MANAGER]: TopicManagerFactory,
} as const;

/**
Expand Down
Loading

0 comments on commit efd8370

Please sign in to comment.