From 9d23dae71ec78a11dcb4d85d0b918edaa36bde85 Mon Sep 17 00:00:00 2001 From: Martin Renvoize Date: Fri, 18 Jul 2025 11:52:08 +0100 Subject: [PATCH] Bug 40447: Add comprehensive JSDoc documentation to Cypress plugins This patch adds detailed JSDoc documentation to all Cypress testing plugin files. Documentation added to: - insertData.js: Data insertion utilities with examples - api-client.js: API wrapper functions with usage patterns - mockData.js: Schema-based mock data generation - auth.js: Authentication helper functions - db.js: Database query utilities - readYamlFile.js: YAML file reading utilities - index.js: Main plugin configuration Each function now includes: - Complete parameter descriptions with types - Return value documentation - Practical usage examples - Error conditions and handling - Module-level overviews This addresses the documentation gap identified during QA review, hopefully making it easier for future devs to adopt the new framework of tools. --- t/cypress/plugins/api-client.js | 121 ++++++++++++++++++ t/cypress/plugins/auth.js | 28 +++++ t/cypress/plugins/db.js | 49 ++++++++ t/cypress/plugins/index.js | 48 +++++++ t/cypress/plugins/insertData.js | 203 ++++++++++++++++++++++++++++++ t/cypress/plugins/mockData.js | 120 ++++++++++++++++++ t/cypress/plugins/readYamlFile.js | 31 +++++ 7 files changed, 600 insertions(+) diff --git a/t/cypress/plugins/api-client.js b/t/cypress/plugins/api-client.js index db246e419e4..750b5d1a3dc 100644 --- a/t/cypress/plugins/api-client.js +++ b/t/cypress/plugins/api-client.js @@ -1,7 +1,33 @@ +/** + * Koha API Client for Cypress Testing + * + * This module provides a wrapper around the Koha API client for use in Cypress tests. + * It handles authentication, request preparation, and provides convenient methods + * for making API calls during test execution. + * + * @module api-client + */ + const { APIClient } = require("./dist/api-client.cjs.js"); const client = APIClient.default.koha; +/** + * Prepares request parameters for API calls by extracting and organizing headers and URL. + * + * @function prepareRequest + * @param {Object} params - Request parameters + * @param {string} params.baseUrl - Base URL for the API + * @param {string} params.endpoint - API endpoint path + * @param {string} [params.authHeader] - Authorization header value + * @param {Object} [params.headers={}] - Additional headers to include + * @param {...*} params.rest - Other parameters to pass through + * @returns {Object} Prepared request object + * @returns {string} returns.url - Complete URL for the request + * @returns {Object} returns.headers - Combined headers object + * @returns {Object} returns.rest - Pass-through parameters + * @private + */ const prepareRequest = params => { const { baseUrl, endpoint, authHeader, headers = {}, ...rest } = params; const url = baseUrl + endpoint; @@ -12,6 +38,33 @@ const prepareRequest = params => { return { url, headers: finalHeaders, rest }; }; +/** + * Performs a GET request to the Koha API. + * + * @function apiGet + * @param {Object} params - Request parameters + * @param {string} params.baseUrl - Base URL for the API + * @param {string} params.endpoint - API endpoint path + * @param {string} [params.authHeader] - Authorization header value + * @param {Object} [params.headers={}] - Additional headers to include + * @param {...*} params.rest - Additional parameters for the request + * @returns {Promise<*>} API response data + * @example + * // Get a list of patrons + * const patrons = await apiGet({ + * baseUrl: 'http://localhost:8081', + * endpoint: '/api/v1/patrons', + * authHeader: 'Basic dGVzdDp0ZXN0' + * }); + * + * @example + * // Get a specific patron with query parameters + * const patron = await apiGet({ + * baseUrl: 'http://localhost:8081', + * endpoint: '/api/v1/patrons?q={"patron_id":123}', + * authHeader: 'Basic dGVzdDp0ZXN0' + * }); + */ const apiGet = params => { const { url, headers, rest } = prepareRequest(params); return client.get({ @@ -21,6 +74,32 @@ const apiGet = params => { }); }; +/** + * Performs a POST request to the Koha API. + * + * @function apiPost + * @param {Object} params - Request parameters + * @param {string} params.baseUrl - Base URL for the API + * @param {string} params.endpoint - API endpoint path + * @param {string} [params.authHeader] - Authorization header value + * @param {Object} [params.headers={}] - Additional headers to include + * @param {Object} [params.body] - Request body data + * @param {...*} params.rest - Additional parameters for the request + * @returns {Promise<*>} API response data + * @example + * // Create a new patron + * const newPatron = await apiPost({ + * baseUrl: 'http://localhost:8081', + * endpoint: '/api/v1/patrons', + * authHeader: 'Basic dGVzdDp0ZXN0', + * body: { + * firstname: 'John', + * surname: 'Doe', + * library_id: 'CPL', + * category_id: 'PT' + * } + * }); + */ const apiPost = params => { const { url, headers, rest } = prepareRequest(params); return client.post({ @@ -30,6 +109,29 @@ const apiPost = params => { }); }; +/** + * Performs a PUT request to the Koha API. + * + * @function apiPut + * @param {Object} params - Request parameters + * @param {string} params.baseUrl - Base URL for the API + * @param {string} params.endpoint - API endpoint path + * @param {string} [params.authHeader] - Authorization header value + * @param {Object} [params.headers={}] - Additional headers to include + * @param {Object} [params.body] - Request body data + * @param {...*} params.rest - Additional parameters for the request + * @returns {Promise<*>} API response data + * @example + * // Update a patron + * const updatedPatron = await apiPut({ + * baseUrl: 'http://localhost:8081', + * endpoint: '/api/v1/patrons/123', + * authHeader: 'Basic dGVzdDp0ZXN0', + * body: { + * email: 'newemail@example.com' + * } + * }); + */ const apiPut = params => { const { url, headers, rest } = prepareRequest(params); return client.put({ @@ -39,6 +141,25 @@ const apiPut = params => { }); }; +/** + * Performs a DELETE request to the Koha API. + * + * @function apiDelete + * @param {Object} params - Request parameters + * @param {string} params.baseUrl - Base URL for the API + * @param {string} params.endpoint - API endpoint path + * @param {string} [params.authHeader] - Authorization header value + * @param {Object} [params.headers={}] - Additional headers to include + * @param {...*} params.rest - Additional parameters for the request + * @returns {Promise<*>} API response data + * @example + * // Delete a patron + * await apiDelete({ + * baseUrl: 'http://localhost:8081', + * endpoint: '/api/v1/patrons/123', + * authHeader: 'Basic dGVzdDp0ZXN0' + * }); + */ const apiDelete = params => { const { url, headers, rest } = prepareRequest(params); return client.delete({ diff --git a/t/cypress/plugins/auth.js b/t/cypress/plugins/auth.js index 6104c0c626d..1f01366ee5d 100644 --- a/t/cypress/plugins/auth.js +++ b/t/cypress/plugins/auth.js @@ -1,5 +1,33 @@ +/** + * Authentication utilities for Cypress testing + * + * This module provides authentication helper functions for use in Cypress tests + * when making API calls that require authentication. + * + * @module auth + */ + const { Buffer } = require("buffer"); +/** + * Generates a Basic Authentication header from username and password. + * + * @function getBasicAuthHeader + * @param {string} username - Username for authentication + * @param {string} password - Password for authentication + * @returns {string} Basic authentication header value in format "Basic " + * @example + * // Generate auth header for API calls + * const authHeader = getBasicAuthHeader('koha', 'koha'); + * // Returns: "Basic a29oYTprb2hh" + * + * // Use with API client + * const response = await apiGet({ + * baseUrl: 'http://localhost:8081', + * endpoint: '/api/v1/patrons', + * authHeader: getBasicAuthHeader('koha', 'koha') + * }); + */ const getBasicAuthHeader = (username, password) => { const credentials = Buffer.from(`${username}:${password}`).toString( "base64" diff --git a/t/cypress/plugins/db.js b/t/cypress/plugins/db.js index fb4568886cf..92f5234d460 100644 --- a/t/cypress/plugins/db.js +++ b/t/cypress/plugins/db.js @@ -1,5 +1,21 @@ +/** + * Database Query Utilities for Cypress Testing + * + * This module provides direct database access for Cypress tests when API + * endpoints are not available or when direct database operations are needed + * for test setup and cleanup. + * + * @module db + */ + const mysql = require("mysql2/promise"); +/** + * Database connection configuration + * + * @todo Replace hardcoded credentials with environment variables + * @type {Object} + */ const connectionConfig = { host: "db", user: "koha_kohadev", @@ -7,6 +23,39 @@ const connectionConfig = { database: "koha_kohadev", }; +/** + * Executes a SQL query with optional parameters. + * + * @async + * @function query + * @param {string} sql - SQL query string with optional parameter placeholders (?) + * @param {Array} [params=[]] - Array of parameter values for the query + * @returns {Promise} Query results as an array of rows + * @throws {Error} When database connection or query execution fails + * @description This function: + * - Creates a new database connection for each query + * - Uses parameterized queries to prevent SQL injection + * - Automatically closes the connection after execution + * - Returns the raw result rows from the database + * + * @example + * // Simple SELECT query + * const patrons = await query('SELECT * FROM borrowers LIMIT 10'); + * + * @example + * // Parameterized query for safety + * const patron = await query( + * 'SELECT * FROM borrowers WHERE borrowernumber = ?', + * [123] + * ); + * + * @example + * // DELETE query with multiple parameters + * await query( + * 'DELETE FROM issues WHERE issue_id IN (?, ?, ?)', + * [1, 2, 3] + * ); + */ async function query(sql, params = []) { const connection = await mysql.createConnection(connectionConfig); const [rows] = await connection.execute(sql, params); diff --git a/t/cypress/plugins/index.js b/t/cypress/plugins/index.js index 2f919b78b2f..5970890dc02 100644 --- a/t/cypress/plugins/index.js +++ b/t/cypress/plugins/index.js @@ -1,3 +1,14 @@ +/** + * Cypress Plugin Configuration + * + * This is the main Cypress plugin configuration file that registers all + * testing utilities as Cypress tasks. It provides a bridge between Cypress + * tests and the various utility modules for data generation, API access, + * and database operations. + * + * @module cypress-plugins + */ + const { startDevServer } = require("@cypress/webpack-dev-server"); const { buildSampleObject, buildSampleObjects } = require("./mockData.js"); @@ -17,6 +28,43 @@ const { query } = require("./db.js"); const { apiGet, apiPost, apiPut, apiDelete } = require("./api-client.js"); +/** + * Cypress plugin configuration function. + * + * @function + * @param {Function} on - Cypress plugin registration function + * @param {Object} config - Cypress configuration object + * @param {string} config.baseUrl - Base URL for the application under test + * @param {Object} config.env - Environment variables from cypress.config.js + * @param {string} config.env.apiUsername - Username for API authentication + * @param {string} config.env.apiPassword - Password for API authentication + * @returns {Object} Modified Cypress configuration + * @description This function: + * - Registers all testing utilities as Cypress tasks + * - Sets up authentication headers for API calls + * - Configures the development server for component testing + * - Provides automatic parameter injection for common arguments + * + * Available Cypress tasks: + * - Data Generation: buildSampleObject, buildSampleObjects + * - Data Insertion: insertSampleBiblio, insertSampleHold, insertSampleCheckout, insertSamplePatron + * - Data Cleanup: deleteSampleObjects + * - API Access: apiGet, apiPost, apiPut, apiDelete + * - Database Access: query + * - Authentication: getBasicAuthHeader + * + * @example + * // Usage in Cypress tests + * cy.task('insertSampleBiblio', { item_count: 2 }).then(result => { + * // Test with the created biblio + * }); + * + * @example + * // API call through task + * cy.task('apiGet', { endpoint: '/api/v1/patrons' }).then(patrons => { + * // Work with patron data + * }); + */ module.exports = (on, config) => { const baseUrl = config.baseUrl; const authHeader = getBasicAuthHeader( diff --git a/t/cypress/plugins/insertData.js b/t/cypress/plugins/insertData.js index 4b522b2dc9a..3aa062611a3 100644 --- a/t/cypress/plugins/insertData.js +++ b/t/cypress/plugins/insertData.js @@ -1,8 +1,51 @@ +/** + * Koha Cypress Testing Data Insertion Utilities + * + * This module provides functions to create and manage test data for Cypress tests. + * It handles creating complete bibliographic records, patrons, holds, checkouts, + * and other Koha objects with proper relationships and dependencies. + * + * @module insertData + */ + const { buildSampleObject, buildSampleObjects } = require("./mockData.js"); const { query } = require("./db.js"); const { apiGet, apiPost } = require("./api-client.js"); +/** + * Creates a complete bibliographic record with associated items and libraries. + * + * @async + * @function insertSampleBiblio + * @param {Object} params - Configuration parameters + * @param {number} params.item_count - Number of items to create for this biblio + * @param {Object} [params.options] - Additional options + * @param {boolean} [params.options.different_libraries] - If true, creates different libraries for each item + * @param {string} params.baseUrl - Base URL for API calls + * @param {string} params.authHeader - Authorization header for API calls + * @returns {Promise} Created biblio with items, libraries, and item_type + * @returns {Object} returns.biblio - The created bibliographic record + * @returns {Array} returns.items - Array of created item records + * @returns {Array} returns.libraries - Array of created library records + * @returns {Object} returns.item_type - The created item type record + * @example + * // Create a biblio with 3 items using the same library + * const result = await insertSampleBiblio({ + * item_count: 3, + * baseUrl: 'http://localhost:8081', + * authHeader: 'Basic dGVzdDp0ZXN0' + * }); + * + * @example + * // Create a biblio with 2 items using different libraries + * const result = await insertSampleBiblio({ + * item_count: 2, + * options: { different_libraries: true }, + * baseUrl: 'http://localhost:8081', + * authHeader: 'Basic dGVzdDp0ZXN0' + * }); + */ const insertSampleBiblio = async ({ item_count, options, @@ -160,6 +203,39 @@ const insertSampleBiblio = async ({ return { biblio, items: createdItems, libraries, item_type }; }; +/** + * Creates a hold request for a bibliographic record or item. + * + * @async + * @function insertSampleHold + * @param {Object} params - Configuration parameters + * @param {Object} [params.item] - Item to place hold on (optional if biblio provided) + * @param {Object} [params.biblio] - Biblio to place hold on (optional if item provided) + * @param {string} [params.library_id] - Library ID for pickup location (defaults to item's home library) + * @param {string} params.baseUrl - Base URL for API calls + * @param {string} params.authHeader - Authorization header for API calls + * @returns {Promise} Created hold with associated patron and patron_category + * @returns {Object} returns.hold - The created hold record + * @returns {Object} returns.patron - The patron who placed the hold + * @returns {Object} returns.patron_category - The patron's category + * @throws {Error} When neither library_id nor item is provided + * @example + * // Create a hold on a specific item + * const holdResult = await insertSampleHold({ + * item: { item_id: 123, home_library_id: 'CPL' }, + * baseUrl: 'http://localhost:8081', + * authHeader: 'Basic dGVzdDp0ZXN0' + * }); + * + * @example + * // Create a biblio-level hold + * const holdResult = await insertSampleHold({ + * biblio: { biblio_id: 456 }, + * library_id: 'CPL', + * baseUrl: 'http://localhost:8081', + * authHeader: 'Basic dGVzdDp0ZXN0' + * }); + */ const insertSampleHold = async ({ item, biblio, @@ -199,6 +275,38 @@ const insertSampleHold = async ({ return { hold, patron, patron_category }; }; +/** + * Creates a checkout record with associated biblio, item, and optional patron. + * + * @async + * @function insertSampleCheckout + * @param {Object} params - Configuration parameters + * @param {Object} [params.patron] - Existing patron to check out to (creates new if not provided) + * @param {string} params.baseUrl - Base URL for API calls + * @param {string} params.authHeader - Authorization header for API calls + * @returns {Promise} Created checkout with all associated records + * @returns {Object} returns.biblio - The bibliographic record + * @returns {Array} returns.items - Array of item records + * @returns {Array} returns.libraries - Array of library records + * @returns {Object} returns.item_type - The item type record + * @returns {Object} returns.checkout - The checkout record + * @returns {Object} [returns.patron] - The patron record (if generated) + * @returns {Object} [returns.patron_category] - The patron category (if generated) + * @example + * // Create a checkout with a new patron + * const checkoutResult = await insertSampleCheckout({ + * baseUrl: 'http://localhost:8081', + * authHeader: 'Basic dGVzdDp0ZXN0' + * }); + * + * @example + * // Create a checkout for an existing patron + * const checkoutResult = await insertSampleCheckout({ + * patron: { patron_id: 123 }, + * baseUrl: 'http://localhost:8081', + * authHeader: 'Basic dGVzdDp0ZXN0' + * }); + */ const insertSampleCheckout = async ({ patron, baseUrl, authHeader }) => { const { biblio, items, libraries, item_type } = await insertSampleBiblio({ item_count: 1, @@ -248,6 +356,35 @@ const insertSampleCheckout = async ({ patron, baseUrl, authHeader }) => { }; }; +/** + * Creates a patron record with associated library and category. + * + * @async + * @function insertSamplePatron + * @param {Object} params - Configuration parameters + * @param {Object} [params.library] - Library to assign patron to (creates new if not provided) + * @param {Object} [params.patron_category] - Patron category to assign (creates new if not provided) + * @param {string} params.baseUrl - Base URL for API calls + * @param {string} params.authHeader - Authorization header for API calls + * @returns {Promise} Created patron with associated records + * @returns {Object} returns.patron - The created patron record + * @returns {Object} [returns.library] - The library record (if generated) + * @returns {Object} [returns.patron_category] - The patron category record (if generated) + * @example + * // Create a patron with new library and category + * const patronResult = await insertSamplePatron({ + * baseUrl: 'http://localhost:8081', + * authHeader: 'Basic dGVzdDp0ZXN0' + * }); + * + * @example + * // Create a patron for an existing library + * const patronResult = await insertSamplePatron({ + * library: { library_id: 'CPL' }, + * baseUrl: 'http://localhost:8081', + * authHeader: 'Basic dGVzdDp0ZXN0' + * }); + */ const insertSamplePatron = async ({ library, patron_category, @@ -324,6 +461,33 @@ const insertSamplePatron = async ({ }; }; +/** + * Deletes test objects from the database in the correct order to respect foreign key constraints. + * + * @async + * @function deleteSampleObjects + * @param {Object|Array} allObjects - Object(s) to delete, can be single object or array + * @returns {Promise} True if deletion was successful + * @description This function handles cleanup of test data by: + * - Accepting single objects or arrays of objects + * - Grouping objects by type (holds, checkouts, patrons, items, etc.) + * - Deleting in dependency order to avoid foreign key violations + * - Supporting all major Koha object types + * @example + * // Delete a single test result + * await deleteSampleObjects(checkoutResult); + * + * @example + * // Delete multiple test results + * await deleteSampleObjects([biblioResult, holdResult, checkoutResult]); + * + * @example + * // Delete after creating test data + * const biblio = await insertSampleBiblio({ item_count: 2, baseUrl, authHeader }); + * const hold = await insertSampleHold({ item: biblio.items[0], baseUrl, authHeader }); + * // ... run tests ... + * await deleteSampleObjects([biblio, hold]); + */ const deleteSampleObjects = async allObjects => { if (!Array.isArray(allObjects)) { allObjects = [allObjects]; @@ -458,6 +622,20 @@ const deleteSampleObjects = async allObjects => { return true; }; +/** + * Creates a library record via API, filtering out unsupported fields. + * + * @async + * @function insertLibrary + * @param {Object} params - Configuration parameters + * @param {Object} params.library - Library object to insert + * @param {string} params.baseUrl - Base URL for API calls + * @param {string} params.authHeader - Authorization header for API calls + * @returns {Promise} Created library record + * @private + * @description This is a helper function that removes fields not supported by the API + * before creating the library record. + */ const insertLibrary = async ({ library, baseUrl, authHeader }) => { const { pickup_items, @@ -476,6 +654,31 @@ const insertLibrary = async ({ library, baseUrl, authHeader }) => { }); }; +/** + * Generic function to insert various types of Koha objects. + * + * @async + * @function insertObject + * @param {Object} params - Configuration parameters + * @param {string} params.type - Type of object to insert ('library', 'item_type', 'hold', 'checkout', 'vendor', 'basket') + * @param {Object} params.object - Object data to insert + * @param {string} params.baseUrl - Base URL for API calls + * @param {string} params.authHeader - Authorization header for API calls + * @returns {Promise} Created object or true if successful + * @throws {Error} When object type is not supported + * @private + * @description This is a generic helper function that handles the specifics of creating + * different types of Koha objects. Each object type may require different field filtering, + * API endpoints, or database operations. + * + * Supported object types: + * - library: Creates library via API + * - item_type: Creates item type via database query + * - hold: Creates hold via API + * - checkout: Creates checkout via API with confirmation token support + * - vendor: Creates vendor via API + * - basket: Creates basket via database query + */ const insertObject = async ({ type, object, baseUrl, authHeader }) => { if (type == "library") { const keysToKeep = ["library_id", "name"]; diff --git a/t/cypress/plugins/mockData.js b/t/cypress/plugins/mockData.js index a6fe15a099d..c7de7e052e9 100644 --- a/t/cypress/plugins/mockData.js +++ b/t/cypress/plugins/mockData.js @@ -1,10 +1,42 @@ +/** + * Mock Data Generation for Cypress Testing + * + * This module provides functions to generate realistic test data for Koha objects + * based on OpenAPI schema definitions. It uses Faker.js to generate random data + * that conforms to the API specifications. + * + * @module mockData + */ + const { faker } = require("@faker-js/faker"); const { readYamlFile } = require("./../plugins/readYamlFile.js"); const { query } = require("./db.js"); const fs = require("fs"); +/** + * Cache to store generated ID values to prevent duplicates + * @type {Set} + */ const generatedDataCache = new Set(); +/** + * Generates mock data for a specific data type based on OpenAPI schema properties. + * + * @function generateMockData + * @param {string} type - The data type (string, integer, boolean, array, number, date, date-time) + * @param {Object} properties - OpenAPI schema properties for the field + * @param {Array} [properties.enum] - Enumerated values to choose from + * @param {number} [properties.maxLength] - Maximum length for strings + * @param {number} [properties.minLength] - Minimum length for strings + * @returns {*} Generated mock data appropriate for the type + * @private + * @example + * // Generate a string with max length 50 + * const name = generateMockData('string', { maxLength: 50 }); + * + * // Generate from enum values + * const status = generateMockData('string', { enum: ['active', 'inactive'] }); + */ const generateMockData = (type, properties) => { if (properties.hasOwnProperty("enum")) { let values = properties.enum; @@ -55,6 +87,26 @@ const generateMockData = (type, properties) => { } }; +/** + * Generates mock data for an entire object based on OpenAPI schema properties. + * + * @function generateDataFromSchema + * @param {Object} properties - OpenAPI schema properties object + * @param {Object} [values={}] - Override values for specific fields + * @returns {Object} Generated mock object with all required fields + * @private + * @description This function: + * - Iterates through all properties in the schema + * - Generates appropriate mock data for each field + * - Handles object relationships (libraries, items, etc.) + * - Ensures unique values for ID fields + * - Applies any override values provided + * + * Special handling for object relationships: + * - home_library/holding_library -> generates library object + * - item_type -> generates item_type object + * - Automatically sets corresponding _id fields + */ const generateDataFromSchema = (properties, values = {}) => { const mockData = {}; const ids = {}; @@ -149,6 +201,37 @@ const generateDataFromSchema = (properties, values = {}) => { return mockData; }; +/** + * Builds an array of sample objects based on OpenAPI schema definitions. + * + * @function buildSampleObjects + * @param {Object} params - Configuration parameters + * @param {string} params.object - Object type to generate (must match YAML file name) + * @param {Object} [params.values] - Override values for specific fields + * @param {number} [params.count=1] - Number of objects to generate + * @returns {Array} Array of generated objects + * @throws {Error} When object type is not supported or generation fails + * @description This function: + * - Reads the OpenAPI schema from api/v1/swagger/definitions/{object}.yaml + * - Generates the specified number of objects + * - Applies any override values to all generated objects + * - Ensures all objects conform to the API schema + * + * @example + * // Generate 3 patron objects + * const patrons = buildSampleObjects({ + * object: 'patron', + * count: 3 + * }); + * + * @example + * // Generate 2 items with specific library + * const items = buildSampleObjects({ + * object: 'item', + * values: { library_id: 'CPL' }, + * count: 2 + * }); + */ const buildSampleObjects = ({ object, values, count = 1 }) => { const yamlPath = `api/v1/swagger/definitions/${object}.yaml`; if (!fs.existsSync(yamlPath)) { @@ -168,6 +251,43 @@ const buildSampleObjects = ({ object, values, count = 1 }) => { return generatedObject; }; +/** + * Builds a single sample object based on OpenAPI schema definitions. + * + * @function buildSampleObject + * @param {Object} params - Configuration parameters + * @param {string} params.object - Object type to generate (must match YAML file name) + * @param {Object} [params.values={}] - Override values for specific fields + * @returns {Object} Generated object conforming to API schema + * @throws {Error} When object type is not supported or generation fails + * @description This is a convenience function that generates a single object + * by calling buildSampleObjects with count=1 and returning the first result. + * + * Supported object types include: + * - patron: Library patron/borrower + * - item: Bibliographic item + * - biblio: Bibliographic record + * - library: Library/branch + * - hold: Hold/reservation request + * - checkout: Circulation checkout + * - vendor: Acquisitions vendor + * - basket: Acquisitions basket + * - And others as defined in api/v1/swagger/definitions/ + * + * @example + * // Generate a single patron + * const patron = buildSampleObject({ object: 'patron' }); + * + * @example + * // Generate an item with specific values + * const item = buildSampleObject({ + * object: 'item', + * values: { + * barcode: '12345678', + * home_library_id: 'CPL' + * } + * }); + */ const buildSampleObject = ({ object, values = {} }) => { return buildSampleObjects({ object, values })[0]; }; diff --git a/t/cypress/plugins/readYamlFile.js b/t/cypress/plugins/readYamlFile.js index 369650c44d5..321904c875a 100644 --- a/t/cypress/plugins/readYamlFile.js +++ b/t/cypress/plugins/readYamlFile.js @@ -1,7 +1,38 @@ +/** + * YAML File Reading Utilities for Cypress Testing + * + * This module provides utilities for reading and parsing YAML files, + * primarily used for loading OpenAPI schema definitions during test + * data generation. + * + * @module readYamlFile + */ + const path = require("path"); const fs = require("fs"); const yaml = require("yaml"); +/** + * Reads and parses a YAML file. + * + * @function readYamlFile + * @param {string} filePath - Path to the YAML file (relative or absolute) + * @returns {Object} Parsed YAML content as a JavaScript object + * @throws {Error} When file doesn't exist or YAML parsing fails + * @description This function: + * - Resolves the file path to an absolute path + * - Checks if the file exists before attempting to read + * - Reads the file content as UTF-8 text + * - Parses the YAML content into a JavaScript object + * + * @example + * // Read an OpenAPI schema definition + * const patronSchema = readYamlFile('api/v1/swagger/definitions/patron.yaml'); + * + * @example + * // Read a configuration file + * const config = readYamlFile('./config/test-config.yaml'); + */ const readYamlFile = filePath => { const absolutePath = path.resolve(filePath); if (!fs.existsSync(absolutePath)) { -- 2.50.1