From a4ec6d5fc0ac3bfb38508a7814b912bcedc11e58 Mon Sep 17 00:00:00 2001 From: foogabob Date: Sat, 15 Jul 2023 12:30:56 -0400 Subject: [PATCH] Migrates https://github.com/DickinsonCollege/FD2School-FarmData2/pull/209 --- .../dbtest/dbtest.get.seedings.spec.js | 102 ++++++++ .../fd2_example/dbtest/dbtest.html | 48 ++++ .../fd2_example/dbtest/dbtest.maps.spec.js | 106 ++++++++ .../dbtest/dbtest.modify.seedings.spec.js | 240 ++++++++++++++++++ .../dbtest/dbtest.sessionToken.spec.js | 68 +++++ 5 files changed, 564 insertions(+) create mode 100644 farmdata2/farmdata2_modules/fd2_example/dbtest/dbtest.get.seedings.spec.js create mode 100644 farmdata2/farmdata2_modules/fd2_example/dbtest/dbtest.html create mode 100644 farmdata2/farmdata2_modules/fd2_example/dbtest/dbtest.maps.spec.js create mode 100644 farmdata2/farmdata2_modules/fd2_example/dbtest/dbtest.modify.seedings.spec.js create mode 100644 farmdata2/farmdata2_modules/fd2_example/dbtest/dbtest.sessionToken.spec.js diff --git a/farmdata2/farmdata2_modules/fd2_example/dbtest/dbtest.get.seedings.spec.js b/farmdata2/farmdata2_modules/fd2_example/dbtest/dbtest.get.seedings.spec.js new file mode 100644 index 0000000000..baa3954e4d --- /dev/null +++ b/farmdata2/farmdata2_modules/fd2_example/dbtest/dbtest.get.seedings.spec.js @@ -0,0 +1,102 @@ +/** + * The tests in this file illustrate how to get seeding logs from + * the database using the functions in the FarmOSAPI library and then + * make assertions about their contents. + */ + +/* + * The dayjs library can be imported into a test using the following + * line. This is particularly useful for conversions between dates + * and time stamps. + */ +const dayjs = require('dayjs') + +var FarmOSAPI = require("../../resources/FarmOSAPI.js") +var getRecord = FarmOSAPI.getRecord +var getAllPages = FarmOSAPI.getAllPages + +describe("Examples of getting seeding logs via the FarmOSAPI functions in a test", () => { + + beforeEach(() => { + cy.login("manager1", "farmdata2") + + /* + * Load any maps (see dbtest.maps.spec) or get a session token + * (see dbtest.sessionToken.spec.js) as necessary here. + */ + + cy.visit("/farm/fd2-example/dbTest") + cy.waitForPage() + }) + + /** + * If you have the id of a log it can be retrieved using the getRecord + * function from the FarmOSAPI library. + */ + it("Get a seeding log by its id", () => { + + /* + * Request the log with id=6. This is a direct seeding of Radish in + * CHUAU-2 on February 04, 2019. + */ + cy.wrap(getRecord("/log.json?id=6")).as("get-log") + + /* + * Wait for the promise returned from getRecord to resolve. + */ + cy.get("@get-log") + .then((response) => { + + /* + * Once the response is received we can make assertions about the values + * in the response to verify that it is as expected. + * + * To know the structure and content of the log it is helpful to access + * the URL in a browser or through a tool such as Hoppscotch or postman + * and inspect the JSON directly. + */ + expect(response.data.list[0].movement.area[0].name).to.equal("CHUAU-2") + expect(response.data.list[0].type).to.equal("farm_seeding") + expect(response.data.list[0].log_category[0].name).to.equal("Direct Seedings") + }) + }) + + /** + * If you want all of the logs in a date range, they can be retrieved + * using the getAllPAges function from the FarmOSAPI library. + */ + it("Get all of the seeding logs in a date range", () => { + + // Get the start and end timestamps for the date range we want. + let start = dayjs("2020-05-01","YYYY-MM-DD").unix() + // Add 1 day here to get to the end of May 5th. + let end = dayjs("2020-05-05","YYYY-MM-DD").add(1,'day').unix() + + /* + * Request all of the farm seedings between 2020-05-01 and 2020-05-05. There + * were 14 seeding in this date range. + */ + let url = "/log.json?type=farm_seeding×tamp[gt]="+start+"×tamp[lt]="+end + let seedingLogs = [] + cy.wrap(getAllPages(url, seedingLogs)).as("get-logs") + + /* + * Wait for the promise returned from getAllPages to resolve. + */ + cy.get("@get-logs") + .then((response) => { + + /* + * Once the response is received we can make assertions about the values + * in the array that was filled with the results to verify that they are + * as expected. + * + * To know the structure and content of the array it is helpful to access + * the URL in a browser or through a tool such as Hoppscotch or postman + * and inspect the JSON directly. + */ + + expect(seedingLogs.length).to.equal(14) + }) + }) +}) \ No newline at end of file diff --git a/farmdata2/farmdata2_modules/fd2_example/dbtest/dbtest.html b/farmdata2/farmdata2_modules/fd2_example/dbtest/dbtest.html new file mode 100644 index 0000000000..8f73b9bfde --- /dev/null +++ b/farmdata2/farmdata2_modules/fd2_example/dbtest/dbtest.html @@ -0,0 +1,48 @@ +
+ +

Unlike other sample modules, this sample module does not have content here. + Instead, the dbTest sample module consists of a number of .spec.js + files that illustrate common patterns used in tests that + interact with the database. The contents of those files should be reviewed + their comments read and studied. In addition, the tests can be run in + the Cypress Test environment.

+ +

Each of the files has comments describing what they are doing. It is recommended + to study the files in the order below as later examples may not include comments + describing things that are documented in an earlier example.

+
    +
  1. dbtest.maps.spec.js
  2. : Shows how to get maps (e.g. userToIDMap) from the database and use them in tests. +
  3. dbtest.get.seedings.js
  4. : Shows how to get seeding logs from the database and use them in tests. +
  5. dbtest.sessionToken.spec.js
  6. : Shows how to get a session token needed for operations that modify the database. +
  7. dbtest.modify.seedings.spec.js
  8. : Shows how to create, delete and update seeding logs in the database. +
+ + +
{{ pageLoaded }}
+
+ + \ No newline at end of file diff --git a/farmdata2/farmdata2_modules/fd2_example/dbtest/dbtest.maps.spec.js b/farmdata2/farmdata2_modules/fd2_example/dbtest/dbtest.maps.spec.js new file mode 100644 index 0000000000..10b1b30b2d --- /dev/null +++ b/farmdata2/farmdata2_modules/fd2_example/dbtest/dbtest.maps.spec.js @@ -0,0 +1,106 @@ +/** + * The tests in this file show how to get a map from the FarmOSAPI and + * use it in tests. This can be useful when the one of the maps is + * needed to translate between ids and names in a test (e.g. from + * crop id to crop name or vice versa). + */ + +/** + * Tests that interact with the database will often make use of the + * Functions in the FarmOSAPI library to access and modify the database. + * + * The lines below illustrate how functions from the FarmOSAPI library + * can be brought into a spec.js file for testing. + * + * The farmdata2/README.md contains information about the documentation + * for all of the functions in the FarmOSAPI library. + */ +var FarmOSAPI = require('../../resources/FarmOSAPI.js') +var getUserToIDMap = FarmOSAPI.getUserToIDMap + +describe('Example of getting a map to via the FarmOSAPI functions in a test', () => { + + /* + * If multiple tests in the same describe will be using the userToIDMap + * then define a variable, like userMap here, and set it in the beforeEach + * as illustrated below. This variable will then be accessible in all of the + * tests (its). + * + * If the map is only needed in a single test (it) then the same approach can + * be used within that test (it) and this variable will not be necessary. + */ + let userMap = null + + beforeEach(() => { + // Log in + cy.login("manager1", "farmdata2") + .then (() => { + + /* + * Once we are logged in we can Use the getUserToIDMap() function from the FarmOSAPI library + * to get the map. + * + * The getUserToIDMap() function makes an API call to farmOS to get the data and create the map. + * This function returns a promise that resolves to the map when the API call returns. + * + * The map will not be available until the promise returned by the getUserToIDMap() + * resolves. So we need to wait for that promise to resolve. + * The cy.wrap(...).as(...) command is how we setup to wait for a promise to resolve in Cypress. + * + * The name "get-map" used here is just an identifier that describes what we are waiting on. + * It is used below when we actually wait for the promise to resolve. + */ + cy.wrap(getUserToIDMap()).as("get-map") + }) + + /* + * We use cy.get(...).then(...) in Cypress to wait for a promise that has + * been wrapped to resolve. + * + * Here we wait for the promise wrapped as "get-map" above. + */ + cy.get("@get-map") + .then((map) => { + /* + * The parameter map will be set to the map from the promise. Here we assign + * it to the userMap variable so that it will be accessible to all of the it + * tests. + */ + userMap = map + }) + + // Visit the page that will be tested. + cy.visit("/farm/fd2-example/dbTest") + + /* + * Almost always the page being visited will load maps and other + * content in its created() hook. If the page follows the pattern + * used in FarmData2 pages (see Maps.html for an example with explanation) + * then this causes the tests to wait here until the entire page and + * all of its data is loaded before continuing. + */ + cy.waitForPage() + }) + + /** + * All tests can then use the userToIDMap that was assigned to the + * userMap variable in the beforeEach. + */ + it("Use the map in a test", () => { + + /* + * The tests can then use the userMap in tests. + * + * The expect(...) function is like the .should(...) statement. While + * .should(...) is used to make assertions about elements found with cy.get(...), + * expect(...) is used to make assertions about variables. + * + * For every assertion that can be made with .should(...) there is an equivalent + * assertion for use with expect(...). For a complete reference see: + * https://docs.cypress.io/guides/references/assertions + */ + expect(userMap).to.not.be.null + expect(userMap.get("manager1")).to.equal("3") + expect(userMap.get("worker1")).to.equal("5") + }) +}) \ No newline at end of file diff --git a/farmdata2/farmdata2_modules/fd2_example/dbtest/dbtest.modify.seedings.spec.js b/farmdata2/farmdata2_modules/fd2_example/dbtest/dbtest.modify.seedings.spec.js new file mode 100644 index 0000000000..78f7b7f679 --- /dev/null +++ b/farmdata2/farmdata2_modules/fd2_example/dbtest/dbtest.modify.seedings.spec.js @@ -0,0 +1,240 @@ +/** + * The tests in this file show how to create new seeing logs, delete + * seeding logs and modify existing seeding logs. + */ +const dayjs = require('dayjs') + +var FarmOSAPI = require('../../resources/FarmOSAPI.js') +var deleteRecord = FarmOSAPI.deleteRecord +var getSessionToken = FarmOSAPI.getSessionToken +var createRecord = FarmOSAPI.createRecord + +describe('Example of creating and deleting seeding logs in a test', () => { + + /* + * We are going to be modifying the database so we will need a session token. + * We will also be using the session token in multiple tests (its). So we define + * a variable here that will be set in the beforeEach(). + */ + let sessionToken = null + + beforeEach(() => { + // Log in + cy.login("manager1", "farmdata2") + .then (() => { + cy.wrap(getSessionToken()).as("get-token") + }) + + cy.get("@get-token").then((token) => { + sessionToken = token + }) + + cy.visit("/farm/fd2-example/dbTest") + cy.waitForPage() + }) + + /** + * A context allows us to have a beforeEach and an afterEach just for this test. + * + * The beforeEach in the context will run before the it in the context. It can be used + * to create a log that is used for testing. + * + * The afterEach in the context will run after the it in the context. It can be used + * it to delete any logs that were created for the test or during the test. + */ + context("Create a new log, do some tests, delete the log(s) ", () => { + + let logID = null + + /** + * This beforeEach will run before the it in this context. It is used to + * create a new log. The id of the log is saved in the logID variable + * declared in the context so that it can be deleted in the afterEach. + */ + beforeEach(() => { + cy.wrap(makeDirectSeeding("Test Seeding")).as("make-seeding") + //cy.wrap(makeDirectSeeding("Test Seeding")).as("make-seeding") + + cy.get("@make-seeding") + .then((response) => { + logID = response.data.id + }) + }) + + /** + * Do your testing in this it. + */ + it("Do your test using the new log(s) here.", () => { + /* + * This test can be changed to expect(true).to.equal(true) + * in order to check that the record is still deleted by the + * afterEach() even if the test fails. + */ + expect(true).to.equal(true) + }) + + /** + * Delete the log created in the beforeEach so that the database + * is back to where it started. + */ + afterEach(() => { + cy.wrap(deleteRecord("/log/"+logID, sessionToken)).as("delete-seeding") + cy.get("@delete-seeding") + }) + }) + + /** + * This function will return a promise that creates a new Direct Seeding log. + */ + function makeDirectSeeding(name) { + let json = { + "name": name, + "type": "farm_seeding", + "timestamp": dayjs("1999-01-01").unix(), + "done": "1", //any seeding recorded is done. + "notes": { + "value": "This is a test direct seeding", + "format": "farm_format" + }, + "asset": [{ + "id": "6", //Associated planting + "resource": "farm_asset" + }], + "log_category": [{ + "id": "240", + "resource": "taxonomy_term" + }], + "movement": { + "area": [{ + "id": "180", + "resource": "taxonomy_term" + }] + }, + "quantity": [ + { + "measure": "length", + "value": "10", //total row feet + "unit": { + "id": "20", + "resource": "taxonomy_term" + }, + "label": "Amount planted" + }, + { + "measure": "ratio", + "value": "20", + "unit": { + "id": "38", + "resource": "taxonomy_term" + }, + "label": "Rows/Bed" + }, + { + "measure": "time", + "value": "1.23", + "unit": { + "id": "29", + "resource": "taxonomy_term" + }, + "label": "Labor" + }, + { + "measure": "count", + "value": "30", + "unit": { + "id": "15", + "resource": "taxonomy_term" + }, + "label": "Workers" + }, + ], + "created": dayjs().unix(), + "lot_number": "N/A (No Variety)", + "data": "{\"crop_tid\": \"161\"}" + } + + return createRecord('/log', json, sessionToken) + } + + /** + * This function will return a promise that creates a new Tray Seeding log. + */ + function makeTraySeeding(name) { + let json = { + "name": name, + "type": "farm_seeding", + "timestamp": dayjs("1999-01-01").unix(), + "done": "1", //any seeding recorded is done. + "notes": { + "value": "This is a test tray seeding", + "format": "farm_format" + }, + "asset": [{ + "id": "6", //Associated planting + "resource": "farm_asset" + }], + "log_category": [{ + "id": "241", + "resource": "taxonomy_term" + }], + "movement": { + "area": [{ + "id": "180", + "resource": "taxonomy_term" + }] + }, + "quantity": [ + { + "measure": "count", + "value": "10", //number of seed planted + "unit": { + "id": "17", + "resource": "taxonomy_term" + }, + "label": "Seeds planted" + }, + { + "measure": "count", + "value": "20", //number of flats(trays) + "unit": { + "id": "12", + "resource": "taxonomy_term" + }, + "label": "Flats used" + }, + { + "measure": "ratio", + "value": "30", //cells per flat + "unit": { + "id": "37", + "resource": "taxonomy_term" + }, + "label": "Cells/Flat" + }, + { + "measure": "time", + "value": "1.23", //hours worked + "unit": { + "id": "29", + "resource": "taxonomy_term" + }, + "label": "Labor" + }, + { + "measure": "count", + "value": "40", + "unit": { + "id": "15", + "resource": "taxonomy_term" + }, + "label": "Workers" + }, + ], + "created": dayjs().unix(), + "lot_number": "N/A (No Variety)", + "data": "{\"crop_tid\": \"161\"}" + } + + return createRecord('/log', json, sessionToken) + } +}) \ No newline at end of file diff --git a/farmdata2/farmdata2_modules/fd2_example/dbtest/dbtest.sessionToken.spec.js b/farmdata2/farmdata2_modules/fd2_example/dbtest/dbtest.sessionToken.spec.js new file mode 100644 index 0000000000..7efeea7692 --- /dev/null +++ b/farmdata2/farmdata2_modules/fd2_example/dbtest/dbtest.sessionToken.spec.js @@ -0,0 +1,68 @@ +/** + * Any test that modify the database (create logs, modify logs, delete logs) + * will need to have a session token. The session token ensures that the user + * has permission to modify the database. + * + * The sample code in this file illustrates how tests can get a session token. + */ + +var FarmOSAPI = require("../../resources/FarmOSAPI.js") +var getSessionToken = FarmOSAPI.getSessionToken + +describe("Illustration of how to get a session token.", () => { + + /* + * If multiple tests in the same describe will be using the FarmOSAPI functions + * to modify the database then define a variable here and set it in the + * beforeEach as illustrated below. + */ + let sessionToken = null; + + beforeEach(() => { + // Log in as a user with permissions to modify the database. + cy.login("manager1", "farmdata2") + .then(() => { + /* + * Once we are logged in, use the getSessionToken() function from the + * FarmOSAPI library makes an API call to farmOS to get a session token + * as the logged in user. + * + * The cy.wrap(...).as(...) allows us to wait (just below) for the + * API request for the session token to complete before we continue and + * try to use the sessionToken variable. + * + * The name "get-token" is just an identifier for the wrap that we will use + * below when we wait for the API call to complete. + */ + cy.wrap(getSessionToken()).as("get-token") + }) + + /* + * This cy.get uses the name "get-token" from the cy.wrap above to wait here + * until the session token is returned from the API request. When the session + * token is returned it is assigned to the sessionToken variable defined above. + */ + cy.get("@get-token").then((token) => { + sessionToken = token + }) + + // Visit the page that will be tested. + cy.visit("/farm/fd2-example/dbTest") + cy.waitForPage() + }) + + /** + * Any it can then use the sessionToken variable to modify the database. + */ + it("Check that the sessionToken variable is not null", () => { + /* + * The tests would then use the sessionToken in calls to the functions + * in the FarmOSAPI library as needed. The examples in the other .spec.js + * files in this module illustrate those use cases. + * + * Here we test that the sessionToken variable is not null just to show + * that it was set. + */ + expect(sessionToken).to.not.be.null + }) +}) \ No newline at end of file