Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix new lecture creation #741

Merged
merged 9 commits into from
Jan 31, 2025
3 changes: 3 additions & 0 deletions .config/.cypress.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ module.exports = {
viewportHeight: 1000,
viewportWidth: 1400,

// https://docs.cypress.io/app/references/experiments#End-to-End-Testing
experimentalRunAllSpecs: true,

// https://docs.cypress.io/api/plugins/browser-launch-api#Changing-browser-preferences
setupNodeEvents(on, _config) {
on("before:browser:launch", (browser, launchOptions) => {
Expand Down
10 changes: 7 additions & 3 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,10 @@ jobs:
# As the docker-compose.yml file uses contexts like "./../..", we have
# to change the working directory here.
working-directory: docker/test
run: |
docker buildx bake --file ./docker-compose.yml --file ./../../.github/workflows/docker-compose-cache.json
run: >
docker buildx bake
--allow=fs.read=/home/runner/work/mampf/mampf
--file ./docker-compose.yml --file ./../../.github/workflows/docker-compose-cache.json

- name: Run unit tests
working-directory: docker/test
Expand Down Expand Up @@ -88,7 +90,9 @@ jobs:
# to change the working directory here.
working-directory: docker/test
run: >
docker buildx bake -f ./docker-compose.yml -f ./cypress.yml
docker buildx bake
--allow=fs.read=/home/runner/work/mampf/mampf
-f ./docker-compose.yml -f ./cypress.yml
-f ./../../.github/workflows/docker-compose-cache.json

- name: Run Cypress tests
Expand Down
2 changes: 1 addition & 1 deletion app/abilities/lecture_ability.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ def initialize(user)
clear_aliased_actions

can :new, Lecture do
user.course_editor?
user.course_editor? || user.admin?
end

can :create, Lecture do |lecture|
Expand Down
1 change: 0 additions & 1 deletion app/helpers/lectures_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,6 @@ def teacher_select(form, is_new_lecture, lecture = nil)

# TODO: Rubocop bug when trying to break the last object on a new line
select = form.select(:teacher_id, preselection, {}, { class: "selectize",
multiple: true,
data: {
ajax: true,
filled: false,
Expand Down
1 change: 1 addition & 0 deletions app/views/administration/index/_my_lectures.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
class: 'fas fa-plus-circle text-light admin-index-button',
id: 'new-lecture-button',
data: { remote: true, toggle: 'tooltip' },
"data-cy": 'new-lecture-button-admin-index',
title: t('admin.main.create_lecture') %>
<% end %>
</div>
Expand Down
5 changes: 3 additions & 2 deletions app/views/lectures/_new.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
<% term_independent = (from != 'course' && first_course_independent?) ||
(from == 'course' && lecture.course.term_independent) %>
<div class="row">
<div class="col-8">
<div class="col-8" data-cy="new-lecture-course-select-div">
<%= f.label :course_id, t('basics.course'),
class: "form-label" %>
<%= helpdesk(t('admin.lecture.info.course'), false) %>
Expand Down Expand Up @@ -70,7 +70,8 @@
<div class="row">
<div class="col-12 text-center">
<%= f.submit t('buttons.save'),
class: 'btn btn-primary' %>
class: 'btn btn-primary',
"data-cy": 'new-lecture-submit' %>
<button type="button"
id="cancel-new-lecture"
class="btn btn-secondary">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import FactoryBot from "../support/factorybot";
import FactoryBot from "../../support/factorybot";

describe("Lecture edit page", () => {
it("shows content tab button", function () {
Expand All @@ -14,14 +14,23 @@ describe("Lecture edit page", () => {
});
});

function typeCyInInput(selector, waitForUserFill = true) {
function typeCyInInput(selector, waitForUserFill = true, isMultiple = true) {
if (waitForUserFill) {
cy.intercept("GET", "/users/fill_user_select*").as("userFill");
}

cy.getBySelector(selector).find("input:not([type='hidden'])")
.should("have.length", 1).first().as("input");
cy.get("@input").clear(); // without clearing first, tests are flaky (!)

// depending on whether the input is a multiple select or not, we need to
// clear the input field differently
if (isMultiple) {
cy.get("@input").clear();
}
else {
cy.getBySelector(selector).find("a.remove").click();
}

cy.get("@input").click();

// eslint-disable-next-line cypress/unsafe-to-chain-command
Expand Down Expand Up @@ -88,7 +97,7 @@ describe("Lecture people edit page: teacher & editor", () => {

it("allows searching for arbitrary users to assign them as teachers", function () {
cy.visit(this.lecturePeopleUrl);
typeCyInInput("teacher-select");
typeCyInInput("teacher-select", true, false);
shouldContainUsers("teacher-select", this, true, true);
});

Expand Down
29 changes: 29 additions & 0 deletions spec/cypress/e2e/lecture/new_lecture_specs.cy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import FactoryBot from "../../support/factorybot";

describe("New lecture (via admin index page)", () => {
beforeEach(function () {
cy.createUserAndLogin("admin").as("admin");

FactoryBot.create("course").as("course");
cy.then(() => {
FactoryBot.create("term").as("term");
});
});

it("Creates new lecture", function () {
cy.visit("/administration");
cy.getBySelector("new-lecture-button-admin-index").click();

cy.getBySelector("new-lecture-course-select-div").then(($wrapperDiv) => {
cy.wrap($wrapperDiv).selectTom(this.course.title);
});
cy.getBySelector("new-lecture-submit").click();

const successMessage = this.admin.locale === "de" ? "erfolgreich" : "successfully";
cy.get("div.alert")
.should("contain", this.course.title)
.should("contain", this.term.season)
.should("contain", this.admin.name)
.should("contain", successMessage);
});
});
29 changes: 29 additions & 0 deletions spec/cypress/support/commands.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,35 @@ Cypress.Commands.add("clickExpectNewTab", { prevSubject: true }, ($subject, args
return cy.wrap($subject).invoke("removeAttr", "target").click(args);
});

/**
* Selects an option in a Tom Select dropdown. Should be chained off of the
* parenting element (e.g. a div) that contains the select element (among others).
*
* Usage:
* cy.get("div").selectTom("Option 1");
*
* This is necessary since the actual options are hidden in a <select> element.
* The visible options are generated by TomSelect, thus the usual cy.select()
* command won't work since the element is 'hidden'. A bad approach would be to
* just set { force: true } as option as this could lead to flaky tests and
* might silently fail.
*
* @param {string} name The name of the option to select.
*/
Cypress.Commands.add("selectTom", { prevSubject: "element" }, ($subject, name) => {
// Trigger the dropdown to open
cy.wrap($subject).find(".ts-control").find("input").click();

// Assert that the option exists in the DOM as an <option> element
cy.wrap($subject).find("select").find("option").contains(name).should("exist");

// Click on the visible dropdown item
cy.get("div.ts-dropdown").contains(name).then(($dropdownItem) => {
cy.wrap($dropdownItem).should("have.attr", "data-selectable");
cy.wrap($dropdownItem).click();
});
});

Cypress.Commands.add("assertCopiedToClipboard", (_expectedText) => {
cy.fail("Not implemented yet");

Expand Down