Skip to content

Commit

Permalink
[TESTING] Adding new tests to customize class page (hedyorg#3972)
Browse files Browse the repository at this point in the history
**Description**

Recently, the customize class page has gone through a lot of changes, and ever since a handful of bugs emerged. In order to prevent this from happening in the future, this PR adds a handful of tests for the following behaviors:

* Clicking the 'go back' button.
* Clicking the 'save' and 'remove customizations' buttons
* Reordering the adventures through drags and drops.
* Deleting an adventure and adding it again.
* Switching through levels.

Also, minor changes:

* A debug print was removed.
* Added ids to some buttons in order to query them more easily in the tests.
  • Loading branch information
jpelay authored Jan 31, 2023
1 parent 45aa746 commit a4591e9
Show file tree
Hide file tree
Showing 10 changed files with 232 additions and 84 deletions.
4 changes: 2 additions & 2 deletions templates/class-overview.html
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,10 @@ <h1>{{ class_info.name }}</h1><span class="cursor-pointer" onclick='hedyApp.rena
<div class="flex flex-col gap-4">
<div class="flex flex-wrap gap-2">
{% if is_admin and class_info.teacher != username %}
<button class="green-btn" id="customize-class-button" onclick="window.location.href = '/for-teachers/customize-class/{{class_info.id}}'">View customizations</button>
<button class="green-btn" id="customize-class-button" data-cy="customize_class_button" onclick="window.location.href = '/for-teachers/customize-class/{{class_info.id}}'">View customizations</button>
{% else %}
<button class="green-btn" id="add-student" onclick=$('#add_students_options').toggle();$(this).toggleClass('green-btn');$(this).toggleClass('blue-btn');>{{_('add_students')}}</button>
<button class="green-btn" id="customize-class-button" onclick="window.location.href = '/for-teachers/customize-class/{{class_info.id}}'">{{_('customize_class')}}</button>
<button class="green-btn" id="customize-class-button" data-cy="customize_class_button" onclick="window.location.href = '/for-teachers/customize-class/{{class_info.id}}'">{{_('customize_class')}}</button>
{% endif %}
<button class="green-btn" id="stats_button" onclick="window.location.href = '/stats/class/{{class_info.id}}'">{{_('class_stats')}}</button>
<button class="green-btn" id="logs_button" onclick="window.location.href = '/logs/class/{{class_info.id}}'">{{_('class_logs')}}</button>
Expand Down
21 changes: 11 additions & 10 deletions templates/customize-class.html
Original file line number Diff line number Diff line change
Expand Up @@ -31,17 +31,17 @@ <h3 class="px-4 w-3/12">{{_('select_levels')}}</h3>
<h3 class="px-4">{{_('select_adventures')}}</h3>
<div class="flex flex-row w-full mb-4">
<div class="flex flex-row justify-center items-center mr-2 w-1/12 ml-4">
<select class="flex flex-row py-2 px-1 mt-2 h-10 justify-center items-center text-center w-full" name="levels" id="adventures">
<select class="flex flex-row py-2 px-1 mt-2 h-10 justify-center items-center text-center w-full" name="levels" id="adventures" data-cy="adventures">
<option value="none" selected disabled hidden> {{_('select_a_level')}} </option>
{% for i in range(1, max_level + 1) %}
<option id="select-{{i}}" {% if customizations and i not in customizations['levels'] %}class="hidden"{% endif %}value="{{ i }}">{{ _('level_title') }} {{ i }}</option>
{% endfor %}
</select>
</div>
<div id="sortadventures" class="flex flex-row w-9/12">
<div id="sortadventures" data-cy="sortadventures" class="flex flex-row w-9/12">
{% if customizations and customizations['sorted_adventures'] %}
{% for level, adventures in customizations['sorted_adventures'].items() %}
<div id="level-{{ level }}" class="adventures-tab">
<div id="level-{{ level }}" data-cy="level-{{ level }}" class="adventures-tab">
{% for adventure in adventures %}
{% if adventure.name in adventure_names %}
{# Note: this code is copy/pasted in teachers.ts. If you change it here, also change it there #}
Expand All @@ -55,7 +55,7 @@ <h3 class="px-4">{{_('select_adventures')}}</h3>
{% endfor %}
{% else %}
{% for level, adventures in adventures_default_order.items() %}
<div id="level-{{ level }}" class="adventures-tab">
<div id="level-{{ level }}" data-cy="level-{{ level }}" class="adventures-tab">
{% for adventure in adventures %}
<div draggable="true" class="tab z-10 whitespace-nowrap flex items-center justify-left relative" tabindex="0" adventure="{{ adventure }}" level = "{{ level }}" from-teacher = "false">
<span id="remove" class="absolute top-0.5 right-0.5 text-gray-600 hover:text-red-400 fa-regular fa-circle-xmark"></span>
Expand All @@ -67,7 +67,7 @@ <h3 class="px-4">{{_('select_adventures')}}</h3>
{% endif %}
</div>
<div class="flex flex-row justify-center items-center ml-2 w-2/12 mr-4">
<select class="flex flex-row py-2 px-1 w-full mt-2 h-10 justify-center items-center text-center" name="levels" id="available">
<select class="flex flex-row py-2 px-1 w-full mt-2 h-10 justify-center items-center text-center" name="levels" id="available" data-cy="available_adventures_current_level">
<option value="none" selected>{{_('available_adventures_level')}} -</option>
</select>
</div>
Expand All @@ -87,7 +87,7 @@ <h3 class="px-4">{{_('opening_dates')}}</h3>
</thead>
<tbody>
{% for i in range(1, max_level + 1) %}
<tr class="{% if customizations and i not in customizations['levels'] %}hidden{% endif %} opening_date_container" id="opening_date_level_{{ i }}">
<tr class="{% if customizations and i not in customizations['levels'] %}hidden{% endif %} opening_date_container" id="opening_date_level_{{ i }}" data-cy="opening_date_level_{{ i }}">
<td class="border-r border-t border-gray-400">{{ _('level_title') }} {{ i }}</td>
<td class="border-t border-gray-400 ltr:pl-2 rtl:pr-2">
<input level="{{ i }}"
Expand Down Expand Up @@ -119,7 +119,8 @@ <h3 class="px-4">{{_('unlock_thresholds')}}</h3>
<tr>
<td class="text-left border-t border-r border-gray-400">{{_('quiz_score')}}</td>
<td class="border-t border-gray-400 px-4 pt-2">
<input class="threshold_settings_value appearance-none rounded-lg w-full px-2" id="quiz" {% if "quiz" in customizations['level_thresholds'] %}value="{{ customizations['level_thresholds']['quiz'] }}"{% endif %} min="1" max="100" placeholder="e.g. 75 ({{_('percentage')}})" type="number">
<input class="threshold_settings_value appearance-none rounded-lg w-full px-2"
data-cy="quiz_input" id="quiz" {% if "quiz" in customizations['level_thresholds'] %}value="{{ customizations['level_thresholds']['quiz'] }}"{% endif %} min="1" max="100" placeholder="e.g. 75 ({{_('percentage')}})" type="number">
</td>
</tr>
</tbody>
Expand Down Expand Up @@ -182,12 +183,12 @@ <h3 class="px-4">{{_('other_settings')}}</h3>
<div class="flex flex-row ml-auto gap-2">
{# TODO JP: make a button that resets the adventures to the default in Hedy content #}
<!-- <button class="yellow-btn text-white" onclick="hedyApp.modal.confirm('{{_('reset_adventure_prompt')}}', function(){$('.adventure_level_input').prop('checked', false);$('.teacher_adventures_checkbox').prop('checked', false);});">{{_('reset_adventures')}}</button> -->
<button class="blue-btn" onclick="hedyApp.save_customizations('{{class_info.id}}')">{{_('save')}}</button>
<button data-cy="save_customizations" class="blue-btn" onclick="hedyApp.save_customizations('{{class_info.id}}')">{{_('save')}}</button>
</div>
<div class="flex flex-row ml-auto gap-2">
<button class="red-btn {% if not customizations %}hidden{% endif %}" id="remove_customizations_button" onclick='hedyApp.remove_customizations("{{class_info.id}}", {{_('remove_customizations_prompt')|tojson}})'>
<button class="red-btn {% if not customizations %}hidden{% endif %}" id="remove_customizations_button" data-cy="remove_customizations_button" onclick='hedyApp.remove_customizations("{{class_info.id}}", {{_('remove_customizations_prompt')|tojson}})'>
{{_('remove_customization')}}</button>
<button class="green-btn" onclick="validate_changes()">{{_('back_to_class')}}</button>
<button class="green-btn" data-cy="back_to_class" onclick="validate_changes()">{{_('back_to_class')}}</button>
</div>
</div>
{% endif %}
Expand Down
2 changes: 1 addition & 1 deletion templates/for-teachers.html
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ <h2>{{_('my_classes')}}</h2>
<tbody>
{% for class in teacher_classes %}
<tr>
<td class="px-4 py-2"><a href="for-teachers/class/{{class.id}}" class="view_class">{{class.name|e}}</a></td>
<td class="px-4 py-2"><a href="for-teachers/class/{{class.id}}" class="view_class" data-cy="view_class_link">{{class.name|e}}</a></td>
<td class="text-center p-2">{{class.students|length}}</td>
<td class="text-center p-2"><a class="no-underline cursor-pointer" onclick='hedyApp.duplicate_class("{{class.id}}", {{_('class_name_prompt')|tojson}})')><span class="fas fa-copy"></span></a></td>
<td class="text-center p-2"><a class="no-underline cursor-pointer" onclick='hedyApp.delete_class("{{class.id}}", {{_('delete_class_prompt')|tojson}})'>🗑️</a></td>
Expand Down
4 changes: 2 additions & 2 deletions templates/layout.html
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
<div id="modal-confirm" class="hidden">
<div id="modal-confirm-text" class="text-white mb-8 text-xl"></div>
<div class="ltr:ml-auto rtl:mr-auto ltr:mr-auto rtl:ml-auto mt-4 flex flex-row justify-center">
<button id="modal-yes-button" class="green-btn block m-4 w-40 pb-4 pt-4">{{_('yes')}}</button>
<button id="modal-yes-button" data-cy="modal_yes_button" class="green-btn block m-4 w-40 pb-4 pt-4">{{_('yes')}}</button>
<button id="modal-no-button" class="red-btn block m-4 w-40 pb-4 pt-4">{{_('no')}}</button>
</div>
</div>
Expand Down Expand Up @@ -71,7 +71,7 @@
<div id="modal-alert" class="z-50 fixed hidden" role="alert" style="top: 10%; left: 50%; transform: translate(-50%, -50%);">
<div id="modal_alert_container" class="mx-auto text-center bg-green-100 border-green-400 text-green-700 border rounded px-4 py-3">
<span id="modal_alert_title" class="block sm:inline"></span>
<span id="modal_alert_text" class="block sm:inline"></span>
<span id="modal_alert_text" data-cy="modal_alert_text" class="block sm:inline"></span>
<svg class="fill-current h-6 w-6 text-green-500 block sm:inline" id="modal-alert-button" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><title>Close</title><path d="M14.348 14.849a1.2 1.2 0 0 1-1.697 0L10 11.819l-2.651 3.029a1.2 1.2 0 1 1-1.697-1.697l2.758-3.15-2.759-3.152a1.2 1.2 0 1 1 1.697-1.697L10 8.183l2.651-3.031a1.2 1.2 0 1 1 1.697 1.697l-2.758 3.152 2.758 3.15a1.2 1.2 0 0 1 0 1.698z"/></svg>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
import {loginForTeacher} from '../../tools/login/login.js'
import { createClass } from "../../tools/classes/class";

describe('customize class page', () => {
beforeEach(() => {
loginForTeacher();
createClass();
cy.getBySel('view_class_link').first().click(); // Press on view class button
cy.getBySel('customize_class_button').click(); // Press customize class button
});

it('checks the option checkboxes', () => {
// following code checks every single checkbox on the current page:
cy.get('[type="checkbox"]').check({force:true})
cy.get('[type="checkbox"]').should('be.checked')
cy.get('[type="checkbox"]').uncheck()
cy.get('[type="checkbox"]').should('be.not.checked')
});

it('goes back to the view class page', () => {
cy.getBySel('back_to_class')
.should('be.visible')
.should('not.be.disabled')
.click();
// We should be in the view class page
cy.url()
.should('include', Cypress.config('baseUrl') + Cypress.env('class_page'));
});

it('the adventure dragger is hidden and the level dropdown changes which adventures are displayed', () => {
// Click on level 1
cy.getBySel('adventures')
.select('1')
.should('have.value', '1');

// level 1 should be visible and level 2 not
cy.getBySel("level-1")
.should('be.visible');

cy.getBySel("level-2")
.should('not.be.visible');

// after selecting level 2 it should be visible and level 1 not
cy.getBySel('adventures')
.select('2')
.should('have.value', '2');

cy.getBySel("level-1")
.should('not.be.visible');

cy.getBySel("level-2")
.should('be.visible');
});

it('tests if the opening tests are not empty', () => {
// The following line has a bug in cypress:
// cy.getBySel("opening_date_level_" + index).type("2023-01-01").should("have.value", "2023-01-01")
// The following tests only checks if the field is not empty using a for loop:
var levelarray = Array.from({length:18},(v, k)=>k+1) // length reflects how many levels there are
cy.wrap(levelarray).each((index) => {
cy.getBySel("opening_date_level_" + index).type("2023-01-01").should("not.be.empty");
});
});

it('the quiz score holds the value typed to it', () => {
// testing quiz score feature
cy.getBySel('quiz_input').type("50").should("have.value", "50");
});

it('removes the adventure and checks that it is added to the available adventures drop down and removed from the dragger', () => {

cy.getBySel('sortadventures')
.children()
.should('not.be.visible');

// Click on level 2
cy.getBySel("adventures")
.select('2')
.should('have.value', '2');

// The available adventures dropdown should only include the default option
cy.getBySel("available_adventures_current_level")
.children()
.should('have.length', 1)

// store the name of the adventure we're going to delete
cy.get('[data-cy="level-2"] div:first')
.invoke('attr', 'adventure')
.as('adventure')
.then(adventure => {
// Get the first adventure, and click its remove button
cy.get('[data-cy="level-2"] div:first span')
.click();

// The available adventures dropdown should now include the new adventure
cy.getBySel("available_adventures_current_level")
.children()
.should('have.length', 2);

// the added option should be the last
cy.get('[data-cy="available_adventures_current_level"] option:last')
.should('have.id', `remove-${adventure}`);

// after selecting the adventure, it shouldn't be among the options
cy.getBySel("available_adventures_current_level")
.select(1)
.children()
.should('have.length', 1);

// the adventure should now be last
cy.get('[data-cy="level-2"] div:last')
.should('have.attr', 'adventure')
.and('eq', adventure);
});
});

it('saves the customizations and then removes them, checking that the remove button hides accordingly', () => {
// Since this class is new it shouldn't have customizations
cy.getBySel('remove_customizations_button')
.should('not.be.visible');

// We save the customizations first
cy.getBySel("save_customizations")
.should('be.visible')
.should('not.be.disabled')
.click();

cy.getBySel('modal_alert_text')
.should('be.visible');

// Now that it has customizations we can remove them
cy.getBySel('remove_customizations_button')
.should('be.visible')
.should('not.be.disabled')
.click();

cy.getBySel('modal_yes_button')
.should('be.visible')
.click();

// It shouldn't have the button again
cy.getBySel('remove_customizations_button')
.should('not.be.visible');
});

it('selects two adventures and swaps them using drag and drop', () => {
cy.getBySel('sortadventures')
.children()
.should('not.be.visible');

// Click on level 1
cy.getBySel("adventures")
.select('1')
.should('have.value', '1');

// Now it should be visible
cy.getBySel('level-1').should('be.visible');

// Get the first and second adventure
cy.getBySel('level-1')
.children()
.eq(0)
.invoke('attr', 'adventure')
.as('first_adventure');

cy.getBySel('level-1')
.children()
.eq(1)
.invoke('attr', 'adventure')
.as('second_adventure');

// Getting their values first, and then moving them around
cy.get('@first_adventure').then(first_adventure => {
cy.get('@second_adventure').then(second_adventure => {

// Move the second adventure to the first place
cy.getBySel('level-1')
.children()
.eq(1)
.trigger('dragstart')
.siblings()
.should('have.attr', 'class')
.and('contain', 'drop-adventures-hint');

cy.getBySel('level-1')
.children()
.eq(0)
.trigger('drop')
.trigger('dragend');

// they should be inverted now
cy.getBySel('level-1')
.children()
.eq(0)
.should('have.attr', 'adventure')
.and('eq', second_adventure);

cy.getBySel('level-1')
.children()
.eq(1)
.should('have.attr', 'adventure')
.and('eq', first_adventure);
})
})
});
});

This file was deleted.

This file was deleted.

Loading

0 comments on commit a4591e9

Please sign in to comment.