From 8c8c2fc0a87810e6cfcf1fee739ec20b42b54325 Mon Sep 17 00:00:00 2001 From: Splines <37160523+Splines@users.noreply.github.com> Date: Wed, 14 Aug 2024 17:35:35 +0200 Subject: [PATCH 1/4] Fix failing UserCleanerJob (#678) --- app/workers/{user_cleaner.rb => user_cleaner_job.rb} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename app/workers/{user_cleaner.rb => user_cleaner_job.rb} (100%) diff --git a/app/workers/user_cleaner.rb b/app/workers/user_cleaner_job.rb similarity index 100% rename from app/workers/user_cleaner.rb rename to app/workers/user_cleaner_job.rb From 0d39b9e6b4f06b0b33568a6d249c83fa43607f89 Mon Sep 17 00:00:00 2001 From: Splines <37160523+Splines@users.noreply.github.com> Date: Tue, 20 Aug 2024 17:02:27 +0200 Subject: [PATCH 2/4] Add `just` commands for docker logs (#681) * Add `just` commands for docker logs * Fix `logs` command comment --- .config/commands/docker.justfile | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.config/commands/docker.justfile b/.config/commands/docker.justfile index 15872ed4b..4106c2897 100644 --- a/.config/commands/docker.justfile +++ b/.config/commands/docker.justfile @@ -9,6 +9,19 @@ help: cd {{justfile_directory()}}/docker/development/ docker compose up {{args}} +# Starts the dev docker containers (detached) & shows MaMpf logs +up-logs *args: + #!/usr/bin/env bash + cd {{justfile_directory()}}/docker/development/ + docker compose up -d {{args}} + docker compose logs -f mampf + +# Shows the log of the specified container +@logs name="mampf": + #!/usr/bin/env bash + cd {{justfile_directory()}}/docker/development/ + docker compose logs -f {{name}} + # Starts the dev docker containers and preseeds the database [confirm("This will reset all your data in the database locally. Continue? (y/n)")] up-reseed *args: From 7429387b46bd4c563d80fe4289279c07c156e0fe Mon Sep 17 00:00:00 2001 From: Splines Date: Wed, 21 Aug 2024 19:05:33 +0200 Subject: [PATCH 3/4] Init future possibility to check clipboard content --- .config/.cypress.js | 30 ++++++++++++++++ spec/cypress/e2e/vouchers_spec.cy.js | 11 ++++++ spec/cypress/support/commands.js | 51 ++++++++++++++++++++++++++++ 3 files changed, 92 insertions(+) diff --git a/.config/.cypress.js b/.config/.cypress.js index 5d8e10485..da8f635df 100644 --- a/.config/.cypress.js +++ b/.config/.cypress.js @@ -3,5 +3,35 @@ module.exports = { // Base URL is set via Docker environment variable viewportHeight: 1000, viewportWidth: 1400, + + // https://docs.cypress.io/api/plugins/browser-launch-api#Changing-browser-preferences + setupNodeEvents(on, _config) { + on("before:browser:launch", (browser, launchOptions) => { + if (browser.family === "chromium" && browser.name !== "electron") { + // auto open devtools + launchOptions.args.push("--auto-open-devtools-for-tabs"); + + // TODO (clipboard): We use the obsolete clipboard API from browsers, i.e. + // document.execCommand("copy"). There's a new Clipboard API that is supported + // by modern browsers. Once we switch to that API, use the following code + // to allow requesting permission (clipboard permission) in a non-secure + // context (http). Remaining TODO in this case: search for the equivalent + // flag in Firefox & Electron (if we also want to test them). + // launchOptions.args.push("--unsafely-treat-insecure-origin-as-secure=http://mampf:3000"); + } + + if (browser.family === "firefox") { + // auto open devtools + launchOptions.args.push("-devtools"); + } + + if (browser.name === "electron") { + // auto open devtools + launchOptions.preferences.devTools = true; + } + + return launchOptions; + }); + }, }, }; diff --git a/spec/cypress/e2e/vouchers_spec.cy.js b/spec/cypress/e2e/vouchers_spec.cy.js index b277a1297..02321ee5f 100644 --- a/spec/cypress/e2e/vouchers_spec.cy.js +++ b/spec/cypress/e2e/vouchers_spec.cy.js @@ -68,6 +68,17 @@ describe("If the lecture is not a seminar", () => { testInvalidateVoucher(role); }); }); + + it.skip("copies the voucher hash to the clipboard", function () { + ROLES.filter(role => role !== "speaker").forEach((role) => { + cy.getBySelector(`create-${role}-voucher-btn`).click(); + cy.getBySelector(`${role}-voucher-secure-hash`).then(($hash) => { + const hashText = $hash.text(); + cy.getBySelector(`copy-${role}-voucher-btn`).click(); + cy.assertCopiedToClipboard(hashText); + }); + }); + }); }); }); diff --git a/spec/cypress/support/commands.js b/spec/cypress/support/commands.js index f965e0486..5d2a6db15 100644 --- a/spec/cypress/support/commands.js +++ b/spec/cypress/support/commands.js @@ -20,6 +20,57 @@ Cypress.Commands.add("clickExpectNewTab", { prevSubject: true }, ($subject, args return cy.wrap($subject).invoke("removeAttr", "target").click(args); }); +Cypress.Commands.add("assertCopiedToClipboard", (_expectedText) => { + cy.fail("Not implemented yet"); + + // An old method would consist of something like this: + // adapted from https://stackoverflow.com/a/69571115 + // and https://stackoverflow.com/a/33928558 + + ////////////////////////////////////////////////////////////////////////////// + // NEW PROPOSED SOLUTION for the new Clipboard API + ////////////////////////////////////////////////////////////////////////////// + + // TODO (clipboard): We currently use the obsolete clipboard API from browsers, + // i.e. document.execCommand("copy") via the clipboard.js library. + // There's a new Clipboard API that is supported by modern browsers. + // Once we switch to that API, use the following code to test copying to + // the clipboard. Also see this GitHub issue for more information: + // https://github.com/cypress-io/cypress/issues/2752 + // + // Note that another option to test the clipboard content then would be + // https://github.com/cypress-io/cypress/issues/2752#issuecomment-1039285381 + // which wouldn't even require requesting permissions but might have its + // own limitations. + + // Request clipboard permissions + // by https://stackoverflow.com/a/73329952/9655481 + // Note that this won't work by default in a non-secure context (http), so we need to + // pass the flag --unsafely-treat-insecure-origin-as-secure=http://mampf:3000 + // to the browser when starting it (see the cypress config) + // cy.wrap(Cypress.automation("remote:debugger:protocol", { + // command: "Browser.grantPermissions", + // params: { + // permissions: ["clipboardReadWrite", "clipboardSanitizedWrite"], + // }, + // })); + + // Make sure clipboard permissions were granted + // https://stackoverflow.com/questions/61650737/how-to-fetch-copied-to-clipboard-content-in-cypress/73329952#comment137190789_69571115 + // cy.window().its("navigator.permissions") + // .then(api => api.query({ name: "clipboard-read" })) + // .its("state").should("equal", "granted"); + + // https://stackoverflow.com/a/75928308/ + // cy.window().its("navigator.clipboard") + // .then(clip => clip.readText()) + // .should("equal", expectedText); +}); + +//////////////////////////////////////////////////////////////////////////////// +// Custom commands for backend interaction +//////////////////////////////////////////////////////////////////////////////// + Cypress.Commands.add("cleanDatabase", () => { return BackendCaller.callCypressRoute("database_cleaner", "cy.cleanDatabase()", {}); }); From 6d09e9e6b98467477ccc1cb4cfc9805555c05527 Mon Sep 17 00:00:00 2001 From: fosterfarrell9 <28628554+fosterfarrell9@users.noreply.github.com> Date: Wed, 21 Aug 2024 19:44:12 +0200 Subject: [PATCH 4/4] Fix lecture edit page if active term is not set (#683) * Add missing case that Term.active is nil * Add unit tests for lecture#stale? * Make lecture#older_than? method private * Add cypress test for lecture edit page * Make better use of .then() Co-authored-by: Splines <37160523+Splines@users.noreply.github.com> * Add docstring * Remove obsolete line * Rename for consistency * Add missing punctuation to docstring --------- Co-authored-by: Splines <37160523+Splines@users.noreply.github.com> Co-authored-by: Splines --- app/models/lecture.rb | 16 ++++++---- app/views/lectures/edit/_form.html.erb | 1 + spec/cypress/e2e/lectures_spec.cy.js | 15 +++++++++ spec/factories/terms.rb | 8 +++++ spec/models/lecture_spec.rb | 44 ++++++++++++++++++++++++++ 5 files changed, 78 insertions(+), 6 deletions(-) create mode 100644 spec/cypress/e2e/lectures_spec.cy.js diff --git a/app/models/lecture.rb b/app/models/lecture.rb index 7fd197471..973ac82d0 100644 --- a/app/models/lecture.rb +++ b/app/models/lecture.rb @@ -830,12 +830,9 @@ def speakers User.where(id: SpeakerTalkJoin.where(talk: talks).select(:speaker_id)) end - def older_than?(timespan) - return true unless term - - term.begin_date <= Term.active.begin_date - timespan - end - + # Determines if the lecture is stale (i.e. older than one year). + # The age of the lecture is determined by the begin date of the term + # in which it was given and the begin date of the current term. def stale? older_than?(1.year) end @@ -947,4 +944,11 @@ def only_one_lecture errors.add(:course, :already_present) end + + def older_than?(timespan) + return false unless Term.active + return true unless term + + term.begin_date <= Term.active.begin_date - timespan + end end diff --git a/app/views/lectures/edit/_form.html.erb b/app/views/lectures/edit/_form.html.erb index 5060e5b82..00e2608c3 100644 --- a/app/views/lectures/edit/_form.html.erb +++ b/app/views/lectures/edit/_form.html.erb @@ -15,6 +15,7 @@ id="lecture-nav-content" type="button" role="tab" href="#content" data-bs-toggle="pill" data-bs-target="#lecture-pane-content" + data-cy="content-tab-btn" aria-controls="lecture-pane-content" aria-selected="true"> <%= t('content') %> diff --git a/spec/cypress/e2e/lectures_spec.cy.js b/spec/cypress/e2e/lectures_spec.cy.js new file mode 100644 index 000000000..96d3994ee --- /dev/null +++ b/spec/cypress/e2e/lectures_spec.cy.js @@ -0,0 +1,15 @@ +import FactoryBot from "../support/factorybot"; + +describe("Lecture edit page", () => { + it("shows content tab button", function () { + cy.createUserAndLogin("teacher").then((teacher) => { + FactoryBot.create("lecture", "with_teacher_by_id", + { teacher_id: teacher.id }).as("lecture"); + }); + + cy.then(() => { + cy.visit(`/lectures/${this.lecture.id}/edit`); + cy.getBySelector("content-tab-btn").should("be.visible"); + }); + }); +}); diff --git a/spec/factories/terms.rb b/spec/factories/terms.rb index 9a1c6e949..3ecd347c3 100644 --- a/spec/factories/terms.rb +++ b/spec/factories/terms.rb @@ -8,5 +8,13 @@ trait :summer do season { "SS" } end + + trait :winter do + season { "WS" } + end + + trait :active do + active { true } + end end end diff --git a/spec/models/lecture_spec.rb b/spec/models/lecture_spec.rb index d2ba05682..59966f827 100644 --- a/spec/models/lecture_spec.rb +++ b/spec/models/lecture_spec.rb @@ -86,6 +86,50 @@ end end + describe "#stale?" do + context "when there is no active term" do + it "returns false" do + lecture = FactoryBot.build(:lecture) + expect(lecture.stale?).to be(false) + end + end + + context "when there is an active term" do + let(:year) { 2024 } + + before(:each) do + FactoryBot.create(:term, :summer, :active, year: year) + end + + context "and there is no term associated with the lecture" do + it "returns true" do + lecture = FactoryBot.build(:lecture, :term_independent) + expect(lecture.stale?).to be(true) + end + end + + context "and the lecture term begin date is before the active term" \ + "begin date minus 1 year" do + let(:lecture_term) { FactoryBot.build(:term, :summer, year: year - 1) } + + it "returns true" do + lecture = FactoryBot.build(:lecture, term: lecture_term) + expect(lecture.stale?).to be(true) + end + end + + context "when the lecture term begin date is not older than the" \ + "active term begin date minus 1 year" do + let(:lecture_term) { FactoryBot.build(:term, :winter, year: year - 1) } + + it "returns false" do + lecture = FactoryBot.build(:lecture, term: lecture_term) + expect(lecture.stale?).to be(false) + end + end + end + end + # Test methods -- NEEDS TO BE REFACTORED # describe '#tags' do