diff --git a/cypress.config.ts b/cypress.config.ts index 26d306c5..0297ae27 100644 --- a/cypress.config.ts +++ b/cypress.config.ts @@ -1,7 +1,7 @@ import { defineConfig } from "cypress"; import { grantAdminRole, deleteUser } from "./cypress/e2e/contexts/user/tasks"; import { deleteTorrent } from "./cypress/e2e/contexts/torrent/tasks"; -import { deleteCategory } from "./cypress/e2e/contexts/category/tasks"; +import { deleteCategory, addCategory } from "./cypress/e2e/contexts/category/tasks"; import { DatabaseConfig } from "./cypress/e2e/common/database"; function databaseConfig (config: Cypress.PluginConfigOptions): DatabaseConfig { @@ -15,17 +15,23 @@ export default defineConfig({ baseUrl: "http://localhost:3000", setupNodeEvents (on, config) { on("task", { - grantAdminRole: ({ username }) => { - return grantAdminRole(username, databaseConfig(config)); + // Category context + deleteCategory: ({ name }) => { + return deleteCategory(name, databaseConfig(config)); + }, + addCategory: ({ name }) => { + return addCategory(name, databaseConfig(config)); }, + // Torrent context deleteTorrent: ({ infohash }) => { return deleteTorrent(infohash, databaseConfig(config)); }, + // User context + grantAdminRole: ({ username }) => { + return grantAdminRole(username, databaseConfig(config)); + }, deleteUser: ({ username }) => { return deleteUser(username, databaseConfig(config)); - }, - deleteCategory: ({ name }) => { - return deleteCategory(name, databaseConfig(config)); } }); } diff --git a/cypress/e2e/common/commands.ts b/cypress/e2e/common/commands.ts new file mode 100644 index 00000000..1f01f832 --- /dev/null +++ b/cypress/e2e/common/commands.ts @@ -0,0 +1,6 @@ +// Common commands + +Cypress.Commands.add("go_to_settings", () => { + cy.get("div[data-cy=\"user-menu\"]").click(); + cy.get("li[data-cy=\"admin-settings-link\"]").click(); +}); diff --git a/cypress/e2e/contexts/category/commands.ts b/cypress/e2e/contexts/category/commands.ts index 9bc06db0..2f1c492b 100644 --- a/cypress/e2e/contexts/category/commands.ts +++ b/cypress/e2e/contexts/category/commands.ts @@ -1,5 +1,9 @@ // Custom commands for category context -Cypress.Commands.add("delete_category", (name) => { +Cypress.Commands.add("delete_category_from_database", (name) => { cy.task("deleteCategory", { name }); }); + +Cypress.Commands.add("add_category_to_database", (name) => { + cy.task("addCategory", { name }); +}); diff --git a/cypress/e2e/contexts/category/fixtures.ts b/cypress/e2e/contexts/category/fixtures.ts new file mode 100644 index 00000000..cbde8076 --- /dev/null +++ b/cypress/e2e/contexts/category/fixtures.ts @@ -0,0 +1,7 @@ +export function random_category_name (): string { + return `category-${random_category_id()}`; +} + +function random_category_id (): number { + return Math.floor(Math.random() * 1000000); +} diff --git a/cypress/e2e/contexts/category/specs/add.cy.ts b/cypress/e2e/contexts/category/specs/add.cy.ts index dd244657..49ffbe07 100644 --- a/cypress/e2e/contexts/category/specs/add.cy.ts +++ b/cypress/e2e/contexts/category/specs/add.cy.ts @@ -1,4 +1,5 @@ import { RegistrationForm, random_user_registration_data } from "../../user/registration"; +import { random_category_name } from "../fixtures"; describe("The admin user", () => { let registration_form: RegistrationForm; @@ -9,30 +10,30 @@ describe("The admin user", () => { }); after(() => { - cy.delete_user(registration_form.username); + cy.delete_user_from_database(registration_form.username); }); it("should be able to add a new category", () => { + const category_name = random_category_name(); + // Make sure the category does not exist - cy.delete_category("new category"); + cy.delete_category_from_database(category_name); - // Go to admin settings - cy.get("div[data-cy=\"user-menu\"]").click(); - cy.get("li[data-cy=\"admin-settings-link\"]").click(); + cy.go_to_settings(); // Click categories tab cy.contains("a", "categories").click(); // Fill new category name - cy.get("input[data-cy=\"add-category-input\"]").type("new category"); + cy.get("input[data-cy=\"add-category-input\"]").type(category_name); // Add category cy.get("button[data-cy=\"add-category-button\"]").click(); // The new category should appear in the list - cy.contains("new category (0)"); + cy.contains(`${category_name} (0)`); - cy.delete_category("new category"); + cy.delete_category_from_database(category_name); }); }); @@ -45,12 +46,11 @@ describe("A non admin authenticated user", () => { }); after(() => { - cy.delete_user(registration_form.username); + cy.delete_user_from_database(registration_form.username); }); it("should not be able to add a new category", () => { cy.visit("/admin/settings/categories"); - cy.contains("Please login to manage admin settings."); }); }); @@ -58,7 +58,6 @@ describe("A non admin authenticated user", () => { describe("A guest user", () => { it("should not be able to add a new category", () => { cy.visit("/admin/settings/categories"); - cy.contains("Please login to manage admin settings."); }); }); diff --git a/cypress/e2e/contexts/category/specs/delete.cy.ts b/cypress/e2e/contexts/category/specs/delete.cy.ts new file mode 100644 index 00000000..6a38f77c --- /dev/null +++ b/cypress/e2e/contexts/category/specs/delete.cy.ts @@ -0,0 +1,64 @@ +import { RegistrationForm, random_user_registration_data } from "../../user/registration"; +import { random_category_name } from "../fixtures"; + +describe("The admin user", () => { + let registration_form: RegistrationForm; + + before(() => { + registration_form = random_user_registration_data(); + cy.register_as_admin_and_login(registration_form); + }); + + after(() => { + cy.delete_user_from_database(registration_form.username); + }); + + it("should be able to delete a category", () => { + const category_name = random_category_name(); + + cy.add_category_to_database(category_name); + + cy.go_to_settings(); + + // Click categories tab + cy.contains("a", "categories").click(); + + // Delete the category + cy.get(`button[data-cy="delete-category-${category_name}"]`).click(); + + // Confirm alert should pop up + cy.on("window:confirm", (str) => { + expect(str).to.equal(`Are you sure you want to delete ${category_name}?`); + }); + + // Confirm delete + cy.on("window:confirm", () => true); + + cy.get(`[data-cy="delete-category-${category_name}"]`).should("not.exist"); + }); +}); + +describe("A non admin authenticated user", () => { + let registration_form: RegistrationForm; + + before(() => { + registration_form = random_user_registration_data(); + cy.register_and_login(registration_form); + }); + + after(() => { + cy.delete_user_from_database(registration_form.username); + }); + + it("should not be able to delete category", () => { + cy.visit("/admin/settings/categories"); + cy.contains("Please login to manage admin settings."); + }); +}); + +describe("A guest user", () => { + it("should not be able to delete a category", () => { + cy.visit("/admin/settings/categories"); + cy.contains("Please login to manage admin settings."); + }); +}); diff --git a/cypress/e2e/contexts/category/tasks.ts b/cypress/e2e/contexts/category/tasks.ts index 4273bdae..3ff4660f 100644 --- a/cypress/e2e/contexts/category/tasks.ts +++ b/cypress/e2e/contexts/category/tasks.ts @@ -12,6 +12,16 @@ export const deleteCategory = async (name: string, db_config: DatabaseConfig): P } }; +// Task to add a new category +export const addCategory = async (name: string, db_config: DatabaseConfig): Promise => { + try { + const result = await runDatabaseQuery(addCategoryQuery(name), db_config); + return name; + } catch (err) { + return await Promise.reject(err); + } +}; + // Database query specifications function deleteCategoryQuery (name: string): DatabaseQuery { @@ -20,3 +30,10 @@ function deleteCategoryQuery (name: string): DatabaseQuery { params: [name] }; } + +function addCategoryQuery (name: string): DatabaseQuery { + return { + query: "INSERT INTO torrust_categories (name) VALUES (?)", + params: [name] + }; +} diff --git a/cypress/e2e/contexts/torrent/commands.ts b/cypress/e2e/contexts/torrent/commands.ts index 7691b1c7..47d76bca 100644 --- a/cypress/e2e/contexts/torrent/commands.ts +++ b/cypress/e2e/contexts/torrent/commands.ts @@ -33,7 +33,7 @@ Cypress.Commands.add("upload_torrent", (torrent_info) => { cy.get("button[data-cy=\"upload-form-submit\"]").click(); }); -Cypress.Commands.add("delete_torrent", (torrent_info, infohash) => { +Cypress.Commands.add("delete_torrent_from_database_and_fixture", (torrent_info, infohash) => { // Delete the torrent in the database cy.task("deleteTorrent", { infohash }); diff --git a/cypress/e2e/contexts/torrent/specs/download.cy.ts b/cypress/e2e/contexts/torrent/specs/download.cy.ts index 50b97951..7ed7500f 100644 --- a/cypress/e2e/contexts/torrent/specs/download.cy.ts +++ b/cypress/e2e/contexts/torrent/specs/download.cy.ts @@ -11,7 +11,7 @@ describe("A registered user", () => { }); after(() => { - cy.delete_user(registration_form.username); + cy.delete_user_from_database(registration_form.username); }); it("should be able to download a preexisting torrent", () => { @@ -32,7 +32,7 @@ describe("A registered user", () => { // Delete the test torrent generated for this test const torrentInfoHash = parseInfoHash(interception.response.headers["x-torrust-torrent-infohash"]); - cy.delete_torrent(torrent_info, torrentInfoHash); + cy.delete_torrent_from_database_and_fixture(torrent_info, torrentInfoHash); }); }); }); @@ -49,7 +49,7 @@ describe("A guest user", () => { }); after(() => { - cy.delete_user(uploader_registration_form.username); + cy.delete_user_from_database(uploader_registration_form.username); }); it("should be able to download a preexisting torrent", () => { @@ -79,7 +79,7 @@ describe("A guest user", () => { // Delete the test torrent generated for this test const torrentInfoHash = parseInfoHash(interception.response.headers["x-torrust-torrent-infohash"]); - cy.delete_torrent(torrent_info, torrentInfoHash); + cy.delete_torrent_from_database_and_fixture(torrent_info, torrentInfoHash); }); }); }); diff --git a/cypress/e2e/contexts/torrent/specs/upload.cy.ts b/cypress/e2e/contexts/torrent/specs/upload.cy.ts index b28eb201..d9cda849 100644 --- a/cypress/e2e/contexts/torrent/specs/upload.cy.ts +++ b/cypress/e2e/contexts/torrent/specs/upload.cy.ts @@ -11,7 +11,7 @@ describe("A registered user", () => { }); after(() => { - cy.delete_user(registration_form.username); + cy.delete_user_from_database(registration_form.username); }); it("should be able to upload a torrent", () => { diff --git a/cypress/e2e/contexts/user/commands.ts b/cypress/e2e/contexts/user/commands.ts index 8ee08694..7489b01d 100644 --- a/cypress/e2e/contexts/user/commands.ts +++ b/cypress/e2e/contexts/user/commands.ts @@ -21,7 +21,7 @@ Cypress.Commands.add("register_as_admin", (registration_form) => { cy.task("grantAdminRole", { username: registration_form.username }); }); -Cypress.Commands.add("delete_user", (username) => { +Cypress.Commands.add("delete_user_from_database", (username) => { cy.task("deleteUser", { username }); }); diff --git a/cypress/e2e/contexts/user/specs/authentication.cy.ts b/cypress/e2e/contexts/user/specs/authentication.cy.ts index 8a039152..1bb5e9c2 100644 --- a/cypress/e2e/contexts/user/specs/authentication.cy.ts +++ b/cypress/e2e/contexts/user/specs/authentication.cy.ts @@ -21,7 +21,7 @@ describe("A registered user", () => { cy.url().should("include", "/torrents"); - cy.delete_user(registration_form.username); + cy.delete_user_from_database(registration_form.username); }); }); @@ -40,6 +40,6 @@ describe("The website admin", () => { // If the user is an admin, the link to admin settings should be available cy.get("li[data-cy=\"admin-settings-link\"]"); - cy.delete_user(registration_form.username); + cy.delete_user_from_database(registration_form.username); }); }); diff --git a/cypress/support/commands.ts b/cypress/support/commands.ts index 506f37fd..9296f10c 100644 --- a/cypress/support/commands.ts +++ b/cypress/support/commands.ts @@ -1,27 +1,36 @@ import "../e2e/contexts/user/commands"; import "../e2e/contexts/torrent/commands"; import "../e2e/contexts/category/commands"; +import "../e2e/common/commands"; import { RegistrationForm } from "../e2e/contexts/user/registration"; import { TestTorrentInfo } from "cypress/e2e/contexts/torrent/test_torrent_info"; declare global { namespace Cypress { interface Chainable { - // User: Registration + // Common command + go_to_settings(): Chainable + + // User context: Registration register(registration_form: RegistrationForm): Chainable register_as_admin(registration_form: RegistrationForm): Chainable - delete_user(username: string): Chainable - // User: Authentication + + // User context: Authentication login(username: string, password: string): Chainable logout(): Chainable - // User: others + + // User context: Others register_and_login(registration_form: RegistrationForm): Chainable register_as_admin_and_login(registration_form: RegistrationForm): Chainable - // Torrent + delete_user_from_database(username: string): Chainable + + // Torrent context upload_torrent(torrent_info: TestTorrentInfo): Chainable - delete_torrent(torrent_info: TestTorrentInfo, infohash: string): Chainable - // Category - delete_category(name: string): Chainable + delete_torrent_from_database_and_fixture(torrent_info: TestTorrentInfo, infohash: string): Chainable + + // Category context + delete_category_from_database(name: string): Chainable + add_category_to_database(name: string): Chainable } } } diff --git a/pages/admin/settings/categories.vue b/pages/admin/settings/categories.vue index 6fde6b6b..38dcb918 100644 --- a/pages/admin/settings/categories.vue +++ b/pages/admin/settings/categories.vue @@ -4,7 +4,7 @@